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