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

File Contents

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 <svn_path.h>

#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 <svn_error_codes.h> 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 <svn_error_codes.h>

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; i<NUM_NAMED_REVISIONS; i++)
      if (p == named_revision_strings[i]) {
	revision->kind = 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; i<NUM_NAMED_REVISIONS; i++)
    named_revision_strings[i] = make_shared_string(named_revision_names[i]);
  pop_stack ();

  prog.type = PIKE_T_PROGRAM;
  prog.subtype = 0;

  start_new_program ();
  ADD_STORAGE (struct version_obj);

  map_variable ("major", "int", 0, OFFSETOF (version_obj, major), PIKE_T_INT);
  map_variable ("minor", "int", 0, OFFSETOF (version_obj, minor), PIKE_T_INT);
  map_variable ("patch", "int", 0, OFFSETOF (version_obj, patch), PIKE_T_INT);
  map_variable ("tag", "string", 0, OFFSETOF (version_obj, tag), PIKE_T_STRING);

  ADD_FUNCTION ("create", f_version_create, tFunc (tInt tInt tInt
						   tOr (tStr, tVoid), tVoid),
		ID_STATIC);
  ADD_FUNCTION ("_sprintf", f_version_sprintf, tFunc (tInt tMapping, tStr),
		ID_STATIC);
  ADD_FUNCTION ("compatible", f_version_compatible, tFunc (tObj, tInt01), 0);

  version_program = end_program ();
  add_program_constant ("Version", version_program, 0);

  start_new_program ();
  low_inherit (generic_error_program, 0, -1, 0, 0, NULL);

  error_obj_off = off = ADD_STORAGE (struct error_obj);

  map_variable ("apr_err", "int", 0, off + OFFSETOF (error_obj, apr_err),
		PIKE_T_INT);
  map_variable ("message", "string", 0, off + OFFSETOF (error_obj, message),
		PIKE_T_STRING);

  map_variable ("child", "object(this_program)", 0,
		off + OFFSETOF (error_obj, child), PIKE_T_MIXED);
  map_variable ("file", "string", 0, off + OFFSETOF (error_obj, file),
		PIKE_T_STRING);
  map_variable ("line", "int", 0, off + OFFSETOF (error_obj, line),
		PIKE_T_INT);

  add_string_constant("error_type", "Subversion.Error", 0);
  add_integer_constant("is_svn_error", 1, 0);

  ADD_FUNCTION ("create", f_error_create, tFunc (tStr tOr (tInt, tVoid)
						 tOr (tInt, tVoid), tVoid),
		ID_STATIC);

  for (errdef = error_table; errdef->errdesc != 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<NUM_NAMED_REVISIONS; i++)
    do_free_string (named_revision_strings[i]);
  if (svn_pike_delta_editor_program)
    free_program (svn_pike_delta_editor_program);
}

/*! @endmodule
 */

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