Home modules.gotpike.org
Username: Password: [Create Account]
[Forgot Password?]

Modules

ADT
Database
GTK2
GUI
IP
PiJAX
Public
Sql
Stdio
Subversion
System
Tools
Xosd
lua
v4l2
wx

Recent Changes

Public.ZeroMQ 1.1
Public.Template.Mustache 1.0
Public.Protocols.XMPP 1.4
Sql.Provider.jdbc 1.0
Public.Storage.Manta 1.0

Popular Downloads

Public.Parser.JSON2 1.0
Public.Parser.JSON 0.2
GTK2 2.23
Public.Web.FCGI 1.8
Public.Parser.XML2 1.48


Module Information
Subversion
Viewing 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 
#include 

/*! @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; iauth_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 */


gotpike.org | Copyright © 2004 - 2019 | Pike is a trademark of Department of Computer and Information Science, Linköping University