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

File Contents

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 */

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