#include <string.h>
#include "svn_private_config.h"
#include "svn_pools.h"
#include "svn_error.h"
#include "svn_fs.h"
#include "svn_repos.h"
#include "svn_string.h"
#include "svn_time.h"
#include "svn_sorts.h"
#include "svn_path.h"
#include "svn_props.h"
#include "repos.h"
#include <assert.h>
static svn_error_t *
get_time(apr_time_t *tm,
svn_fs_t *fs,
svn_revnum_t rev,
apr_pool_t *pool)
{
svn_string_t *date_str;
SVN_ERR(svn_fs_revision_prop(&date_str, fs, rev, SVN_PROP_REVISION_DATE,
pool));
if (! date_str)
return svn_error_createf
(SVN_ERR_FS_GENERAL, NULL,
_("Failed to find time on revision %ld"), rev);
SVN_ERR(svn_time_from_cstring(tm, date_str->data, pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_repos_dated_revision(svn_revnum_t *revision,
svn_repos_t *repos,
apr_time_t tm,
apr_pool_t *pool)
{
svn_revnum_t rev_mid, rev_top, rev_bot, rev_latest;
apr_time_t this_time;
svn_fs_t *fs = repos->fs;
SVN_ERR(svn_fs_youngest_rev(&rev_latest, fs, pool));
rev_bot = 0;
rev_top = rev_latest;
while (rev_bot <= rev_top)
{
rev_mid = (rev_top + rev_bot) / 2;
SVN_ERR(get_time(&this_time, fs, rev_mid, pool));
if (this_time > tm)
{
apr_time_t previous_time;
if ((rev_mid - 1) < 0)
{
*revision = 0;
break;
}
SVN_ERR(get_time(&previous_time, fs, rev_mid - 1, pool));
if (previous_time <= tm)
{
*revision = rev_mid - 1;
break;
}
rev_top = rev_mid - 1;
}
else if (this_time < tm)
{
apr_time_t next_time;
if ((rev_mid + 1) > rev_latest)
{
*revision = rev_latest;
break;
}
SVN_ERR(get_time(&next_time, fs, rev_mid + 1, pool));
if (next_time > tm)
{
*revision = rev_mid;
break;
}
rev_bot = rev_mid + 1;
}
else
{
*revision = rev_mid;
break;
}
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_repos_get_committed_info(svn_revnum_t *committed_rev,
const char **committed_date,
const char **last_author,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
svn_fs_t *fs = svn_fs_root_fs(root);
svn_string_t *committed_date_s, *last_author_s;
SVN_ERR(svn_fs_node_created_rev(committed_rev, root, path, pool));
SVN_ERR(svn_fs_revision_prop(&committed_date_s, fs, *committed_rev,
SVN_PROP_REVISION_DATE, pool));
SVN_ERR(svn_fs_revision_prop(&last_author_s, fs, *committed_rev,
SVN_PROP_REVISION_AUTHOR, pool));
*committed_date = committed_date_s ? committed_date_s->data : NULL;
*last_author = last_author_s ? last_author_s->data : NULL;
return SVN_NO_ERROR;
}
svn_error_t *
svn_repos_history(svn_fs_t *fs,
const char *path,
svn_repos_history_func_t history_func,
void *history_baton,
svn_revnum_t start,
svn_revnum_t end,
svn_boolean_t cross_copies,
apr_pool_t *pool)
{
return svn_repos_history2(fs, path, history_func, history_baton,
NULL, NULL,
start, end, cross_copies, pool);
}
svn_error_t *
svn_repos_history2(svn_fs_t *fs,
const char *path,
svn_repos_history_func_t history_func,
void *history_baton,
svn_repos_authz_func_t authz_read_func,
void *authz_read_baton,
svn_revnum_t start,
svn_revnum_t end,
svn_boolean_t cross_copies,
apr_pool_t *pool)
{
svn_fs_history_t *history;
apr_pool_t *oldpool = svn_pool_create(pool);
apr_pool_t *newpool = svn_pool_create(pool);
const char *history_path;
svn_revnum_t history_rev;
svn_fs_root_t *root;
if (! SVN_IS_VALID_REVNUM(start))
return svn_error_createf
(SVN_ERR_FS_NO_SUCH_REVISION, 0,
_("Invalid start revision %ld"), start);
if (! SVN_IS_VALID_REVNUM(end))
return svn_error_createf
(SVN_ERR_FS_NO_SUCH_REVISION, 0,
_("Invalid end revision %ld"), end);
if (start > end)
{
svn_revnum_t tmprev = start;
start = end;
end = tmprev;
}
SVN_ERR(svn_fs_revision_root(&root, fs, end, pool));
if (authz_read_func)
{
svn_boolean_t readable;
SVN_ERR(authz_read_func(&readable, root, path,
authz_read_baton, pool));
if (! readable)
return svn_error_create(SVN_ERR_AUTHZ_UNREADABLE, NULL, NULL);
}
SVN_ERR(svn_fs_node_history(&history, root, path, oldpool));
do
{
apr_pool_t *tmppool;
SVN_ERR(svn_fs_history_prev(&history, history, cross_copies, newpool));
if (! history)
break;
SVN_ERR(svn_fs_history_location(&history_path, &history_rev,
history, newpool));
if (history_rev < start)
break;
if (authz_read_func)
{
svn_boolean_t readable;
svn_fs_root_t *history_root;
SVN_ERR(svn_fs_revision_root(&history_root, fs,
history_rev, newpool));
SVN_ERR(authz_read_func(&readable, history_root, history_path,
authz_read_baton, newpool));
if (! readable)
break;
}
SVN_ERR(history_func(history_baton, history_path,
history_rev, newpool));
svn_pool_clear(oldpool);
tmppool = oldpool;
oldpool = newpool;
newpool = tmppool;
}
while (history);
svn_pool_destroy(oldpool);
svn_pool_destroy(newpool);
return SVN_NO_ERROR;
}
static svn_error_t *
check_readability(svn_fs_root_t *root,
const char *path,
svn_repos_authz_func_t authz_read_func,
void *authz_read_baton,
apr_pool_t *pool)
{
svn_boolean_t readable;
SVN_ERR(authz_read_func(&readable, root, path, authz_read_baton, pool));
if (! readable)
return svn_error_create(SVN_ERR_AUTHZ_UNREADABLE, NULL,
_("Unreadable path encountered; access denied"));
return SVN_NO_ERROR;
}
static svn_error_t *
check_ancestry_of_peg_path(svn_boolean_t *is_ancestor,
svn_fs_t *fs,
const char *fs_path,
svn_revnum_t peg_revision,
svn_revnum_t future_revision,
apr_pool_t *pool)
{
svn_fs_root_t *root;
svn_fs_history_t *history;
const char *path;
svn_revnum_t revision;
apr_pool_t *lastpool, *currpool;
lastpool = svn_pool_create(pool);
currpool = svn_pool_create(pool);
SVN_ERR(svn_fs_revision_root(&root, fs, future_revision, pool));
SVN_ERR(svn_fs_node_history(&history, root, fs_path, lastpool));
fs_path = NULL;
while (1)
{
apr_pool_t *tmppool;
SVN_ERR(svn_fs_history_prev(&history, history, TRUE, currpool));
if (!history)
break;
SVN_ERR(svn_fs_history_location(&path, &revision, history, currpool));
if (!fs_path)
fs_path = apr_pstrdup(pool, path);
if (revision <= peg_revision)
break;
svn_pool_clear(lastpool);
tmppool = lastpool;
lastpool = currpool;
currpool = tmppool;
}
assert(fs_path != NULL);
*is_ancestor = (history && strcmp(path, fs_path) == 0);
return SVN_NO_ERROR;
}
svn_error_t *
svn_repos_trace_node_locations(svn_fs_t *fs,
apr_hash_t **locations,
const char *fs_path,
svn_revnum_t peg_revision,
apr_array_header_t *location_revisions_orig,
svn_repos_authz_func_t authz_read_func,
void *authz_read_baton,
apr_pool_t *pool)
{
apr_array_header_t *location_revisions;
svn_revnum_t *revision_ptr, *revision_ptr_end;
svn_fs_root_t *root;
const char *path;
svn_revnum_t revision;
svn_boolean_t is_ancestor;
apr_pool_t *lastpool, *currpool;
const svn_fs_id_t *id;
assert(location_revisions_orig->elt_size == sizeof(svn_revnum_t));
if (*fs_path != '/')
fs_path = apr_pstrcat(pool, "/", fs_path, NULL);
if (authz_read_func)
{
svn_fs_root_t *peg_root;
SVN_ERR(svn_fs_revision_root(&peg_root, fs, peg_revision, pool));
SVN_ERR(check_readability(peg_root, fs_path,
authz_read_func, authz_read_baton, pool));
}
*locations = apr_hash_make(pool);
lastpool = svn_pool_create(pool);
currpool = svn_pool_create(pool);
location_revisions = apr_array_copy(pool, location_revisions_orig);
qsort(location_revisions->elts, location_revisions->nelts,
sizeof(*revision_ptr), svn_sort_compare_revisions);
revision_ptr = (svn_revnum_t *)location_revisions->elts;
revision_ptr_end = revision_ptr + location_revisions->nelts;
is_ancestor = FALSE;
while (revision_ptr < revision_ptr_end && *revision_ptr > peg_revision)
{
svn_pool_clear(currpool);
SVN_ERR(check_ancestry_of_peg_path(&is_ancestor, fs, fs_path,
peg_revision, *revision_ptr,
currpool));
if (is_ancestor)
break;
++revision_ptr;
}
revision = is_ancestor ? *revision_ptr : peg_revision;
path = fs_path;
if (authz_read_func)
{
SVN_ERR(svn_fs_revision_root(&root, fs, revision, pool));
SVN_ERR(check_readability(root, fs_path, authz_read_func,
authz_read_baton, pool));
}
while (revision_ptr < revision_ptr_end)
{
apr_pool_t *tmppool;
svn_fs_root_t *croot;
svn_revnum_t crev, srev;
const char *cpath, *spath, *remainder;
SVN_ERR(svn_fs_revision_root(&root, fs, revision, currpool));
SVN_ERR(svn_fs_closest_copy(&croot, &cpath, root, path, currpool));
if (! croot)
break;
if (authz_read_func)
{
svn_boolean_t readable;
svn_fs_root_t *tmp_root;
SVN_ERR(svn_fs_revision_root(&tmp_root, fs, revision, currpool));
SVN_ERR(authz_read_func(&readable, tmp_root, path,
authz_read_baton, currpool));
if (! readable)
{
return SVN_NO_ERROR;
}
}
crev = svn_fs_revision_root_revision(croot);
while ((revision_ptr < revision_ptr_end) && (*revision_ptr >= crev))
{
apr_hash_set(*locations, revision_ptr, sizeof(*revision_ptr),
apr_pstrdup(pool, path));
revision_ptr++;
}
SVN_ERR(svn_fs_copied_from(&srev, &spath, croot, cpath, currpool));
while ((revision_ptr < revision_ptr_end) && (*revision_ptr > srev))
revision_ptr++;
remainder = (strcmp(cpath, path) == 0) ? "" :
svn_path_is_child(cpath, path, currpool);
path = svn_path_join(spath, remainder, currpool);
revision = srev;
svn_pool_clear(lastpool);
tmppool = lastpool;
lastpool = currpool;
currpool = tmppool;
}
SVN_ERR(svn_fs_revision_root(&root, fs, revision, currpool));
SVN_ERR(svn_fs_node_id(&id, root, path, pool));
while (revision_ptr < revision_ptr_end)
{
svn_node_kind_t kind;
const svn_fs_id_t *lrev_id;
svn_pool_clear(currpool);
SVN_ERR(svn_fs_revision_root(&root, fs, *revision_ptr, currpool));
SVN_ERR(svn_fs_check_path(&kind, root, path, currpool));
if (kind == svn_node_none)
break;
SVN_ERR(svn_fs_node_id(&lrev_id, root, path, currpool));
if (! svn_fs_check_related(id, lrev_id))
break;
apr_hash_set(*locations, revision_ptr, sizeof(*revision_ptr),
apr_pstrdup(pool, path));
revision_ptr++;
}
svn_pool_destroy(lastpool);
svn_pool_destroy(currpool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_repos_get_file_revs(svn_repos_t *repos,
const char *path,
svn_revnum_t start,
svn_revnum_t end,
svn_repos_authz_func_t authz_read_func,
void *authz_read_baton,
svn_repos_file_rev_handler_t handler,
void *handler_baton,
apr_pool_t *pool)
{
apr_pool_t *iter_pool, *last_pool;
svn_fs_history_t *history;
apr_array_header_t *revnums = apr_array_make(pool, 0,
sizeof(svn_revnum_t));
apr_array_header_t *paths = apr_array_make(pool, 0, sizeof(char *));
apr_hash_t *last_props;
svn_fs_root_t *root, *last_root;
const char *last_path;
int i;
svn_node_kind_t kind;
iter_pool = svn_pool_create(pool);
last_pool = svn_pool_create(pool);
SVN_ERR(svn_fs_revision_root(&root, repos->fs, end, pool));
SVN_ERR(svn_fs_check_path(&kind, root, path, pool));
if (kind != svn_node_file)
return svn_error_createf
(SVN_ERR_FS_NOT_FILE, NULL, _("'%s' is not a file"), path);
SVN_ERR(svn_fs_node_history(&history, root, path, last_pool));
while (1)
{
const char* rev_path;
svn_revnum_t rev;
apr_pool_t *tmp_pool;
svn_pool_clear(iter_pool);
SVN_ERR(svn_fs_history_prev(&history, history, TRUE, iter_pool));
if (!history)
break;
SVN_ERR(svn_fs_history_location(&rev_path, &rev, history, iter_pool));
if (authz_read_func)
{
svn_boolean_t readable;
svn_fs_root_t *tmp_root;
SVN_ERR(svn_fs_revision_root(&tmp_root, repos->fs, rev, iter_pool));
SVN_ERR(authz_read_func(&readable, tmp_root, rev_path,
authz_read_baton, iter_pool));
if (! readable)
{
break;
}
}
*(svn_revnum_t*) apr_array_push(revnums) = rev;
*(char **) apr_array_push(paths) = apr_pstrdup(pool, rev_path);
if (rev <= start)
break;
tmp_pool = iter_pool;
iter_pool = last_pool;
last_pool = tmp_pool;
}
assert(revnums->nelts > 0);
last_root = NULL;
last_path = NULL;
last_props = apr_hash_make(last_pool);
for (i = revnums->nelts; i > 0; --i)
{
svn_revnum_t rev = APR_ARRAY_IDX(revnums, i - 1, svn_revnum_t);
const char *rev_path = APR_ARRAY_IDX(paths, i - 1, const char *);
apr_hash_t *rev_props;
apr_hash_t *props;
apr_array_header_t *prop_diffs;
svn_txdelta_stream_t *delta_stream;
svn_txdelta_window_handler_t delta_handler = NULL;
void *delta_baton = NULL;
apr_pool_t *tmp_pool;
svn_boolean_t contents_changed;
svn_pool_clear(iter_pool);
SVN_ERR(svn_fs_revision_proplist(&rev_props, repos->fs,
rev, iter_pool));
SVN_ERR(svn_fs_revision_root(&root, repos->fs, rev, iter_pool));
SVN_ERR(svn_fs_node_proplist(&props, root, rev_path, iter_pool));
SVN_ERR(svn_prop_diffs(&prop_diffs, props, last_props, pool));
if (last_root)
SVN_ERR(svn_fs_contents_changed(&contents_changed,
last_root, last_path,
root, rev_path, iter_pool));
else
contents_changed = TRUE;
SVN_ERR(handler(handler_baton, rev_path, rev, rev_props,
contents_changed ? &delta_handler : NULL,
contents_changed ? &delta_baton : NULL,
prop_diffs, iter_pool));
if (delta_handler)
{
SVN_ERR(svn_fs_get_file_delta_stream(&delta_stream,
last_root, last_path,
root, rev_path,
iter_pool));
SVN_ERR(svn_txdelta_send_txstream(delta_stream,
delta_handler, delta_baton,
iter_pool));
}
last_root = root;
last_path = rev_path;
last_props = props;
tmp_pool = iter_pool;
iter_pool = last_pool;
last_pool = tmp_pool;
}
svn_pool_destroy(last_pool);
svn_pool_destroy(iter_pool);
return SVN_NO_ERROR;
}