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/svnmod.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 "version.h"


/*! @module Subversion
 *!
 *!  This module provides binding to the Subversion revision control
 *!  system.  See @url{http://subversion.tigris.org/@} for more
 *!  information about Subversion.
 */


/* Revision specifiers */

/*! @decl typedef int|string Revision
 *!
 *!  Either an integer specifying a particular revision number, or
 *!  one of the following strings:
 *!  @dl
 *!   @item '"head"'
 *!      The youngest revision in the repository
 *!   @item '"first"'
 *!      The very first revision (always 0)
 *!   @item '"prev"'
 *!      The revision before the latest change
 *!   @item '"base"'
 *!      The revision that is currently checked out
 *!   @item '"committed"'
 *!      The revision where the file was most recently changed
 *!   @item '"changed"'
 *!      Same as @tt{"committed"@}
 *!  @enddl
 */
static const char *named_revision_names[] = {
  "head", "first", "prev", "base", "committed", "changed"
};
#define NUM_NAMED_REVISIONS ((sizeof(named_revision_names)/	\
			     sizeof(named_revision_names[0])))
static enum svn_opt_revision_kind named_revision_kinds[NUM_NAMED_REVISIONS] = {
  svn_opt_revision_head, svn_opt_revision_number, svn_opt_revision_previous,
  svn_opt_revision_base, svn_opt_revision_committed, svn_opt_revision_committed
};
static struct pike_string *named_revision_strings[NUM_NAMED_REVISIONS];


/* Subversion module versions */

/*! @class Version
 *!
 *!  Version number for a Subversion component.
 */
struct version_obj {
  /*! @decl int major
   *!
   *!   Major version number
   */
  INT_TYPE major;

  /*! @decl int minor
   *!
   *!   Minor version number
   */
  INT_TYPE minor;

  /*! @decl int patch
   *!
   *!   Patch number
   */
  INT_TYPE patch;

  /*! @decl string tag
   *!
   *!   The version tag
   */
  struct pike_string *tag;
};
static struct program *version_program;

static void f_version_sprintf (INT32 args)
{
  struct version_obj *vo = (struct version_obj *)Pike_fp->current_storage;
  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(%d.%d.%d%s)");
    ref_push_object (Pike_fp->current_object);
    f_object_program (1);
    push_int (vo->major);
    push_int (vo->minor);
    push_int (vo->patch);
    if (vo->tag == NULL)
      push_text ("");
    else
      ref_push_string (vo->tag);
    f_sprintf (6);
  } else
    push_undefined ();
}

/*! @decl int(0..1) compatible( Version lib_version )
 *!
 *!  Check library version compatibility. Returns 1 if this version
 *!  is compatible with the library version, provided in @[lib_version].
 *!
 *!  This function checks for version compatibility as per our
 *!  guarantees, but requires an exact match when linking to an
 *!  unreleased library. A development client is always compatible with
 *!  a previous released library.
 *!
 *! @param lib_version
 *!   Library version to check against
 */
static void f_version_compatible (INT32 args)
{
  struct version_obj *vo = (struct version_obj *)Pike_fp->current_storage;
  struct object *o;
  struct pike_string *tag;
  svn_version_t ver1, ver2;
  svn_boolean_t r;

  get_all_args ("compatible", args, "%o", &o);

  ver1.major = vo->major;
  ver1.minor = vo->minor;
  ver1.patch = vo->patch;
  if((tag = svn_pike_to_utf8 (vo->tag)) == NULL)
    ver1.tag = "";
  else
    ver1.tag = APR_STR0 (tag);
  memset(&ver2, 0, sizeof(ver2));
  ref_push_object (o);
  push_text ("major");
  o_index ();
  if (Pike_sp[-1].type == PIKE_T_INT)
    ver2.major = Pike_sp[-1].u.integer;
  ref_push_object (o);
  push_text ("minor");
  o_index ();
  if (Pike_sp[-1].type == PIKE_T_INT)
    ver2.minor = Pike_sp[-1].u.integer;
  ref_push_object (o);
  push_text ("patch");
  o_index ();
  if (Pike_sp[-1].type == PIKE_T_INT)
    ver2.patch = Pike_sp[-1].u.integer;
  ref_push_object (o);
  push_text ("tag");
  o_index ();
  if (Pike_sp[-1].type == PIKE_T_STRING) {
    f_string_to_utf8 (1);
    ver2.tag = APR_STR0 (Pike_sp[-1].u.string);
  } else
    ver2.tag = "";
  r = svn_ver_compatible (&ver1, &ver2);
  do_free_string (tag);
  pop_n_elems (args+4);
  push_int (r? 1 : 0);
}

/*! @decl void create( int major, int minor, int patch, string tag )
 *!
 *!  Create a version object with the specified version numbers and tag
 *!
 *! @param major
 *!   Major version number
 *! @param minor
 *!   Minor version number
 *! @param patch
 *!   Patch number
 *! @param tag
 *!   The version tag
 */
static void f_version_create (INT32 args)
{
  INT_TYPE major, minor, patch;
  struct pike_string *tag = NULL;
  struct version_obj *vo = (struct version_obj *)Pike_fp->current_storage;

  get_all_args ("Version", args, (args > 3? "%i%i%i%W" : "%i%i%i"),
		&major, &minor, &patch, &tag);

  vo->major = major;
  vo->minor = minor;
  vo->patch = patch;
  if (tag == NULL)
    push_text ("");
  else
    ref_push_string (tag);
  assign_to_short_svalue ((union anything *)&vo->tag, PIKE_T_STRING,
			  Pike_sp-1);
  pop_n_elems (args+1);
}

/* Convert a svn_version_t* to a Subversion.Version and push it on the stack */

void svn_pike_push_version (const svn_version_t *version)
{
  push_int (version->major);
  push_int (version->minor);
  push_int (version->patch);
  svn_pike_push_utf8 (version->tag);
  push_object (clone_object (version_program, 4));
}
/*! @endclass */


/* Error internal object structure */

/*! @class Error
 *!
 *!  An error generated by the Subversion framwork.
 */

/* This is a bit hackish; we want to build an array, but not exactly
   the array that  knows how to build.  Therefore
   we need to define alternate macros, which we can do by defining
   DOXYGEN_SHOULD_SKIP_THIS, which circuments the regular defintions. */
#define SVN_ERROR_BUILD_ARRAY
#define DOXYGEN_SHOULD_SKIP_THIS
#define SVN_ERROR_START \
        static const err_defn error_table[] = {
#define SVN_ERRDEF(num, offset, str) { num, #num },
#define SVN_ERROR_END { 0, NULL } };

typedef struct {
  svn_errno_t errcode;
  const char *errdesc;
} err_defn;
#include 

struct error_obj {
  /*! @decl int apr_err
   *!
   *!   APR error value, possibly SVN_ custom err.
   */
  INT_TYPE apr_err;

  /*! @decl string message
   *!
   *!   Details from producer of error.
   */
  struct pike_string *message;

  /*! @decl Error child
   *!
   *!   The error we "wrap".
   */
  struct svalue child;

  /*! @decl string file
   *!
   *!   Source file where the error originated, if available.
   */
  struct pike_string *file;

  /*! @decl int line
   *!
   *!   Source line where the error originated, if available.
   */
  INT_TYPE line;
};
static ptrdiff_t error_obj_off;
static struct program *error_program;
/*! @endclass */


/* Parse a revision argument */

void svn_pike_check_rev_arg (const char *func, INT32 args, INT32 n,
			     svn_opt_revision_t *revision)
{
  revision->kind = svn_opt_revision_unspecified;
  revision->value.number = 0;
  if (args <= n || IS_UNDEFINED (&Pike_sp[n-args]))
    return;

  if (Pike_sp[n-args].type == PIKE_T_INT) {
    revision->kind = svn_opt_revision_number;
    revision->value.number = Pike_sp[n-args].u.integer;
    return;
  }

  if (Pike_sp[n-args].type == PIKE_T_STRING) {
    struct pike_string *p;
    int i;
    ref_push_string (Pike_sp[n-args].u.string);
    f_lower_case(1);
    p = Pike_sp[-1].u.string;
    for (i=0; ikind = named_revision_kinds[i];
	pop_stack ();
	return;
      }
    pop_stack ();
    Pike_error ("Bad argument %d to %s(). No such named revision.\n",
		n+1, func);
  }

  Pike_error ("Bad argument %d to %s(). Expected Subversion.Revision\n",
	      n+1, func);
}


/* Parse the non-recursive argument, if present */

svn_boolean_t svn_pike_check_recurse_arg (const char *func, INT32 args, INT32 n)
{
  if (args <= n || IS_UNDEFINED (&Pike_sp[n-args]))
    return TRUE;

  if (Pike_sp[n-args].type == PIKE_T_INT)
    return !Pike_sp[n-args].u.integer;

  Pike_error ("Bad argument %d to %s(). Expected int|void\n", n+1, func);
}


/* Parse the force argument, if present */

svn_boolean_t svn_pike_check_force_arg (const char *func, INT32 args, INT32 n)
{
  if (args <= n || IS_UNDEFINED (&Pike_sp[n-args]))
    return FALSE;

  if (Pike_sp[n-args].type == PIKE_T_INT)
    return !!Pike_sp[n-args].u.integer;

  Pike_error ("Bad argument %d to %s(). Expected int|void\n", n+1, func);
}


/* C stub for calling log_receiver_func */

svn_error_t *svn_pike_log_receiver_func_stub (void *baton,
					      apr_hash_t *changed_paths,
					      svn_revnum_t revision,
					      const char *author,
					      const char *date,
					      const char *message,
					      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 {
      push_int (revision);
      svn_pike_push_utf8 (author);
      svn_pike_push_utf8 (date);
      svn_pike_push_utf8 (message);
      if(changed_paths) {
	INT32 n_items = 0;
	apr_hash_index_t *hi;
	for (hi = apr_hash_first (pool, changed_paths);
	     hi;
	     hi = apr_hash_next (hi)) {
	  const void *name;
	  void *change;
	  apr_hash_this (hi, &name, NULL, (void **)&change);
	  svn_pike_push_utf8 ((const char *)name);
	  push_int (((svn_log_changed_path_t *)change)->action);
	  svn_pike_push_utf8 (((svn_log_changed_path_t *)change)->copyfrom_path);
	  push_int (((svn_log_changed_path_t *)change)->copyfrom_rev);
	  f_aggregate (3);
	  n_items ++;
	}
	f_aggregate_mapping(2*n_items);
	apply_svalue ((struct svalue *)baton, 5);
      } else      
	apply_svalue ((struct svalue *)baton, 4);
      pop_stack ();
    }
    UNSETJMP (recovery);

    THREADS_ALLOW ();
  } while (0);

  return err;
}


/* Convert an array of target pathnames to an APR array */
apr_array_header_t *svn_pike_make_targets_array (struct array *targets,
						 apr_pool_t *pool,
						 svn_boolean_t allow_empty)
{
  apr_array_header_t *ret;
  int i;

  if (targets == NULL || targets->size == 0) {
    if (allow_empty)
      return apr_array_make (pool, 0, sizeof (const char *));
    apr_pool_destroy (pool);
    Pike_error ("No targets specified\n");
  }

  ret = apr_array_make (pool, targets->size, sizeof (const char *));

  for (i = 0; i < targets->size; i++) {
    push_svalue (&ITEM (targets)[i]);
    f_string_to_utf8 (1);
    (*((const char **) apr_array_push (ret))) =
      apr_pstrdup (pool,
		   svn_path_canonicalize (APR_STR0 (Pike_sp[-1].u.string), pool));
    pop_stack ();
  }

  return ret;
}


/* Convert a hash of property values to a Pike mapping */
void svn_pike_make_propmap (struct svalue *ret, apr_hash_t *props,
			    int utf8_mode)
{
  if (props) {
    apr_hash_index_t *hi;
    INT32 n_items = 0;
    for (hi = apr_hash_first (apr_hash_pool_get(props), props);
	 hi;
	 hi = apr_hash_next (hi)) {
      const void *name;
      void *val;
      apr_hash_this (hi, &name, NULL, &val);

      svn_pike_push_utf8 ((const char *)name);
      push_string (make_shared_binary_string (((svn_string_t *)val)->data,
					      ((svn_string_t *)val)->len));
      if (utf8_mode > 0 ||
	  (utf8_mode < 0 && svn_prop_needs_translation ((const char *)name)))
	f_utf8_to_string (1);
      n_items ++;
    }
    f_aggregate_mapping(2*n_items);
    *ret = *--Pike_sp;  
  } else {
    ret->u.integer = 0;
    ret->type = PIKE_T_INT;
    ret->subtype = NUMBER_UNDEFINED;
  }
}


/* Convert a hash of svn_dirent_t:s to a Pike mapping */
void svn_pike_make_dirents (struct svalue *ret, apr_hash_t *dirents)
{
  if (dirents) {
    apr_hash_index_t *hi;
    INT32 n_items = 0;
    for (hi = apr_hash_first (apr_hash_pool_get(dirents), dirents);
	 hi;
	 hi = apr_hash_next (hi)) {
      const void *name;
      void *dirent;
      apr_hash_this (hi, &name, NULL, &dirent);

      svn_pike_push_utf8 ((const char *)name);
      push_int (((svn_dirent_t *)dirent)->kind);
      push_int (((svn_dirent_t *)dirent)->size);
      push_int (((svn_dirent_t *)dirent)->has_props);
      push_int (((svn_dirent_t *)dirent)->created_rev);
      svn_pike_push_time (((svn_dirent_t *)dirent)->time);
      svn_pike_push_utf8 (((svn_dirent_t *)dirent)->last_author);
      f_aggregate (6);
      n_items ++;
    }
    f_aggregate_mapping(2*n_items);
    *ret = *--Pike_sp;  
  } else {
    ret->u.integer = 0;
    ret->type = PIKE_T_INT;
    ret->subtype = NUMBER_UNDEFINED;
  }
}


/* Convert a timestamp from apr_time_t and push it on the stack */

void svn_pike_push_time (apr_time_t t)
{
  apr_os_imp_time_t ostime, *otp = &ostime;
  if (apr_os_imp_time_get(&otp, &t))
    push_int(0);
  else
    push_int(ostime.tv_sec);
}


/* Convert a string from UTF-8 and push it on the stack */

void svn_pike_push_utf8 (const char *str)
{
  if (str == NULL)
    push_int (0);
  else {
    push_text (str);
    f_utf8_to_string (1);
  }
}


/* Convert a string to UTF-8 */

struct pike_string *svn_pike_to_utf8 (struct pike_string *s)
{
  if (s != NULL) {
    ref_push_string(s);
    f_string_to_utf8 (1);
    s = (--Pike_sp)->u.string;
  }
  return s;
}


#if PIKE_MAJOR_VERSION==7 && PIKE_MINOR_VERSION<6
#define error_message desc
#define error_backtrace backtrace
#endif

/* Convert a svn_error_t* to a Subversion.Error and push it on the stack */

static void push_svn_error (svn_error_t *err)
{
  struct error_obj *eo;
  struct generic_error_struct *go;

  if (err->message) {
    push_text (err->message);
    f_utf8_to_string (1);
  } else
    push_text ("no message");
  push_int (err->apr_err);
  push_object (clone_object (error_program, 2));
  if(Pike_sp[-1].type != PIKE_T_OBJECT || Pike_sp[-1].u.object == NULL) {
    pop_stack ();
    push_error ("Failed to clone Subversion.Error object\n");
    return;
  }

  eo = (struct error_obj *)
    (get_storage (Pike_sp[-1].u.object, error_program) + error_obj_off);
  go = (struct generic_error_struct *)
    (get_storage (Pike_sp[-1].u.object, generic_error_program) +
     generic_error_offset);

  if (err->child) {
    push_svn_error (err->child);
    assign_svalue (&eo->child, Pike_sp-1);
    pop_stack ();
  }

  if (err->file) {
    push_text (err->file);
    f_utf8_to_string (1);
    assign_to_short_svalue ((union anything *)&eo->file, PIKE_T_STRING,
			    Pike_sp-1);
    pop_stack ();
  }
  eo->line = err->line;

  ref_push_string (eo->message);
  while (eo != NULL && eo->child.type == PIKE_T_OBJECT &&
	 eo->child.u.object != NULL) {
    char *storage = get_storage (eo->child.u.object, error_program);
    eo = (storage != NULL?
	  (struct error_obj *)(storage + error_obj_off) : NULL);
    if (eo != NULL && eo->message != NULL) {
      push_text ("\n");
      ref_push_string (eo->message);
      f_add (3);
    }
  }
  push_text ("\n");
  f_add (2);
  assign_to_short_svalue ((union anything *)&go->error_message, PIKE_T_STRING,
			  Pike_sp-1);
  pop_stack ();
}


/* Prepare a svn_error_t* to be thrown as a pike error */

void svn_pike_set_error (svn_error_t *err)
{
  push_svn_error (err);
  assign_svalue (&throw_value, Pike_sp-1);
  pop_stack ();
  throw_severity = THROW_ERROR;
  svn_error_clear (err);
}


/* Convert a pike error to a svn_error_t* */

svn_error_t *svn_pike_make_svn_error (struct svalue *v)
{
  union anything *a;
  struct generic_error_struct *gen_err;
  apr_status_t apr_err = APR_EGENERAL/*SVN_ERR_GENERAL*/;
  int pushed_message = 0;
  svn_error_t *err, *child = NULL;
  char *storage;

  if (v->type == PIKE_T_ARRAY && v->u.array->size &&
      (a=low_array_get_item_ptr (v->u.array, 0, PIKE_T_STRING)) != NULL &&
      a->string != NULL) {
    ref_push_string (a->string);
  } else if (v->type == PIKE_T_OBJECT &&
	     (storage = get_storage (v->u.object, generic_error_program)
	      ) != NULL &&
	     (gen_err =
	      (struct generic_error_struct *)(storage + generic_error_offset)
	      ) ->error_message != NULL) {
    if ((storage = get_storage (v->u.object, error_program)) != NULL) {
      struct error_obj *svn_err =
	(struct error_obj *) (storage + error_obj_off);
      apr_err = svn_err->apr_err;
      if (!UNSAFE_IS_ZERO (&svn_err->child))
	child = svn_pike_make_svn_error (&svn_err->child);
      if (svn_err->message) {
	ref_push_string (svn_err->message);
	pushed_message = 1;
      }
    }
    if (!pushed_message)
      ref_push_string (gen_err->error_message);
  } else {
    apr_err = APR_EGENERAL;
    push_constant_text ("Nonstandard pike exception thrown.\n");
  }
  f_string_to_utf8 (1);
  if (!pushed_message && Pike_sp[-1].u.string->len > 0 &&
      APR_STR0(Pike_sp[-1].u.string)[Pike_sp[-1].u.string->len-1] == '\n') {
    push_int (0);
    push_int (Pike_sp[-2].u.string->len-2);
    o_range();
  }
  err = svn_error_create (apr_err, child, APR_STR0(Pike_sp[-1].u.string));
  pop_stack ();
  return err;
}

/*! @class Error */
/*! @decl void create( string message, int|void apr_err )
 *!
 *!  Create an error object with a specific error code for throwing from
 *!  a Subversion callback.
 *!
 *! @param message
 *!   Human readable error message (without trailing linefeed)
 *! @param apr_err
 *!   The APR error code.
 */
static void f_error_create (INT32 args)
{
  struct pike_string *msg;
  struct error_obj *eo =
    (struct error_obj *)(Pike_fp->current_storage + error_obj_off);
  struct generic_error_struct *go =
    (struct generic_error_struct *)
    (get_storage (Pike_fp->current_object, generic_error_program) +
     generic_error_offset);

  get_all_args ("Error", args, "%W", &msg);
  if (args >= 2) {
    if (Pike_sp[1-args].type != PIKE_T_INT)
      Pike_error ("Bad argument 2 to Error(). Expected int|void\n");
    eo->apr_err = Pike_sp[1-args].u.integer;    
  }

  f_backtrace (0);
  push_int (0);
  push_int (Pike_sp[-2].u.array->size-2);
  o_range ();
  assign_to_short_svalue ((union anything *)&go->error_backtrace,
			  PIKE_T_ARRAY, Pike_sp-1);
  pop_stack ();

  if (msg == NULL)
    push_int (0);
  else
    ref_push_string (msg);
  assign_to_short_svalue ((union anything *)&eo->message, PIKE_T_STRING,
			  Pike_sp-1);
  push_text ("\n");
  f_add (2);
  assign_to_short_svalue ((union anything *)&go->error_message, PIKE_T_STRING,
			  Pike_sp-1);
  pop_n_elems (args+1);
}

#ifdef error_message
#undef error_message
#endif
#ifdef error_backtrace
#undef error_backtrace
#endif
/*! @endclass */


/*! @class Stream
 *!
 *!  An abstract octet stream, provided to functions that do I/O on
 *!  files.  Any class that implements these methods can be used.
 */

/*! @decl string read( int bytes )
 *!
 *!  Read up to @[bytes] bytes of data from the stream, blocking if
 *!  necessary.  End of stream is indicated by returning an empty string.
 */
static svn_error_t *read_fn_stub (void *baton, char *buffer, apr_size_t *len)
{
  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 {
      push_int (*len);
      apply ((struct object *)baton, "read", 1);
      if (Pike_sp[-1].type != PIKE_T_STRING ||
	  Pike_sp[-1].u.string->size_shift)
	Pike_error ("read must return string (narrow)\n");
      if (Pike_sp[-1].u.string->len > *len)
	Pike_error ("read returned too much data!\n");
      MEMCPY (buffer, APR_STR0 (Pike_sp[-1].u.string),
	      *len = Pike_sp[-1].u.string->len);
      pop_stack ();
    }
    UNSETJMP (recovery);

    THREADS_ALLOW ();
  } while (0);

  return err;
}

/*! @decl int write( string data )
 *!
 *!  Write some data to the stream.  Return the number of bytes that
 *!  were actually written.
 */
static svn_error_t *write_fn_stub (void *baton, const char *buffer,
				   apr_size_t *len)
{
  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 {
      push_string (make_shared_binary_string (buffer, *len));
      apply ((struct object *)baton, "write", 1);
      if (Pike_sp[-1].type != PIKE_T_INT)
	Pike_error ("write must return int\n");
      *len = Pike_sp[-1].u.integer;
      pop_stack ();
    }
    UNSETJMP (recovery);

    THREADS_ALLOW ();
  } while (0);

  return err;
}


/*! @decl void close( )
 *!
 *!  Close the stream.
 */
static svn_error_t *close_fn_stub (void *baton)
{
  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 {
      apply ((struct object *)baton, "close", 0);
      pop_stack ();
    }
    UNSETJMP (recovery);

    THREADS_ALLOW ();
  } while (0);

  return err;
}


/* Parse the stream argument */

void svn_pike_check_stream_arg (const char *func, INT32 args, INT32 n,
				svn_stream_t **ret, apr_pool_t *pool_in,
				apr_pool_t **pool_out)
{
  if (n < 0) {
    n = ~n;
    if (args <= n || UNSAFE_IS_ZERO (&Pike_sp[n-args])) {
      if (pool_out)
	*pool_out = svn_pool_create (pool_in);
      *ret = NULL;
      return;
    }
  }

  if (args <= n || Pike_sp[n-args].type != PIKE_T_OBJECT) {
    if (pool_in && !pool_out)
      apr_pool_destroy (pool_in);
    Pike_error ("Bad argument %d to %s(). Expected Stream\n", n+1, func);
  }

  if (pool_out)
    *pool_out = pool_in = svn_pool_create (pool_in);

  *ret = svn_stream_create (Pike_sp[n-args].u.object, pool_in);

  svn_stream_set_read (*ret, read_fn_stub);
  svn_stream_set_write (*ret, write_fn_stub);
  svn_stream_set_close (*ret, close_fn_stub);
}


/* Parse file arguments, if present */

apr_os_file_t svn_pike_check_file_arg (const char *func, INT32 args, INT32 n)
{
#ifdef WIN32
  extern HANDLE da_handle[];
#endif
  int fd = -1;

  if (args <= n || UNSAFE_IS_ZERO (&Pike_sp[n-args]))
    return NO_FILE_SPECIFIED;

  if (Pike_sp[n-args].type != PIKE_T_OBJECT)
    Pike_error ("Bad argument %d to %s(). Expected Stdio.File|void\n",
		n+1, func);

  apply (Pike_sp[n-args].u.object, (char *)"query_fd", 0);
  if (Pike_sp[-1].type == PIKE_T_INT && !Pike_sp[-1].subtype)
    fd = Pike_sp[-1].u.integer;
  pop_stack ();

  if (fd < 0)
    Pike_error ("Bad argument %d to %s(), not a real file.\n", n+1, func);

#ifdef WIN32
  return da_handle[fd];
#else
  return fd;
#endif  
}


/*! @endclass */


/* Wrapper for C level svn_stream_t:s */

static ptrdiff_t svnstream_off;
static struct program *svnstream_program;
#define THIS_SVNSTREAM ((struct svnstream_obj *)Pike_fp->current_storage)
static void init_svnstream_obj (struct object *o)
{
  struct svnstream_obj *ss = THIS_SVNSTREAM;
  ss->pool = NULL;
  ss->stream = NULL;
}
static void exit_svnstream_obj (struct object *o)
{
  struct svnstream_obj *ss = THIS_SVNSTREAM;
  if (ss->pool) {
    apr_pool_destroy (ss->pool);
    ss->pool = NULL;
  }
  ss->stream = NULL;
}
static void f_svnstream_read (INT32 args)
{
  struct svnstream_obj *ss = THIS_SVNSTREAM;
  apr_size_t alen;
  INT_TYPE len;
  svn_error_t *err;
  struct string_builder sb;

  get_all_args ("read", args, "%i", &len);

  init_string_builder_alloc (&sb, alen = len, 0);
  
  THREADS_ALLOW ();

  err = svn_stream_read (ss->stream, APR_STR0 (sb.s), &alen);

  THREADS_DISALLOW ();

  if (err) {
    free_string_builder (&sb);
    svn_pike_set_error (err);
    pike_throw ();
  } else {
    if (alen < len)
      sb.s->len = alen;
    pop_n_elems (args);
    push_string (finish_string_builder (&sb));
  }
}
static void f_svnstream_write (INT32 args)
{
  struct svnstream_obj *ss = THIS_SVNSTREAM;
  apr_size_t alen;
  svn_error_t *err;
  struct pike_string *data;

  get_all_args ("read", args, "%S", &data);

  alen = data->len;

  THREADS_ALLOW ();

  err = svn_stream_write (ss->stream, APR_STR0 (data), &alen);

  THREADS_DISALLOW ();

  if (err) {
    svn_pike_set_error (err);
    pike_throw ();
  } else {
    pop_n_elems (args);
    push_int (alen);
  }
}
static void f_svnstream_close (INT32 args)
{
  struct svnstream_obj *ss = THIS_SVNSTREAM;
  svn_error_t *err;

  THREADS_ALLOW ();

  err = svn_stream_close (ss->stream);

  THREADS_DISALLOW ();

  if (err) {
    svn_pike_set_error (err);
    pike_throw ();
  } else
    pop_n_elems (args);
}
void svn_pike_push_svnstream (svn_stream_t *stream, apr_pool_t *pool)
{
  struct svnstream_obj *ss;
  struct object *o;
  push_object (o = clone_object (svnstream_program, 0));
  ss = (struct svnstream_obj *)
    (get_storage (o, svnstream_program) + svnstream_off);
  ss->pool = pool;
  ss->stream = stream;
}


/* Initialize Subversion module */

PIKE_MODULE_INIT
{
  int i;
  ptrdiff_t off;
  struct program *stream_program;
  struct svalue prog;
  const err_defn *errdef;

  /* Make sure APR (and APR pools in particular) is initialized. */
  apr_status_t apr_err = apr_initialize();
  if (apr_err)
    Pike_error ("Subversion: apr_initialize() failed\n");

  ref_push_type_value (make_pike_type (tOr (tInt, tStr)));
  push_constant_text ("Revision");
  add_constant (Pike_sp[-1].u.string, Pike_sp-2, ID_INLINE);
  pop_stack ();
  for (i=0; ierrdesc != NULL; errdef ++)
    add_integer_constant (errdef->errdesc, errdef->errcode, 0);

  error_program = end_program ();
  add_program_constant ("Error", error_program, 0);

  start_new_program ();

  ADD_PROTOTYPE ("read", tFunc (tInt, tStr), 0);
  ADD_PROTOTYPE ("write", tFunc (tStr, tInt), 0);
  ADD_PROTOTYPE ("close", tFunc (tNone, tVoid), 0);

  stream_program = end_program ();
  add_program_constant ("Stream", stream_program, 0);

  start_new_program ();
  prog.u.program = stream_program;
  do_inherit(&prog, 0, NULL);
  svnstream_off = ADD_STORAGE (struct svnstream_obj);
  set_init_callback (init_svnstream_obj);
  set_exit_callback (exit_svnstream_obj);

  ADD_FUNCTION ("read", f_svnstream_read, tFunc (tInt, tStr), 0);
  ADD_FUNCTION ("write", f_svnstream_write, tFunc (tStr, tInt), 0);
  ADD_FUNCTION ("close", f_svnstream_close, tFunc (tNone, tVoid), 0);

  svnstream_program = end_program ();
  add_program_constant ("NativeStream", svnstream_program, ID_STATIC);

  /*! @decl constant NODE_NONE
   *! @decl constant NODE_FILE
   *! @decl constant NODE_DIR
   *! @decl constant NODE_UNKNOWN
   *!
   *! The various types of nodes in the Subversion filesystem.
   */
  ADD_INT_CONSTANT ("NODE_NONE", svn_node_none, 0);
  ADD_INT_CONSTANT ("NODE_FILE", svn_node_file, 0);
  ADD_INT_CONSTANT ("NODE_DIR", svn_node_dir, 0);
  ADD_INT_CONSTANT ("NODE_UNKNOWN", svn_node_unknown, 0);

  svn_pike_init_config ();
  svn_pike_init_auth ();
  svn_pike_init_client ();
  svn_pike_init_delta ();
  svn_pike_init_fs ();
  svn_pike_init_ra ();
}


/* Deinit module */

PIKE_MODULE_EXIT
{
  int i;
  for (i=0; i

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