/* ====================================================================
* Copyright (c) 2003-2006, Martin Hauner
* http://subcommander.tigris.org
*
* Subcommander is licensed as described in the file doc/COPYING, which
* you should have received as part of this distribution.
* ====================================================================
*/
// sc
#include "Client.h"
#include "ErrorImpl.h"
#include "Revision.h"
#include "Notify.h"
#include "WcStatus.h"
#include "WcNotify.h"
#include "DirEntry.h"
#include "Info.h"
#include "CommitInfo.h"
#include "CommitItem.h"
#include "CommitBaton.h"
#include "StatusBaton.h"
#include "CancelBaton.h"
#include "BlameBaton.h"
#include "LogBaton.h"
#include "LogEntry.h"
#include "BlameLine.h"
#include "InfoBaton.h"
#include "PropListItem.h"
#include "PropGetItem.h"
#include "ClientContext.h"
#include "AuthPromptProvider.h"
#include "DiffSummarizeBaton.h"
#include "DiffSummarize.h"
#include "VisualDiff.h"
#include "VisualMerge.h"
#include "Error.h"
#include "util/apr.h"
#include "util/String.h"
#include "util/iconvstream.h"
#include "subversion/repos_diff2.h"
// svn
#include <svn_client.h>
#include <svn_path.h>
#include <svn_config.h>
#include <svn_time.h>
// apr
#include <apr_hash.h>
// sys
#include <cassert>
#include <strstream>
using sc::Success;
namespace svn
{
///////////////////////////////////////////////////////////////////////////////
//
// svn_client_get_commit_log_t
class CommitFns
{
public:
static svn_error_t* get_commit_log( const char** logMsg, const char** tmpFile,
apr_array_header_t* commitItems, void* baton, apr_pool_t* pool )
{
CommitBaton* cb = static_cast<CommitBaton*>(baton);
CommitItems items;
for( int i = 0; i < commitItems->nelts; i++ )
{
svn_client_commit_item_t* item =
(APR_ARRAY_IDX(commitItems,i,svn_client_commit_item_t*));
items.push_back( CommitItemPtr(new CommitItem(item)) );
}
*logMsg = 0;
*tmpFile = 0;
sc::String log;
sc::String file;
bool commit = cb->getLogMsg( log, file, items );
if( commit )
{
*logMsg = apr_pstrdup( pool, log.getStr() );
// \todo handle tmpFile
}
return SVN_NO_ERROR;
}
};
//
///////////////////////////////////////////////////////////////////////////////
//
// svn_cancel_func_t
class CancelFns
{
public:
static svn_error_t* check_cancel( void* baton )
{
CancelBaton* cb = static_cast<CancelBaton*>(baton);
if( cb->isCanceled() )
{
return svn_error_create( SVN_ERR_CANCELLED, 0, "canceled.." );
}
return SVN_NO_ERROR;
}
};
//
///////////////////////////////////////////////////////////////////////////////
//
// svn_log_message_receiver_t
class LogFns
{
public:
static svn_error_t* log( void *baton, apr_hash_t* changedPaths, svn_revnum_t rev,
const char* author, const char* date, const char* msg, apr_pool_t* pool )
{
LogBaton* lb = static_cast<LogBaton*>(baton);
Date tdate = 0;
svn_time_from_cstring( &tdate, date, pool );
LogEntryPtr entry = LogEntryPtr( new LogEntry(rev,tdate,sc::String(author),sc::String(msg)) );
if( changedPaths )
{
apr_hash_index_t *hi;
for( hi = apr_hash_first(pool,changedPaths); hi; hi = apr_hash_next(hi) )
{
const char* key;
svn_log_changed_path_t* item;
apr_hash_this( hi, (const void**)&key, NULL, (void**)&item );
LogEntry::Path path( key, item );
entry->addPath( path );
}
}
lb->receiveMessage( entry );
return SVN_NO_ERROR;
}
};
//
///////////////////////////////////////////////////////////////////////////////
//
// svn_wc_notify_func_t
class NotifyFns
{
public:
static void notify( void *baton, const svn_wc_notify_t *notify, apr_pool_t *pool )
{
NotifyBaton* nb = static_cast<NotifyBaton*>(baton);
WcNotifyPtr ny( new WcNotify(notify) );
nb->notify(ny);
}
};
//
///////////////////////////////////////////////////////////////////////////////
//
// svn_client_ctx_t, svn_auth_baton_t, prompt providers..
class AuthFns
{
public:
static AuthPromptProvider* ab_cast( void* baton )
{
return static_cast<AuthPromptProvider*>(baton);
}
// svn_auth_simple_prompt_func_t
static svn_error_t* simple_prompt( svn_auth_cred_simple_t **cred_p, void *baton,
const char *realm, const char *username, svn_boolean_t may_save, apr_pool_t *pool )
{
AuthPromptProvider* auth = ab_cast(baton);
AuthPromptProvider::simpleCredentials cred;
bool result = auth->simplePrompt( realm, username, may_save != 0, cred );
if( ! result )
{
return svn_error_create( SVN_ERR_AUTHN_CREDS_UNAVAILABLE, 0, "simple prompt" );
}
svn_auth_cred_simple_t *ret = (svn_auth_cred_simple_t*)apr_pcalloc( pool, sizeof(*ret) );
// TODO remove when fixed in subversion
ret->username = apr_pstrdup( pool, (const char*)cred._username ? (const char*)cred._username : "" );
ret->password = apr_pstrdup( pool, (const char*)cred._password ? (const char*)cred._password : "" );
ret->may_save = cred._save;
*cred_p = ret;
return SVN_NO_ERROR;
}
// svn_auth_username_prompt_func_t
static svn_error_t* username_prompt( svn_auth_cred_username_t **cred_p, void *baton,
const char *realm, svn_boolean_t may_save, apr_pool_t *pool )
{
// when is this called?
// sample implementation in subversion/cmdline/prompt.c
return SVN_NO_ERROR;
}
// svn_auth_ssl_server_trust_prompt_func_t
static svn_error_t* ssl_server_trust_prompt( svn_auth_cred_ssl_server_trust_t **cred_p,
void *baton, const char *realm, apr_uint32_t failures,
const svn_auth_ssl_server_cert_info_t *cert_info, svn_boolean_t may_save, apr_pool_t *pool )
{
AuthPromptProvider* auth = ab_cast(baton);
AuthPromptProvider::sslServerCertInfo certinfo;
certinfo.hostname = cert_info->hostname;
certinfo.fingerprint = cert_info->fingerprint;
certinfo.validFrom = cert_info->valid_from;
certinfo.validUntil = cert_info->valid_until;
certinfo.issuerDName = cert_info->issuer_dname;
certinfo.asciiCert = cert_info->ascii_cert;
AuthPromptProvider::sslServerTrustCredentials cred;
bool result = auth->sslServerTrustPrompt( realm, failures, certinfo, may_save != 0, cred );
if( ! result )
{
return svn_error_create( SVN_ERR_AUTHN_CREDS_UNAVAILABLE, 0, "ssl server trust prompt" );
}
svn_auth_cred_ssl_server_trust_t *ret =
(svn_auth_cred_ssl_server_trust_t*)apr_pcalloc( pool, sizeof(*ret) );
ret->accepted_failures = cred._acceptedFailures;
ret->may_save = cred._save;
*cred_p = ret;
return SVN_NO_ERROR;
}
// svn_auth_ssl_client_cert_prompt_func_t
static svn_error_t* ssl_client_cert_prompt( svn_auth_cred_ssl_client_cert_t **cred_p,
void *baton, const char *realm, svn_boolean_t may_save, apr_pool_t *pool )
{
// sample implementation in subversion/cmdline/prompt.c
return SVN_NO_ERROR;
}
// svn_auth_ssl_client_cert_pw_prompt_func_t
static svn_error_t* ssl_client_cert_pw_prompt( svn_auth_cred_ssl_client_cert_pw_t **cred_p,
void *baton, const char *realm, svn_boolean_t may_save, apr_pool_t *pool )
{
// sample implementation in subversion/cmdline/prompt.c
return SVN_NO_ERROR;
}
};
//
///////////////////////////////////////////////////////////////////////////////
//
// *svn_wc_status_func2_t, status_baton
class StatusFns
{
public:
StatusFns( StatusBaton* baton ) : _baton(baton)
{
}
static StatusFns* sf_cast( void* v )
{
return static_cast<StatusFns*>(v);
}
static void status( void* baton, const char* path, svn_wc_status2_t* state )
{
StatusFns* fns = sf_cast(baton);
if( state )
{
WcStatusPtr wcs( new WcStatus( sc::String(path), state ) );
fns->getBaton()->status( path, wcs );
}
else
{
fns->getBaton()->status( path, WcStatusPtr() );
}
}
StatusBaton* getBaton() const
{
return _baton;
}
private:
StatusBaton* _baton;
};
//
///////////////////////////////////////////////////////////////////////////////
//
class BlameFns
{
public:
BlameFns( BlameBaton* baton ) : _baton(baton)
{
}
static BlameFns* bf_cast( void* v )
{
return static_cast<BlameFns*>(v);
}
static svn_error_t* receive( void* baton, apr_int64_t lineNo, svn_revnum_t revision,
const char* author, const char* date, const char* line, apr_pool_t* pool )
{
BlameFns* fns = bf_cast(baton);
Date tdate = 0;
if( date )
{
svn_time_from_cstring( &tdate, date, pool );
}
BlameLine* bline = new BlameLine( lineNo, revision, sc::String(author), tdate,
sc::String(line) );
fns->getBaton()->receive(bline);
return SVN_NO_ERROR;
}
BlameBaton* getBaton() const
{
return _baton;
}
private:
BlameBaton* _baton;
};
//
///////////////////////////////////////////////////////////////////////////////
//
// *svn_diff_summarize_func_t, diff_summarize_baton
class DiffSummarizeFns
{
public:
DiffSummarizeFns( DiffSummarizeBaton* baton ) : _baton(baton)
{
}
static DiffSummarizeFns* dsf_cast( void* v )
{
return static_cast<DiffSummarizeFns*>(v);
}
static svn_error_t* summarize( const svn_client_diff_summarize_t *diff,
void *baton, apr_pool_t *pool )
{
DiffSummarizeFns* fns = dsf_cast(baton);
if( diff )
{
DiffSummarizePtr dp( new DiffSummarize(diff) );
fns->getBaton()->summarize(dp);
}
return SVN_NO_ERROR;
}
DiffSummarizeBaton* getBaton() const
{
return _baton;
}
private:
DiffSummarizeBaton* _baton;
};
//
///////////////////////////////////////////////////////////////////////////////
//
class InfoFns
{
public:
InfoFns( InfoBaton* baton ) : _baton(baton)
{
}
static InfoFns* if_cast( void* v )
{
return static_cast<InfoFns*>(v);
}
typedef svn_error_t *(*svn_info_receiver_t)
(void *baton,
const char *path,
const svn_info_t *info,
apr_pool_t *pool);
static svn_error_t* receive( void* baton, const char* path, const svn_info_t* info,
apr_pool_t* pool )
{
InfoFns* fns = if_cast(baton);
InfoPtr infp( new Info( sc::String(path), info ) );
fns->getBaton()->info(infp);
return SVN_NO_ERROR;
}
InfoBaton* getBaton() const
{
return _baton;
}
private:
InfoBaton* _baton;
};
//
///////////////////////////////////////////////////////////////////////////////
//
ClientContext::ClientContext( AuthPromptProvider* pp, CancelBaton* cb,
NotifyBaton* nb, CommitBaton* cob, const sc::String& diff, const sc::String&
merge ) : _authProv(pp), _diff(diff), _merge(merge)
{
// see subversion/clients/commandline/main.c for what is going on here..
_pool = apr::createPool(0);
svn_error_t* err;
// make sure the system-wide subversion configuration exits.
err = svn_config_ensure( 0, _pool );
// \todo handle error
err = svn_client_create_context( &_context, _pool );
// \todo handle error
// load system wide configuration
err = svn_config_get_config( &(_context->config), 0, _pool );
// \todo handle error
// log message callback
_context->log_msg_func = CommitFns::get_commit_log;
_context->log_msg_baton = cob;
// set up our cancellation support
_context->cancel_func = CancelFns::check_cancel;
_context->cancel_baton = cb;
// set up notify callback
_context->notify_func2 = NotifyFns::notify;
_context->notify_baton2 = nb;
// authentication
//svn_boolean_t store_password_val = TRUE;
svn_auth_provider_object_t* provider;
svn_auth_baton_t* ab;
// the whole list of registered providers
apr_array_header_t *providers = apr_array_make( _pool, 10,
sizeof(svn_auth_provider_object_t*) );
// The main disk-caching auth providers, for both
// 'username/password' creds and 'username' creds.
svn_client_get_simple_provider( &provider, _pool );
APR_ARRAY_PUSH( providers, svn_auth_provider_object_t* ) = provider;
svn_client_get_username_provider( &provider, _pool );
APR_ARRAY_PUSH( providers, svn_auth_provider_object_t* ) = provider;
// ... server-cert, client-cert, and client-cert-password providers.
svn_client_get_ssl_server_trust_file_provider( &provider, _pool );
APR_ARRAY_PUSH( providers, svn_auth_provider_object_t* ) = provider;
svn_client_get_ssl_client_cert_file_provider( &provider, _pool );
APR_ARRAY_PUSH( providers, svn_auth_provider_object_t* ) = provider;
svn_client_get_ssl_client_cert_pw_file_provider( &provider, _pool );
APR_ARRAY_PUSH( providers, svn_auth_provider_object_t* ) = provider;
// Two basic prompt providers: username/password, and just username.
svn_client_get_simple_prompt_provider(
&provider, AuthFns::simple_prompt, pp, 2, _pool );
APR_ARRAY_PUSH( providers, svn_auth_provider_object_t* ) = provider;
svn_client_get_username_prompt_provider(
&provider, AuthFns::username_prompt, pp, 2, _pool );
APR_ARRAY_PUSH( providers, svn_auth_provider_object_t* ) = provider;
// Three ssl prompt providers: server-certs, client-certs, and
// client-cert-passphrases.
svn_client_get_ssl_server_trust_prompt_provider(
&provider, AuthFns::ssl_server_trust_prompt, pp, _pool );
APR_ARRAY_PUSH( providers, svn_auth_provider_object_t* ) = provider;
svn_client_get_ssl_client_cert_prompt_provider(
&provider, AuthFns::ssl_client_cert_prompt, pp, 2, _pool );
APR_ARRAY_PUSH( providers, svn_auth_provider_object_t* ) = provider;
svn_client_get_ssl_client_cert_pw_prompt_provider(
&provider, AuthFns::ssl_client_cert_pw_prompt, pp, 2, _pool );
APR_ARRAY_PUSH( providers, svn_auth_provider_object_t* ) = provider;
// build the authentication baton
svn_auth_open( &ab, providers, _pool );
_context->auth_baton = ab;
}
ClientContext::~ClientContext()
{
delete (CancelBaton*)_context->cancel_baton;
delete (NotifyBaton*)_context->notify_baton;
delete (CommitBaton*)_context->log_msg_baton;
delete _authProv;
apr::destroyPool(_pool);
}
CancelBaton* ClientContext::getCancelBaton() const
{
return (CancelBaton*)_context->cancel_baton;
}
const sc::String& ClientContext::getDiffCmd() const
{
return _diff;
}
const sc::String& ClientContext::getMergeCmd() const
{
return _merge;
}
//
///////////////////////////////////////////////////////////////////////////////
/**
* create a subversion Client object. The Client object takes ownership of
* the ClientContext pointer.
*
* \param context the ClientContext.
* \param pool an optional root pool.
*/
Client::Client( ClientContext* context, apr_pool_t *pool )
{
_pool = apr::createPool(pool);
_context = context;
}
Client::~Client()
{
delete _context;
apr::destroyPool(_pool);
}
sc::Error* Client::checkout( Revnumber* resultRev, const sc::String& url,
const sc::String& path, const Revision& peg, const Revision& rev, bool recurse )
{
apr::Pool pool( _pool );
const char* curl = preparePathOrUrl( url, pool );
const char* cpath = preparePathOrUrl( path, pool );
svn_revnum_t resrev = 0;
svn_opt_revision_t svnrev;
svn_opt_revision_t svnpeg;
convertRevision( &rev, &svnrev );
convertRevision( &peg, &svnpeg );
svn_error_t* err = svn_client_checkout2( &resrev, curl, cpath, &svnpeg, &svnrev,
recurse, false /* todo external*/, _context->_context, pool );
if( err )
{
return wrapError(err);
}
*resultRev = resrev;
return Success;
}
sc::Error* Client::update( Revnumber* resultRev, const sc::String& path,
const Revision& rev, bool recurse )
{
apr::Pool pool( _pool );
const char* cpath = preparePathOrUrl( path, pool );
svn_revnum_t resrev = 0;
svn_opt_revision_t svnrev;
convertRevision( &rev, &svnrev );
svn_error_t* err = svn_client_update( &resrev, cpath, &svnrev, recurse,
_context->_context, pool );
if( err )
{
return wrapError(err);
}
*resultRev = resrev;
return Success;
}
sc::Error* Client::switchx( Revnumber* resultRev, const sc::String& path,
const sc::String& url, const Revision& rev, bool recurse )
{
apr::Pool pool( _pool );
const char* cpath = preparePathOrUrl( path, pool );
const char* curl = preparePathOrUrl( url, pool );
svn_revnum_t resrev = 0;
svn_opt_revision_t svnrev;
convertRevision( &rev, &svnrev );
svn_error_t* err = svn_client_switch( &resrev, cpath, curl, &svnrev, recurse,
_context->_context, pool );
if( err )
{
return wrapError(err);
}
*resultRev = resrev;
return Success;
}
sc::Error* Client::status( Revnumber* resultRev, const sc::String& path,
const Revision& rev, StatusBaton *baton, bool recurse, bool all, bool update,
bool ignore )
{
apr::Pool pool( _pool );
const char *cpath = preparePathOrUrl( path, pool );
svn_revnum_t resrev = 0;
svn_opt_revision_t svnrev;
convertRevision( &rev, &svnrev );
StatusFns fns(baton);
svn_error_t* err = svn_client_status2( &resrev, cpath, &svnrev,
StatusFns::status, &fns, recurse, all, update, ignore, false /*todo external*/,
_context->_context, pool );
if( err )
{
return wrapError(err);
}
*resultRev = resrev;
return Success;
}
sc::Error* Client::commit( CommitInfo& resultInfo, const Paths& paths,
bool recurse, bool keepLocks )
{
apr::Pool pool( _pool );
apr_array_header_t* patharr = apr_array_make( pool, (int)paths.size(),
sizeof(char*) );
for( Paths::const_iterator it = paths.begin(); it != paths.end(); it++ )
{
const char* cpath = preparePathOrUrl( *it, pool );
APR_ARRAY_PUSH( patharr, const char* ) = cpath;
}
svn_client_commit_info_t* commitInfo = 0;
svn_error_t* err = svn_client_commit2( &commitInfo, patharr, recurse,
keepLocks, _context->_context, pool );
if( err )
{
return wrapError(err);
}
resultInfo = commitInfo;
return Success;
}
sc::Error* Client::ls( const sc::String& pathOrUrl, const Revision& rev,
bool recurse, DirEntries& entries )
{
apr::Pool pool(_pool);
const char *cpath = preparePathOrUrl( pathOrUrl, pool );
apr_hash_t* hash;
svn_opt_revision_t svnrev;
convertRevision( &rev, &svnrev );
svn_error_t* err = svn_client_ls( &hash, cpath, &svnrev, recurse,
_context->_context, pool );
if( err )
{
return wrapError(err);
}
apr_hash_index_t* hi;
for( hi = apr_hash_first(_pool,hash); hi; hi = apr_hash_next(hi) )
{
const char* key;
apr_ssize_t klen;
svn_dirent_t* val;
apr_hash_this( hi, (const void**)&key, &klen, (void**)&val );
sc::String name = pathOrUrl;
name += "/";
name += sc::String(key,klen);
entries.push_back( DirEntryPtr(new DirEntry(name,val)) );
}
return Success;
}
sc::Error* Client::mkdir( CommitInfo& resultInfo, const Paths& pathsOrUrls )
{
apr::Pool pool( _pool );
apr_array_header_t* patharr = apr_array_make( pool, (int)pathsOrUrls.size(),
sizeof(char*) );
for( Paths::const_iterator it = pathsOrUrls.begin(); it != pathsOrUrls.end(); it++ )
{
const char* cpath = preparePathOrUrl( *it, pool );
APR_ARRAY_PUSH( patharr, const char* ) = cpath;
}
svn_client_commit_info_t* commitInfo = 0;
svn_error_t* err = svn_client_mkdir( &commitInfo, patharr,
_context->_context, pool );
if( err )
{
return wrapError(err);
}
resultInfo = commitInfo;
return Success;
}
#if 0
Error*Client::add( const sc::String& path, bool recurse, bool force )
{
apr::Pool pool( _pool );
const char* cpath = svn_path_canonicalize( path, pool );
svn_error_t* err = svn_client_add2( cpath, recurse, force, _context->_context, pool );
if( err )
{
return wrapError(err);
}
return Success;
}
#endif
sc::Error* Client::add( const Paths& paths, bool recurse, bool force )
{
apr::Pool pool( _pool );
for( Paths::const_iterator it = paths.begin(); it != paths.end(); it++ )
{
const char* cpath = preparePathOrUrl( *it, pool );
svn_error_t* err = svn_client_add2( cpath, recurse, force,
_context->_context, pool );
if( err )
{
return wrapError(err);
}
}
return Success;
}
sc::Error* Client::deletex( CommitInfo& resultInfo, const Paths& pathsOrUrls,
bool force )
{
apr::Pool pool( _pool );
apr_array_header_t* patharr = apr_array_make( pool, (int)pathsOrUrls.size(),
sizeof(char*) );
for( Paths::const_iterator it = pathsOrUrls.begin(); it != pathsOrUrls.end(); it++ )
{
const char* cpath = preparePathOrUrl( *it, pool );
APR_ARRAY_PUSH( patharr, const char* ) = cpath;
}
svn_client_commit_info_t* commitInfo = 0;
svn_error_t* err = svn_client_delete( &commitInfo, patharr, force,
_context->_context, pool );
if( err )
{
return wrapError(err);
}
resultInfo = commitInfo;
return Success;
}
sc::Error* Client::revert( const Paths& paths, bool recurse )
{
apr::Pool pool( _pool );
apr_array_header_t* patharr = apr_array_make( pool, (int)paths.size(),
sizeof(char*) );
for( Paths::const_iterator it = paths.begin(); it != paths.end(); it++ )
{
const char* cpath = preparePathOrUrl( *it, pool );
APR_ARRAY_PUSH( patharr, const char* ) = cpath;
}
svn_error_t* err = svn_client_revert( patharr, recurse,
_context->_context, pool );
if( err )
{
return wrapError(err);
}
return Success;
}
sc::Error* Client::import( CommitInfo& resultInfo, const sc::String& path,
const sc::String& url, bool recurse )
{
apr::Pool pool( _pool );
const char* cpath = preparePathOrUrl( path, pool );
const char* curl = preparePathOrUrl( url, pool );
svn_client_commit_info_t* commitInfo = 0;
svn_error_t* err = svn_client_import( &commitInfo, cpath, curl, ! recurse,
_context->_context, _pool );
if( err )
{
return wrapError(err);
}
resultInfo = commitInfo;
return Success;
}
sc::Error* Client::blame( const sc::String& pathOrUrl, const Revision& begin,
const Revision& end, BlameBaton* baton )
{
apr::Pool pool(_pool);
const char *cpath = preparePathOrUrl( pathOrUrl, pool );
svn_opt_revision_t revBegin;
convertRevision( &begin, &revBegin );
svn_opt_revision_t revEnd;
convertRevision( &end, &revEnd );
BlameFns fns(baton);
svn_error_t* err = svn_client_blame( cpath, &revBegin, &revEnd,
BlameFns::receive, &fns, _context->_context, pool );
if( err )
{
return wrapError(err);
}
return Success;
}
sc::Error* Client::cleanup( const sc::String& path )
{
apr::Pool pool( _pool );
const char* cpath = preparePathOrUrl( path, pool );
svn_error_t* err = svn_client_cleanup( cpath, _context->_context, pool );
if( err )
{
return wrapError(err);
}
return Success;
}
sc::Error* Client::relocate( const sc::String& path, const sc::String& fromUrl,
const sc::String& toUrl, bool recurse )
{
apr::Pool pool( _pool );
const char* cpath = preparePathOrUrl( path, pool );
const char* cfromUrl = preparePathOrUrl( fromUrl, pool );
const char* ctoUrl = preparePathOrUrl( toUrl, pool );
svn_error_t* err = svn_client_relocate( cpath, cfromUrl, ctoUrl, recurse,
_context->_context, pool );
if( err )
{
return wrapError(err);
}
return Success;
}
sc::Error* Client::resolved( const sc::String& path, bool recurse )
{
apr::Pool pool( _pool );
const char* cpath = preparePathOrUrl( path, pool );
svn_error_t* err = svn_client_resolved( cpath, recurse, _context->_context,
pool );
if( err )
{
return wrapError(err);
}
return Success;
}
sc::Error* Client::log( const Paths& pathsOrUrls, const Revision& start,
const Revision& stop, int limit, bool discoverChangedPaths, bool strictNodeHistory,
LogBaton* baton )
{
apr::Pool pool( _pool );
apr_array_header_t* patharr = apr_array_make( pool, (int)pathsOrUrls.size(),
sizeof(char*) );
for( Paths::const_iterator it = pathsOrUrls.begin(); it != pathsOrUrls.end(); it++ )
{
const char* cpath = preparePathOrUrl( *it, pool );
APR_ARRAY_PUSH( patharr, const char* ) = cpath;
}
svn_opt_revision_t crev1;
svn_opt_revision_t crev2;
convertRevision( &start, &crev1 );
convertRevision( &stop, &crev2 );
svn_error_t* err = svn_client_log2( patharr, &crev1, &crev2, limit, discoverChangedPaths,
strictNodeHistory, &LogFns::log, baton, _context->_context, pool );
if( err )
{
return wrapError(err);
}
return Success;
}
sc::Error* Client::diff( const sc::String& pathOrUrl1, const Revision& rev1,
const sc::String& pathOrUrl2, const Revision& rev2, bool recurse,
bool ancestry, bool deleted, bool patch, sc::String& patchFile )
{
apr::Pool pool( _pool );
apr_array_header_t* dopts = apr_array_make( pool, 0, sizeof(char*) );
const char* cpath1 = preparePathOrUrl( pathOrUrl1, pool );
const char* cpath2 = preparePathOrUrl( pathOrUrl2, pool );
svn_opt_revision_t crev1;
svn_opt_revision_t crev2;
convertRevision( &rev1, &crev1 );
convertRevision( &rev2, &crev2 );
apr_file_t *outfile;
apr_file_t *errfile;
apr_status_t status;
const char* outfileName;
apr_time_t now = apr_time_now();
char* nowstr = apr_psprintf( pool, "%" APR_TIME_T_FMT, now );
apr_int32_t flags = APR_CREATE|APR_WRITE;
if( ! patch )
{
flags |= APR_DELONCLOSE;
}
{
// todo error handling..
//char* err = apr_strerror( status, errbuf, 255 );
//assert( status == APR_SUCCESS );
const char* tempdir = 0;
status = apr_temp_dir_get( &tempdir, pool );
char* tempout = apr_pstrcat( pool, tempdir, "/sc_stdout.log.", nowstr, NULL );
status = apr_file_open( &outfile, tempout, flags, APR_OS_DEFAULT, pool );
outfileName = tempout;
}
{
const char* tempdir = 0;
status = apr_temp_dir_get( &tempdir, pool );
char* temperr = apr_pstrcat( _pool, tempdir, "/sc_stderr.log.", nowstr, NULL );
status = apr_file_open( &errfile, temperr, flags, APR_OS_DEFAULT, pool );
}
svn_error_t* err = svn_client_diff( dopts, cpath1, &crev1, cpath2, &crev2,
recurse, !ancestry, !deleted, outfile, errfile, _context->_context, pool );
apr_file_close(outfile);
apr_file_close(errfile);
if( err )
{
return wrapError(err);
}
patchFile = outfileName;
return Success;
}
sc::Error* Client::diff( const sc::String& srcPathOrUrl, const Revision& rev1,
const Revision& rev2, const Revision& peg, bool recurse, bool ancestry,
bool deleted, bool patch, sc::String& patchFile )
{
apr::Pool pool( _pool );
apr_array_header_t* dopts = apr_array_make( pool, 0, sizeof(char*) );
const char* cpath = preparePathOrUrl( srcPathOrUrl, pool );
svn_opt_revision_t crev1;
svn_opt_revision_t crev2;
svn_opt_revision_t cpeg;
convertRevision( &rev1, &crev1 );
convertRevision( &rev2, &crev2 );
convertRevision( &peg, &cpeg );
apr_file_t *outfile;
apr_file_t *errfile;
apr_status_t status;
const char* outfileName;
apr_time_t now = apr_time_now();
char* nowstr = apr_psprintf( pool, "%" APR_TIME_T_FMT, now );
apr_int32_t flags = APR_CREATE|APR_WRITE;
if( ! patch )
{
flags |= APR_DELONCLOSE;
}
{
const char* tempdir = 0;
status = apr_temp_dir_get( &tempdir, pool );
char* tempout = apr_pstrcat( pool, tempdir, "/sc_stdout.log.", nowstr, NULL );
status = apr_file_open( &outfile, tempout, flags, APR_OS_DEFAULT, pool );
outfileName = tempout;
}
{
const char* tempdir = 0;
status = apr_temp_dir_get( &tempdir, pool );
char* temperr = apr_pstrcat( _pool, tempdir, "/sc_stderr.log.", nowstr, NULL );
status = apr_file_open( &errfile, temperr, flags, APR_OS_DEFAULT, pool );
}
svn_error_t* err = svn_client_diff_peg( dopts, cpath, &cpeg, &crev1, &crev2,
recurse, !ancestry, !deleted, outfile, errfile, _context->_context, pool );
apr_file_close(outfile);
apr_file_close(errfile);
if( err )
{
return wrapError(err);
}
patchFile = outfileName;
return Success;
}
sc::Error* Client::diffSummarize( const sc::String& pathOrUrl1,
const Revision& rev1, const sc::String& pathOrUrl2, const Revision& rev2,
DiffSummarizeBaton* baton, bool recurse, bool ancestry )
{
apr::Pool pool( _pool );
const char* cpath1 = preparePathOrUrl( pathOrUrl1, pool );
const char* cpath2 = preparePathOrUrl( pathOrUrl2, pool );
svn_opt_revision_t crev1;
svn_opt_revision_t crev2;
convertRevision( &rev1, &crev1 );
convertRevision( &rev2, &crev2 );
DiffSummarizeFns fns(baton);
svn_error_t* err = svn_client_diff_summarize( cpath1, &crev1,
cpath2, &crev2, recurse, !ancestry, DiffSummarizeFns::summarize,
&fns, _context->_context, pool );
if( err )
{
return wrapError(err);
}
return Success;
}
sc::Error* Client::diffSummarize( const sc::String& srcPathOrUrl,
const Revision& rev1, const Revision& rev2, const Revision& peg,
DiffSummarizeBaton* baton, bool recurse, bool ancestry )
{
apr::Pool pool( _pool );
const char* cpath = preparePathOrUrl( srcPathOrUrl, pool );
svn_opt_revision_t crev1;
svn_opt_revision_t crev2;
svn_opt_revision_t cpeg;
convertRevision( &rev1, &crev1 );
convertRevision( &rev2, &crev2 );
convertRevision( &peg, &cpeg );
DiffSummarizeFns fns(baton);
svn_error_t* err = svn_client_diff_summarize_peg( cpath, &cpeg,
&crev1, &crev2, recurse, !ancestry, DiffSummarizeFns::summarize,
&fns, _context->_context, pool );
if( err )
{
return wrapError(err);
}
return Success;
}
sc::Error* Client::visualDiff( const sc::String& pathOrUrl1, const Revision& rev1,
const sc::String& pathOrUrl2, const Revision& rev2 )
{
apr::Pool pool( _pool );
const char* cpath1 = preparePathOrUrl( pathOrUrl1, pool );
const char* cpath2 = preparePathOrUrl( pathOrUrl2, pool );
svn_opt_revision_t crev1;
svn_opt_revision_t crev2;
convertRevision( &rev1, &crev1 );
convertRevision( &rev2, &crev2 );
VisualDiff vdiff( _context->_context, _context->getDiffCmd(), pool );
svn_error_t* err = vdiff.run( cpath1, &crev1, cpath2, &crev2 );
if( err )
{
return wrapError(err);
}
return Success;
}
sc::Error* Client::visualDiff( const sc::String& srcPathOrUrl, const Revision& rev1,
const Revision& rev2, const Revision& peg )
{
apr::Pool pool( _pool );
const char* cpath = preparePathOrUrl( srcPathOrUrl, pool );
svn_opt_revision_t crev1;
svn_opt_revision_t crev2;
svn_opt_revision_t cpeg;
convertRevision( &rev1, &crev1 );
convertRevision( &rev2, &crev2 );
convertRevision( &peg, &cpeg );
VisualDiff vdiff( _context->_context, _context->getDiffCmd(), pool );
svn_error_t* err = vdiff.run( cpath, &crev1, &crev2, &cpeg );
if( err )
{
return wrapError(err);
}
return Success;
}
sc::Error* Client::merge( const sc::String& pathOrUrl1, const Revision& rev1,
const sc::String& pathOrUrl2, const Revision& rev2,
const sc::String& targetPath, bool recurse, bool ancestry, bool force,
bool dryRun )
{
apr::Pool pool( _pool );
const char* cpath1 = preparePathOrUrl( pathOrUrl1, pool );
const char* cpath2 = preparePathOrUrl( pathOrUrl2, pool );
svn_opt_revision_t crev1;
svn_opt_revision_t crev2;
convertRevision( &rev1, &crev1 );
convertRevision( &rev2, &crev2 );
const char* ctarget = svn_path_canonicalize( targetPath, pool );
svn_error_t* err = svn_client_merge( cpath1, &crev1, cpath2, &crev2,
ctarget, recurse, !ancestry, force, dryRun, _context->_context, pool );
if( err )
{
return wrapError(err);
}
return Success;
}
sc::Error* Client::merge( const sc::String& srcPathOrUrl, const Revision& rev1,
const Revision& rev2, const Revision& peg, const sc::String& targetPath,
bool recurse, bool ancestry, bool force, bool dryRun )
{
apr::Pool pool( _pool );
const char* cpath1 = preparePathOrUrl( srcPathOrUrl, pool );
svn_opt_revision_t crev1;
svn_opt_revision_t crev2;
svn_opt_revision_t cpeg;
convertRevision( &rev1, &crev1 );
convertRevision( &rev2, &crev2 );
convertRevision( &peg, &cpeg );
const char* ctarget = preparePathOrUrl( targetPath, pool );
svn_error_t* err = svn_client_merge_peg( cpath1, &crev1, &crev2, &cpeg,
ctarget, recurse, !ancestry, force, dryRun, _context->_context, pool );
if( err )
{
return wrapError(err);
}
return Success;
}
sc::Error* Client::visualMerge( const WcStatusPtr file )
{
apr::Pool pool( _pool );
VisualMerge vmerge( _context->_context, _context->getMergeCmd(), pool );
svn_error_t* err = vmerge.run( file );
if( err )
{
return wrapError(err);
}
return Success;
}
sc::Error* Client::copy( CommitInfo& resultInfo, const Paths& srcPathsOrUrls,
const Revision& srcRev, const sc::String& dstPathOrUrl )
{
apr::Pool pool( _pool );
for( Paths::const_iterator it = srcPathsOrUrls.begin(); it != srcPathsOrUrls.end(); it++ )
{
const char* csrc = preparePathOrUrl( *it, pool );
svn_opt_revision_t crev;
convertRevision( &srcRev, &crev );
const char* cdst = preparePathOrUrl( dstPathOrUrl, pool );
svn_client_commit_info_t* commitInfo = 0;
svn_error_t* err = svn_client_copy( &commitInfo, csrc, &crev, cdst,
_context->_context, pool );
if( err )
{
return wrapError(err);
}
resultInfo = commitInfo;
}
return Success;
}
sc::Error* Client::move( CommitInfo& resultInfo, const Paths& srcPathsOrUrls,
const Revision& srcRev, const sc::String& dstPathOrUrl, bool force )
{
apr::Pool pool( _pool );
for( Paths::const_iterator it = srcPathsOrUrls.begin(); it != srcPathsOrUrls.end(); it++ )
{
const char* csrc = preparePathOrUrl( *it, pool );
svn_opt_revision_t crev;
convertRevision( &srcRev, &crev );
const char* cdst = preparePathOrUrl( dstPathOrUrl, pool );
svn_client_commit_info_t* commitInfo = 0;
svn_error_t* err = svn_client_move( &commitInfo, csrc, &crev, cdst, force,
_context->_context, pool );
if( err )
{
return wrapError(err);
}
resultInfo = commitInfo;
}
return Success;
}
sc::Error* Client::exportx( Revnumber* resultRev, const sc::String& srcPathOrUrl,
const Revision& srcRev, const sc::String& dstPath, bool force,
const sc::String& eol )
{
apr::Pool pool( _pool );
const char *csrc = preparePathOrUrl( srcPathOrUrl, pool );
const char *cdst = preparePathOrUrl( dstPath, pool );
svn_revnum_t resrev = 0;
svn_opt_revision_t svnrev;
convertRevision( &srcRev, &svnrev );
svn_error_t* err = svn_client_export2( &resrev, csrc, cdst, &svnrev, force,
eol, _context->_context, pool );
if( err )
{
return wrapError(err);
}
*resultRev = resrev;
return Success;
}
class MemoryStream
{
public:
MemoryStream( apr::Pool& pool )
{
_stream = svn_stream_create( this, pool );
svn_stream_set_read( _stream, read_fn );
svn_stream_set_write( _stream, write_fn );
svn_stream_set_close( _stream, close_fn );
}
static svn_error_t* read_fn( void* baton, char* buffer, apr_size_t* len )
{
return SVN_NO_ERROR;
}
static svn_error_t* write_fn( void* baton, const char* buffer, apr_size_t* len )
{
MemoryStream* stream = static_cast<MemoryStream*>(baton);
stream->_str += sc::String( buffer, *len );
return SVN_NO_ERROR;
}
static svn_error_t* close_fn( void* baton )
{
return SVN_NO_ERROR;
}
operator svn_stream_t*() const
{
return _stream;
}
const sc::String& get() const
{
return _str;
}
private:
svn_stream_t* _stream;
sc::String _str;
};
sc::Error* Client::cat( sc::String& out, const sc::String& pathOrUrl,
const Revision& rev )
{
apr::Pool pool( _pool );
const char *cpath = preparePathOrUrl( pathOrUrl, pool );
svn_opt_revision_t svnrev;
convertRevision( &rev, &svnrev );
MemoryStream stream( pool );
svn_error_t* err = svn_client_cat( stream, cpath, &svnrev,
_context->_context, pool );
if( err )
{
return wrapError(err);
}
// convert the file that was just converted by svn_client_cat from utf8
// to the local encoding back to utf8...
sc::String tmp = stream.get();
std::strstream s1( (char*)tmp.getStr(), (std::streamsize)tmp.getByteCnt(),
std::ios_base::binary|std::ios_base::in );
iconvistream s2( s1, "*", "utf-8" );
char buf[2] = {};
while( ! s2.eof() )
{
s2.read( buf, sizeof(buf)-1 );
out += buf;
}
//
return Success;
}
sc::Error* Client::proplist( PropListItems& items, const sc::String& pathOrUrl,
const Revision& rev, bool recurse )
{
apr::Pool pool( _pool );
const char *cpath = preparePathOrUrl( pathOrUrl, pool );
apr_array_header_t* propItems;
svn_opt_revision_t svnrev;
convertRevision( &rev, &svnrev );
svn_error_t* err = svn_client_proplist( &propItems, cpath, &svnrev, recurse,
_context->_context, pool );
if( err )
{
return wrapError(err);
}
for( int i = 0; i < propItems->nelts; i++ )
{
svn_client_proplist_item_t* item =
(APR_ARRAY_IDX(propItems,i,svn_client_proplist_item_t*));
items.push_back( PropListItemPtr(new PropListItem(item)) );
}
return Success;
}
sc::Error* Client::propget( PropGetItems& items, const sc::String& propName,
const sc::String& pathOrUrl, const Revision& rev, bool recurse )
{
apr::Pool pool( _pool );
const char *cpath = preparePathOrUrl( pathOrUrl, pool );
svn_opt_revision_t svnrev;
convertRevision( &rev, &svnrev );
apr_hash_t* prophash = 0;
svn_error_t* err = svn_client_propget( &prophash, propName, cpath, &svnrev,
recurse, _context->_context, pool );
if( err )
{
return wrapError(err);
}
apr_hash_index_t* hi;
for( hi = apr_hash_first(_pool,prophash); hi; hi = apr_hash_next(hi) )
{
const char* key;
apr_ssize_t klen;
svn_string_t* val;
apr_hash_this( hi, (const void**)&key, &klen, (void**)&val );
items.push_back( PropGetItemPtr(
new PropGetItem(sc::String(key,klen),sc::String(val->data))) );
}
return Success;
}
sc::Error* Client::propset( const sc::String& propName, const sc::String& propVal,
const sc::String& path, bool recurse )
{
apr::Pool pool( _pool );
const char* cpath = preparePathOrUrl( path, pool );
svn_string_t* cval = propVal.getStr() ? svn_string_create( propVal, pool ) : 0;
svn_error_t* err = svn_client_propset( propName, cval, cpath, recurse, pool );
if( err )
{
return wrapError(err);
}
return Success;
}
sc::Error* Client::propsetrev( const sc::String& propName,
const sc::String& propVal, const sc::String& pathOrUrl,
const Revision& srcRev, Revnumber* resultRev, bool force )
{
apr::Pool pool( _pool );
const char* url = 0;
svn_error_t* err = svn_client_url_from_path( &url, pathOrUrl, pool );
if( err )
{
return wrapError(err);
}
svn_revnum_t resultrev = 0;
const char* curl = preparePathOrUrl( url, pool );
svn_string_t* cval = svn_string_create( propVal, pool );
svn_opt_revision_t svnrev;
convertRevision( &srcRev, &svnrev );
err = svn_client_revprop_set( propName, cval, curl, &svnrev, &resultrev,
force, _context->_context, pool );
if( err )
{
return wrapError(err);
}
*resultRev = resultrev;
return Success;
}
sc::Error* Client::lock( const Paths& pathsOrUrls, const sc::String& comment,
bool stealLocks )
{
apr::Pool pool( _pool );
apr_array_header_t* patharr = apr_array_make( pool, (int)pathsOrUrls.size(),
sizeof(char*) );
for( Paths::const_iterator it = pathsOrUrls.begin(); it != pathsOrUrls.end(); it++ )
{
const char* cpath = preparePathOrUrl( *it, pool );
APR_ARRAY_PUSH( patharr, const char* ) = cpath;
}
svn_error_t* err = svn_client_lock( patharr, comment.getStr(), stealLocks,
_context->_context, pool );
if( err )
{
return wrapError(err);
}
return Success;
}
sc::Error* Client::unlock( const Paths& pathsOrUrls, bool breakLocks )
{
apr::Pool pool( _pool );
apr_array_header_t* patharr = apr_array_make( pool, (int)pathsOrUrls.size(), sizeof(char*) );
for( Paths::const_iterator it = pathsOrUrls.begin(); it != pathsOrUrls.end(); it++ )
{
const char* cpath = preparePathOrUrl( *it, pool );
APR_ARRAY_PUSH( patharr, const char* ) = cpath;
}
svn_error_t* err = svn_client_unlock( patharr, breakLocks, _context->_context, pool );
if( err )
{
return wrapError(err);
}
return Success;
}
sc::Error* Client::info( const sc::String& pathOrUrl, const Revision& rev, const Revision& peg,
InfoBaton* baton, bool recurse )
{
apr::Pool pool( _pool );
const char *cpath = preparePathOrUrl( pathOrUrl, pool );
svn_opt_revision_t svnrev;
svn_opt_revision_t pegrev;
convertRevision( &rev, &svnrev );
convertRevision( &peg, &pegrev );
InfoFns ib(baton);
svn_error_t* err = svn_client_info( cpath, &pegrev, &svnrev, InfoFns::receive,
&ib, recurse, _context->_context, pool );
if( err )
{
return wrapError(err);
}
return Success;
}
sc::Error* Client::details( Revnumber* resultRev, sc::String& root,
const sc::String& pathOrUrl )
{
class Ra
{
public:
static svn_error_t* open_tmp_file( apr_file_t **f, void *callback_baton,
apr_pool_t *pool )
{
const char* path;
const char* ignore;
SVN_ERR( svn_io_temp_dir( &path, pool ) );
path = svn_path_join( path, "tempfile", pool );
SVN_ERR( svn_io_open_unique_file( f, &ignore, path, ".tmp", true, pool ) );
return SVN_NO_ERROR;
}
};
apr::Pool pool( _pool );
const char* url;
void* raSession;
void* raBaton;
svn_ra_plugin_t* raLib;
svn_ra_callbacks_t* raCb = (svn_ra_callbacks_t*)apr_pcalloc( pool, sizeof(*raCb));
raCb->open_tmp_file = Ra::open_tmp_file;
raCb->auth_baton = _context->_context->auth_baton;
const char *resultUrl;
*resultRev = SVN_INVALID_REVNUM;
SC_SVN_ERR( svn_client_url_from_path( &url, pathOrUrl, pool ) );
SC_SVN_ERR( svn_ra_init_ra_libs( &raBaton, pool ) );
SC_SVN_ERR( svn_ra_get_ra_library( &raLib, raBaton, url, pool ) );
SC_SVN_ERR( raLib->open( &raSession, url, raCb, 0, _context->_context->config, pool) );
SC_SVN_ERR( raLib->get_latest_revnum( raSession, resultRev, pool ) );
SC_SVN_ERR( raLib->get_repos_root( raSession, &resultUrl, pool ) );
root = sc::String(resultUrl);
return Success;
}
bool Client::isWorkingCopy( const sc::String& path )
{
apr::Pool pool;
svn_wc_adm_access_t* adm;
// handle an empty path, svn_path_canonicalize crashes when it gets an
// empty string.
if( path.isEmpty() )
{
return false;
}
const char* cpath = preparePathOrUrl( path, pool );
svn_error_t* err = svn_wc_adm_open2( &adm, NULL, cpath, false, 0, pool );
if( err && err->apr_err == SVN_ERR_WC_NOT_DIRECTORY )
{
return false;
}
return true;
}
#if 0
/**
* \brief get the revision number of 'head' on the given path or url.
*
* \param resultRev variable to store the 'head' revision number of pathOrUrl in.
* \param pathOrUrl the utf-8 encoded path or url we want the 'head' revision from.
* \return the error object or SVN_SUCCESS.
*/
// based on libsvn_client/cat.c:svn_client_cat
Error* Client::getHeadRevision( Revnumber* resultRev, const sc::String& pathOrUrl )
{
apr::Pool pool( _pool );
const char* url;
void* raSession;
void* raBaton;
svn_ra_plugin_t* raLib;
*resultRev = SVN_INVALID_REVNUM;
SC_SVN_ERR( svn_client_url_from_path( &url, pathOrUrl, pool ) );
SC_SVN_ERR( svn_ra_init_ra_libs( &raBaton, pool ) );
SC_SVN_ERR( svn_ra_get_ra_library( &raLib, raBaton, url, pool ) );
SC_SVN_ERR( svn_client__open_ra_session( &raSession, raLib, url,
0, 0, 0, false, false, _context->_context, pool ) ); // raLib->open?
SC_SVN_ERR( raLib->get_latest_revnum( raSession, resultRev, pool ) );
return SC_SVN_SUCCESS;
}
Error* Client::getReposRoot( const sc::String& pathOrUrl, sc::String& root )
{
apr::Pool pool( _pool );
const char* url;
void* raSession;
void* raBaton;
svn_ra_plugin_t* raLib;
const char *resultUrl;
SC_SVN_ERR( svn_client_url_from_path( &url, pathOrUrl, pool ) );
SC_SVN_ERR( svn_ra_init_ra_libs( &raBaton, pool ) );
SC_SVN_ERR( svn_ra_get_ra_library( &raLib, raBaton, url, pool ) );
SC_SVN_ERR( svn_client__open_ra_session( &raSession, raLib, url,
0, 0, 0, false, false, _context->_context, pool ) ); //raLib->open?
SC_SVN_ERR( raLib->get_repos_root( raSession, &resultUrl, pool ) );
root = sc::String(resultUrl);
return SC_SVN_SUCCESS;
}
#endif
const ClientContext* Client::getContext() const
{
return _context;
}
void Client::setCommitBaton( CommitBaton* baton )
{
_context->_context->log_msg_baton = baton;
}
/**
* \brief fills an svn_opt_revsion_t structure from a Revision object.
*
* \param rev the source Revision object.
* \param svnrev the target svn_opt_revision_t structure.
*/
void Client::convertRevision( const Revision* rev, svn_opt_revision_t* svnrev )
{
svnrev->kind = (svn_opt_revision_kind)rev->getKind();
switch( rev->getKind() )
{
case Revision_Number:
{
const RevisionNumber* daterev = static_cast<const RevisionNumber*>(rev);
svnrev->value.number = daterev->getNumber();
break;
}
case Revision_Date:
{
const RevisionDate* daterev = static_cast<const RevisionDate*>(rev);
svnrev->value.date = daterev->getDate();
break;
}
default:
{
break;
}
}
}
const char* Client::preparePathOrUrl( const char* pathOrUrl, apr_pool_t* pool )
{
// based on svn_opt_args_to_target_array2
const char* prepared = pathOrUrl;
if( svn_path_is_url(pathOrUrl) )
{
// do anything necessary to create proper url
prepared = svn_path_uri_from_iri( prepared, pool );
prepared = svn_path_uri_autoescape( prepared, pool );
prepared = svn_path_canonicalize( prepared, pool );
}
else
{
prepared = svn_path_canonicalize( prepared, pool );
}
return prepared;
}
} // namespace
syntax highlighted by Code2HTML, v. 0.9.1