
Contents of /Subversion-0.112/ra.c:
/* ================================================================
* Copyright (c) 2000-2004 CollabNet. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowledgment: "This product includes
* software developed by CollabNet (http://www.Collab.Net/)."
* Alternately, this acknowledgment may appear in the software itself, if
* and wherever such third-party acknowledgments normally appear.
*
* 4. The hosted project names must not be used to endorse or promote
* products derived from this software without prior written
* permission. For written permission, please contact info@collab.net.
*
* 5. Products derived from this software may not use the "Tigris" name
* nor may "Tigris" appear in their names without prior written
* permission of CollabNet.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 COLLABNET OR ITS 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.
*
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of CollabNet.
*/
#include "svnmod.h"
#include <svn_ra.h>
#include <svn_path.h>
/*! @module Subversion */
/*! @module RA
*!
*! The RA (Repository Access) submodule provides direct access to the
*! RA layer of Subversion.
*/
/* Internal module structure */
struct ra_obj {
void *ra_baton;
apr_pool_t *pool;
};
#define THIS_RA ((struct ra_obj *)(Pike_fp->current_storage))
static void init_ra_obj (struct object *o)
{
struct ra_obj *ra = THIS_RA;
ra->ra_baton = NULL;
ra->pool = NULL;
}
static void exit_ra_obj (struct object *o)
{
struct ra_obj *ra = THIS_RA;
if (ra->pool) {
apr_pool_destroy (ra->pool);
ra->pool = NULL;
}
}
static void init_ra_baton (struct ra_obj *ra)
{
if (!ra->pool)
ra->pool = svn_pool_create (NULL);
if (!ra->ra_baton) {
svn_error_t *err = svn_ra_init_ra_libs (&ra->ra_baton, ra->pool);
if (err) {
svn_pike_set_error (err);
pike_throw ();
}
}
}
/*! @class Library
*!
*! A class which encapsulates all the functionality of a particular
*! repository-access implementation.
*/
/* Internal object structure */
struct lib_obj {
apr_pool_t *pool;
svn_ra_plugin_t *plugin;
/*! @decl string name
*!
*! The proper name of the RA library, (e.g. "ra_dav" or "ra_local").
*/
struct pike_string *name;
/*! @decl string description
*!
*! Short doc string.
*/
struct pike_string *description;
};
#define THIS_LIB ((struct lib_obj *)(Pike_fp->current_storage))
static struct program *ra_lib_program;
static ptrdiff_t ra_lib_ident, ra_lib_offs;
static void init_lib_obj (struct object *o)
{
struct lib_obj *lib = THIS_LIB;
lib->plugin = NULL;
lib->pool = NULL;
}
static void exit_lib_obj (struct object *o)
{
struct lib_obj *lib = THIS_LIB;
if (lib->pool) {
apr_pool_destroy (lib->pool);
lib->pool = NULL;
}
}
static void f_ra_lib_sprintf (INT32 args)
{
INT_TYPE mode;
struct mapping *opts;
get_all_args ("_sprintf", args, "%i%m", &mode, &opts);
pop_n_elems (args);
if (mode == 'O' && THIS_LIB->name) {
push_constant_text ("%O(%s)");
ref_push_object (Pike_fp->current_object);
f_object_program (1);
ref_push_string (THIS_LIB->name);
f_sprintf (3);
} else
push_undefined ();
}
/*! @class Session
*!
*! A repository access session against a particular repository
*/
/* Internal object structure */
struct session_obj {
apr_pool_t *pool;
void *session_baton;
svn_ra_plugin_t *plugin;
svn_ra_callbacks_t cb;
struct pike_string *url;
struct object *callbacks;
struct array *auth_providers;
struct mapping *config;
};
#define THIS ((struct session_obj *)(Pike_fp->current_storage))
static struct program *ra_session_program;
static ptrdiff_t ra_session_ident, ra_session_offs;
/*! @class Callbacks
*!
*! An interface for providing some interaction methods between the
*! RA layer and the client application.
*/
static apr_status_t svn_pike_cleanup_obj (void *arg)
{
do_free_object (arg);
return APR_SUCCESS;
}
/*! @decl Stdio.File open_tmp_file( )
*!
*! Open a unique temporary file for writing in the working copy.
*! This file will be automatically deleted when closed.
*/
static svn_error_t *open_tmp_file_stub (apr_file_t **fp, void *baton,
apr_pool_t *pool)
{
JMP_BUF recovery;
svn_error_t *err = SVN_NO_ERROR;
if(fp) *fp = NULL;
do {
struct thread_state *_tmp=thread_state_for_id (th_self ());
HIDE_GLOBAL_VARIABLES ();
THREADS_DISALLOW ();
if (SETJMP (recovery)) {
err = svn_pike_make_svn_error (&throw_value);
} else {
apr_os_file_t osfile;
struct object *o = ((struct session_obj *)baton)->callbacks;
if (!o)
Pike_error ("No callback object installed!\n");
apply (o, "open_tmp_file", 0);
osfile = svn_pike_check_file_arg ("open_tmp_file", 1, 0);
if (osfile == NO_FILE_SPECIFIED)
Pike_error ("No file returned from open_tmp_file()\n");
if (apr_os_file_put (fp, &osfile, APR_READ | APR_WRITE | APR_DELONCLOSE,
pool))
Pike_error ("Failed to open file\n");
apr_pool_cleanup_register (pool, Pike_sp[-1].u.object,
svn_pike_cleanup_obj, NULL);
Pike_sp--;
}
UNSETJMP (recovery);
THREADS_ALLOW ();
} while (0);
return err;
}
/*! @decl string get_wc_prop( string path, string name )
*!
*! Fetch working copy properties.
*!
*! @param path
*! Path to the node for which to fetch properties, relative to the
*! the root of the session.
*! @param name
*! The name of the property to fetch.
*!
*! @returns
*! The property value, or 0 if the property is not present.
*/
static svn_error_t *get_wc_prop_stub (void *baton,
const char *relpath, const char *name,
const svn_string_t **value,
apr_pool_t *pool)
{
JMP_BUF recovery;
svn_error_t *err = SVN_NO_ERROR;
if(value) *value = NULL;
do {
struct thread_state *_tmp=thread_state_for_id (th_self ());
HIDE_GLOBAL_VARIABLES ();
THREADS_DISALLOW ();
if (SETJMP (recovery)) {
err = svn_pike_make_svn_error (&throw_value);
} else {
struct object *o = ((struct session_obj *)baton)->callbacks;
if (o) {
svn_pike_push_utf8 (relpath);
svn_pike_push_utf8 (name);
apply (o, "get_wc_prop", 2);
if (UNSAFE_IS_ZERO (Pike_sp-1))
;
else if (Pike_sp[-1].type == PIKE_T_STRING) {
struct pike_string *ret;
if (svn_prop_needs_translation (name))
ret = svn_pike_to_utf8 (Pike_sp[-1].u.string);
else if (Pike_sp[-1].u.string->size_shift)
Pike_error ("Binary property can't contain wide characters.\n");
else
copy_shared_string (ret, Pike_sp[-1].u.string);
if(value && ret)
*value = svn_string_ncreate (APR_STR0 (ret), ret->len, pool);
do_free_string(ret);
} else
Pike_error ("get_wc_prop must return a string\n");
pop_stack ();
}
}
UNSETJMP (recovery);
THREADS_ALLOW ();
} while (0);
return err;
}
/*! @decl void set_wc_prop( string path, string name, string value )
*!
*! This is a function which allows the RA layer to store new
*! working copy properties during update-like operations.
*!
*! @param path
*! Path to the node for which to store properties, relative to the
*! the root of the session.
*! @param name
*! The name of the property to store.
*! @param value
*! The new value to store for the property, or 0 if the property
*! should be deleted.
*/
static svn_error_t *set_wc_prop_stub (void *baton,
const char *path, const char *name,
const svn_string_t *value,
apr_pool_t *pool)
{
JMP_BUF recovery;
svn_error_t *err = SVN_NO_ERROR;
do {
struct thread_state *_tmp=thread_state_for_id (th_self ());
HIDE_GLOBAL_VARIABLES ();
THREADS_DISALLOW ();
if (SETJMP (recovery)) {
err = svn_pike_make_svn_error (&throw_value);
} else {
struct object *o = ((struct session_obj *)baton)->callbacks;
if (o) {
svn_pike_push_utf8 (path);
svn_pike_push_utf8 (name);
if (value) {
push_string (make_shared_binary_string (value->data, value->len));
if (svn_prop_needs_translation (name))
f_utf8_to_string (1);
} else
push_int (0);
apply (o, "set_wc_prop", 3);
pop_stack ();
}
}
UNSETJMP (recovery);
THREADS_ALLOW ();
} while (0);
return err;
}
/*! @decl void push_wc_prop( string path, string name, string value )
*!
*! This is a function which allows the RA layer to store new
*! working copy properties as part of a commit.
*!
*! Note that this might not actually store the new property before
*! returning, but instead schedule it to be changed as part of
*! post-commit processing (in which case a successful commit means the
*! properties got written). Thus, during the commit, it is possible
*! to invoke this function to set a new value for a wc prop, then read
*! the wc prop back from the working copy and get the *old* value.
*!
*! @param path
*! Path to the node for which to store properties, relative to the
*! the root of the session.
*! @param name
*! The name of the property to store.
*! @param value
*! The new value to store for the property, or 0 if the property
*! should be deleted.
*/
static svn_error_t *push_wc_prop_stub (void *baton,
const char *path, const char *name,
const svn_string_t *value,
apr_pool_t *pool)
{
JMP_BUF recovery;
svn_error_t *err = SVN_NO_ERROR;
do {
struct thread_state *_tmp=thread_state_for_id (th_self ());
HIDE_GLOBAL_VARIABLES ();
THREADS_DISALLOW ();
if (SETJMP (recovery)) {
err = svn_pike_make_svn_error (&throw_value);
} else {
struct object *o = ((struct session_obj *)baton)->callbacks;
if (o) {
svn_pike_push_utf8 (path);
svn_pike_push_utf8 (name);
if (value) {
push_string (make_shared_binary_string (value->data, value->len));
if (svn_prop_needs_translation (name))
f_utf8_to_string (1);
} else
push_int (0);
apply (o, "push_wc_prop", 3);
pop_stack ();
}
}
UNSETJMP (recovery);
THREADS_ALLOW ();
} while (0);
return err;
}
/*! @decl void invalidate_wc_props( string path, string name )
*!
*! This is a function which allows the RA layer to invalidate
*! (i.e., remove) working copy properties. Unlike @[push_wc_prop],
*! this has immediate effect. If it returns success, the properties
*! have been removed.
*!
*! @param path
*! Path to the node for which to remove properties, relative to the
*! the root of the session.
*! @param name
*! The name of the property to remove.
*/
static svn_error_t *invalidate_wc_props_stub (void *baton,
const char *path,
const char *name,
apr_pool_t *pool)
{
JMP_BUF recovery;
svn_error_t *err = SVN_NO_ERROR;
do {
struct thread_state *_tmp=thread_state_for_id (th_self ());
HIDE_GLOBAL_VARIABLES ();
THREADS_DISALLOW ();
if (SETJMP (recovery)) {
err = svn_pike_make_svn_error (&throw_value);
} else {
struct object *o = ((struct session_obj *)baton)->callbacks;
if (o) {
svn_pike_push_utf8 (path);
svn_pike_push_utf8 (name);
apply (o, "invalidate_wc_props", 2);
pop_stack ();
}
}
UNSETJMP (recovery);
THREADS_ALLOW ();
} while (0);
return err;
}
/*! @endclass */
static void init_session_obj (struct object *o)
{
struct session_obj *s = THIS;
s->plugin = NULL;
s->pool = NULL;
s->session_baton = NULL;
s->cb.open_tmp_file = open_tmp_file_stub;
s->cb.get_wc_prop = get_wc_prop_stub;
s->cb.set_wc_prop = set_wc_prop_stub;
s->cb.push_wc_prop = push_wc_prop_stub;
s->cb.invalidate_wc_props = invalidate_wc_props_stub;
s->cb.auth_baton = NULL;
}
static void exit_session_obj (struct object *o)
{
struct session_obj *s = THIS;
if (s->pool) {
apr_pool_destroy (s->pool);
s->pool = NULL;
}
}
static void f_ra_session_sprintf (INT32 args)
{
INT_TYPE mode;
struct mapping *opts;
get_all_args ("_sprintf", args, "%i%m", &mode, &opts);
pop_n_elems (args);
if (mode == 'O') {
push_constant_text ("%O(%O, %O, %O, %O)");
ref_push_object (Pike_fp->current_object);
f_object_program (1);
if (THIS->url)
ref_push_string (THIS->url);
else
push_int (0);
if (THIS->callbacks)
ref_push_object (THIS->callbacks);
else
push_int (0);
if (THIS->auth_providers)
ref_push_array (THIS->auth_providers);
else
push_int (0);
if (THIS->config)
ref_push_mapping (THIS->config);
else
push_int (0);
f_sprintf (6);
} else
push_undefined ();
}
/*! @class CommitEditor
*!
*! An editor for committing changes to the repository
*/
/*! @decl inherit Delta.DeltaEditor */
/* Internal object structure */
struct commiteditor_obj {
struct svalue callback;
};
static struct program *ra_commiteditor_program;
static ptrdiff_t ra_commiteditor_ident, ra_commiteditor_offs;
#define THIS_COMMITEDITOR ((struct commiteditor_obj *)(Pike_fp->current_storage + ra_commiteditor_offs))
#define THIS_DELTAEDITOR ((struct deltaeditor_obj *)(Pike_fp->current_storage))
/* Stub for calling the commit callback */
static svn_error_t *commit_callback_stub (svn_revnum_t new_revision,
const char *date,
const char *author,
void *baton)
{
JMP_BUF recovery;
svn_error_t *err = SVN_NO_ERROR;
struct commiteditor_obj *ed = baton;
if (ed->callback.type == PIKE_T_INT)
return err;
do {
struct thread_state *_tmp=thread_state_for_id (th_self ());
HIDE_GLOBAL_VARIABLES ();
THREADS_DISALLOW ();
if (SETJMP (recovery)) {
err = svn_pike_make_svn_error (&throw_value);
} else {
push_int (new_revision);
svn_pike_push_utf8 (date);
svn_pike_push_utf8 (author);
apply_svalue (&ed->callback, 3);
pop_stack ();
}
UNSETJMP (recovery);
THREADS_ALLOW ();
} while (0);
return err;
}
/*! @decl void create( string log_msg, function(int,string,string:void)|void callback )
*!
*! Create an editor for committing changes to the repository.
*!
*! @param log_msg
*! The log message for the commit.
*! @param callback
*! The commit callback
*!
*! @seealso
*! @[get_commit_editor()]
*/
static void f_ra_commiteditor_create (INT32 args)
{
struct deltaeditor_obj *de = THIS_DELTAEDITOR;
struct commiteditor_obj *ed = THIS_COMMITEDITOR;
struct object *sess_obj = LOW_PARENT_INFO(Pike_fp->current_object,
ra_commiteditor_program)->parent;
struct session_obj *s;
struct pike_string *msg;
char *msg_copy;
svn_error_t *err;
if (sess_obj)
s = (struct session_obj *)(get_storage (sess_obj, ra_session_program)
+ ra_session_offs);
else
Pike_error ("Can't get parent object for Subversion.RA.Session.CommitEditor!\n");
get_all_args ("get_commit_editor", args, "%W", &msg);
if (args > 1)
assign_svalue (&ed->callback, &Pike_sp[1-args]);
msg = svn_pike_to_utf8 (msg);
de->pool = svn_pool_create (s->pool);
msg_copy = apr_pstrdup (de->pool, APR_STR0 (msg) );
do_free_string (msg);
THREADS_ALLOW ();
err = (*s->plugin->get_commit_editor) (s->session_baton, &de->editor,
&de->editor_baton, msg_copy,
commit_callback_stub, ed,
de->pool);
THREADS_DISALLOW ();
if (err) {
svn_pike_set_error (err);
pike_throw ();
} else
pop_n_elems (args);
}
/*! @endclass */
/*! @class Reporter
*!
*! A class for reporting current revision information
*/
/* Internal object structure */
struct reporter_obj {
apr_pool_t *pool;
const svn_ra_reporter_t *reporter;
void *report_baton;
};
static struct program *ra_reporter_program;
static ptrdiff_t ra_reporter_ident;
#define THIS_REPORTER ((struct reporter_obj *)(Pike_fp->current_storage))
static void init_reporter_obj (struct object *o)
{
struct reporter_obj *rep = THIS_REPORTER;
rep->pool = NULL;
rep->reporter = NULL;
rep->report_baton = NULL;
}
static void exit_reporter_obj (struct object *o)
{
struct reporter_obj *rep = THIS_REPORTER;
if (rep->pool) {
apr_pool_destroy (rep->pool);
rep->pool = NULL;
}
rep->reporter = NULL;
rep->report_baton = NULL;
}
/*! @decl void set_path( string path, int revision, int|void start_empty )
*!
*! Describe a working copy @[path] as being at a particular @[revision].
*!
*! This will @b{override@} any previous @[set_path()] calls made on
*! parent paths.
*!
*! @param path
*! Path of the entry to describe, relative to the URL specified
*! in @[open()].
*! @param revision
*! The current revision for this entry.
*! @param start_empty
*! If non-zero and @[path] is a directory, the implementor should
*! assume the directory has no entries or props.
*/
static void f_set_path (INT32 args)
{
struct reporter_obj *rep = THIS_REPORTER;
struct pike_string *path;
INT_TYPE rev;
svn_boolean_t start_empty;
svn_error_t *err;
if(!rep->reporter)
Pike_error("Reporter is not open.\n");
get_all_args ("set_path", args, "%W%i", &path, &rev);
start_empty = svn_pike_check_force_arg ("set_path", args, 2);
path = svn_pike_to_utf8 (path);
THREADS_ALLOW ();
err = (*rep->reporter->set_path) (rep->report_baton,
svn_path_canonicalize (APR_STR0 (path),
rep->pool),
rev, start_empty, rep->pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
do_free_string (path);
if (err)
pike_throw();
else
pop_n_elems (args);
}
/*! @decl void delete_path( string path )
*!
*! Describing a working copy @[path] as missing.
*!
*! @param path
*! Path of the entry which is missing, relative to the URL
*! specified in @[open()].
*/
static void f_delete_path (INT32 args)
{
struct reporter_obj *rep = THIS_REPORTER;
struct pike_string *path;
svn_error_t *err;
if(!rep->reporter)
Pike_error("Reporter is not open.\n");
get_all_args ("delete_path", args, "%W", &path);
path = svn_pike_to_utf8 (path);
THREADS_ALLOW ();
err = (*rep->reporter->delete_path) (rep->report_baton,
svn_path_canonicalize (APR_STR0 (path),
rep->pool),
rep->pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
do_free_string (path);
if (err)
pike_throw();
else
pop_n_elems (args);
}
/*! @decl void link_path( string path, string URL, int revision, @
*! int|void start_empty )
*! Like @[set_path()], but differs in that @[path] in the working copy
*! (relative to the root of the report driver) isn't a reflection of
*! @[path] in the repository (relative to the URL specified when
*! opening the RA layer), but is instead a reflection of a different
*! repository @[URL] at @[revision].
*!
*! @param path
*! Path in the working copy (relative to the root of the report driver).
*! @param URL
*! Repository URL of the repository item that this working copy
*! entry reflects.
*! @param revision
*! Revision of @[URL] that this working copy entry reflects.
*! @param start_empty
*! If non-zero and @[path] is a directory, the implementor should
*! assume the directory has no entries or props.
*/
static void f_link_path (INT32 args)
{
struct reporter_obj *rep = THIS_REPORTER;
struct pike_string *path, *url;
INT_TYPE rev;
svn_boolean_t start_empty;
svn_error_t *err;
if(!rep->reporter)
Pike_error("Reporter is not open.\n");
get_all_args ("link_path", args, "%W%W%i", &path, &url, &rev);
start_empty = svn_pike_check_force_arg ("link_path", args, 3);
path = svn_pike_to_utf8 (path);
url = svn_pike_to_utf8 (url);
THREADS_ALLOW ();
err = (*rep->reporter->link_path) (rep->report_baton,
svn_path_canonicalize (APR_STR0 (path),
rep->pool),
APR_STR0 (url), rev, start_empty, rep->pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
do_free_string (path);
do_free_string (url);
if (err)
pike_throw();
else
pop_n_elems (args);
}
/*! @decl void finish_report( )
*!
*! WC calls this when the state report is finished; any directories
*! or files not explicitly `set' above are assumed to be at the
*! baseline revision originally passed into @[do_update()].
*/
static void f_finish_report (INT32 args)
{
struct reporter_obj *rep = THIS_REPORTER;
svn_error_t *err;
if(!rep->reporter)
Pike_error("Reporter is not open.\n");
THREADS_ALLOW ();
err = (*rep->reporter->finish_report) (rep->report_baton, rep->pool);
THREADS_DISALLOW ();
if (err) {
svn_pike_set_error (err);
pike_throw();
} else
pop_n_elems (args);
}
/*! @decl void abort_report( )
*!
*! If an error occurs during a report, this routine should cause the
*! filesystem transaction to be aborted & cleaned up.
*/
static void f_abort_report (INT32 args)
{
struct reporter_obj *rep = THIS_REPORTER;
svn_error_t *err;
if(!rep->reporter)
Pike_error("Reporter is not open.\n");
THREADS_ALLOW ();
err = (*rep->reporter->abort_report) (rep->report_baton, rep->pool);
THREADS_DISALLOW ();
if (err) {
svn_pike_set_error (err);
pike_throw();
} else
pop_n_elems (args);
}
/*! @endclass */
static apr_status_t svn_pike_cleanup_svalue (void *arg)
{
do_free_svalue (arg);
return APR_SUCCESS;
}
/* Stub for calling FileRevHandlers */
static svn_error_t *file_rev_handler_stub(void *baton, const char *path,
svn_revnum_t rev, apr_hash_t *rprops,
svn_txdelta_window_handler_t *dh,
void **dh_baton,
apr_array_header_t *prop_diffs,
apr_pool_t *pool)
{
JMP_BUF recovery;
svn_error_t *err = SVN_NO_ERROR;
struct svalue *sv = baton;
if (sv->type == PIKE_T_INT)
return err;
do {
struct thread_state *_tmp=thread_state_for_id (th_self ());
HIDE_GLOBAL_VARIABLES ();
THREADS_DISALLOW ();
if (SETJMP (recovery)) {
err = svn_pike_make_svn_error (&throw_value);
} else {
int i;
const svn_prop_t *props = (const svn_prop_t *) prop_diffs->elts;
int nprop = prop_diffs->nelts;
svn_pike_push_utf8 (path);
push_int (rev);
svn_pike_make_propmap (Pike_sp, rprops, -1);
Pike_sp ++;
for (i = 0; i < nprop; i ++) {
svn_pike_push_utf8 (props[i].name);
if (props[i].value == NULL)
push_int (0);
else {
push_string (make_shared_binary_string (props[i].value->data,
props[i].value->len));
if (svn_prop_needs_translation (props[i].name))
f_utf8_to_string (1);
}
}
f_aggregate_mapping (2 * nprop);
apply_svalue (sv, 4);
if (Pike_sp[-1].type != PIKE_T_INT) {
struct svalue *sv2 = apr_palloc (pool, sizeof (*sv2));
assign_svalue_no_free (sv2, &Pike_sp[-1]);
apr_pool_cleanup_register (pool, sv, svn_pike_cleanup_svalue, NULL);
*dh = svn_pike_window_handler_stub;
*dh_baton = sv2;
}
pop_stack ();
}
UNSETJMP (recovery);
THREADS_ALLOW ();
} while (0);
return err;
}
/*! @decl int get_latest_revnum( )
*!
*! Get the latest revision number from the repository. This is
'! useful for the `svn status' command. :)
*!
*! @returns
*! The number of the latest revision in the repository.
*/
static void f_get_latest_revnum (INT32 args)
{
struct session_obj *s = THIS;
svn_error_t *err;
svn_revnum_t rev = 0;
apr_pool_t *pool;
pool = svn_pool_create (s->pool);
THREADS_ALLOW ();
err = (*s->plugin->get_latest_revnum) (s->session_baton, &rev, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
apr_pool_destroy (pool);
if (err)
pike_throw ();
else {
pop_n_elems (args);
push_int (rev);
}
}
/*! @decl int get_dated_revision( int time )
*!
*! Get the latest revision number at time @[time].
*!
*! @param time
*! A UNIX timestamp.
*!
*! @returns
*! The number of the latest revision in the repository at
*! the specified point in time.
*/
static void f_get_dated_revision (INT32 args)
{
struct session_obj *s = THIS;
svn_error_t *err;
svn_revnum_t rev = 0;
apr_os_imp_time_t ostime, *otp = &ostime;
apr_time_t atime = 0;
INT_TYPE ptime = 0;
apr_pool_t *pool;
get_all_args ("get_dated_revision", args, "%i", &ptime);
pool = svn_pool_create (s->pool);
ostime.tv_sec = ptime;
ostime.tv_usec = 0;
apr_os_imp_time_put (&atime, &otp, pool);
THREADS_ALLOW ();
err = (*s->plugin->get_dated_revision) (s->session_baton, &rev, atime, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
apr_pool_destroy (pool);
if (err)
pike_throw ();
else {
pop_n_elems (args);
push_int (rev);
}
}
/*! @decl void change_rev_prop( int revnum, string name, string value )
*!
*! Set the property @[name] to @[value] on revision @[revnum].
*! Please note that properties attached to revisions are @b{unversioned@}.
*!
*! @param revnum
*! The number of the revision to set the property on.
*! @param name
*! The name of the property to set.
*! @param value
*! The value to set the property to.
*/
static void f_change_rev_prop (INT32 args)
{
struct session_obj *s = THIS;
svn_error_t *err;
INT_TYPE rev;
struct pike_string *propname, *propval;
svn_string_t propval_string;
apr_pool_t *pool;
get_all_args ("change_rev_prop", args, "%i%W%W", &rev, &propname, &propval);
propname = svn_pike_to_utf8 (propname);
if (propval) {
if (svn_prop_needs_translation (APR_STR0(propname)))
propval = svn_pike_to_utf8 (propval);
else
reference_shared_string (propval);
if (propval->size_shift) {
do_free_string (propname);
do_free_string (propval);
Pike_error("Binary property can't contain wide characters.\n");
}
propval_string.data = APR_STR0 (propval);
propval_string.len = propval->len;
}
pool = svn_pool_create (s->pool);
THREADS_ALLOW ();
err = (*s->plugin->change_rev_prop) (s->session_baton, rev,
APR_STR0(propname), &propval_string, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
apr_pool_destroy (pool);
do_free_string (propname);
do_free_string (propval);
if (err)
pike_throw ();
else
pop_n_elems (args);
}
/*! @decl mapping(string:string) rev_proplist( int revnum )
*!
*! Get all revision properties for revision @[revnum].
*!
*! @param revnum
*! The number of the revision to get the properties for.
*!
*! @returns
*! A mapping from property name to property value.
*/
static void f_rev_proplist (INT32 args)
{
struct session_obj *s = THIS;
svn_error_t *err;
apr_hash_t *props = NULL;
INT_TYPE rev;
struct svalue ret;
apr_pool_t *pool;
get_all_args ("rev_proplist", args, "%i", &rev);
pool = svn_pool_create (s->pool);
THREADS_ALLOW ();
err = (*s->plugin->rev_proplist) (s->session_baton, rev, &props, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
else
svn_pike_make_propmap (&ret, props, -1);
apr_pool_destroy (pool);
if (err)
pike_throw ();
else {
pop_n_elems (args);
*Pike_sp++ = ret;
}
}
/*! @decl string rev_prop( int revnum, string name )
*!
*! Get the revision property @[name] for revision @[revnum].
*!
*! @param revnum
*! The number of the revision to get the property for.
*! @param name
*! The name of the property to get.
*!
*! @returns
*! The property value, or 0 the the property is not set for this revision.
*/
static void f_rev_prop (INT32 args)
{
struct session_obj *s = THIS;
svn_error_t *err;
INT_TYPE rev;
struct pike_string *propname;
svn_string_t *propval_string = NULL;
apr_pool_t *pool;
get_all_args ("rev_prop", args, "%i%W", &rev, &propname);
propname = svn_pike_to_utf8 (propname);
pool = svn_pool_create (s->pool);
THREADS_ALLOW ();
err = (*s->plugin->rev_prop) (s->session_baton, rev,
APR_STR0(propname), &propval_string, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
else {
pop_n_elems (args);
if (propval_string) {
push_string (make_shared_binary_string (propval_string->data,
propval_string->len));
if (svn_prop_needs_translation (APR_STR0(propname)))
f_utf8_to_string (1);
} else
push_int (0);
}
apr_pool_destroy (pool);
do_free_string (propname);
if (err)
pike_throw();
}
/*! @decl CommitEditor get_commit_editor( string log_msg, function(int,string,string:void)|void callback )
*!
*! Create a @[CommitEditor] for committing changes to the repository,
*! using @[log_msg] as the log message. The revisions being committed
*! against are passed to the editor functions, starting with the rev
*! argument to open_root. The path root of the commit is in the
*! Session's url.
*!
*! @param log_msg
*! The log message for the commit.
*! @param callback
*! A callback that will be called if the commit succeeds. The arguments
*! are as follows:
*! @array
*! @elem int new_revision
*! The number of the revision created by the commit.
*! @elem string date
*! The datestamp of the commit.
*! @elem string author
*! The author name recorded in the commit.
*! @endarray
*/
static void f_get_commit_editor (INT32 args)
{
push_object (parent_clone_object (ra_commiteditor_program,
Pike_fp->current_object,
ra_commiteditor_ident +
Pike_fp->context.identifier_level,
args));
}
/*! @decl array(int|mapping) get_file( string path, int revnum, @
*! Stream|void stream )
*!
*! Push the contents of file @[path] at @[revnum] into an existing
*! @[stream].
*!
*! @param path
*! Path to fetch the contents for, interpreted relative to the URL
*! of this Session.
*! @param revnum
*! The number of the revision to fetch (-1 means "head").
*! @param stream
*! The stream to write the file contents to. If not specified (or zero),
*! just fetch the properties.
*!
*! @returns
*! An array with two elements:
*! @array
*! @elem int revision
*! The actual revision number which was fetched (useful when fetching
*! "head")
*! @elem mapping(string:string) props
*! A mapping from property name to property value containing @b{all@}
*! the properties of the file in this revision, including ones generated
*! internally by the SCM system.
*! @endarray
*/
static void f_get_file (INT32 args)
{
struct session_obj *s = THIS;
svn_error_t *err;
INT_TYPE rev;
struct pike_string *path;
svn_stream_t *stream_stub;
apr_pool_t *pool;
svn_revnum_t ret_rev;
apr_hash_t *props = NULL;
struct svalue ret;
get_all_args ("get_file", args, "%W%i", &path, &rev);
svn_pike_check_stream_arg ("get_file", args, ~2, &stream_stub,
s->pool, &pool);
path = svn_pike_to_utf8 (path);
THREADS_ALLOW ();
ret_rev = rev;
err = (*s->plugin->get_file) (s->session_baton,
svn_path_canonicalize (APR_STR0(path), pool), rev,
stream_stub, &ret_rev, &props, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
else
svn_pike_make_propmap (&ret, props, -1);
apr_pool_destroy (pool);
do_free_string (path);
if (err)
pike_throw ();
else {
pop_n_elems (args);
push_int (ret_rev);
*Pike_sp++ = ret;
f_aggregate (2);
}
}
/*! @decl array(int|mapping) get_dir( string path, int revision )
*!
*! List all entries of directory @[path] at @[revision].
*!
*! @param path
*! The directory to list, interpreted relative to the url of this Session.
*! @param revision
*! The revision to list, or -1 for "head".
*!
*! @returns
*! An array with three elements:
*! @array
*! @elem int revision
*! The actual revision number which was listed (useful when listing
*! "head")
*! @elem mapping(string:string) props
*! A mapping from property name to property value containing @b{all@}
*! the properties of the directory in this revision, including ones
*! generated internally by the SCM system.
*! @elem mapping(string:array) entries
*! A mapping from entry name to entry information. The values
*! are as follows:
*! @array
*! @elem int kind
*! Node kind (dir, file), see @[check_path()].
*! @elem int size
*! Length of file text, or 0 for directories.
*! @elem int has_props
*! Non-zero if the node has properties.
*! @elem int created_rev
*! Last revision in which this node changed.
*! @elem int time
*! Time of created_rev (modification time).
*! @elem string last_author
*! Author of created_rev
*! @endarray
*! @endarray
*/
static void f_get_dir (INT32 args)
{
struct session_obj *s = THIS;
svn_error_t *err;
INT_TYPE rev;
struct pike_string *path;
svn_revnum_t ret_rev;
apr_hash_t *props = NULL, *dirents = NULL;
struct svalue ret1, ret2;
apr_pool_t *pool;
get_all_args ("get_dir", args, "%W%i", &path, &rev);
path = svn_pike_to_utf8 (path);
pool = svn_pool_create (s->pool);
THREADS_ALLOW ();
ret_rev = rev;
err = (*s->plugin->get_dir) (s->session_baton,
svn_path_canonicalize (APR_STR0(path), pool), rev,
&dirents, &ret_rev, &props, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
else {
svn_pike_make_propmap (&ret1, props, -1);
svn_pike_make_dirents (&ret2, dirents);
}
apr_pool_destroy (pool);
do_free_string (path);
if (err)
pike_throw ();
else {
pop_n_elems (args);
push_int (ret_rev);
*Pike_sp++ = ret1;
*Pike_sp++ = ret2;
f_aggregate (3);
}
}
/*! @decl Reporter do_update( Delta.DeltaEditor update_editor, @
*! int revision_to_update_to, @
*! string|void update_target, @
*! int|void non_recursive )
*!
*! Ask the network layer to update a working copy.
*!
*! The client initially provides an @[update_editor] to the
*! RA layer; this editor contains knowledge of where the change will
*! begin in the working copy (when @[Delta.DeltaEditor.open_root()]
*! is called).
*!
*! In return, the client receives a @[Reporter]. The client then
*! describes its working-copy revision numbers by making calls into
*! the @[Reporter] object; the RA layer assumes that all paths are
*! relative to the URL used to create this session.
*!
*! When finished, the client calls @[Reporter.finish_report()]. The
*! RA layer then does a complete drive of @[update_editor], ending with
*! @[Delta.DeltaEditor.close_edit()], to update the working copy.
*!
*! The caller may not perform any RA operations using this @[Session]
*! before finishing the report, and may not perform any RA operations
*! using this @[Session] from within the editing operations of
*! @[update_editor].
*!
*! @param update_editor
*! Any object implementing the @[Delta.DeltaEditor] interface.
*! The RA layer will drive this editor to perform the necessary
*! alterations to bring the working copy up to date.
*! @param revision_to_update_to
*! The working copy will be updated to this revision, or the
*! "latest" revision if this arg is invalid.
*! @param update_target
*! An optional single path component will restrict the scope of things
*! affected by the update to an entry in the directory represented by
*! the session's URL, or empty if the entire directory is meant to be
*! updated.
*! @param non_recursive
*! If non-zero, subdirectories will not be updated.
*/
static void f_do_update (INT32 args)
{
struct session_obj *s = THIS;
struct object *update_obj;
INT_TYPE revision_to_update_to;
struct pike_string *update_target = NULL;
svn_boolean_t recurse;
svn_error_t *err;
struct object *rep_obj;
struct reporter_obj *rep;
const svn_delta_editor_t *update_editor;
void *update_baton;
get_all_args ("do_update", args, (args>2? "%o%i%W":"%o%i"), &update_obj,
&revision_to_update_to, &update_target);
recurse = svn_pike_check_recurse_arg ("do_update", args, 3);
update_target = svn_pike_to_utf8 (update_target);
rep_obj = parent_clone_object (ra_reporter_program,
Pike_fp->current_object,
ra_reporter_ident +
Pike_fp->context.identifier_level,
0);
rep = (struct reporter_obj *)(get_storage (rep_obj, ra_reporter_program));
rep->pool = svn_pool_create (s->pool);
svn_pike_delta_editor_from_obj (update_obj, &update_editor, &update_baton,
rep->pool);
THREADS_ALLOW ();
err = (*s->plugin->do_update) (s->session_baton, &rep->reporter,
&rep->report_baton, revision_to_update_to,
(update_target?
svn_path_canonicalize (APR_STR0(update_target),
rep->pool) : ""),
recurse, update_editor, update_baton,
rep->pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
do_free_string (update_target);
if (err) {
free_object (rep_obj);
pike_throw ();
} else {
pop_n_elems (args);
push_object (rep_obj);
}
}
/*! @decl Reporter do_switch( Delta.DeltaEditor switch_editor, @
*! string switch_url, @
*! int revision_to_switch_to, @
*! string|void switch_target, @
*! int|void non_recursive )
*!
*! Ask the network layer to 'switch' a working copy to a new
*! @[switch_url]; it's another form of @[do_update()].
*!
*! The client initially provides a @[switch_editor] to the
*! RA layer; this editor contains knowledge of where the change will
*! begin in the working copy (when @[Delta.DeltaEditor.open_root()]
*! is called).
*!
*! In return, the client receives a @[Reporter]. The client then
*! describes its working-copy revision numbers by making calls into
*! the @[Reporter] object; the RA layer assumes that all paths are
*! relative to the URL used to create this session.
*!
*! When finished, the client calls @[Reporter.finish_report()]. The
*! RA layer then does a complete drive of @[switch_editor], ending with
*! @[Delta.DeltaEditor.close_edit()], to switch the working copy.
*!
*! The caller may not perform any RA operations using this @[Session]
*! before finishing the report, and may not perform any RA operations
*! using this @[Session] from within the editing operations of
*! @[switch_editor].
*!
*! @param switch_editor
*! Any object implementing the @[Delta.DeltaEditor] interface.
*! The RA layer will drive this editor to perform the necessary
*! alterations to switch the working copy to the new URL.
*! @param switch_url
*! The new URL for the working copy.
*! @param revision_to_switch_to
*! The working copy will be switched to this revision, or the
*! "latest" revision if this arg is invalid.
*! @param switch_target
*! An optional single path component will restrict the scope of things
*! affected by the switch to an entry in the directory represented by
*! the session's URL, or empty if the entire directory is meant to be
*! switched.
*! @param non_recursive
*! If non-zero, subdirectories will not be switched.
*/
static void f_do_switch (INT32 args)
{
struct session_obj *s = THIS;
struct object *switch_obj;
INT_TYPE revision_to_switch_to;
struct pike_string *switch_url, *switch_target = NULL;
svn_boolean_t recurse;
svn_error_t *err;
struct object *rep_obj;
struct reporter_obj *rep;
const svn_delta_editor_t *switch_editor;
void *switch_baton;
get_all_args ("do_switch", args, (args>3? "%o%W%i%W":"%o%W%i"), &switch_obj,
&switch_url, &revision_to_switch_to, &switch_target);
recurse = svn_pike_check_recurse_arg ("do_switch", args, 4);
switch_target = svn_pike_to_utf8 (switch_target);
switch_url = svn_pike_to_utf8 (switch_url);
rep_obj = parent_clone_object (ra_reporter_program,
Pike_fp->current_object,
ra_reporter_ident +
Pike_fp->context.identifier_level,
0);
rep = (struct reporter_obj *)(get_storage (rep_obj, ra_reporter_program));
rep->pool = svn_pool_create (s->pool);
svn_pike_delta_editor_from_obj (switch_obj, &switch_editor, &switch_baton,
rep->pool);
THREADS_ALLOW ();
err = (*s->plugin->do_switch) (s->session_baton, &rep->reporter,
&rep->report_baton, revision_to_switch_to,
(switch_target?
svn_path_canonicalize (APR_STR0 (switch_target),
rep->pool) : ""),
recurse,
svn_path_canonicalize (APR_STR0 (switch_url),
rep->pool),
switch_editor, switch_baton, rep->pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
do_free_string (switch_target);
do_free_string (switch_url);
if (err) {
free_object (rep_obj);
pike_throw ();
} else {
pop_n_elems (args);
push_object (rep_obj);
}
}
/*! @decl Reporter do_status( Delta.DeltaEditor status_editor, @
*! int revision, string|void status_target, @
*! int|void non_recursive )
*!
*! Ask the network layer to describe the status of a working copy
*! with respect to @[revision] of the repository (or HEAD, if
*! @[revision] is invalid).
*!
*! The client initially provides a @[status_editor] to the
*! RA layer; this editor contains knowledge of where the change will
*! begin in the working copy (when @[Delta.DeltaEditor.open_root()]
*! is called).
*!
*! In return, the client receives a @[Reporter]. The client then
*! describes its working-copy revision numbers by making calls into
*! the @[Reporter] object; the RA layer assumes that all paths are
*! relative to the URL used to create this session.
*!
*! When finished, the client calls @[Reporter.finish_report()]. The
*! RA layer then does a complete drive of @[status_editor], ending with
*! @[Delta.DeltaEditor.close_edit()], to report, essentially, what would
*! be modified in the working copy were the client to call @[do_update()].
*!
*! The caller may not perform any RA operations using this @[Session]
*! before finishing the report, and may not perform any RA operations
*! using this @[Session] from within the editing operations of
*! @[status_editor].
*!
*! @param status_editor
*! Any object implementing the @[Delta.DeltaEditor] interface.
*! The RA layer will drive this editor to describe the modifications
*! that would bring the working copy up to date.
*! @param revision
*! The revision that the working copy will be described with regard to.
*! If invalid, HEAD will be used.
*! @param status_target
*! An optional single path component will restrict the scope of
*! the status report to an entry in the directory represented by
*! the session's URL, or empty if the entire directory is meant to
*! be examined.
*! @param non_recursive
*! If non-zero, subdirectories will not be examined.
*/
static void f_do_status (INT32 args)
{
struct session_obj *s = THIS;
struct object *status_obj;
INT_TYPE revision;
struct pike_string *status_target = NULL;
svn_boolean_t recurse;
svn_error_t *err;
struct object *rep_obj;
struct reporter_obj *rep;
const svn_delta_editor_t *status_editor;
void *status_baton;
get_all_args ("do_status", args, (args>2? "%o%i%W":"%o%i"), &status_obj,
&revision, &status_target);
recurse = svn_pike_check_recurse_arg ("do_status", args, 3);
status_target = svn_pike_to_utf8 (status_target);
rep_obj = parent_clone_object (ra_reporter_program,
Pike_fp->current_object,
ra_reporter_ident +
Pike_fp->context.identifier_level,
0);
rep = (struct reporter_obj *)(get_storage (rep_obj, ra_reporter_program));
rep->pool = svn_pool_create (s->pool);
svn_pike_delta_editor_from_obj (status_obj, &status_editor, &status_baton,
rep->pool);
THREADS_ALLOW ();
err = (*s->plugin->do_status) (s->session_baton, &rep->reporter,
&rep->report_baton,
(status_target?
svn_path_canonicalize (APR_STR0(status_target),
rep->pool) : ""),
revision, recurse, status_editor,
status_baton, rep->pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
do_free_string (status_target);
if (err) {
free_object (rep_obj);
pike_throw ();
} else {
pop_n_elems (args);
push_object (rep_obj);
}
}
/*! @decl Reporter do_diff( Delta.DeltaEditor diff_editor, @
*! string versus_url, int revision, @
*! string|void diff_target, @
*! int|void non_recursive, @
*! int|void ignore_ancestry )
*!
*! Ask the network layer to 'diff' a working copy against
*! @[versus_url]; it's another form of @[do_update()].
*!
*! The client initially provides a @[diff_editor] to the
*! RA layer; this editor contains knowledge of where the common diff
*! root is in the working copy (when @[Delta.DeltaEditor.open_root()]
*! is called).
*!
*! In return, the client receives a @[Reporter]. The client then
*! describes its working-copy revision numbers by making calls into
*! the @[Reporter] object; the RA layer assumes that all paths are
*! relative to the URL used to create this session.
*!
*! When finished, the client calls @[Reporter.finish_report()]. The
*! RA layer then does a complete drive of @[diff_editor], ending with
*! @[Delta.DeltaEditor.close_edit()], to transmit the diff.
*!
*! The caller may not perform any RA operations using this @[Session]
*! before finishing the report, and may not perform any RA operations
*! using this @[Session] from within the editing operations of
*! @[diff_editor].
*!
*! @param diff_editor
*! Any object implementing the @[Delta.DeltaEditor] interface.
*! The RA layer will drive this editor to transmit the diff.
*! @param versus_url
*! The URL against which the working copy will be diffed.
*! @param revision
*! The working copy will be diffed to this revision, or the
*! head if this arg is -1.
*! @param diff_target
*! An optional single path component will restrict the scope of the
*! diff to an entry in the directory represented by the session's
*! URL, or empty if the entire directory is meant to be one of the
*! diff paths.
*! @param non_recursive
*! If non-zero, subdirectories will not be diffed.
*! @param ignore_ancestry
*! Used to control whether or not items being diffed will be
*! checked for relatedness first. Unrelated items are typically
*! transmitted to the editor as a deletion of one thing and the
*! addition of another, but if this argument is non-zero, unrelated
*! items will be diffed as if they were related.
*/
static void f_do_diff (INT32 args)
{
struct session_obj *s = THIS;
struct object *diff_obj;
INT_TYPE revision;
struct pike_string *versus_url, *diff_target = NULL;
svn_boolean_t recurse, ignore_ancestry;
svn_error_t *err;
struct object *rep_obj;
struct reporter_obj *rep;
const svn_delta_editor_t *diff_editor;
void *diff_baton;
get_all_args ("do_diff", args, (args>3? "%o%W%i%W":"%o%W%i"), &diff_obj,
&versus_url, &revision, &diff_target);
recurse = svn_pike_check_recurse_arg ("do_diff", args, 4);
ignore_ancestry = svn_pike_check_force_arg ("do_diff", args, 5);
diff_target = svn_pike_to_utf8 (diff_target);
versus_url = svn_pike_to_utf8 (versus_url);
rep_obj = parent_clone_object (ra_reporter_program,
Pike_fp->current_object,
ra_reporter_ident +
Pike_fp->context.identifier_level,
0);
rep = (struct reporter_obj *)(get_storage (rep_obj, ra_reporter_program));
rep->pool = svn_pool_create (s->pool);
svn_pike_delta_editor_from_obj (diff_obj, &diff_editor, &diff_baton,
rep->pool);
THREADS_ALLOW ();
err = (*s->plugin->do_diff) (s->session_baton, &rep->reporter,
&rep->report_baton, revision,
(diff_target?
svn_path_canonicalize (APR_STR0 (diff_target),
rep->pool) : ""),
recurse, ignore_ancestry,
svn_path_canonicalize (APR_STR0 (versus_url),
rep->pool),
diff_editor, diff_baton, rep->pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
do_free_string (diff_target);
do_free_string (versus_url);
if (err) {
free_object (rep_obj);
pike_throw ();
} else {
pop_n_elems (args);
push_object (rep_obj);
}
}
/*! @decl void get_log( array(string) paths, @
*! function(int, string, string, string, @
*! mapping(string:array(int|string))|void:void) log_func,@
*! int start, int end, int|void discover_changed_paths,@
*! int|void strict_node_history )
*!
*! Call @[log_func] on each log message from @[start] to @[end].
*! @[start] may be greater or less than @[end]; this just controls
*! whether the log messages are processed in descending or ascending
*! revision number order.
*!
*! The caller may not invoke any RA operations using this @[Session]
*! from within @[log_func].
*!
*! @param paths
*! If non-zero and has one or more elements, then only show
*! revisions in which at least one of @[paths] was changed
*! (i.e., if file, text or props changed; if dir, props changed
*! or an entry was added or deleted). Each path is relative
*! to the session's common parent.
*! @param log_func
*! This function is invoked once on each log message. The
*! parameters are as follows:
*! @array
*! @elem int revision
*! The revision this log message concerns.
*! @elem string author
*! The author that committed this revision.
*! @elem string date
*! The date when this revision was committed.
*! @elem string message
*! The log message for this revision.
*! @elem mapping(string:array(int|string)) changed_paths
*! A mapping from entry name to change information for all
*! entries that were changed in this revision (if such information
*! was requested). The values in the mapping are as follows:
*! @array
*! @elem int action
*! 'A'dd, 'D'elete, 'R'eplace, 'M'odify
*! @elem string copyfrom_path
*! Source path of copy (if any).
*! @elem int copyfrom_rev
*! Source revision of copy (if any).
*! @endarray
*! @endarray
*! @param start
*! The first revision for which log messages are desired. Use -1
*! for the youngest revision.
*! @param end
*! The last revision for which log messages are desired. Use -1
*! for the youngest revision.
*! @param discover_changed_paths
*! If set, then the changed_paths argument to log_func
*! will be passed on each invocation.
*! @param strict_node_history
*! If set, copy history will not be traversed (if any exists) when
*! harvesting the revision logs for each path.
*/
static void f_get_log (INT32 args)
{
struct session_obj *s = THIS;
struct array *targets;
apr_array_header_t *targets_aa;
svn_error_t *err;
apr_pool_t *pool;
svn_boolean_t discover_changed_paths, strict_node_history;
INT_TYPE start, end;
struct svalue *log_receiver_func;
get_all_args ("get_log", args, "%A%*%i%i", &targets, &log_receiver_func,
&start, &end);
discover_changed_paths = svn_pike_check_force_arg ("get_log", args, 3);
strict_node_history = svn_pike_check_force_arg ("get_log", args, 4);
pool = svn_pool_create (s->pool);
targets_aa = svn_pike_make_targets_array (targets, pool, TRUE);
THREADS_ALLOW ();
err = (*s->plugin->get_log) (s->session_baton, targets_aa, start, end,
discover_changed_paths, strict_node_history,
svn_pike_log_receiver_func_stub,
log_receiver_func, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
apr_pool_destroy (pool);
if (err)
pike_throw ();
else
pop_n_elems (args);
}
/*! @decl int check_path( string path, int revision )
*!
*! Get the node kind associated with @[path] at @[revision].
*! If @[path] does not exist under @[revision], return @[NODE_NONE].
*!
*! @param path
*! Path of the node to get the kind of, relative to the session's
*! parent URL.
*! @param revision
*! Revision number that the path is relative to.
*!
*! @returns
*! The kind of node:
*! @int
*! @value NODE_NONE
*! No such path exists at that revision.
*! @value NODE_FILE
*! Node is a regular file.
*! @value NODE_DIR
*! Node is a directory.
*! @value NODE_UNKNOWN
*! Something's there, but we don't know what.
*! @endint
*/
static void f_check_path (INT32 args)
{
struct session_obj *s = THIS;
svn_error_t *err;
struct pike_string *path;
INT_TYPE rev;
svn_node_kind_t kind;
apr_pool_t *pool;
get_all_args ("check_path", args, "%W%i", &path, &rev);
path = svn_pike_to_utf8 (path);
pool = svn_pool_create (s->pool);
THREADS_ALLOW ();
err = (*s->plugin->check_path) (s->session_baton,
svn_path_canonicalize (APR_STR0 (path), pool),
rev, &kind, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
apr_pool_destroy (pool);
do_free_string (path);
if (err)
pike_throw ();
else {
pop_n_elems (args);
push_int (kind);
}
}
/*! @decl string get_uuid( )
*!
*! Get the repository's UUID.
*/
static void f_get_uuid (INT32 args)
{
struct session_obj *s = THIS;
svn_error_t *err;
const char *uuid;
struct pike_string *ret = NULL;
apr_pool_t *pool;
pool = svn_pool_create (s->pool);
THREADS_ALLOW ();
err = (*s->plugin->get_uuid) (s->session_baton, &uuid, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
else
ret = make_shared_string (uuid);
apr_pool_destroy (pool);
if (err)
pike_throw ();
else {
pop_n_elems (args);
push_string (ret);
f_utf8_to_string (1);
}
}
/*! @decl string get_repos_root( )
*!
*! Get the repository's root URL. The value will not
*! include a trailing '/'.
*/
static void f_get_repos_root (INT32 args)
{
struct session_obj *s = THIS;
svn_error_t *err;
const char *url;
struct pike_string *ret = NULL;
apr_pool_t *pool;
pool = svn_pool_create (s->pool);
THREADS_ALLOW ();
err = (*s->plugin->get_repos_root) (s->session_baton, &url, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
else
ret = make_shared_string (url);
apr_pool_destroy (pool);
if (err)
pike_throw ();
else {
pop_n_elems (args);
push_string (ret);
f_utf8_to_string (1);
}
}
/*! @decl mapping(int:string) get_locations( string path, int peg_revision, @
*! array(int) location_revisions )
*!
*! Return the locations at the repository revisions @[location_revisions]
*! of the file @[path] present at the repository in revision
*! @[peg_revision].
*!
*! NOTE: For servers older than 1.1, this function will throw an
*! SVN_ERR_RA_NOT_IMPLEMENTED error.
*!
*! @param path
*! A path relative to the URL to which the RA session was opened.
*! @param peg_revision
*! The revision at which the node of interrest has this path.
*! @param location_revisions
*! An array of revision numbers.
*!
*! @returns
*! A mapping from the revisions to their appropriate absolute paths.
*! If the file doesn't exist in a location_revision, that revision will
*! be ignored.
*/
static void f_get_locations (INT32 args)
{
struct session_obj *s = THIS;
svn_error_t *err;
apr_hash_t *locs;
struct pike_string *path;
INT_TYPE peg_rev;
struct array *revs_arr;
apr_array_header_t *loc_revs;
apr_pool_t *pool;
struct svalue ret;
int i;
get_all_args ("get_locations", args, "%W%i%a", &path, &peg_rev, &revs_arr);
path = svn_pike_to_utf8 (path);
pool = svn_pool_create (s->pool);
loc_revs = apr_array_make (pool, revs_arr->size, sizeof (svn_revnum_t));
for (i = 0; i < revs_arr->size; i++)
if (ITEM (revs_arr)[i].type != PIKE_T_INT) {
apr_pool_destroy (pool);
do_free_string (path);
Pike_error ("Non-int in array\n");
} else
(*((svn_revnum_t *) apr_array_push (loc_revs))) =
ITEM (revs_arr)[i].u.integer;
THREADS_ALLOW ();
err = (*s->plugin->get_locations) (s->session_baton, &locs,
APR_STR0 (path), peg_rev, loc_revs, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
else {
apr_hash_index_t *hi;
INT32 n_items = 0;
for (hi = apr_hash_first (pool, locs); hi; hi = apr_hash_next (hi)) {
const void *idx;
void *val;
apr_hash_this (hi, &idx, NULL, &val);
push_int (*(const svn_revnum_t *) idx);
svn_pike_push_utf8 ((const char *)val);
n_items ++;
}
f_aggregate_mapping(2*n_items);
ret = *--Pike_sp;
}
apr_pool_destroy (pool);
do_free_string (path);
if (err)
pike_throw ();
else {
pop_n_elems (args);
*Pike_sp++ = ret;
}
}
/*! @decl void get_file_revs( string path, int start, int end, @
*! FileRevHandler handler )
*!
*! Retrieve a subset of the interesting revisions of a file @[path]
*! as seen in revision @[end]. Call @[handler] for each such revision.
*! See @[FileSystem.history_prev] for a discussion of interesting revisions.
*!
*! If there is an interesting revision of the file that is less than or
*! equal to @[start], the iteration will start at that revision. Else, the
*! iteration will start at the first revision of the file in the repository,
*! which has to be less than or equal to @[end]. Note that if the function
*! succeeds, @[handler] will have been called at least once.
*!
*! In a series of calls, the file contents for the first interesting revision
*! will be provided as a text delta against the empty file. In the following
*! calls, the delta will be against the contents for the previous call.
*!
*! NOTE: This functionality is not available in pre-1.1 servers. If the
*! server doesn't implement it, an SVN_ERR_RA_NOT_IMPLEMENTED error is
*! thrown.
*/
static void f_get_file_revs (INT32 args)
{
struct session_obj *s = THIS;
svn_error_t *err;
struct svalue *handler;
struct pike_string *path;
INT_TYPE start_rev, end_rev;
apr_pool_t *pool;
get_all_args ("get_file_revs", args, "%W%i%i%*", &path, &start_rev,
&end_rev, &handler);
path = svn_pike_to_utf8 (path);
pool = svn_pool_create (s->pool);
THREADS_ALLOW ();
err = (*s->plugin->get_file_revs) (s->session_baton, APR_STR0 (path),
start_rev, end_rev,
file_rev_handler_stub, handler, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
apr_pool_destroy (pool);
do_free_string (path);
if (err)
pike_throw ();
else
pop_n_elems (args);
}
/*! @decl void create( string URL, Callbacks callbacks, @
*! array(Auth.AuthProvider)|void auth_providers, @
*! mapping(string:Config)|void config )
*!
*! Create a repository access session for the repository at @[URL].
*!
*! @param URL
*! The URL of the repository.
*! @param callbacks
*! An object containing callbacks which allows an RA layer to "pull"
*! information from the client application, or possibly store information.
*! @param auth_providers
*! An ordered list of objects providing authentication credentials.
*! @param config
*! A mapping from names to @[Config]s. For example, the @[Config]
*! for the "~/.subversion/config" file is under the index "config".
*/
static void f_ra_session_create (INT32 args)
{
struct session_obj *s = THIS;
struct object *lib_obj = LOW_PARENT_INFO(Pike_fp->current_object,
ra_session_program)->parent;
struct lib_obj *lib;
struct pike_string *url;
svn_error_t *err;
apr_hash_t *config;
if (lib_obj)
lib = (struct lib_obj *)(get_storage (lib_obj, ra_lib_program)
+ ra_lib_offs);
else
Pike_error ("Can't get parent object for Subversion.RA.Session!\n");
do_free_mapping (s->config);
do_free_array (s->auth_providers);
do_free_object (s->callbacks);
do_free_string (s->url);
s->callbacks = NULL;
s->url = NULL;
s->auth_providers = NULL;
s->config = NULL;
get_all_args ("open", args, (args>3? "%W%O%A%m" :
(args>2? "%W%O%A" : "%W%O")),
&s->url, &s->callbacks, &s->auth_providers, &s->config);
if (s->url)
s->url->refs++;
if (s->callbacks)
s->callbacks->refs++;
if (s->auth_providers)
s->auth_providers = copy_array(s->auth_providers);
if (s->config)
s->config = copy_mapping(s->config);
if (!s->pool)
s->pool = svn_pool_create (lib->pool);
s->plugin = lib->plugin;
if (!s->cb.auth_baton) {
if (s->auth_providers) {
apr_array_header_t *aa =
apr_array_make (s->pool, s->auth_providers->size,
sizeof (svn_auth_provider_object_t *));
int i;
if (!aa)
Pike_error ("Out of memory.\n");
for (i=0; i<s->auth_providers->size; i++) {
svn_auth_provider_object_t *po;
if (ITEM (s->auth_providers)[i].type != PIKE_T_OBJECT ||
!(po = (svn_auth_provider_object_t *)
get_storage (ITEM (s->auth_providers)[i].u.object,
svn_pike_auth_provider_program)))
Pike_error ("Not an AuthProvider object.\n");
*(svn_auth_provider_object_t **)apr_array_push (aa) = po;
}
svn_auth_open (&s->cb.auth_baton, aa, s->pool);
} else
svn_auth_open (&s->cb.auth_baton,
apr_array_make (s->pool, 0,
sizeof (svn_auth_provider_object_t *)),
s->pool);
}
config = svn_pike_mapping_to_config_hash (s->config, s->pool);
url = svn_pike_to_utf8 (s->url);
THREADS_ALLOW ();
err = (*s->plugin->open) (&s->session_baton,
svn_path_canonicalize (APR_STR0 (url), s->pool),
&s->cb, s, config, s->pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
do_free_string (url);
if (err)
pike_throw ();
else
pop_n_elems (args);
}
/*! @endclass */
/*! @decl Session open( string URL, Session.Callbacks callbacks, @
*! array(Auth.AuthProvider)|void auth_providers, @
*! mapping(string:Config)|void config )
*!
*! Create a repository access session for the repository at @[URL].
*!
*! @param URL
*! The URL of the repository.
*! @param callbacks
*! An object containing callbacks which allows an RA layer to "pull"
*! information from the client application, or possibly store information.
*! @param auth_providers
*! An ordered list of objects providing authentication credentials.
*! @param config
*! A mapping from names to @[Config]s. For example, the @[Config]
*! for the "~/.subversion/config" file is under the index "config".
*/
static void f_ra_lib_open (INT32 args)
{
push_object (parent_clone_object (ra_session_program,
Pike_fp->current_object,
ra_session_ident +
Pike_fp->context.identifier_level,
args));
}
/*! @decl Version get_version( )
*!
*! Return the plugin's version information.
*/
static void f_ra_lib_get_version (INT32 args)
{
struct lib_obj *lib = THIS_LIB;
pop_n_elems (args);
svn_pike_push_version (lib->plugin->get_version ());
}
/*! @decl void create( string URL )
*!
*! Create a repository access library for the repository at @[URL].
*!
*! @param URL
*! The URL of the repository.
*/
static void f_ra_lib_create (INT32 args)
{
struct lib_obj *lib = THIS_LIB;
struct object *ra_obj = LOW_PARENT_INFO(Pike_fp->current_object,
ra_lib_program)->parent;
struct ra_obj *ra;
struct pike_string *url;
svn_error_t *err;
if (ra_obj)
ra = (struct ra_obj *)ra_obj->storage;
else
Pike_error ("Can't get parent object for Subversion.RA.Library!\n");
get_all_args ("get_ra_library", args, "%W", &url);
if (!ra->ra_baton)
init_ra_baton(ra);
url = svn_pike_to_utf8 (url);
if(!lib->pool)
lib->pool = svn_pool_create (ra->pool);
THREADS_ALLOW ();
err = svn_ra_get_ra_library (&lib->plugin, ra->ra_baton,
svn_path_canonicalize (APR_STR0 (url), lib->pool),
lib->pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
do_free_string (url);
if (err)
pike_throw ();
else {
pop_n_elems (args);
svn_pike_push_utf8 (lib->plugin->name);
svn_pike_push_utf8 (lib->plugin->description);
do_free_string (lib->name);
do_free_string (lib->description);
lib->description = (--Pike_sp)->u.string;
lib->name = (--Pike_sp)->u.string;
}
}
/*! @endclass */
/*! @decl Library get_ra_library( string URL )
*!
*! Create a repository access library for the repository at @[URL].
*!
*! @param URL
*! The URL of the repository.
*/
static void f_get_ra_library (INT32 args)
{
push_object (parent_clone_object (ra_lib_program, Pike_fp->current_object,
ra_lib_ident +
Pike_fp->context.identifier_level,
args));
}
/*! @decl string print_ra_libraries( )
*!
*! Returns a string that is a textual list of all available
*! RA libraries.
*/
static void f_print_ra_libraries (INT32 args)
{
apr_pool_t *pool;
svn_error_t *err;
svn_stringbuf_t *sbuf = NULL;
struct pike_string *ret = NULL;
struct ra_obj *ra = THIS_RA;
if (!ra->ra_baton)
init_ra_baton(ra);
pool = svn_pool_create (ra->pool);
THREADS_ALLOW ();
err = svn_ra_print_ra_libraries (&sbuf, ra->ra_baton, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
else if (sbuf)
ret = make_shared_binary_string (sbuf->data, sbuf->len);
apr_pool_destroy (pool);
if (err)
pike_throw ();
else {
pop_n_elems (args);
if (ret) {
push_string (ret);
f_utf8_to_string (1);
} else
push_undefined ();
}
}
/*! @decl Version version( )
*!
*! Get libsvn_ra version information.
*/
static void f_version (INT32 args)
{
pop_n_elems (args);
svn_pike_push_version (svn_ra_version ());
}
/* Initialize RA submodule */
void svn_pike_init_ra (void)
{
struct program *ra_program;
struct svalue prog;
prog.type = PIKE_T_PROGRAM;
prog.subtype = 0;
start_new_program ();
ADD_STORAGE (struct ra_obj);
set_init_callback(init_ra_obj);
set_exit_callback(exit_ra_obj);
ADD_FUNCTION ("get_ra_library", f_get_ra_library,
tFunc (tStr, tObj), 0);
ADD_FUNCTION ("print_ra_libraries", f_print_ra_libraries,
tFunc (tNone, tStr), 0);
ADD_FUNCTION ("version", f_version, tFunc (tNone, tObj), 0);
/*! @decl typedef function(string,int,mapping(string:string), @
*! mapping(string:string): @
*! Delta.DeltaWindowHandler) FileRevHandler
*!
*! A callback function type for use in @[get_file_revs].
*! The arguments are:
*! @array
*! @elem string path
*! The pathname of the file,
*! @elem int rev
*! The revision.
*! @elem mapping(string:string) rev_props
*! The revision properties.
*! @elem mapping(string:string) prop_diffs
*! A mapping indicating the property delta for this and the
*! previous revision.
*! @endarray
*!
*! If the return value is non-zero, it should be a handler to be
*! called with the delta between the previous revision and this one
*! after the return of this callback.
*/
ref_push_type_value (make_pike_type (tFunc (tStr tInt tMap(tStr,tStr)
tMap(tStr,tStr),
tFunc (tObj, tVoid))));
push_constant_text ("FileRevHandler");
add_constant (Pike_sp[-1].u.string, Pike_sp-2, ID_INLINE);
pop_stack ();
start_new_program ();
Pike_compiler->new_program->flags |= PROGRAM_USES_PARENT;
ra_lib_offs = ADD_STORAGE (struct lib_obj);
set_init_callback(init_lib_obj);
set_exit_callback(exit_lib_obj);
map_variable ("name", "string", 0,
ra_lib_offs + OFFSETOF (lib_obj, name), PIKE_T_STRING);
map_variable ("description", "string", 0,
ra_lib_offs + OFFSETOF (lib_obj, description), PIKE_T_STRING);
ADD_FUNCTION ("open", f_ra_lib_open, tFunc (tStr tObj
tOr (tArr (tObj), tVoid),
tVoid), 0);
ADD_FUNCTION ("get_version", f_ra_lib_get_version, tFunc (tNone, tObj), 0);
ADD_FUNCTION ("create", f_ra_lib_create, tFunc (tStr, tVoid),
ID_STATIC);
ADD_FUNCTION ("_sprintf", f_ra_lib_sprintf, tFunc (tInt tMapping, tStr),
ID_STATIC);
start_new_program ();
Pike_compiler->new_program->flags |= PROGRAM_USES_PARENT;
ra_session_offs = ADD_STORAGE (struct session_obj);
set_init_callback(init_session_obj);
set_exit_callback(exit_session_obj);
map_variable ("config", "mapping", ID_STATIC,
ra_session_offs + OFFSETOF (session_obj, config),
PIKE_T_MAPPING);
map_variable ("auth_providers", "array", ID_STATIC,
ra_session_offs + OFFSETOF (session_obj, auth_providers),
PIKE_T_ARRAY);
map_variable ("callbacks", "object", ID_STATIC,
ra_session_offs + OFFSETOF (session_obj, callbacks),
PIKE_T_OBJECT);
map_variable ("url", "string", ID_STATIC,
ra_session_offs + OFFSETOF (session_obj, url),
PIKE_T_STRING);
ADD_FUNCTION ("get_latest_revnum", f_get_latest_revnum,
tFunc (tNone, tInt), 0);
ADD_FUNCTION ("get_dated_revision", f_get_dated_revision,
tFunc (tInt, tInt), 0);
ADD_FUNCTION ("change_rev_prop", f_change_rev_prop,
tFunc (tInt tStr tStr, tVoid), 0);
ADD_FUNCTION ("rev_proplist", f_rev_proplist,
tFunc (tInt, tMap(tStr, tStr)), 0);
ADD_FUNCTION ("rev_prop", f_rev_prop, tFunc (tInt tStr, tStr), 0);
ADD_FUNCTION ("get_commit_editor", f_get_commit_editor,
tFunc (tStr tOr (tFunc (tInt tStr tStr, tVoid), tVoid), tObj),
0);
ADD_FUNCTION ("get_file", f_get_file,
tFunc (tStr tInt tObj, tArr (tOr (tInt, tMapping))), 0);
ADD_FUNCTION ("get_dir", f_get_dir,
tFunc (tStr tInt, tArr (tOr (tInt, tMapping))), 0);
ADD_FUNCTION ("do_update", f_do_update,
tFunc (tObj tInt tOr (tStr, tVoid) tOr (tInt, tVoid), tObj),0);
ADD_FUNCTION ("do_switch", f_do_switch,
tFunc (tObj tStr tInt tOr (tStr, tVoid) tOr (tInt, tVoid),
tObj),0);
ADD_FUNCTION ("do_status", f_do_status,
tFunc (tObj tInt tOr (tStr, tVoid) tOr (tInt, tVoid), tObj),0);
ADD_FUNCTION ("do_diff", f_do_diff,
tFunc (tObj tStr tInt tOr (tStr, tVoid) tOr (tInt, tVoid)
tOr (tInt, tVoid), tObj),0);
ADD_FUNCTION ("get_log", f_get_log,
tFunc (tArr (tStr) tFunc (tInt tStr tStr tStr
tOr (tMap (tStr,
tArr (tOr (tInt,
tStr))),
tVoid),
tVoid)
tInt tInt tOr (tInt, tVoid)
tOr (tInt, tVoid), tVoid), 0);
ADD_FUNCTION ("check_path", f_check_path, tFunc (tStr tInt, tInt), 0);
ADD_FUNCTION ("get_uuid", f_get_uuid, tFunc (tNone, tStr), 0);
ADD_FUNCTION ("get_repos_root", f_get_repos_root, tFunc (tNone, tStr), 0);
ADD_FUNCTION ("get_locations", f_get_locations,
tFunc (tStr tInt tArr (tInt), tMap (tInt, tStr)), 0);
ADD_FUNCTION ("get_file_revs", f_get_file_revs,
tFunc (tStr tInt tInt tFunc(tStr tInt tMap(tStr,tStr)
tMap(tStr,tStr),
tFunc (tObj, tVoid)), tVoid), 0);
ADD_FUNCTION ("create", f_ra_session_create, tFunc (tStr tObj
tOr (tArr (tObj), tVoid),
tVoid),
ID_STATIC);
ADD_FUNCTION ("_sprintf", f_ra_session_sprintf, tFunc (tInt tMapping, tStr),
ID_STATIC);
start_new_program ();
ADD_PROTOTYPE ("open_tmp_file", tFunc (tNone, tVoid), 0);
ADD_PROTOTYPE ("get_authenticator", tFunc (tNone, tVoid), 0);
ADD_PROTOTYPE ("get_wc_prop", tFunc (tStr tStr, tStr), 0);
ADD_PROTOTYPE ("set_wc_prop", tFunc (tStr tStr tStr, tVoid), 0);
ADD_PROTOTYPE ("push_wc_prop", tFunc (tStr tStr tStr, tVoid), 0);
end_class ("Callbacks", 0);
start_new_program ();
Pike_compiler->new_program->flags |= PROGRAM_USES_PARENT;
prog.u.program = svn_pike_delta_editor_program;
do_inherit(&prog, 0, NULL);
ra_commiteditor_offs = ADD_STORAGE (struct commiteditor_obj);
map_variable ("callback", "function", ID_STATIC,
ra_commiteditor_offs + OFFSETOF (commiteditor_obj, callback),
PIKE_T_MIXED);
ADD_FUNCTION ("create", f_ra_commiteditor_create,
tFunc (tStr tOr (tFunc (tInt tStr tStr, tVoid), tVoid), tVoid),
ID_STATIC);
ra_commiteditor_program = end_program ();
ra_commiteditor_ident = add_program_constant ("CommitEditor",
ra_commiteditor_program, 0);
start_new_program ();
Pike_compiler->new_program->flags |= PROGRAM_USES_PARENT;
ADD_STORAGE (struct reporter_obj);
set_init_callback(init_reporter_obj);
set_exit_callback(exit_reporter_obj);
ADD_FUNCTION ("set_path", f_set_path, tFunc (tStr tInt tOr (tInt, tVoid),
tVoid), 0);
ADD_FUNCTION ("delete_path", f_delete_path, tFunc (tStr, tVoid), 0);
ADD_FUNCTION ("link_path", f_link_path,
tFunc (tStr tStr tInt tOr (tInt, tVoid), tVoid), 0);
ADD_FUNCTION ("finish_report", f_finish_report, tFunc (tNone, tVoid), 0);
ADD_FUNCTION ("abort_report", f_abort_report, tFunc (tNone, tVoid), 0);
ra_reporter_program = end_program ();
ra_reporter_ident = add_program_constant ("Reporter", ra_reporter_program, 0);
ra_session_program = end_program ();
ra_session_ident = add_program_constant ("Session", ra_session_program, 0);
ra_lib_program = end_program ();
ra_lib_ident = add_program_constant ("Library", ra_lib_program, 0);
ra_program = end_program ();
add_object_constant ("RA", clone_object (ra_program, 0), 0);
free_program (ra_program);
}
/*! @endmodule */
/*! @endmodule */