blob: 081c8b23be9a7b1c33c265602cbc996b91d0d6d8 [file] [log] [blame]
/* update-editor.c --- a 'pipe' editor that intercepts dir_delta()'s
* drive of the wc update editor.
*
* ====================================================================
* Copyright (c) 2000-2002 CollabNet. All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://subversion.tigris.org/license-1.html.
* If newer versions of this license are posted there, you may use a
* newer version instead, at your option.
*
* This software consists of voluntary contributions made by many
* individuals. For exact contribution history, see the revision
* history and logs, available at http://subversion.tigris.org/.
* ====================================================================
*/
#include <string.h>
#include "apr_pools.h"
#include "apr_file_io.h"
#include "svn_pools.h"
#include "svn_error.h"
#include "svn_path.h"
#include "svn_delta.h"
#include "svn_fs.h"
#include "svn_repos.h"
#include "ra_local.h"
/*** Editor batons. ***/
struct edit_baton
{
/* Private pool for allocating my own batons and doing path telescoping. */
apr_pool_t *pool;
/* The active RA session; important, because it contains the open fs. */
svn_ra_local__session_baton_t *session;
/* Location in fs where where the edit will begin. */
svn_stringbuf_t *base_path;
/* A cached root object for the revision we're updating to (will be
set by set_target_revision) */
svn_fs_root_t *root;
};
/* NOTE: There are no custom dir or file batons defined here; instead,
the svn_pipe_dir_baton and svn_pipe_file_baton have (void *) fields
that will simply point to a telescoping svn_stringbuf_t path. */
/*** Helpers ***/
/* Stolen from ra_dav:fetch.c. :-) */
typedef svn_error_t * (*prop_setter_t) (void *baton,
svn_stringbuf_t *name,
svn_stringbuf_t *value);
/* Fetch any 'entry props' for ROOT/PATH. Then depending on the value
of IS_DIR, push these properties to REAL_EDITOR (using REAL_BATON)
via change_file_prop() or change_dir_prop(). */
static svn_error_t *
send_entry_props (svn_fs_root_t *root,
const svn_string_t *path,
const svn_delta_edit_fns_t *real_editor,
void *real_baton,
svn_boolean_t is_dir,
apr_pool_t *pool)
{
svn_revnum_t committed_rev;
svn_string_t *committed_date, *last_author;
svn_stringbuf_t *name, *value;
prop_setter_t pset_func;
char *revision_str = NULL;
apr_pool_t *subpool = svn_pool_create (pool);
if (is_dir)
pset_func = real_editor->change_dir_prop;
else
pset_func = real_editor->change_file_prop;
/* At this time, there are exactly three pieces of fs-specific
information we want to fetch and send via propsets. This
list might grow, however. */
SVN_ERR (svn_repos_get_committed_info (&committed_rev,
&committed_date,
&last_author,
root, path, subpool));
/* A root/path will always have a "created rev" field. */
revision_str = apr_psprintf (subpool, "%ld", committed_rev);
name = svn_stringbuf_create (SVN_PROP_ENTRY_COMMITTED_REV, subpool);
value = svn_stringbuf_create (revision_str, subpool);
SVN_ERR ((*pset_func) (real_baton, name, value));
/* Give the update editor either real or NULL values for the date
and author props. */
name = svn_stringbuf_create (SVN_PROP_ENTRY_COMMITTED_DATE, subpool);
if (committed_date)
value = svn_stringbuf_create_from_string (committed_date, subpool);
else
value = NULL;
SVN_ERR ((*pset_func) (real_baton, name, value));
name = svn_stringbuf_create (SVN_PROP_ENTRY_LAST_AUTHOR, subpool);
if (last_author)
value = svn_stringbuf_create_from_string (last_author, subpool);
else
value = NULL;
SVN_ERR ((*pset_func) (real_baton, name, value));
svn_pool_destroy (subpool);
return SVN_NO_ERROR;
}
/*** Custom editor functions ***/
static svn_error_t *
set_target_revision (void *edit_baton, svn_revnum_t target_revision)
{
struct svn_pipe_edit_baton *eb = edit_baton;
struct edit_baton *my_eb = (struct edit_baton *) eb->my_baton;
/* Call the real update editor. */
SVN_ERR ((* (eb->real_editor->set_target_revision)) (eb->real_edit_baton,
target_revision));
/* Make our own edit_baton's root object from the target revision. */
SVN_ERR (svn_fs_revision_root (&(my_eb->root),
my_eb->session->fs, target_revision,
my_eb->pool));
return SVN_NO_ERROR;
}
static svn_error_t *
open_root (void *edit_baton,
svn_revnum_t base_revision,
void **root_baton)
{
struct svn_pipe_edit_baton *eb = edit_baton;
struct edit_baton *my_eb = (struct edit_baton *) eb->my_baton;
struct svn_pipe_dir_baton *d = apr_pcalloc (eb->pool, sizeof (*d));
d->edit_baton = eb;
d->parent_dir_baton = NULL;
/* Call the "real" open_root. */
SVN_ERR ((* (eb->real_editor->open_root)) (eb->real_edit_baton,
base_revision,
&(d->real_dir_baton)));
/* set the pipe_dir_baton's void pointer to a path. */
d->my_baton = svn_string_create_from_buf (my_eb->base_path, my_eb->pool);
/* fetch & send entry props for this path. */
SVN_ERR (send_entry_props (my_eb->root,
d->my_baton, /* path */
eb->real_editor, d->real_dir_baton,
TRUE, /* is_dir */
my_eb->pool));
*root_baton = d;
return SVN_NO_ERROR;
}
static svn_error_t *
open_directory (svn_stringbuf_t *name,
void *parent_baton,
svn_revnum_t base_revision,
void **child_baton)
{
struct svn_pipe_dir_baton *d = parent_baton;
struct svn_pipe_dir_baton *child =
apr_pcalloc (d->edit_baton->pool, sizeof (*child));
struct edit_baton *my_eb = (struct edit_baton *) d->edit_baton->my_baton;
/* ### can toss when svn_path has more svn_string_t ops. */
svn_stringbuf_t *pathbuf;
child->edit_baton = d->edit_baton;
child->parent_dir_baton = d;
/* Call the "real" open_directory. */
SVN_ERR ((* (d->edit_baton->real_editor->open_directory))
(name, d->real_dir_baton, base_revision, &(child->real_dir_baton)));
/* set the pipe_dir_baton's void pointer to a path. */
pathbuf =
svn_stringbuf_create_from_string (child->parent_dir_baton->my_baton,
my_eb->pool);
svn_path_add_component (pathbuf, name);
child->my_baton = svn_string_create_from_buf (pathbuf, my_eb->pool);
/* fetch & send entry props for this path. */
SVN_ERR (send_entry_props (my_eb->root,
child->my_baton, /* path */
d->edit_baton->real_editor, child->real_dir_baton,
TRUE, /* is_dir */
my_eb->pool));
*child_baton = child;
return SVN_NO_ERROR;
}
static svn_error_t *
add_directory (svn_stringbuf_t *name,
void *parent_baton,
svn_stringbuf_t *copyfrom_path,
svn_revnum_t copyfrom_revision,
void **child_baton)
{
struct svn_pipe_dir_baton *d = parent_baton;
struct svn_pipe_dir_baton *child =
apr_pcalloc (d->edit_baton->pool, sizeof (*child));
struct edit_baton *my_eb = (struct edit_baton *) d->edit_baton->my_baton;
/* ### can toss when svn_path has more svn_string_t ops. */
svn_stringbuf_t *pathbuf;
child->edit_baton = d->edit_baton;
child->parent_dir_baton = d;
/* Call the "real" add_directory. */
SVN_ERR ((* (d->edit_baton->real_editor->add_directory))
(name, d->real_dir_baton, copyfrom_path, copyfrom_revision,
&(child->real_dir_baton)));
/* set the pipe_dir_baton's void pointer to a path. */
pathbuf =
svn_stringbuf_create_from_string (child->parent_dir_baton->my_baton,
my_eb->pool);
svn_path_add_component (pathbuf, name);
child->my_baton = svn_string_create_from_buf (pathbuf, my_eb->pool);
/* fetch & send entry props for this path. */
SVN_ERR (send_entry_props (my_eb->root,
child->my_baton, /* path */
d->edit_baton->real_editor, child->real_dir_baton,
TRUE, /* is_dir */
my_eb->pool));
*child_baton = child;
return SVN_NO_ERROR;
}
static svn_error_t *
add_file (svn_stringbuf_t *name,
void *parent_baton,
svn_stringbuf_t *copyfrom_path,
svn_revnum_t copyfrom_revision,
void **file_baton)
{
struct svn_pipe_dir_baton *d = parent_baton;
struct svn_pipe_file_baton *fb
= apr_pcalloc (d->edit_baton->pool, sizeof (*fb));
struct edit_baton *my_eb = (struct edit_baton *) d->edit_baton->my_baton;
/* ### can toss when svn_path has more svn_string_t ops. */
svn_stringbuf_t *pathbuf;
fb->dir_baton = d;
/* Call the "real" add_file. */
SVN_ERR ((* (d->edit_baton->real_editor->add_file))
(name, d->real_dir_baton, copyfrom_path,
copyfrom_revision, &(fb->real_file_baton)));
/* set the pipe_file_baton's void pointer to a path. */
pathbuf = svn_stringbuf_create_from_string (fb->dir_baton->my_baton,
my_eb->pool);
svn_path_add_component (pathbuf, name);
fb->my_baton = svn_string_create_from_buf (pathbuf, my_eb->pool);
/* fetch & send entry props for this path. */
SVN_ERR (send_entry_props (my_eb->root,
fb->my_baton, /* path */
d->edit_baton->real_editor, fb->real_file_baton,
FALSE, /* is_dir */
my_eb->pool));
*file_baton = fb;
return SVN_NO_ERROR;
}
static svn_error_t *
open_file (svn_stringbuf_t *name,
void *parent_baton,
svn_revnum_t base_revision,
void **file_baton)
{
struct svn_pipe_dir_baton *d = parent_baton;
struct svn_pipe_file_baton *fb
= apr_pcalloc (d->edit_baton->pool, sizeof (*fb));
struct edit_baton *my_eb = (struct edit_baton *) d->edit_baton->my_baton;
/* ### can toss when svn_path has more svn_string_t ops. */
svn_stringbuf_t *pathbuf;
fb->dir_baton = d;
/* Call the "real" open_file. */
SVN_ERR ((* (d->edit_baton->real_editor->open_file))
(name, d->real_dir_baton, base_revision, &(fb->real_file_baton)));
/* set the pipe_file_baton's void pointer to a path. */
pathbuf = svn_stringbuf_create_from_string (fb->dir_baton->my_baton,
my_eb->pool);
svn_path_add_component (pathbuf, name);
fb->my_baton = svn_string_create_from_buf (pathbuf, my_eb->pool);
/* fetch & send entry props for this path. */
SVN_ERR (send_entry_props (my_eb->root,
fb->my_baton, /* path */
d->edit_baton->real_editor, fb->real_file_baton,
FALSE, /* is_dir */
my_eb->pool));
*file_baton = fb;
return SVN_NO_ERROR;
}
/*** Public interface. ***/
svn_error_t *
svn_ra_local__get_update_pipe_editor (svn_delta_edit_fns_t **editor,
struct svn_pipe_edit_baton **edit_baton,
const svn_delta_edit_fns_t *update_editor,
void *update_edit_baton,
svn_ra_local__session_baton_t *session,
svn_stringbuf_t *base_path,
apr_pool_t *pool)
{
svn_delta_edit_fns_t *e;
struct svn_pipe_edit_baton *eb;
struct edit_baton *my_eb;
/* Create a 'pipe' editor that wraps around the original update editor: */
svn_delta_old_default_pipe_editor (&e, &eb,
update_editor, update_edit_baton, pool);
/* The default pipe editor just makes direct calls to the
update-editor; but we want to swap in 6 of our own functions
which will send extra entry-props. */
e->set_target_revision = set_target_revision;
e->open_root = open_root;
e->open_directory = open_directory;
e->open_file = open_file;
e->add_directory = add_directory;
e->add_file = add_file;
/* Set up our -private- edit baton. */
my_eb = apr_pcalloc (pool, sizeof(*my_eb));
my_eb->pool = pool;
my_eb->base_path = base_path;
my_eb->session = session;
/* Insert our private edit baton into the public one. */
eb->my_baton = my_eb;
/* Return the pipe editor. */
*edit_baton = eb;
*editor = e;
return SVN_NO_ERROR;
}
/*
* local variables:
* eval: (load-file "../../tools/dev/svn-dev.el")
* end:
*/