
Contents of /Subversion-0.112/client.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_client.h>
#include <svn_sorts.h>
#include <svn_path.h>
/*! @module Subversion */
/*! @class Client
*!
*! The Client class provides convenient access to most user-level
*! operations.
*/
/* Function indices for callbacks */
static int notify_function, log_msg_function, cancel_function;
/* Internal object structure */
struct client_obj {
svn_client_ctx_t *ctx;
apr_pool_t *pool;
struct array *auth_providers;
struct mapping *config;
struct svalue notify_func, log_msg_func, cancel_func;
};
#define THIS ((struct client_obj *)(Pike_fp->current_storage))
/*! @decl void notify_func( string path, int action, int kind, @
*! string mime_type, int content_state, @
*! int prop_state, int revision )
*!
*! This function is called when the state of an entry in the wc
*! changes. Override it if you are interrested in this information.
*!
*! @param path
*! Path to the affected file or directory.
*! @param action
*! The action taken on the entry.
*! @param kind
*! The kind of entry (file or directory), see @[RA.Library.Session.check_path()].
*! @param mime_type
*! The media type for the entry, if available (otherwise 0).
*! @param content_state
*! The new state of the entry contents.
*! @param prop_state
*! The new state of the entry properties.
*! @param revision
*! The new revision of the entry.
*/
static void f_notify_func (INT32 args)
{
pop_n_elems (args);
}
/*! @decl string log_msg_func( array(array(string|int)) commit_items )
*!
*! Override this function to provide log messages for commits.
*!
*! @param commit_items
*! A list of items being committed. Each element has the following
*! format:
*! @array
*! @elem string path
*! Absolute working-copy path of item.
*! @elem int kind
*! Node kind (dir, file).
*! @elem string URL
*! Commit URL for this item.
*! @elem int revision
*! Revision (copyfrom-rev if _IS_COPY).
*! @elem string copyfrom_url
*! copyfrom-url.
*! @elem int state_flags
*! State flags.
*! @endarray
*!
*! @returns
*! The function should return a log message for the commit as a string.
*/
static void f_log_msg_func (INT32 args)
{
pop_n_elems (args);
push_int (0);
}
/*! @decl void cancel_func( )
*!
*! Override this function to enable operations in progress to be aborted.
*! If the operation should continue, the function should do nothing.
*! If not, it should throw an error with apr_err of SVN_ERR_CANCELLED.
*!
*/
static void f_cancel_func (INT32 args)
{
pop_n_elems (args);
push_int (0);
}
/* C stub for calling notify_func */
static void notify_func_stub (void *baton, const char *path,
svn_wc_notify_action_t action,
svn_node_kind_t kind,
const char *mime_type,
svn_wc_notify_state_t content_state,
svn_wc_notify_state_t prop_state,
svn_revnum_t revision)
{
JMP_BUF recovery;
do {
struct thread_state *_tmp=thread_state_for_id (th_self ());
HIDE_GLOBAL_VARIABLES ();
THREADS_DISALLOW ();
if (SETJMP (recovery))
call_handle_error ();
else {
svn_pike_push_utf8 (path);
push_int (action);
push_int (kind);
svn_pike_push_utf8 (mime_type);
push_int (content_state);
push_int (prop_state);
push_int (revision);
apply_svalue ((struct svalue *)baton, 7);
pop_stack ();
}
UNSETJMP (recovery);
THREADS_ALLOW ();
} while (0);
}
/* C stub for calling log_msg_func */
static svn_error_t *log_msg_func_stub (const char **log_msg,
const char **tmp_file,
apr_array_header_t *commit_items,
void *baton, apr_pool_t *pool)
{
JMP_BUF recovery;
svn_error_t *err = SVN_NO_ERROR;
if(tmp_file) *tmp_file = 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 {
if (commit_items && commit_items->elts) {
int i;
struct svn_client_commit_item_t **cis =
(struct svn_client_commit_item_t **) commit_items->elts;
push_array (allocate_array (commit_items->nelts));
for (i=0; i<commit_items->nelts; i++) {
svn_pike_push_utf8 (cis[i]->path);
push_int (cis[i]->kind);
svn_pike_push_utf8 (cis[i]->url);
push_int (cis[i]->revision);
svn_pike_push_utf8 (cis[i]->copyfrom_url);
push_int (cis[i]->state_flags);
f_aggregate (6);
assign_svalue (&ITEM(Pike_sp[-2].u.array)[i], Pike_sp-1);
pop_stack ();
}
} else
push_array (allocate_array (0));
apply_svalue ((struct svalue *)baton, 1);
if (Pike_sp[-1].type != PIKE_T_STRING)
Pike_error ("log_msg_func must return a string\n");
f_string_to_utf8 (1);
*log_msg = apr_pstrdup (pool, APR_STR0 (Pike_sp[-1].u.string));
pop_stack ();
}
UNSETJMP (recovery);
THREADS_ALLOW ();
} while (0);
return err;
}
/* C stub for calling status_func */
static void status_func_stub (void *baton,
const char *path,
svn_wc_status_t *status)
{
JMP_BUF recovery;
do {
struct thread_state *_tmp=thread_state_for_id (th_self ());
HIDE_GLOBAL_VARIABLES ();
THREADS_DISALLOW ();
if (SETJMP (recovery)) {
call_handle_error();
} else {
svn_pike_push_utf8 (path);
if(status) {
push_constant_text("text_status");
push_int(status->text_status);
push_constant_text("prop_status");
push_int(status->prop_status);
push_constant_text("locked");
push_int(status->locked);
push_constant_text("copied");
push_int(status->copied);
push_constant_text("switched");
push_int(status->switched);
push_constant_text("repos_text_status");
push_int(status->repos_text_status);
push_constant_text("repos_prop_status");
push_int(status->repos_prop_status);
if(status->entry) {
svn_wc_entry_t *entry = status->entry;
push_constant_text("name");
svn_pike_push_utf8(entry->name);
push_constant_text("revision");
push_int(entry->revision);
push_constant_text("url");
svn_pike_push_utf8(entry->url);
push_constant_text("repos");
svn_pike_push_utf8(entry->repos);
push_constant_text("kind");
push_int(entry->kind);
push_constant_text("schedule");
push_int(entry->schedule);
push_constant_text("copied");
push_int(entry->copied);
push_constant_text("deleted");
push_int(entry->deleted);
push_constant_text("copyfrom_url");
svn_pike_push_utf8(entry->copyfrom_url);
push_constant_text("copyfrom_rev");
push_int(entry->copyfrom_rev);
push_constant_text("conflict_old");
svn_pike_push_utf8(entry->conflict_old);
push_constant_text("conflict_new");
svn_pike_push_utf8(entry->conflict_new);
push_constant_text("conflict_wrk");
svn_pike_push_utf8(entry->conflict_wrk);
push_constant_text("prejfile");
svn_pike_push_utf8(entry->prejfile);
push_constant_text("text_time");
svn_pike_push_time(entry->text_time);
push_constant_text("prop_time");
svn_pike_push_time(entry->prop_time);
push_constant_text("checksum");
if(entry->checksum)
push_text(entry->checksum);
else
push_int(0);
push_constant_text("cmt_rev");
push_int(entry->cmt_rev);
push_constant_text("cmt_date");
svn_pike_push_time(entry->cmt_date);
push_constant_text("cmt_author");
svn_pike_push_utf8(entry->cmt_author);
f_aggregate_mapping(54);
} else
f_aggregate_mapping(14);
} else
push_int(0);
apply_svalue ((struct svalue *)baton, 2);
pop_stack ();
}
UNSETJMP (recovery);
THREADS_ALLOW ();
} while (0);
}
/* C stub for calling blame_receiver_func */
static svn_error_t *blame_receiver_func_stub (void *baton,
apr_int64_t line_no,
svn_revnum_t revision,
const char *author,
const char *date,
const char *line,
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_int64 (line_no);
push_int (revision);
svn_pike_push_utf8 (author);
svn_pike_push_utf8 (date);
svn_pike_push_utf8 (line);
apply_svalue ((struct svalue *)baton, 5);
pop_stack ();
}
UNSETJMP (recovery);
THREADS_ALLOW ();
} while (0);
return err;
}
/* C stub for calling cancel_func */
static svn_error_t *cancel_func_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_svalue ((struct svalue *)baton, 0);
pop_stack ();
}
UNSETJMP (recovery);
THREADS_ALLOW ();
} while (0);
return err;
}
/* Convert a commit_info struct to a Pike svalue */
static void make_commit_info (struct svalue *ret,
struct svn_client_commit_info_t *commit_info)
{
if (commit_info) {
push_int (commit_info->revision);
svn_pike_push_utf8 (commit_info->date);
svn_pike_push_utf8 (commit_info->author);
f_aggregate (3);
*ret = *--Pike_sp;
} else {
ret->u.integer = 0;
ret->type = PIKE_T_INT;
ret->subtype = NUMBER_NUMBER;
}
}
/* Convert an APR array of property hashes to a Pike mapping */
static void make_proplist (struct svalue *ret, apr_array_header_t *props)
{
if(props) {
svn_client_proplist_item_t **pis =
(svn_client_proplist_item_t **) props->elts;
int i;
for (i=0; i<props->nelts; i++) {
svn_stringbuf_t *name = pis[i]->node_name;
push_string (make_shared_binary_string (name->data, name->len));
f_utf8_to_string (1);
svn_pike_make_propmap (Pike_sp, pis[i]->prop_hash, -1);
Pike_sp ++;
}
f_aggregate_mapping(2*props->nelts);
*ret = *--Pike_sp;
} else {
ret->u.integer = 0;
ret->type = PIKE_T_INT;
ret->subtype = NUMBER_UNDEFINED;
}
}
/*! @decl int checkout( string URL, string path, Revision|void revision, @
*! int|void non_recursive )
*!
*! Checkout a working copy of a Subversion repository.
*!
*! @param URL
*! Repository URL to checkout.
*! @param path
*! Root directory of the newly checked out working copy.
*! @param revision
*! Revision number to checkout, or UNDEFINED for newset revision.
*! @param non_recursive
*! If non-zero, don't checkout subdirectories.
*!
*! @returns
*! The actual revision number that was checked out.
*/
static void f_checkout (INT32 args)
{
struct pike_string *url, *path;
svn_error_t *err;
apr_pool_t *pool;
svn_revnum_t ret_rev;
svn_opt_revision_t revision;
svn_boolean_t recurse;
svn_client_ctx_t *ctx = THIS->ctx;
get_all_args ("checkout", args, "%W%W", &url, &path);
svn_pike_check_rev_arg ("checkout", args, 2, &revision);
recurse = svn_pike_check_recurse_arg ("checkout", args, 3);
url = svn_pike_to_utf8 (url);
path = svn_pike_to_utf8 (path);
pool = svn_pool_create (NULL);
THREADS_ALLOW ();
err = svn_client_checkout (&ret_rev,
svn_path_canonicalize (APR_STR0 (url), pool),
svn_path_canonicalize (APR_STR0 (path), pool),
&revision, recurse, ctx, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
apr_pool_destroy (pool);
do_free_string (path);
do_free_string (url);
if (err)
pike_throw ();
else {
pop_n_elems (args);
push_int (ret_rev);
}
}
/*! @decl int update( string path, Revision|void revision, @
*! int|void nonrecursive )
*!
*! Update a working copy to a new revision.
*!
*! @param path
*! The path of the working tree.
*! @param revision
*! Revision to update to, or UNDEFINED for the latest revision.
*! @param non_recursive
*! If non-zero, don't update subdirectories.
*!
*! @returns
*! The actual revision number that the working copy was updated to.
*/
static void f_update (INT32 args)
{
struct pike_string *path;
svn_error_t *err;
apr_pool_t *pool;
svn_revnum_t ret_rev;
svn_opt_revision_t revision;
svn_boolean_t recurse;
svn_client_ctx_t *ctx = THIS->ctx;
get_all_args ("update", args, "%W", &path);
svn_pike_check_rev_arg ("update", args, 1, &revision);
recurse = svn_pike_check_recurse_arg ("update", args, 2);
path = svn_pike_to_utf8 (path);
pool = svn_pool_create (NULL);
THREADS_ALLOW ();
err = svn_client_update (&ret_rev, svn_path_canonicalize (APR_STR0 (path), pool),
&revision, recurse,
ctx, 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 (ret_rev);
}
}
/*! @decl int switch( string path, string URL, Revision|void revision, @
*! int|void nonrecursive )
*!
*! Switch a working tree to a different branch/tag.
*!
*! @param path
*! The path of the working tree.
*! @param URL
*! New repository URL for this working tree.
*! @param revision
*! Revision to switch to, or UNDEFINED for the latest revision.
*! @param non_recursive
*! If non-zero, don't switch subdirectories.
*!
*! @returns
*! The actual revision number that the working tree was switched to.
*/
static void f_switch (INT32 args)
{
struct pike_string *url, *path;
svn_error_t *err;
apr_pool_t *pool;
svn_revnum_t ret_rev;
svn_opt_revision_t revision;
svn_boolean_t recurse;
svn_client_ctx_t *ctx = THIS->ctx;
get_all_args ("switch", args, "%W%W", &path, &url);
svn_pike_check_rev_arg ("switch", args, 2, &revision);
recurse = svn_pike_check_recurse_arg ("switch", args, 3);
path = svn_pike_to_utf8 (path);
url = svn_pike_to_utf8 (url);
pool = svn_pool_create (NULL);
THREADS_ALLOW ();
err = svn_client_switch (&ret_rev, svn_path_canonicalize (APR_STR0 (path), pool),
svn_path_canonicalize (APR_STR0 (url), pool), &revision,
recurse, ctx, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
apr_pool_destroy (pool);
do_free_string (url);
do_free_string (path);
if (err)
pike_throw ();
else {
pop_n_elems (args);
push_int (ret_rev);
}
}
/*! @decl void add( string path, int|void non_recursive )
*!
*! Schedule a working copy file or directory for addition to
*! the repository. @[path]'s parent must be under revision control
*! already.
*!
*! @param path
*! The path of the file or directory to add.
*! @param non_recursive
*! If non-zero, don't schedule the contents of @[path] for
*! addition if it's a directory.
*/
static void f_add_ (INT32 args)
{
struct pike_string *path;
svn_error_t *err;
apr_pool_t *pool;
svn_boolean_t recurse;
svn_client_ctx_t *ctx = THIS->ctx;
get_all_args ("add", args, "%W", &path);
recurse = svn_pike_check_recurse_arg ("add", args, 1);
path = svn_pike_to_utf8 (path);
pool = svn_pool_create (NULL);
THREADS_ALLOW ();
err = svn_client_add (svn_path_canonicalize (APR_STR0 (path), pool), recurse,
ctx, 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 array(int|string) mkdir( array(string) paths_or_urls )
*!
*! If @[paths_or_urls] contains URLs, immediately attempt to commit the
*! creation of those directory URL in the repository. Else, create
*! the directories on disk, and attempt to schedule it for addition.
*!
*! @param paths_or_urls
*! Paths of directories to schedule for addition, or URLs of directories
*! to create in the repository.
*!
*! @returns
*! If @[paths_or_urls] contains at least one URL, an array containing
*! information about the commit is returned. The elements are as follows:
*! @array
*! @elem int revision
*! The revision in which the directory(/ies) was created.
*! @elem string date
*! The server-side date of the commit.
*! @elem string author
*! The author of the commit.
*! @endarray
*!
*! @seealso
*! @[add()]
*/
static void f_mkdir (INT32 args)
{
struct array *targets;
apr_array_header_t *targets_aa;
svn_error_t *err;
apr_pool_t *pool;
struct svn_client_commit_info_t *commit_info = NULL;
struct svalue ret;
svn_client_ctx_t *ctx = THIS->ctx;
get_all_args ("mkdir", args, "%a", &targets);
pool = svn_pool_create (NULL);
targets_aa = svn_pike_make_targets_array (targets, pool, FALSE);
THREADS_ALLOW ();
err = svn_client_mkdir (&commit_info, targets_aa, ctx, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
else
make_commit_info(&ret, commit_info);
apr_pool_destroy (pool);
if (err)
pike_throw ();
else {
pop_n_elems (args);
*Pike_sp++ = ret;
}
}
/*! @decl array(int|string) delete( array(string) paths_or_urls, int|void force )
*!
*! If @[paths_or_urls] contain URLs, immediately attempt to commit the
*! deletion of those URLs from the repository. Else, schedule the
*! specified working copy paths for removal from the repository.
*!
*! @param paths_or_urls
*! Paths to schedule for deletion, or URLs to immediately
*! remove in the repository.
*! @param force
*! Unless set, the operation will fail if a working copy path
*! that contains locally modified and/or unversioned items is
*! specified.
*!
*! @returns
*! If @[paths_or_urls] contains at least one URL, an array containing
*! information about the commit is returned. The elements are as follows:
*! @array
*! @elem int revision
*! The revision in which the item(s) was removed.
*! @elem string date
*! The server-side date of the commit.
*! @elem string author
*! The author of the commit.
*! @endarray
*/
static void f_delete (INT32 args)
{
struct array *targets;
apr_array_header_t *targets_aa;
svn_error_t *err;
apr_pool_t *pool;
struct svn_client_commit_info_t *commit_info = NULL;
struct svalue ret;
svn_boolean_t force;
svn_client_ctx_t *ctx = THIS->ctx;
get_all_args ("delete", args, "%a", &targets);
force = svn_pike_check_force_arg ("delete", args, 1);
pool = svn_pool_create (NULL);
targets_aa = svn_pike_make_targets_array (targets, pool, FALSE);
THREADS_ALLOW ();
err = svn_client_delete (&commit_info, targets_aa, force, ctx, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
else
make_commit_info (&ret, commit_info);
apr_pool_destroy (pool);
if (err)
pike_throw ();
else {
pop_n_elems (args);
*Pike_sp++ = ret;
}
}
/*! @decl array(int|string) import( string path, string URL, @
*! int|void non_recursive )
*!
*! Import a file or directory into the repository directory @[URL] at
*! head.
*!
*! @param path
*! The path of the file or directory to import.
*! @param URL
*! The URL of the repository directory in which to place the
*! imported item(s).
*! @param non_recursive
*! If non-zero, don't import subdirectories.
*!
*! @returns
*! An array containing information about the commit is returned.
*! The elements are as follows:
*! @array
*! @elem int revision
*! The revision in which the item was imported.
*! @elem string date
*! The server-side date of the commit.
*! @elem string author
*! The author of the commit.
*! @endarray
*/
static void f_import (INT32 args)
{
struct pike_string *path, *url;
svn_error_t *err;
apr_pool_t *pool;
struct svn_client_commit_info_t *commit_info = NULL;
struct svalue ret;
svn_boolean_t recurse;
svn_client_ctx_t *ctx = THIS->ctx;
get_all_args ("import", args, "%W%W", &path, &url);
recurse = svn_pike_check_recurse_arg ("import", args, 2);
path = svn_pike_to_utf8 (path);
url = svn_pike_to_utf8 (url);
pool = svn_pool_create (NULL);
THREADS_ALLOW ();
err = svn_client_import (&commit_info,
svn_path_canonicalize (APR_STR0 (path), pool),
svn_path_canonicalize (APR_STR0 (url), pool),
!recurse, ctx, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
else
make_commit_info (&ret, commit_info);
apr_pool_destroy (pool);
do_free_string (url);
do_free_string (path);
if (err)
pike_throw ();
else {
pop_n_elems (args);
*Pike_sp++ = ret;
}
}
/*! @decl array(int|string) commit( array(string) targets, @
*! int|void non_recursive )
*!
*! Commit a set of files and directorues to the repository at head.
*!
*! @param targets
*! The paths of the files and directories to commit.
*! @param non_recursive
*! If non-zero, don't commit subdirectories.
*!
*! @returns
*! An array containing information about the commit is returned.
*! The elements are as follows:
*! @array
*! @elem int revision
*! The revision in which the targets were committed.
*! @elem string date
*! The server-side date of the commit.
*! @elem string author
*! The author of the commit.
*! @endarray
*/
static void f_commit (INT32 args)
{
struct array *targets;
apr_array_header_t *targets_aa;
svn_error_t *err;
apr_pool_t *pool;
struct svn_client_commit_info_t *commit_info = NULL;
struct svalue ret;
svn_boolean_t recurse;
svn_client_ctx_t *ctx = THIS->ctx;
get_all_args ("commit", args, "%a", &targets);
recurse = svn_pike_check_recurse_arg ("commit", args, 1);
pool = svn_pool_create (NULL);
targets_aa = svn_pike_make_targets_array (targets, pool, FALSE);
THREADS_ALLOW ();
err = svn_client_commit (&commit_info, targets_aa,
!recurse, ctx, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
else
make_commit_info (&ret, commit_info);
apr_pool_destroy (pool);
if (err)
pike_throw ();
else {
pop_n_elems (args);
*Pike_sp++ = ret;
}
}
/*! @decl int status( string path, @
*! function(string, mapping(string:mixed):void) status_func, @
*! int|void non_recursive, @
*! int|void all, int|void update, @
*! Revision|void revision, @
*! int|void no_ignore )
*!
*! Request status information for a file or directory. The status
*! information will be passed to the function @[status_func].
*!
*! @param path
*! The file or directory to get status for.
*! @param status_func
*! A function to invoke once for each file that status is queried on.
*! The parameters to this function are as follows:
*! @array
*! @elem string path
*! The path of the entry that this status information concerns
*! @elem mapping(string:mixed) status
*! A mapping containing status information about the entry.
*! The values in the mapping are as follows:
*! @mapping
*! @member int text_status
*! Status of the textual component.
*! @member int prop_status
*! Status of the property component.
*! @member int locked
*! Entry is locked.
*! @member int copied
*! Entry is copied.
*! @member int switched
*! Entry is switched.
*! @member int repos_text_status
*! Status of the textual component in the repository.
*! @member int repos_prop_status
*! Status of the property component in the repository.
*! @member string name
*! The entry's name.
*! @member int revision
*! Base revision.
*! @member string url
*! URL in repository.
*! @member string repos
*! Canonical repository URL.
*! @member int kind
*! Node kind (file, dir, ...).
*! @member int schedule
*! Scheduling (add, delete, replace ...).
*! @member int copied
*! In a copied state.
*! @member int deleted
*! In a deleted state.
*! @member string copyfrom_url
*! Copyfrom location.
*! @member int copyfrom_rev
*! Copyfrom revision.
*! @member string conflict_old
*! Old version of conflicted file.
*! @member string conflict_new
*! New version of conflicted file.
*! @member string conflict_wrk
*! Wroking version of conflicted file.
*! @member string prejfile
*! Property reject file
*! @member int text_time
*! Last up-to-date time for text contents.
*! @member int prop_time
*! Last up-to-date time for properties.
*! @member string checksum
*! Base64-encoded checksum for the untranslated text base file.
*! @member int cmt_rev
*! Last revision this was changed.
*! @member int cmt_date
*! Last date this was changed.
*! @member string cmt_author
*! Last commit author of this item.
*! @endmapping
*! @endarray
*! @param non_recursive
*! If non-zero, don't query subdirectories.
*! @param all
*! If set, all entries are retrieved; otherwise only "interesting"
*! entries (local mods and/or out-of-date) will be fetched.
*! @param update
*! If set, the repository will be contacted, so that the status
*! structure is augmented with information about out-of-dateness
*! (with respect to @[revision]).
*! @param revision
*! When @[update] is specified, this argument can be used to
*! select the revision against which out-of-dateness is checked.
*! If @[update] is zero, this argument is not used.
*! @param no_ignore
*! If set, the svn:ignore property is ignored.
*!
*! @returns
*! The revision number against which out-of-datedness was checked,
*! if @[update] is set. If @[update] is not set, zero is returned.
*/
static void f_status (INT32 args)
{
struct pike_string *path;
svn_error_t *err;
apr_pool_t *pool;
svn_revnum_t ret_rev = SVN_INVALID_REVNUM;
svn_opt_revision_t revision;
svn_boolean_t recurse, get_all, update, no_ignore;
struct svalue *status_func;
svn_client_ctx_t *ctx = THIS->ctx;
get_all_args ("status", args, "%W%*", &path, &status_func);
recurse = svn_pike_check_recurse_arg ("status", args, 2);
get_all = svn_pike_check_force_arg ("status", args, 3);
update = svn_pike_check_force_arg ("status", args, 4);
no_ignore = svn_pike_check_force_arg ("status", args, 6);
svn_pike_check_rev_arg ("status", args, 5, &revision);
path = svn_pike_to_utf8 (path);
pool = svn_pool_create (NULL);
THREADS_ALLOW ();
err = svn_client_status (&ret_rev, svn_path_canonicalize (APR_STR0 (path), pool),
&revision, status_func_stub, status_func,
recurse, get_all, update, no_ignore,
ctx, 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 (update? ret_rev : 0);
}
}
/*! @decl void log( array(string) targets, @
*! function(int, string, string, string, @
*! mapping(string:array(int|string))|void:void) log_func, @
*! Revision start_revision, Revision end_revision, @
*! int|void discover_changed_paths, @
*! int|void strict_node_history )
*!
*! Call @[log_func] on each log message from start_revision
*! to end_revision in turn, inclusive.
*!
*! @param targets
*! The paths of the files and directories for which log messages
*! are desired.
*! @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_revision
*! The oldest revision for which log messages are desired.
*! @param end_revision
*! The newest revision for which log messages are desired.
*! @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 while harvest revision
*! logs for each target.
*/
static void f_log (INT32 args)
{
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;
svn_opt_revision_t start, end;
struct svalue *log_receiver_func;
svn_client_ctx_t *ctx = THIS->ctx;
get_all_args ("log", args, "%a%*", &targets, &log_receiver_func);
svn_pike_check_rev_arg ("log", args, 1, &start);
svn_pike_check_rev_arg ("log", args, 2, &end);
discover_changed_paths = svn_pike_check_force_arg ("log", args, 3);
strict_node_history = svn_pike_check_force_arg ("log", args, 4);
pool = svn_pool_create (NULL);
targets_aa = svn_pike_make_targets_array (targets, pool, FALSE);
THREADS_ALLOW ();
err = svn_client_log (targets_aa, &start, &end,
discover_changed_paths, strict_node_history,
svn_pike_log_receiver_func_stub, log_receiver_func,
ctx, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
apr_pool_destroy (pool);
if (err)
pike_throw ();
else
pop_n_elems (args);
}
/*! @decl void blame( string path_or_url, @
*! function(int,int,string,string,string:void) receiver, @
*! Revision start_revision, Revision end_revision )
*!
*! Call @[receiver] on each line-blame item associated with revision
*! @[end_revision] of @[path_or_url], using @[start_revision] as the
*! default source of all blame.
*!
*! @param path_or_url
*! Path or URL of the node to examine for blame.
*! @param receiver
*! A function to call for each line-blame item. The parameters
*! are as follows:
*! @array
*! @elem int line_no
*! Line number
*! @elem int revision
*! Revision number
*! @elem string author
*! The author of this line
*! @elem string date
*! Commit datestamp of this line
*! @elem string line
*! The text contents of the line
*! @endarray
*! @param start_revision
*! The default source of all blame.
*! @param end_revision
*! The revision of @[path_or_url] for which to determine blame.
*/
static void f_blame (INT32 args)
{
struct pike_string *path_or_url;
svn_error_t *err;
apr_pool_t *pool;
svn_opt_revision_t start, end;
struct svalue *blame_receiver_func;
svn_client_ctx_t *ctx = THIS->ctx;
get_all_args ("blame", args, "%W%*", &path_or_url, &blame_receiver_func);
svn_pike_check_rev_arg ("log", args, 2, &start);
svn_pike_check_rev_arg ("log", args, 3, &end);
path_or_url = svn_pike_to_utf8 (path_or_url);
pool = svn_pool_create (NULL);
THREADS_ALLOW ();
err = svn_client_blame (svn_path_canonicalize (APR_STR0 (path_or_url), pool),
&start, &end, blame_receiver_func_stub,
blame_receiver_func, ctx, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
apr_pool_destroy (pool);
do_free_string (path_or_url);
if (err)
pike_throw ();
else
pop_n_elems (args);
}
/*! @decl void diff( string path1, Revision revision1, @
*! string path2, Revision revision2, @
*! array(string)|void diff_options, @
*! Stdio.File|void outfile, Stdio.File|void errfile, @
*! int|void non_recursive, int|void diff_deleted, @
*! int|void ignore_ancestry );
*!
*! Produce diff output which describes the delta between
*! @[path1]/@[revision1] and @[path2]/@[revision2].
*!
*! @param path1
*! The path of the first file to compare.
*! @param revision1
*! The revision of the first file to compare.
*! @param path2
*! The path of the second file to compare.
*! @param revision2
*! The revision of the second file to compare.
*! @param diff_options
*! Additional command line options to the diff processes invoked
*! to compare files.
*! @param outfile
*! File to print diff output to. If not specified, @[Stdio.stdout]
*! will be used.
*! @param errfile
*! File to print diff errors to. If not specified, @[Stdio.stderr]
*! will be used.
*! @param non_recursive
*! If non-zero, don't compare subdirectories.
*! @param diff_deleted
*! If non-zero, generate diff output on deleted files.
*! @param ignore_ancestry
*! Controls 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 flag is non-zero, unrelated items will be diffed as
*! if they were related.
*/
static void f_diff_ (INT32 args)
{
struct array *options = NULL;
apr_array_header_t *options_aa;
svn_error_t *err;
apr_pool_t *pool;
struct svalue dummy;
svn_boolean_t recurse, diff_deleted, ignore_ancestry;
struct pike_string *path1, *path2;
svn_opt_revision_t rev1, rev2;
apr_file_t *outfile, *errfile;
apr_os_file_t outosfile, errosfile;
svn_client_ctx_t *ctx = THIS->ctx;
get_all_args ("diff", args, (args>4? "%W%*%W%*%A":"%W%*%W%*"),
&path1, &dummy, &path2, &dummy, &options);
svn_pike_check_rev_arg ("diff", args, 1, &rev1);
svn_pike_check_rev_arg ("diff", args, 3, &rev2);
outosfile = svn_pike_check_file_arg ("diff", args, 5);
errosfile = svn_pike_check_file_arg ("diff", args, 6);
recurse = svn_pike_check_recurse_arg ("diff", args, 7);
diff_deleted = svn_pike_check_force_arg ("diff", args, 8);
ignore_ancestry = svn_pike_check_force_arg ("diff", args, 9);
path1 = svn_pike_to_utf8 (path1);
path2 = svn_pike_to_utf8 (path2);
pool = svn_pool_create (NULL);
if((outosfile == NO_FILE_SPECIFIED?
apr_file_open_stdout (&outfile, pool) :
apr_os_file_put (&outfile, &outosfile, 0, pool)) ||
(errosfile == NO_FILE_SPECIFIED?
apr_file_open_stderr (&errfile, pool) :
apr_os_file_put (&errfile, &errosfile, 0, pool))) {
apr_pool_destroy (pool);
do_free_string (path1);
do_free_string (path2);
Pike_error ("Failed to open stdout/stderr\n");
}
options_aa = svn_pike_make_targets_array (options, pool, TRUE);
THREADS_ALLOW ();
err = svn_client_diff (options_aa, svn_path_canonicalize (APR_STR0(path1), pool),
&rev1, svn_path_canonicalize (APR_STR0(path2), pool),
&rev2, recurse, ignore_ancestry, !diff_deleted,
outfile, errfile, ctx, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
apr_pool_destroy (pool);
do_free_string (path1);
do_free_string (path2);
if (err)
pike_throw ();
else
pop_n_elems (args);
}
/*! @decl void diff_peg( string path, Revision peg_revision, @
*! Revision start_revision, Revision end_revision, @
*! array(string)|void diff_options, @
*! Stdio.File|void outfile, Stdio.File|void errfile, @
*! int|void non_recursive, int|void diff_deleted, @
*! int|void ignore_ancestry );
*!
*! Produce diff output which describes the delta between the
*! filesystem object @[path] in peg revision @[peg_revision], as it
*! changed between @[start_revision] and @[end_revision].
*!
*! @param path
*! The path of the file system object.
*! @param peg_revision
*! The peg revision of @[path].
*! @param start_revision
*! The first revision of the delta.
*! @param end_revision
*! The second revision of the delta.
*! @param diff_options
*! Additional command line options to the diff processes invoked
*! to compare files.
*! @param outfile
*! File to print diff output to. If not specified, @[Stdio.stdout]
*! will be used.
*! @param errfile
*! File to print diff errors to. If not specified, @[Stdio.stderr]
*! will be used.
*! @param non_recursive
*! If non-zero, don't compare subdirectories.
*! @param diff_deleted
*! If non-zero, generate diff output on deleted files.
*! @param ignore_ancestry
*! Controls 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 flag is non-zero, unrelated items will be diffed as
*! if they were related.
*/
static void f_diff_peg (INT32 args)
{
struct array *options = NULL;
apr_array_header_t *options_aa;
svn_error_t *err;
apr_pool_t *pool;
struct svalue dummy;
svn_boolean_t recurse, diff_deleted, ignore_ancestry;
struct pike_string *path;
svn_opt_revision_t peg_rev, start_rev, end_rev;
apr_file_t *outfile, *errfile;
apr_os_file_t outosfile, errosfile;
svn_client_ctx_t *ctx = THIS->ctx;
get_all_args ("diff_peg", args, (args>4? "%W%*%*%*%A":"%W%*%*%*"),
&path, &dummy, &dummy, &dummy, &options);
svn_pike_check_rev_arg ("diff_peg", args, 1, &peg_rev);
svn_pike_check_rev_arg ("diff_peg", args, 2, &start_rev);
svn_pike_check_rev_arg ("diff_peg", args, 3, &end_rev);
outosfile = svn_pike_check_file_arg ("diff_peg", args, 5);
errosfile = svn_pike_check_file_arg ("diff_peg", args, 6);
recurse = svn_pike_check_recurse_arg ("diff_peg", args, 7);
diff_deleted = svn_pike_check_force_arg ("diff_peg", args, 8);
ignore_ancestry = svn_pike_check_force_arg ("diff_peg", args, 9);
path = svn_pike_to_utf8 (path);
pool = svn_pool_create (NULL);
if((outosfile == NO_FILE_SPECIFIED?
apr_file_open_stdout (&outfile, pool) :
apr_os_file_put (&outfile, &outosfile, 0, pool)) ||
(errosfile == NO_FILE_SPECIFIED?
apr_file_open_stderr (&errfile, pool) :
apr_os_file_put (&errfile, &errosfile, 0, pool))) {
apr_pool_destroy (pool);
do_free_string (path);
Pike_error ("Failed to open stdout/stderr\n");
}
options_aa = svn_pike_make_targets_array (options, pool, TRUE);
THREADS_ALLOW ();
err = svn_client_diff_peg (options_aa,
svn_path_canonicalize (APR_STR0(path), pool),
&peg_rev, &start_rev, &end_rev, recurse,
ignore_ancestry, !diff_deleted, outfile, errfile,
ctx, 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 merge( string source1, Revision revision1, @
*! string source2, Revision revision2, @
*! string target_wcpath, int|void non_recursive, @
*! int|void force, int|void dry_run, @
*! int|void ignore_ancestry )
*!
*! Merge changes from @[source1]/@[revision1] to @[source2]/@[revision2]
*! into the working-copy path @[target_wcpath].
*!
*! @param source1
*! The repository URL or working copy path of the first file to compare.
*! @param revision1
*! The revision of the first file to compare.
*! @param source2
*! The repository URL or working copy path of the second file to compare.
*! @param revision2
*! The revision of the second file to compare.
*! @param target_wcpath
*! The working-copy path of the file into which to merge the changes
*! from @[source1]/@[revision1] to @[source2]/@[revision2].
*! @param non_recursive
*! If non-zero, don't merge subdirectories.
*! @param force
*! If non-zero, files deleted between @[source1]/@[revision1] and
*! @[source2]/@[revision2] will be deleted in @[target_wcpath] even
*! if they have local modifications.
*! @param dry_run
*! If non-zero, the merge is carried out, and full notification
*! feedback is provided, but the working copy is not modified.
*! @param ignore_ancestry
*! Controls 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 flag is non-zero, unrelated items will be diffed as if
*! they were related.
*/
static void f_merge (INT32 args)
{
svn_error_t *err;
apr_pool_t *pool;
struct svalue dummy;
svn_boolean_t recurse, force, dry_run, ignore_ancestry;
struct pike_string *url1, *url2, *path;
svn_opt_revision_t rev1, rev2;
svn_client_ctx_t *ctx = THIS->ctx;
get_all_args ("merge", args, "%W%*%W%*%W",
&url1, &dummy, &url2, &dummy, &path);
svn_pike_check_rev_arg ("merge", args, 1, &rev1);
svn_pike_check_rev_arg ("merge", args, 3, &rev2);
recurse = svn_pike_check_recurse_arg ("merge", args, 5);
force = svn_pike_check_force_arg ("merge", args, 6);
dry_run = svn_pike_check_force_arg ("merge", args, 7);
ignore_ancestry = svn_pike_check_force_arg ("merge", args, 8);
url1 = svn_pike_to_utf8 (url1);
url2 = svn_pike_to_utf8 (url2);
path = svn_pike_to_utf8 (path);
pool = svn_pool_create (NULL);
THREADS_ALLOW ();
err = svn_client_merge (svn_path_canonicalize (APR_STR0(url1), pool), &rev1,
svn_path_canonicalize (APR_STR0(url2), pool), &rev2,
svn_path_canonicalize (APR_STR0(path), pool), recurse,
ignore_ancestry, force, dry_run, ctx, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
apr_pool_destroy (pool);
do_free_string (url1);
do_free_string (url2);
do_free_string (path);
if (err)
pike_throw ();
else
pop_n_elems (args);
}
/*! @decl void merge_peg( string source, Revision revision1, @
*! Revision revision2, Revision peg_revision, @
*! string target_wcpath, int|void non_recursive, @
*! int|void force, int|void dry_run, @
*! int|void ignore_ancestry )
*!
*! Merge the changes between the filesystem object @[source] in peg
*! revision @[peg_revision], as it changed between @[revision1] and
*! @[revision2].
*!
*! @param source
*! The repository URL or working copy path of the filesystem object.
*! @param revision1
*! The start revision of the change to merge.
*! @param revision2
*! The end revision of the change to merge.
*! @param peg_revision
*! The peg revision of @[source].
*! @param target_wcpath
*! The working-copy path of the file into which to merge the changes
*! from @[revision1] to @[revision2].
*! @param non_recursive
*! If non-zero, don't merge subdirectories.
*! @param force
*! If non-zero, files deleted between @[revision1] and
*! @[revision2] will be deleted in @[target_wcpath] even if they have
*! local modifications.
*! @param dry_run
*! If non-zero, the merge is carried out, and full notification
*! feedback is provided, but the working copy is not modified.
*! @param ignore_ancestry
*! Controls 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 flag is non-zero, unrelated items will be diffed as if
*! they were related.
*/
static void f_merge_peg (INT32 args)
{
svn_error_t *err;
apr_pool_t *pool;
struct svalue dummy;
svn_boolean_t recurse, force, dry_run, ignore_ancestry;
struct pike_string *source, *path;
svn_opt_revision_t rev1, rev2, peg_rev;
svn_client_ctx_t *ctx = THIS->ctx;
get_all_args ("merge_peg", args, "%W%*%*%*%W",
&source, &dummy, &dummy, &dummy, &path);
svn_pike_check_rev_arg ("merge_peg", args, 1, &rev1);
svn_pike_check_rev_arg ("merge_peg", args, 2, &rev2);
svn_pike_check_rev_arg ("merge_peg", args, 3, &peg_rev);
recurse = svn_pike_check_recurse_arg ("merge_peg", args, 5);
force = svn_pike_check_force_arg ("merge_peg", args, 6);
dry_run = svn_pike_check_force_arg ("merge_peg", args, 7);
ignore_ancestry = svn_pike_check_force_arg ("merge_peg", args, 8);
source = svn_pike_to_utf8 (source);
path = svn_pike_to_utf8 (path);
pool = svn_pool_create (NULL);
THREADS_ALLOW ();
err = svn_client_merge_peg (svn_path_canonicalize (APR_STR0(source), pool),
&rev1, &rev2, &peg_rev,
svn_path_canonicalize (APR_STR0(path), pool),
recurse, ignore_ancestry, force, dry_run,
ctx, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
apr_pool_destroy (pool);
do_free_string (source);
do_free_string (path);
if (err)
pike_throw ();
else
pop_n_elems (args);
}
/*! @decl void cleanup( string dir )
*!
*! Recursively cleanup a working copy directory @[dir], finishing any
*! incomplete operations, removing lockfiles, etc.
*!
*! @param dir
*! The working-copy path of the directory to cleanup
*/
static void f_cleanup (INT32 args)
{
svn_error_t *err;
apr_pool_t *pool;
struct pike_string *path;
svn_client_ctx_t *ctx = THIS->ctx;
get_all_args ("cleanup", args, "%W", &path);
path = svn_pike_to_utf8 (path);
pool = svn_pool_create (NULL);
THREADS_ALLOW ();
err = svn_client_cleanup (svn_path_canonicalize (APR_STR0(path), pool), ctx,
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 relocate( string dir, string from, string to, @
*! int|void non_recursive )
*!
*! Modify a working copy directory @[dir], changing any
*! repository URLs that begin with @[from] to begin with @[to] instead,
*! recursing into subdirectories unless @[non_recursive] is non-zero.
*!
*! @param dir
*! Working copy directory
*! @param from
*! Original URL
*! @param to
*! New URL
*! @param non_recursive
*! If non-zero, don't relocate subdirectories.
*/
static void f_relocate (INT32 args)
{
svn_error_t *err;
apr_pool_t *pool;
svn_boolean_t recurse;
struct pike_string *dir, *from, *to;
svn_client_ctx_t *ctx = THIS->ctx;
get_all_args ("relocate", args, "%W%W%W", &dir, &from, &to);
recurse = svn_pike_check_recurse_arg ("relocate", args, 3);
dir = svn_pike_to_utf8 (dir);
from = svn_pike_to_utf8 (from);
to = svn_pike_to_utf8 (to);
pool = svn_pool_create (NULL);
THREADS_ALLOW ();
err = svn_client_relocate (svn_path_canonicalize (APR_STR0(dir), pool),
svn_path_canonicalize (APR_STR0(from), pool),
svn_path_canonicalize (APR_STR0(to), pool),
recurse, ctx, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
apr_pool_destroy (pool);
do_free_string (dir);
do_free_string (from);
do_free_string (to);
if (err)
pike_throw ();
else
pop_n_elems (args);
}
/*! @decl void revert( array(string) paths, int|void non_recursive )
*!
*! Restore the pristine version of a working copy @[paths], effectively
*! undoing any local modifications.
*!
*! @param paths
*! An array of working-copy paths of the files or directories to revert.
*! @param non_recursive
*! If non-zero, don't revert directory contents.
*/
static void f_revert (INT32 args)
{
struct array *paths;
apr_array_header_t *paths_aa;
svn_error_t *err;
apr_pool_t *pool;
svn_boolean_t recurse;
svn_client_ctx_t *ctx = THIS->ctx;
get_all_args ("revert", args, "%a", &paths);
recurse = svn_pike_check_recurse_arg ("revert", args, 1);
pool = svn_pool_create (NULL);
paths_aa = svn_pike_make_targets_array (paths, pool, FALSE);
THREADS_ALLOW ();
err = svn_client_revert (paths_aa, recurse, ctx, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
apr_pool_destroy (pool);
if (err)
pike_throw ();
else
pop_n_elems (args);
}
/*! @decl void resolved( string path, int|void non_recursive )
*!
*! Remove the 'conflicted' state on a working copy @[path].
*! This will not semantically resolve conflicts; it just allows
*! @[path] to be committed in the future. The implementation details
*! are opaque.
*!
*! @param path
*! The working-copy path of the file which is in a 'conflicted' state.
*! @param non_recursive
*! If non-zero, don't resolv conflicts in subdirectories.
*/
static void f_resolved (INT32 args)
{
svn_error_t *err;
apr_pool_t *pool;
svn_boolean_t recurse;
struct pike_string *path;
svn_client_ctx_t *ctx = THIS->ctx;
get_all_args ("resolved", args, "%W", &path);
recurse = svn_pike_check_recurse_arg ("resolved", args, 1);
path = svn_pike_to_utf8 (path);
pool = svn_pool_create (NULL);
THREADS_ALLOW ();
err = svn_client_resolved (svn_path_canonicalize (APR_STR0(path), pool),
recurse, ctx, 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 array(int|string) copy( string src_path_or_url, @
*! Revision src_revision, @
*! string dst_path_or_url )
*!
*! Copy @[src_path_or_url] to @[dst_path_or_url].
*!
*! @param src_path_or_url
*! Path to schedule for copying, or URL to immediately
*! copy in the repository.
*! @param src_revision
*! The revision of @[src_path_or_url] to copy.
*! @param dst_path_or_url
*! Path where to put the copy, or URL to immediately commit the copy
*! to the repository.
*!
*! @returns
*! If either @[src_path_or_url] or @[dst_path_or_url] is an URL, an
*! array containing information about the commit is returned.
*! The elements are as follows:
*! @array
*! @elem int revision
*! The revision in which the item was copied.
*! @elem string date
*! The server-side date of the commit.
*! @elem string author
*! The author of the commit.
*! @endarray
*/
static void f_copy (INT32 args)
{
struct pike_string *src_path_or_url, *dst_path_or_url;
svn_opt_revision_t rev;
svn_error_t *err;
apr_pool_t *pool;
struct svn_client_commit_info_t *commit_info = NULL;
struct svalue dummy, ret;
svn_client_ctx_t *ctx = THIS->ctx;
get_all_args ("copy", args, "%W%*%W", &src_path_or_url,
&dummy, &dst_path_or_url);
svn_pike_check_rev_arg ("copy", args, 1, &rev);
src_path_or_url = svn_pike_to_utf8 (src_path_or_url);
dst_path_or_url = svn_pike_to_utf8 (dst_path_or_url);
pool = svn_pool_create (NULL);
THREADS_ALLOW ();
err = svn_client_copy (&commit_info,
svn_path_canonicalize (APR_STR0 (src_path_or_url), pool),
&rev,
svn_path_canonicalize (APR_STR0 (dst_path_or_url), pool),
ctx, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
else
make_commit_info (&ret, commit_info);
apr_pool_destroy (pool);
do_free_string (src_path_or_url);
do_free_string (dst_path_or_url);
if (err)
pike_throw ();
else {
pop_n_elems (args);
*Pike_sp++ = ret;
}
}
/*! @decl array(int|string) move( string src_path_or_url, @
*! Revision src_revision, @
*! string dst_path_or_url, int|void force )
*!
*! Move @[src_path_or_url] to @[dst_path_or_url].
*!
*! @param src_path_or_url
*! Path to schedule for moving, or URL to immediately
*! move in the repository.
*! @param src_revision
*! The revision of @[src_path_or_url] to move.
*! @param dst_path_or_url
*! Path where to put the moved item, or URL to immediately commit the
*! move to the repository.
*! @param force
*! Unless set, the operation will fail if a working copy path
*! that contains locally modified and/or unversioned items is
*! specified as the destination.
*!
*! @returns
*! If either @[src_path_or_url] or @[dst_path_or_url] is an URL, an
*! array containing information about the commit is returned.
*! The elements are as follows:
*! @array
*! @elem int revision
*! The revision in which the item was moved.
*! @elem string date
*! The server-side date of the commit.
*! @elem string author
*! The author of the commit.
*! @endarray
*/
static void f_move (INT32 args)
{
struct pike_string *src_path_or_url, *dst_path_or_url;
svn_opt_revision_t rev;
svn_error_t *err;
apr_pool_t *pool;
struct svn_client_commit_info_t *commit_info = NULL;
struct svalue dummy, ret;
svn_boolean_t force;
svn_client_ctx_t *ctx = THIS->ctx;
get_all_args ("move", args, "%W%*%W", &src_path_or_url,
&dummy, &dst_path_or_url);
svn_pike_check_rev_arg ("move", args, 1, &rev);
force = svn_pike_check_force_arg ("move", args, 3);
src_path_or_url = svn_pike_to_utf8 (src_path_or_url);
dst_path_or_url = svn_pike_to_utf8 (dst_path_or_url);
pool = svn_pool_create (NULL);
THREADS_ALLOW ();
err = svn_client_move (&commit_info,
svn_path_canonicalize (APR_STR0 (src_path_or_url), pool),
&rev,
svn_path_canonicalize (APR_STR0 (dst_path_or_url), pool),
force, ctx, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
else
make_commit_info (&ret, commit_info);
apr_pool_destroy (pool);
do_free_string (src_path_or_url);
do_free_string (dst_path_or_url);
if (err)
pike_throw ();
else {
pop_n_elems (args);
*Pike_sp++ = ret;
}
}
/*! @decl void propset( string propname, string propval, string target, @
*! int|void non_recursive )
*!
*! Set property @[propname] to @[propval] on @[target].
*!
*! @param propname
*! The name of the property to set.
*! @param propval
*! The value to set the property to, or zero to delete it.
*! @param target
*! The path of the file or directory on which to set the property.
*! @param non_recursive
*! If non-zero, set the property only on target, even if it is a directory.
*/
static void f_propset (INT32 args)
{
svn_error_t *err;
apr_pool_t *pool;
struct pike_string *propname, *propval=NULL, *target;
struct svalue dummy;
svn_string_t propval_string;
svn_boolean_t recurse;
if(args > 1 && UNSAFE_IS_ZERO (&Pike_sp[1-args]))
get_all_args ("propset", args, "%W%*%W", &propname, &dummy, &target);
else
get_all_args ("propset", args, "%W%W%W", &propname, &propval, &target);
recurse = svn_pike_check_recurse_arg ("propset", args, 3);
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;
}
target = svn_pike_to_utf8 (target);
pool = svn_pool_create (NULL);
THREADS_ALLOW ();
err = svn_client_propset (APR_STR0(propname), (propval? &propval_string : NULL),
svn_path_canonicalize (APR_STR0(target), pool),
recurse, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
apr_pool_destroy (pool);
do_free_string (propname);
do_free_string (propval);
do_free_string (target);
if (err)
pike_throw ();
else
pop_n_elems (args);
}
/*! @decl int revprop_set( string propname, string propval, string URL, @
*! Revision revision, int|void force )
*!
*! Set property @[propname] to @[propval] on revision @[revision] in
*! the repository represented by @[URL].
*!
*! Note that unlike its cousin @[propset()], this function
*! doesn't affect the working copy at all; it's a pure network
*! operation that changes an @b{unversioned@} property attached to a
*! revision. This can be used to tweak log messages, dates, authors,
*! and the like. Be careful: it's a lossy operation.
*!
*! @param propname
*! The name of the property to set.
*! @param propval
*! The value to set the property to, or zero to delete it.
*! @param URL
*! The URL of the repository.
*! @param revision
*! The revision to set the property on.
*! @param force
*! If non-zero, allow newlines in the author property.
*!
*! @returns
*! The actual revision number in which the property was set.
*/
static void f_revprop_set (INT32 args)
{
svn_error_t *err;
apr_pool_t *pool;
struct svalue dummy;
struct pike_string *propname, *propval=NULL, *url;
svn_string_t propval_string;
svn_opt_revision_t rev;
svn_revnum_t ret_rev;
svn_boolean_t force;
svn_client_ctx_t *ctx = THIS->ctx;
if(args > 1 && UNSAFE_IS_ZERO (&Pike_sp[1-args]))
get_all_args ("revprop_set", args, "%W%*%W", &propname, &dummy, &url);
else
get_all_args ("revprop_set", args, "%W%W%W", &propname, &propval, &url);
svn_pike_check_rev_arg ("revprop_set", args, 3, &rev);
force = svn_pike_check_force_arg ("revprop_set", args, 4);
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;
}
url = svn_pike_to_utf8 (url);
pool = svn_pool_create (NULL);
THREADS_ALLOW ();
err = svn_client_revprop_set (APR_STR0(propname),
(propval? &propval_string : NULL),
svn_path_canonicalize (APR_STR0(url), pool),
&rev, &ret_rev, force, ctx, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
apr_pool_destroy (pool);
do_free_string (propname);
do_free_string (propval);
do_free_string (url);
if (err)
pike_throw ();
else {
pop_n_elems (args);
push_int (ret_rev);
}
}
/*! @decl mapping(string:string) propget( string propname, string target, @
*! int|void non_recursive, @
*! Revision|void revision )
*!
*! Get property @[propname] for @[target].
*!
*! @param propname
*! The name of the property to get.
*! @param target
*! The path of the file or directory for which to get the property.
*! @param non_recursive
*! If non-zero, get the property only for target, even if it is a directory.
*! @param revision
*! If specified, the revisison of the property to get. If no revision
*! is given, get the property from the working copy, or from the repository
*! head if the target is an URL.
*!
*! @returns
*! A mapping from pathname to property value, with one element for
*! each node under target which has the named property set.
*/
static void f_propget (INT32 args)
{
svn_error_t *err;
apr_pool_t *pool;
struct pike_string *propname, *target;
struct svalue ret;
svn_boolean_t recurse;
apr_hash_t *props = NULL;
svn_opt_revision_t rev;
svn_client_ctx_t *ctx = THIS->ctx;
get_all_args ("propget", args, "%W%W", &propname, &target);
recurse = svn_pike_check_recurse_arg ("propget", args, 2);
svn_pike_check_rev_arg ("propget", args, 3, &rev);
propname = svn_pike_to_utf8 (propname);
target = svn_pike_to_utf8 (target);
pool = svn_pool_create (NULL);
THREADS_ALLOW ();
err = svn_client_propget (&props, APR_STR0(propname),
svn_path_canonicalize (APR_STR0(target), pool),
&rev, recurse, ctx, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
else
svn_pike_make_propmap (&ret, props,
!!svn_prop_needs_translation (APR_STR0(propname)));
apr_pool_destroy (pool);
do_free_string (propname);
do_free_string (target);
if (err)
pike_throw ();
else {
pop_n_elems (args);
*Pike_sp++ = ret;
}
}
/*! @decl array(int|string) revprop_get( string propname, string URL, @
*! Revision revision )
*!
*! Get property @[propname] for revision @[revision] of the
*! repository represented by @[URL].
*!
*! Note that unlike its cousin @[propget()], this routine
*! doesn't affect the working copy at all; it's a pure network
*! operation that queries an @b{unversioned@} property attached to a
*! revision. This can be query log messages, dates, authors, and the
*! like.
*!
*! @param propname
*! The name of the property to get.
*! @param URL
*! The URL of the repository.
*! @param revision
*! The revision for which to get the property.
*!
*! @returns
*! An array with two elements:
*! @array
*! @elem int revision
*! The actual revision number from which the property value was fetched.
*! @elem string propval
*! The property value itself, or zero if the property was not set.
*! @endarray
*/
static void f_revprop_get (INT32 args)
{
svn_error_t *err;
apr_pool_t *pool;
struct pike_string *propname, *url;
svn_opt_revision_t rev;
svn_revnum_t ret_rev;
svn_string_t *propval_string = NULL;
struct pike_string *propval = NULL;
svn_boolean_t prop_is_utf;
svn_client_ctx_t *ctx = THIS->ctx;
get_all_args ("revprop_get", args, "%W%W", &propname, &url);
svn_pike_check_rev_arg ("revprop_get", args, 2, &rev);
propname = svn_pike_to_utf8 (propname);
url = svn_pike_to_utf8 (url);
pool = svn_pool_create (NULL);
prop_is_utf = svn_prop_needs_translation (APR_STR0(propname));
THREADS_ALLOW ();
err = svn_client_revprop_get (APR_STR0(propname), &propval_string,
svn_path_canonicalize (APR_STR0(url), pool),
&rev, &ret_rev, ctx, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
else if (propval_string)
propval = make_shared_binary_string(propval_string->data,
propval_string->len);
apr_pool_destroy (pool);
do_free_string (propname);
do_free_string (url);
if (err)
pike_throw ();
else {
pop_n_elems (args);
push_int (ret_rev);
if (propval) {
push_string (propval);
if (prop_is_utf)
f_utf8_to_string (1);
} else push_undefined();
f_aggregate (2);
}
}
/*! @decl mapping(string:mapping(string:string)) @
*! proplist( string target, int|void non_recursive, @
*! Revision|void revision )
*!
*! List all properties for @[target].
*!
*! @param target
*! The path of the file or directory for which to list all properties.
*! @param non_recursive
*! If non-zero, list only the properties for target, even if it is
*! a directory.
*! @param revision
*! If specified, the revisison of target to list the properties for.
*! If no revision is given, list the properties from the working copy,
*! or from the repository head if the target is an URL.
*!
*! @returns
*! A mapping from pathnames to property mappings, with one element for
*! each node under target which has properties. Each property mapping
*! is a mapping from property name to property value containing all
*! currently set properties for that node.
*/
static void f_proplist (INT32 args)
{
svn_error_t *err;
apr_pool_t *pool;
struct pike_string *target;
struct svalue ret;
svn_boolean_t recurse;
apr_array_header_t *props = NULL;
svn_opt_revision_t rev;
svn_client_ctx_t *ctx = THIS->ctx;
get_all_args ("proplist", args, "%W", &target);
recurse = svn_pike_check_recurse_arg ("proplist", args, 1);
svn_pike_check_rev_arg ("proplist", args, 2, &rev);
target = svn_pike_to_utf8 (target);
pool = svn_pool_create (NULL);
THREADS_ALLOW ();
err = svn_client_proplist (&props,
svn_path_canonicalize (APR_STR0(target), pool),
&rev, recurse, ctx, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
else
make_proplist (&ret, props);
apr_pool_destroy (pool);
do_free_string (target);
if (err)
pike_throw ();
else {
pop_n_elems (args);
*Pike_sp++ = ret;
}
}
/*! @decl array(int|mapping(string:string)) revprop_list( string URL, @
*! Revision revision )
*!
*! List all properties for revision @[revision] of the repository
*! represented by @[URL].
*!
*! Note that unlike its cousin @[proplist()], this function
*! doesn't read a working copy at all; it's a pure network operation
*! that reads @b{unversioned@} properties attached to a revision.
*!
*! @param URL
*! The URL of the repository.
*! @param revision
*! The revision to list the properties of.
*!
*! @returns
*! An array with two elements:
*! @array
*! @elem int revision
*! The actual revision number for which the property values were listed.
*! @elem mapping(string:string) props
*! A mapping from property name to property value containing all
*! the properties of this revision.
*! @endarray
*/
static void f_revprop_list (INT32 args)
{
svn_error_t *err;
apr_pool_t *pool;
struct svalue ret;
struct pike_string *url;
svn_opt_revision_t rev;
svn_revnum_t ret_rev;
apr_hash_t *props = NULL;
svn_client_ctx_t *ctx = THIS->ctx;
get_all_args ("revprop_list", args, "%W", &url);
svn_pike_check_rev_arg ("revprop_list", args, 1, &rev);
url = svn_pike_to_utf8 (url);
pool = svn_pool_create (NULL);
THREADS_ALLOW ();
err = svn_client_revprop_list (&props,
svn_path_canonicalize (APR_STR0(url), pool),
&rev, &ret_rev, ctx, 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 (url);
if (err)
pike_throw ();
else {
pop_n_elems (args);
push_int (ret_rev);
*Pike_sp++ = ret;
f_aggregate (2);
}
}
/*! @decl int export( string from, string to, Revision|void revision, @
*! int|void force, string|void native_eol )
*!
*! Export the contents of either a subversion repository or
*! a subversion working copy into a 'clean' directory (meaning a
*! directory with no administrative directories).
*!
*! @param from
*! Either the path of a working copy on disk, or an URL to the
*! repository you wish to export.
*! @param to
*! The path to the directory where you wish to create the exported tree.
*! @param revision
*! Revision number to export (only used when exporting from repository).
*! @param force
*! If non-zero, causes the export to overwrite files or directories.
*! @param native_eol
*! Allows you to override the standard eol marker on the platform you
*! are running on. If zero, the standard eol marker will be used.
*! @dl
*! @item '"LF"'
*! The eol marker is line feed.
*! @item '"CR"'
*! The eol marker is carriage return.
*! @item '"CRLF"'
*! The eol marker is carriage return followed by line feed.
*! @enddl
*!
*! @returns
*! The actual revision number that was exported out, if @[from] is an
*! URL. For local exports, -1 is returned.
*/
static void f_export (INT32 args)
{
struct pike_string *src_path, *dst_path, *native_eol = NULL;
svn_error_t *err;
apr_pool_t *pool;
svn_revnum_t ret_rev = SVN_INVALID_REVNUM;
svn_opt_revision_t revision;
svn_boolean_t force;
svn_client_ctx_t *ctx = THIS->ctx;
get_all_args ("export", args, "%W%W", &src_path, &dst_path);
svn_pike_check_rev_arg ("export", args, 2, &revision);
force = svn_pike_check_force_arg ("export", args, 3);
if (args > 4 && Pike_sp[4-args].type == PIKE_T_STRING)
native_eol = Pike_sp[4-args].u.string;
src_path = svn_pike_to_utf8 (src_path);
dst_path = svn_pike_to_utf8 (dst_path);
native_eol = svn_pike_to_utf8 (native_eol);
pool = svn_pool_create (NULL);
THREADS_ALLOW ();
err = svn_client_export2 (&ret_rev,
svn_path_canonicalize (APR_STR0 (src_path), pool),
svn_path_canonicalize (APR_STR0 (dst_path), pool),
&revision, force,
(native_eol? APR_STR0 (native_eol) : NULL), ctx, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
apr_pool_destroy (pool);
do_free_string (src_path);
do_free_string (dst_path);
do_free_string (native_eol);
if (err)
pike_throw ();
else {
pop_n_elems (args);
push_int (ret_rev);
}
}
/*! @decl mapping(string:array) ls( string URL, Revision|void revision, @
*! int|void nonrecursive )
*!
*! List a repository directory or file.
*!
*! @param URL
*! The URL of the repository item to list.
*! @param revision
*! Revision to list, or UNDEFINED for the latest revision.
*! @param non_recursive
*! If non-zero, don't list contents of subdirectories.
*!
*! @returns
*! A mapping from entry name to entry information, or UNDEFINED
*! if no such entry exists in the repository (at the specified revision).
*! The values in the mapping are as follows:
*! @array
*! @elem int kind
*! Node kind (dir, file), see @[RA.Library.Session.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
*/
static void f_ls (INT32 args)
{
struct pike_string *url;
svn_error_t *err;
apr_pool_t *pool;
svn_opt_revision_t revision;
apr_hash_t *dirents = NULL;
struct svalue ret;
svn_boolean_t recurse;
svn_client_ctx_t *ctx = THIS->ctx;
get_all_args ("ls", args, "%W", &url);
svn_pike_check_rev_arg ("ls", args, 1, &revision);
recurse = svn_pike_check_recurse_arg ("ls", args, 2);
url = svn_pike_to_utf8 (url);
pool = svn_pool_create (NULL);
THREADS_ALLOW ();
err = svn_client_ls (&dirents, svn_path_canonicalize (APR_STR0 (url), pool),
&revision, recurse, ctx, pool);
THREADS_DISALLOW ();
if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND) {
dirents = NULL;
err = NULL;
}
if (err)
svn_pike_set_error (err);
else
svn_pike_make_dirents (&ret, dirents);
apr_pool_destroy (pool);
do_free_string (url);
if (err)
pike_throw ();
else {
pop_n_elems (args);
*Pike_sp++ = ret;
}
}
/*! @decl void cat( Stream out, string path_or_url, Revision|void revision )
*!
*! Output the content of a file to a stream.
*!
*! @param out
*! The stream on which to output the file's contents.
*! @param path_or_url
*! Identifies the file to output.
*! @param revision
*! Revision of the contents to output, or UNDEFINED for current contents.
*/
static void f_cat (INT32 args)
{
struct pike_string *path_or_url;
svn_error_t *err;
svn_stream_t *stream_stub;
apr_pool_t *pool;
svn_opt_revision_t revision;
svn_client_ctx_t *ctx = THIS->ctx;
struct object *out_obj;
get_all_args ("cat", args, "%o%W", &out_obj, &path_or_url);
svn_pike_check_rev_arg ("cat", args, 2, &revision);
svn_pike_check_stream_arg ("cat", args, 0, &stream_stub,
NULL, &pool);
path_or_url = svn_pike_to_utf8 (path_or_url);
THREADS_ALLOW ();
err = svn_client_cat (stream_stub,
svn_path_canonicalize (APR_STR0 (path_or_url), pool),
&revision, ctx, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
apr_pool_destroy (pool);
do_free_string (path_or_url);
if (err)
pike_throw ();
else {
pop_n_elems (args);
push_int (0);
}
}
/*! @decl string url_from_path( string path_or_url )
*!
*! If @[path_or_url] is already an URL, return @[path_or_url].
*!
*! If @[path_or_url] is a versioned item, return the entry URL of
*! that item.
*!
*! If @[path_or_url] is unversioned (has no entry), return 0.
*!
*! @param path_or_url
*! The item to get the entry URL of.
*/
static void f_url_from_path (INT32 args)
{
struct pike_string *path_or_url;
svn_error_t *err;
apr_pool_t *pool;
const char *url = NULL;
struct pike_string *ret = NULL;
get_all_args ("url_from_path", args, "%W", &path_or_url);
path_or_url = svn_pike_to_utf8 (path_or_url);
pool = svn_pool_create (NULL);
THREADS_ALLOW ();
err = svn_client_url_from_path (&url,
svn_path_canonicalize (APR_STR0 (path_or_url),
pool), pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
else if (url)
ret = make_shared_string(url);
apr_pool_destroy (pool);
do_free_string (path_or_url);
if (err)
pike_throw ();
else {
pop_n_elems (args);
if (ret) {
push_string (ret);
f_utf8_to_string (1);
} else
push_int (0);
}
}
/*! @decl string uuid_from_url( string url )
*!
*! Get repository UUID for a @[url].
*!
*! @param url
*! The URL to the repository you want the UUID for.
*/
static void f_uuid_from_url (INT32 args)
{
struct pike_string *url;
svn_error_t *err;
apr_pool_t *pool;
svn_client_ctx_t *ctx = THIS->ctx;
const char *uuid = NULL;
struct pike_string *ret = NULL;
get_all_args ("uuid_from_url", args, "%W", &url);
url = svn_pike_to_utf8 (url);
pool = svn_pool_create (NULL);
THREADS_ALLOW ();
err = svn_client_uuid_from_url (&uuid,
svn_path_canonicalize (APR_STR0 (url), pool),
ctx, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
else if (uuid)
ret = make_shared_string(uuid);
apr_pool_destroy (pool);
do_free_string (url);
if (err)
pike_throw ();
else {
pop_n_elems (args);
if (ret) {
push_string (ret);
f_utf8_to_string (1);
} else
push_int (0);
}
}
/*! @decl string uuid_from_path( string path )
*!
*! Return the repository UUID for working-copy @[path].
*!
*! @param path
*! The path to the repository to get the UUID for.
*/
static void f_uuid_from_path (INT32 args)
{
struct pike_string *path;
svn_error_t *err;
apr_pool_t *pool;
svn_wc_adm_access_t *adm_access;
svn_client_ctx_t *ctx = THIS->ctx;
const char *path_can, *uuid = NULL;
struct pike_string *ret = NULL;
get_all_args ("uuid_from_path", args, "%W", &path);
path = svn_pike_to_utf8 (path);
pool = svn_pool_create (NULL);
THREADS_ALLOW ();
path_can = svn_path_canonicalize (APR_STR0 (path), pool);
if (!(err = svn_wc_adm_open (&adm_access, NULL, path_can,
FALSE, FALSE, pool)))
err = svn_client_uuid_from_path (&uuid, path_can, adm_access, ctx, pool);
THREADS_DISALLOW ();
if (err)
svn_pike_set_error (err);
else if (uuid)
ret = make_shared_string(uuid);
apr_pool_destroy (pool);
do_free_string (path);
if (err)
pike_throw ();
else {
pop_n_elems (args);
if (ret) {
push_string (ret);
f_utf8_to_string (1);
} else
push_int (0);
}
}
/*! @decl Version version( )
*!
*! Get libsvn_client version information.
*/
static void f_version (INT32 args)
{
pop_n_elems (args);
svn_pike_push_version (svn_client_version ());
}
/*! @decl void create( array(Auth.AuthProvider)|void auth_providers, @
*! mapping(string:Config)|void config )
*!
*! Create a Subversion client.
*!
*! @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_client_create (INT32 args)
{
struct client_obj *co = THIS;
struct array *auths = NULL;
apr_array_header_t *aa;
INT32 i;
do_free_mapping(co->config);
do_free_array(co->auth_providers);
co->config = NULL;
co->auth_providers = NULL;
if (args)
get_all_args ("create", args, (args>1? "%A%m":"%A"), &auths, &co->config);
if(co->config)
co->config = copy_mapping(co->config);
co->ctx->config = svn_pike_mapping_to_config_hash (co->config, co->pool);
if (auths) {
co->auth_providers = auths = copy_array (auths);
aa = apr_array_make (co->pool, auths->size,
sizeof (svn_auth_provider_object_t *));
if (!aa)
Pike_error ("Out of memory.\n");
for (i=0; i<auths->size; i++) {
svn_auth_provider_object_t *po;
if (ITEM (auths)[i].type != PIKE_T_OBJECT ||
!(po = (svn_auth_provider_object_t *)
get_storage (ITEM (auths)[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 (&co->ctx->auth_baton, aa, co->pool);
}
pop_n_elems (args);
}
static void init_client_obj (struct object *o)
{
struct client_obj *co = THIS;
svn_error_t *err;
int idlevel = Pike_fp->context.identifier_level;
co->pool = svn_pool_create (NULL);
low_object_index_no_free (&co->notify_func, Pike_fp->current_object,
notify_function + idlevel);
low_object_index_no_free (&co->log_msg_func, Pike_fp->current_object,
log_msg_function + idlevel);
low_object_index_no_free (&co->cancel_func, Pike_fp->current_object,
cancel_function + idlevel);
if(!co->pool)
Pike_error("Out of memory.\n");
err = svn_client_create_context(&co->ctx, co->pool);
if (err) {
svn_pike_set_error (err);
pike_throw();
}
svn_auth_open (&co->ctx->auth_baton,
apr_array_make (co->pool, 0,
sizeof (svn_auth_provider_object_t *)),
co->pool);
co->ctx->notify_func = notify_func_stub;
co->ctx->notify_baton = &co->notify_func;
co->ctx->log_msg_func = log_msg_func_stub;
co->ctx->log_msg_baton = &co->log_msg_func;
co->ctx->cancel_func = cancel_func_stub;
co->ctx->cancel_baton = &co->cancel_func;
co->ctx->config = NULL;
}
static void exit_client_obj (struct object *o)
{
struct client_obj *co = THIS;
free_svalue (&co->log_msg_func);
free_svalue (&co->notify_func);
free_svalue (&co->cancel_func);
if (co->pool) {
apr_pool_destroy (co->pool);
co->pool = NULL;
}
}
/* Initialize Client class */
void svn_pike_init_client (void)
{
ptrdiff_t off;
start_new_program ();
off = ADD_STORAGE (struct client_obj);
set_init_callback(init_client_obj);
set_exit_callback(exit_client_obj);
map_variable ("config", "mapping", ID_STATIC,
off + OFFSETOF (client_obj, config),
PIKE_T_MAPPING);
map_variable ("auth_providers", "array", ID_STATIC,
off + OFFSETOF (client_obj, auth_providers),
PIKE_T_ARRAY);
notify_function = ADD_FUNCTION ("notify_func", f_notify_func,
tFunc (tStr tInt tInt tStr tInt tInt tInt,
tVoid), 0);
log_msg_function = ADD_FUNCTION ("log_msg_func", f_log_msg_func,
tFunc (tArr (tArr (tOr (tStr, tInt))),
tStr), 0);
cancel_function = ADD_FUNCTION ("cancel_func", f_cancel_func,
tFunc (tNone, tVoid), 0);
ADD_FUNCTION ("create", f_client_create, tFunc (tOr (tArr (tObj), tVoid),
tVoid), ID_STATIC);
ADD_FUNCTION ("checkout", f_checkout, tFunc (tStr tStr tOr (tRev, tVoid)
tOr (tInt, tVoid), tInt), 0);
ADD_FUNCTION ("update", f_update, tFunc (tStr tOr (tRev, tVoid)
tOr (tInt, tVoid), tInt), 0);
ADD_FUNCTION ("switch", f_switch, tFunc (tStr tStr tOr (tRev, tVoid)
tOr (tInt, tVoid), tInt), 0);
ADD_FUNCTION ("add", f_add_, tFunc (tStr tOr (tInt, tVoid), tVoid), 0);
ADD_FUNCTION ("mkdir", f_mkdir, tFunc (tArr (tStr),
tArr (tOr (tInt, tStr))), 0);
ADD_FUNCTION ("delete", f_delete, tFunc (tArr (tStr) tOr (tInt, tVoid),
tArr (tOr (tInt, tStr))), 0);
ADD_FUNCTION ("import", f_import, tFunc (tStr tStr tOr (tInt, tVoid),
tArr (tOr (tInt, tStr))), 0);
ADD_FUNCTION ("commit", f_commit, tFunc (tArr (tStr) tOr (tInt, tVoid),
tArr (tOr (tInt, tStr))), 0);
ADD_FUNCTION ("status", f_status, tFunc (tStr
tFunc (tStr tMap (tStr, tMixed),
tVoid) tOr (tInt, tVoid)
tOr (tInt, tVoid) tOr (tInt, tVoid)
tOr (tRev, tVoid) tOr (tInt, tVoid),
tInt), 0);
ADD_FUNCTION ("log", f_log, tFunc (tArr (tStr) tFunc (tInt tStr tStr tStr
tOr (tMap (tStr,
tArr (tOr (tInt,
tStr))),
tVoid),
tVoid)
tRev tRev tOr (tInt, tVoid)
tOr (tInt, tVoid), tVoid), 0);
ADD_FUNCTION ("blame", f_blame, tFunc (tStr tFunc (tInt tInt tStr tStr tStr,
tVoid)
tRev tRev, tVoid), 0);
ADD_FUNCTION ("diff", f_diff_, tFunc (tStr tRev tStr tRev tOr (tArr (tStr),
tVoid)
tOr (tObjImpl_STDIO_FD, tVoid)
tOr (tObjImpl_STDIO_FD, tVoid)
tOr (tInt, tVoid) tOr (tInt, tVoid)
tOr (tInt, tVoid), tVoid), 0);
ADD_FUNCTION ("diff_peg", f_diff_peg, tFunc (tStr tRev tRev tRev
tOr (tArr (tStr), tVoid)
tOr (tObjImpl_STDIO_FD, tVoid)
tOr (tObjImpl_STDIO_FD, tVoid)
tOr (tInt, tVoid)
tOr (tInt, tVoid)
tOr (tInt, tVoid), tVoid), 0);
ADD_FUNCTION ("merge", f_merge, tFunc (tStr tRev tStr tRev tStr
tOr (tInt, tVoid) tOr (tInt, tVoid)
tOr (tInt, tVoid) tOr (tInt, tVoid),
tVoid), 0);
ADD_FUNCTION ("merge_peg", f_merge_peg, tFunc (tStr tRev tRev tRev tStr
tOr (tInt, tVoid)
tOr (tInt, tVoid)
tOr (tInt, tVoid)
tOr (tInt, tVoid), tVoid), 0);
ADD_FUNCTION ("cleanup", f_cleanup, tFunc (tStr, tVoid), 0);
ADD_FUNCTION ("relocate", f_relocate, tFunc (tStr tStr tStr tOr (tInt, tVoid),
tVoid), 0);
ADD_FUNCTION ("revert", f_revert, tFunc (tArr (tStr) tOr (tInt, tVoid),
tVoid), 0);
ADD_FUNCTION ("resolved", f_resolved, tFunc (tStr tOr (tInt, tVoid),
tVoid), 0);
ADD_FUNCTION ("copy", f_copy, tFunc (tStr tRev tStr,
tArr (tOr (tInt, tStr))), 0);
ADD_FUNCTION ("move", f_move, tFunc (tStr tRev tStr tOr (tInt, tVoid),
tArr (tOr (tInt, tStr))), 0);
ADD_FUNCTION ("propset", f_propset, tFunc (tStr tStr tStr tOr (tInt, tVoid),
tVoid), 0);
ADD_FUNCTION ("revprop_set", f_revprop_set, tFunc (tStr tStr tStr tRev
tOr (tInt, tVoid), tInt),
0);
ADD_FUNCTION ("propget", f_propget, tFunc (tStr tStr tOr (tInt, tVoid),
tMap (tStr, tStr)), 0);
ADD_FUNCTION ("revprop_get", f_revprop_get,
tFunc (tStr tStr tRev, tArr (tOr (tInt, tStr))), 0);
ADD_FUNCTION ("proplist", f_proplist, tFunc (tStr tOr (tInt, tVoid),
tMap (tStr, tMap (tStr, tStr))),
0);
ADD_FUNCTION ("revprop_list", f_revprop_list,
tFunc (tStr tRev, tArr (tOr (tInt, tMap (tStr, tStr)))), 0);
ADD_FUNCTION ("export", f_export, tFunc (tStr tStr tOr (tRev, tVoid)
tOr (tInt, tVoid) tOr (tStr, tVoid),
tInt), 0);
ADD_FUNCTION ("ls", f_ls, tFunc (tStr tOr (tRev, tVoid) tOr (tInt, tVoid),
tMap (tStr, tArray)), 0);
ADD_FUNCTION ("cat", f_cat, tFunc (tObj tStr tOr (tRev, tVoid), tVoid), 0);
ADD_FUNCTION ("url_from_path", f_url_from_path, tFunc (tStr, tStr), 0);
ADD_FUNCTION ("uuid_from_url", f_uuid_from_url, tFunc (tStr, tStr), 0);
ADD_FUNCTION ("uuid_from_path", f_uuid_from_path, tFunc (tStr, tStr), 0);
ADD_FUNCTION ("version", f_version, tFunc (tNone, tObj), 0);
end_class ("Client", 0);
}
/*! @endclass */
/*! @endmodule */