Index: Makefile.in =================================================================== --- Makefile.in (revision 12622) +++ Makefile.in (working copy) @@ -68,12 +68,14 @@ swig_py_libdir = @libdir@ swig_java_libdir = @libdir@ swig_pl_libdir = @libdir@ +swig_rb_libdir = @libdir@ ### these possibly need further discussion swig_pydir = @libdir@/svn-python/libsvn swig_pydir_extra = @libdir@/svn-python/svn swig_javadir = @libdir@/svn-java swig_pldir = @libdir@/svn-perl +swig_rbdir = @libdir@/$(SWIG_RB_SITE_ARCH_DIR)/svn/ext javahl_javadir = @libdir@/svn-javahl javahl_javahdir = @libdir@/svn-javahl/include @@ -129,6 +131,11 @@ SWIG_JAVA_COMPILE = @SWIG_JAVA_COMPILE@ SWIG_JAVA_LINK = @SWIG_JAVA_LINK@ SWIG_PL_INCLUDES = @SWIG_PL_INCLUDES@ +SWIG_RB_INCLUDES = @SWIG_RB_INCLUDES@ -I$(SWIG_SRC_DIR)/ruby/libsvn_swig_ruby +SWIG_RB_COMPILE = @SWIG_RB_COMPILE@ +SWIG_RB_LINK = @SWIG_RB_LINK@ +SWIG_RB_SITE_LIB_DIR = @SWIG_RB_SITE_LIB_DIR@ +SWIG_RB_SITE_ARCH_DIR = @SWIG_RB_SITE_ARCH_DIR@ JAVAHL_INCLUDES= @JNI_INCLUDES@ -I$(abs_builddir)/subversion/bindings/java/javahl/include @@ -155,6 +162,7 @@ COMPILE_SWIG_PY = $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) $(CPPFLAGS) -DSWIGPYTHON $(CFLAGS) $(SWIG_PY_INCLUDES) $(INCLUDES) -o $@ -c COMPILE_SWIG_JAVA = $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) $(CPPFLAGS) $(CFLAGS) $(SWIG_JAVA_INCLUDES) $(INCLUDES) -o $@ -c COMPILE_SWIG_PL = $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) $(CPPFLAGS) $(CFLAGS) $(SWIG_PL_INCLUDES) $(INCLUDES) -o $@ -c +COMPILE_SWIG_RB = $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) $(CPPFLAGS) $(CFLAGS) $(SWIG_RB_INCLUDES) $(INCLUDES) -o $@ -c # special compilation for files destined for javahl (i.e. C++) COMPILE_JAVAHL_CXX = $(LIBTOOL) $(LTCXXFLAGS) --mode=compile $(COMPILE_CXX) $(JAVAHL_INCLUDES) -o $@ -c @@ -173,14 +181,24 @@ ### should we protect against swig not being available? RUN_SWIG_PY = $(SWIG) $(SWIG_NORUNTIME_FLAG) -python $(SWIG_INCLUDES) $(SWIG_PY_INCLUDES) -o $@ RUN_SWIG_JAVA = cd ${SWIG_BUILD_DIR}/java/org/tigris/subversion/swig && $(SWIG) $(SWIG_NORUNTIME_FLAG) -java -package 'org.tigris.subversion.swig' $(SWIG_INCLUDES) -o ${abs_builddir}/$@ +RUN_SWIG_RB = \ + $(RUBY) $(top_srcdir)/build/generate-ruby-wrapper.rb \ + ${SWIG_SRC_DIR}/ruby/svn \ + _`$(RUBY) -e "print File.basename('$*').sub(/^svn_/, '')"` && \ + $(SWIG) $(SWIG_NORUNTIME_FLAG) -ruby \ + -prefix svn::ext:: \ + -feature _`$(RUBY) -e "print File.basename('$*').sub(/^svn_/, '')"` \ + $(SWIG_INCLUDES) $(SWIG_RB_INCLUDES) -o $@ # Compilation of SWIG-generated C source code COMPILE_PY_WRAPPER = $(LIBTOOL) $(LTFLAGS) --mode=compile $(SWIG_PY_COMPILE) $(CPPFLAGS) $(SWIG_INCLUDES) $(SWIG_PY_INCLUDES) -prefer-pic -c -o $@ COMPILE_JAVA_WRAPPER = $(LIBTOOL) $(LTFLAGS) --mode=compile $(SWIG_JAVA_COMPILE) -DSWIGJAVA $(SWIG_INCLUDES) $(SWIG_JAVA_INCLUDES) -prefer-pic -c -o $@ +COMPILE_RB_WRAPPER = $(LIBTOOL) $(LTFLAGS) --mode=compile $(SWIG_RB_COMPILE) $(CPPFLAGS) $(SWIG_INCLUDES) $(SWIG_RB_INCLUDES) -prefer-pic -c -o $@ # these commands link the wrapper objects into an extension library/module LINK_PY_WRAPPER = $(LIBTOOL) $(LTFLAGS) --mode=link $(SWIG_PY_LINK) $(SWIG_LDFLAGS) -rpath $(swig_pydir) -avoid-version -module LINK_JAVA_WRAPPER = $(LIBTOOL) $(LTFLAGS) --mode=link $(SWIG_JAVA_LINK) $(SWIG_LDFLAGS) -rpath $(swig_javadir) -avoid-version -module +LINK_RB_WRAPPER = $(LIBTOOL) $(LTFLAGS) --mode=link $(SWIG_RB_LINK) $(SWIG_LDFLAGS) -rpath $(swig_rbdir) -avoid-version -module LINK_JAVAHL_CXX = $(LIBTOOL) $(LTCXXFLAGS) --mode=link $(CXX) $(LT_LDFLAGS) $(CFLAGS) $(LDFLAGS) $(LT_CXX_LIBADD) -rpath $(libdir) @@ -203,6 +221,8 @@ INSTALL_SWIG_JAVA = $(INSTALL_LIB) INSTALL_SWIG_JAVA_LIB = $(INSTALL_LIB) INSTALL_SWIG_PL_LIB = $(INSTALL_LIB) +INSTALL_SWIG_RB = $(INSTALL_LIB) +INSTALL_SWIG_RB_LIB = $(INSTALL_LIB) INSTALL_JAVAHL_LIB = $(INSTALL_LIB) @@ -218,6 +238,7 @@ # bindings. SWIG_PL_DIR = $(abs_builddir)/subversion/bindings/swig/perl SWIG_JAVA_DIR = $(abs_builddir)/subversion/bindings/swig/java +SWIG_RB_DIR = $(abs_builddir)/subversion/bindings/swig/ruby ### Enhance using Makefile generator to support JAR installation. INSTALL_EXTRA_SWIG_JAVA=\ @@ -231,6 +252,15 @@ INSTALL_EXTRA_JAVAHL_LIB=@INSTALL_EXTRA_JAVAHL_LIB@ +INSTALL_EXTRA_SWIG_RB=\ + $(RUBY) -run -e mkdir -- -p $(DESTDIR)${exec_prefix}/lib/$(SWIG_RB_SITE_LIB_DIR)/svn; \ + (cd $(SWIG_RB_DIR); \ + find svn \ + -name "*.rb" \ + -exec $(RUBY) -run -e install -- \ + $(SWIG_RB_DIR)/\{\} \ + $(DESTDIR)@libdir@/$(SWIG_RB_SITE_LIB_DIR)/\{\} \;) + APXS = @APXS@ PYTHON = @PYTHON@ @@ -250,6 +280,8 @@ swig_java_java_CLASSPATH=$(SWIG_JAVA_DIR)/build:$(JAVA_CLASSPATH) swig_java_tests_CLASSPATH=$(SWIG_JAVA_DIR)/build:$(JAVA_CLASSPATH) +RUBY = @RUBY@ + ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ @@ -599,3 +631,14 @@ clean-swig-pl: $(SWIG_PL_DIR)/native/Makefile cd $(SWIG_PL_DIR)/native; $(MAKE) clean + +clean-swig-rb: + for d in $(SWIG_RB_DIR) $(SWIG_RB_DIR)/libsvn_swig_ruby; \ + do \ + cd $$d; \ + rm -rf svn_*.c _*.c core.c *.lo *.la *.o .libs; \ + done; \ + for i in $(SWIG_SRC_DIR)/*.i; \ + do \ + rm -f $(SWIG_RB_DIR)/svn/`$(RUBY) -e "print File.basename(ARGV.first, '.*').sub(/^svn_/, '')" $$i`.rb; \ + done Index: build.conf =================================================================== --- build.conf (revision 12622) +++ build.conf (working copy) @@ -26,7 +26,7 @@ [options] includes = subversion/include/*.h -swig-languages = python java perl +swig-languages = python java perl ruby [static-apache] # Makefile.in and config.m4 (omit README) @@ -68,6 +68,7 @@ subversion/bindings/swig/python subversion/bindings/swig/java/org/tigris/subversion/swig subversion/bindings/swig/perl + subversion/bindings/swig/ruby # ---------------------------------------------------------------------------- # @@ -282,7 +283,7 @@ type = swig path = subversion/bindings/swig sources = svn_client.i -libs = libsvn_swig_py libsvn_swig_java libsvn_swig_perl libsvn_client libsvn_subr apr +libs = libsvn_swig_py libsvn_swig_java libsvn_swig_perl libsvn_swig_ruby libsvn_client libsvn_subr apr nonlibs = swig_core description = Subversion client library bindings @@ -290,7 +291,7 @@ type = swig path = subversion/bindings/swig sources = svn_delta.i -libs = libsvn_swig_py libsvn_swig_java libsvn_swig_perl libsvn_delta libsvn_subr +libs = libsvn_swig_py libsvn_swig_java libsvn_swig_perl libsvn_swig_ruby libsvn_delta libsvn_subr nonlibs = swig_core description = Subversion delta library bindings @@ -298,7 +299,7 @@ type = swig path = subversion/bindings/swig sources = svn_fs.i -libs = libsvn_swig_py libsvn_swig_java libsvn_swig_perl libsvn_fs libsvn_subr +libs = libsvn_swig_py libsvn_swig_java libsvn_swig_perl libsvn_swig_ruby libsvn_fs libsvn_subr nonlibs = swig_core description = Subversion FS library bindings @@ -306,7 +307,7 @@ type = swig path = subversion/bindings/swig sources = svn_ra.i -libs = libsvn_swig_py libsvn_swig_java libsvn_swig_perl libsvn_ra libsvn_subr +libs = libsvn_swig_py libsvn_swig_java libsvn_swig_perl libsvn_swig_ruby libsvn_ra libsvn_subr nonlibs = swig_core description = Subversion RA library bindings @@ -314,7 +315,7 @@ type = swig path = subversion/bindings/swig sources = svn_repos.i -libs = libsvn_swig_py libsvn_swig_java libsvn_swig_perl libsvn_repos libsvn_subr +libs = libsvn_swig_py libsvn_swig_java libsvn_swig_perl libsvn_swig_ruby libsvn_repos libsvn_subr nonlibs = swig_core description = Subversion repository library bindings @@ -322,7 +323,7 @@ type = swig path = subversion/bindings/swig sources = svn_wc.i -libs = libsvn_swig_py libsvn_swig_java libsvn_swig_perl libsvn_wc libsvn_subr +libs = libsvn_swig_py libsvn_swig_java libsvn_swig_perl libsvn_swig_ruby libsvn_wc libsvn_subr nonlibs = swig_core description = Subversion WC library bindings @@ -330,7 +331,7 @@ type = swig path = subversion/bindings/swig sources = core.i -libs = libsvn_swig_py libsvn_swig_java libsvn_swig_perl +libs = libsvn_swig_py libsvn_swig_java libsvn_swig_perl libsvn_swig_ruby libsvn_delta libsvn_diff libsvn_subr apr description = Subversion core library bindings include-runtime = yes @@ -412,6 +413,21 @@ compile-cmd = $(COMPILE_SWIG_PL) msvc-static = yes +# SWIG utility library for Ruby modules +[libsvn_swig_ruby] +type = swig_lib +lang = ruby +path = subversion/bindings/swig/ruby/libsvn_swig_ruby +sources = swigutil_rb.c +libs = libsvn_subr libsvn_delta aprutil apriconv apr +nonlibs = swig_core +# need SWIG_LDFLAGS to be able to link swig_runtime +link-cmd = $(LINK) $(SWIG_LDFLAGS) $(SWIG_RB_LIBS) +install = swig-rb-lib +# need special build rule to include +compile-cmd = $(COMPILE_SWIG_RB) +msvc-static = yes + # ---------------------------------------------------------------------------- # # JavaHL targets @@ -882,6 +898,12 @@ libs = swig_client swig_delta swig_fs swig_ra swig_repos swig_wc swig_core lang = perl +[__SWIG_RUBY__] +type = swig_project +path = build/win32 +libs = swig_client swig_delta swig_fs swig_ra swig_repos swig_wc swig_core +lang = ruby + [__JAVAHL__] type = project path = build/win32 Index: configure.in =================================================================== --- configure.in (revision 12622) +++ configure.in (working copy) @@ -584,6 +584,8 @@ AC_PATH_PROG(PERL, perl, none) +AC_PATH_PROG(RUBY, ruby, none) + SVN_CHECK_SWIG dnl try to find the "makeinfo" program @@ -755,6 +757,7 @@ SVN_CONFIG_SCRIPT(tools/hook-scripts/commit-email.pl) SVN_CONFIG_SCRIPT(tools/hook-scripts/propchange-email.pl) SVN_CONFIG_SCRIPT(subversion/bindings/swig/perl/native/Makefile.PL) +SVN_CONFIG_SCRIPT(subversion/bindings/swig/ruby/test/run-test.sh) AC_OUTPUT Index: build/ac-macros/swig.m4 =================================================================== --- build/ac-macros/swig.m4 (revision 12622) +++ build/ac-macros/swig.m4 (working copy) @@ -58,7 +58,7 @@ bindings=$1 if test "$bindings" = "all"; then - bindings="perl,python,java" + bindings="perl,python,java,ruby" fi for binding in `echo "$bindings" | sed -e "s/,/ /g"`; do @@ -109,7 +109,7 @@ # packages/rpm/redhat-7.x/subversion.spec # packages/rpm/redhat-8.x/subversion.spec if test -n "$SWIG_VERSION" && test "$SWIG_VERSION" -ge "103019" -a \ - "$SWIG_VERSION" -lt "103022"; then + "$SWIG_VERSION" -lt "103023"; then SWIG_SUITABLE=yes AC_CACHE_CHECK([for swig library directory], [ac_cv_swig_swiglib_dir],[ ac_cv_swig_swiglib_dir="`$SWIG -swiglib`" @@ -222,6 +222,37 @@ fi fi + if test "$RUBY" != "none" -a "$SWIG_SUITABLE" = "yes" -a "$svn_swig_bindings_enable_ruby" = "yes"; then + AC_MSG_NOTICE([Configuring Ruby SWIG binding]) + SWIG_CLEAN_RULES="$SWIG_CLEAN_RULES clean-swig-rb" + + AC_CACHE_CHECK([for Ruby includes], [ac_cv_ruby_includes],[ + ac_cv_ruby_includes="-I. -I`$RUBY -rrbconfig -e 'print Config::CONFIG.fetch(%q(archdir))'`" + ]) + SWIG_RB_INCLUDES="\$(SWIG_INCLUDES) $ac_cv_ruby_includes" + + AC_CACHE_CHECK([for compiling Ruby extensions], [ac_cv_ruby_compile],[ + ac_cv_ruby_compile="`$RUBY -rrbconfig -e 'print Config::CONFIG.fetch(%q(CC))'` -g \$(SWIG_RB_INCLUDES)" + # ac_cv_ruby_compile="`$RUBY -rrbconfig -e 'print Config::CONFIG.fetch(%q(CC)), %q( ), Config::CONFIG.fetch(%q(CFLAGS))'` \$(SWIG_RB_INCLUDES)" + ]) + SWIG_RB_COMPILE="$ac_cv_ruby_compile" + + AC_CACHE_CHECK([for linking Ruby extensions], [ac_cv_ruby_link],[ + ac_cv_ruby_link="`$RUBY -rrbconfig -e 'print Config::CONFIG.fetch(%q(LDSHARED))'`" + ]) + SWIG_RB_LINK="$ac_cv_ruby_link" + + AC_CACHE_CHECK([for installing Ruby scripts], [ac_cv_ruby_site_lib],[ + ac_cv_ruby_site_lib="`$RUBY -rrbconfig -e 'print Config::CONFIG.fetch(%q(sitelibdir)).sub(/^.*lib\//, %q())'`" + ]) + SWIG_RB_SITE_LIB_DIR="$ac_cv_ruby_site_lib" + + AC_CACHE_CHECK([for installing Ruby extensions], [ac_cv_ruby_site_arch],[ + ac_cv_ruby_site_arch="`$RUBY -rrbconfig -e 'print Config::CONFIG.fetch(%q(sitearchdir)).sub(/^.*lib\//, %q())'`" + ]) + SWIG_RB_SITE_ARCH_DIR="$ac_cv_ruby_site_arch" + fi + fi AC_SUBST(SWIG_CLEAN_RULES) AC_SUBST(SWIG_NORUNTIME_FLAG) @@ -233,6 +264,11 @@ AC_SUBST(SWIG_JAVA_COMPILE) AC_SUBST(SWIG_JAVA_LINK) AC_SUBST(SWIG_PL_INCLUDES) + AC_SUBST(SWIG_RB_LINK) + AC_SUBST(SWIG_RB_INCLUDES) + AC_SUBST(SWIG_RB_COMPILE) + AC_SUBST(SWIG_RB_SITE_LIB_DIR) + AC_SUBST(SWIG_RB_SITE_ARCH_DIR) AC_SUBST(SWIG_LIBSWIG_DIR) AC_SUBST(SWIG_LDFLAGS) ]) Index: build/generate-ruby-wrapper.rb =================================================================== --- build/generate-ruby-wrapper.rb (revision 0) +++ build/generate-ruby-wrapper.rb (revision 0) @@ -0,0 +1,31 @@ +require "erb" + +prefix = ARGV.shift + +ARGV.each do |target| + target_name = File.basename(target).sub(/\..+$/, '') + base_name = target_name[1..-1] + ruby_file_name = "#{base_name}.rb" + additional_target = "#{base_name}_additional" + additional_file_name = "#{additional_target}.rb" + module_name = "#{target_name[1,1].upcase}#{target_name[2..-1]}" + File.open(File.join(prefix, ruby_file_name), "w") do |f| + f.print(<<-EOC) +require "English" +require "svn/error" +require "svn/util" +require "svn/ext/#{target_name}" + +module Svn + module #{module_name} + Util.set_constants(Ext::#{module_name}, self) + Util.set_methods(Ext::#{module_name}, self) + end +end +EOC + if File.exist?(File.join(prefix, additional_file_name)) + f.puts + f.puts("require 'svn/#{additional_target}'") + end + end +end Index: subversion/bindings/swig/svn_ra.i =================================================================== --- subversion/bindings/swig/svn_ra.i (revision 12622) +++ subversion/bindings/swig/svn_ra.i (working copy) @@ -96,7 +96,6 @@ /* ----------------------------------------------------------------------- */ -%include svn_ra.h %{ #include "svn_ra.h" @@ -111,7 +110,12 @@ #ifdef SWIGPERL #include "swigutil_pl.h" #endif + +#ifdef SWIGRUBY +#include "swigutil_rb.h" +#endif %} +%include svn_ra.h #ifdef SWIGPERL %include ra_plugin.hi Index: subversion/bindings/swig/core.i =================================================================== --- subversion/bindings/swig/core.i (revision 12622) +++ subversion/bindings/swig/core.i (working copy) @@ -184,7 +184,7 @@ these types (as 'type **') will always be an OUT param */ %apply SWIGTYPE **OUTPARAM { - svn_auth_baton_t **, svn_diff_t ** + svn_auth_baton_t **, svn_diff_t **, svn_config_t ** } /* ----------------------------------------------------------------------- @@ -215,6 +215,11 @@ $1 = malloc(temp); $2 = ($2_ltype)&temp; } +%typemap(ruby, in) (char *buffer, apr_size_t *len) ($*2_type temp) { + temp = NUM2LONG($input); + $1 = malloc(temp); + $2 = ($2_ltype)&temp; +} /* ### need to use freearg or somesuch to ensure the string is freed. ### watch out for 'return' anywhere in the binding code. */ @@ -229,6 +234,11 @@ free($1); argvi++; } +%typemap(ruby, argout, fragment="output_helper") (char *buffer, apr_size_t *len) +{ + $result = output_helper($result, *$2 == 0 ? Qnil : rb_str_new($1, *$2)); + free($1); +} /* ----------------------------------------------------------------------- fix up the svn_stream_write() ptr/len arguments @@ -247,6 +257,12 @@ $1 = SvPV($input, temp); $2 = ($2_ltype)&temp; } +%typemap(ruby, in) (const char *data, apr_size_t *len) ($*2_type temp) +{ + $1 = StringValuePtr($input); + temp = RSTRING($input)->len; + $2 = ($2_ltype)&temp; +} %typemap(python, argout, fragment="t_output_helper") (const char *data, apr_size_t *len) { $result = t_output_helper($result, PyInt_FromLong(*$2)); @@ -256,6 +272,11 @@ $result = sv_2mortal (newSViv(*$2)); } +%typemap(ruby, argout, fragment="output_helper") (const char *data, apr_size_t *len) +{ + $result = output_helper($result, LONG2NUM(*$2)); +} + /* ----------------------------------------------------------------------- auth provider convertors */ @@ -282,6 +303,11 @@ } } +%typemap(ruby, in) apr_array_header_t *providers +{ + $1 = svn_swig_rb_array_to_auth_provider_object_apr_array($input, _global_pool); +} + /* ----------------------------------------------------------------------- auth parameter set/get */ @@ -382,6 +408,11 @@ $2 = (void *)$input; }; +%typemap(ruby, in, numinputs=0) apr_hash_t **cfg_hash = apr_hash_t **OUTPUT; +%typemap(ruby, argout) apr_hash_t **cfg_hash { + $result = svn_swig_rb_apr_hash_to_hash_swig_type(*$1, "svn_config_t *"); +} + %typemap(python,in,numinputs=0) apr_hash_t **cfg_hash = apr_hash_t **OUTPUT; %typemap(python,argout,fragment="t_output_helper") apr_hash_t **cfg_hash { $result = t_output_helper( @@ -391,36 +422,20 @@ /* Allow None to be passed as config_dir argument */ %typemap(python,in,parse="z") const char *config_dir ""; +%typemap(ruby, in) const char *config_dir { + if (NIL_P($input)) { + $1 = ""; + } else { + $1 = StringValuePtr($input); + } +} #ifdef SWIGPYTHON PyObject *svn_swig_py_exception_type(void); #endif - /* ----------------------------------------------------------------------- */ -%include svn_types.h -%include svn_pools.h -%include svn_version.h -%include svn_time.h -%include svn_props.h -%include svn_opt.h -%include svn_auth.h -%include svn_config.h -%include svn_version.h - - -/* SWIG won't follow through to APR's defining this to be empty, so we - need to do it manually, before SWIG sees this in svn_io.h. */ -#define __attribute__(x) - -%include svn_io.h - -#ifdef SWIGPERL -%include svn_diff.h -%include svn_error.h -#endif - %{ #include #include @@ -449,8 +464,38 @@ #ifdef SWIGPERL #include "swigutil_pl.h" #endif + +#ifdef SWIGRUBY +#include "swigutil_rb.h" +#endif %} +#ifdef SWIGRUBY +%ignore svn_stream_read; +%ignore svn_stream_write; +%ignore svn_stream_close; +#endif +%include svn_types.h +%include svn_pools.h +%include svn_version.h +%include svn_time.h +%include svn_props.h +%include svn_opt.h +%include svn_auth.h +%include svn_config.h +%include svn_version.h + +/* SWIG won't follow through to APR's defining this to be empty, so we + need to do it manually, before SWIG sees this in svn_io.h. */ +#define __attribute__(x) + +%include svn_io.h + +#ifdef SWIGPERL +%include svn_diff.h +%include svn_error.h +#endif + #ifdef SWIGPYTHON %init %{ /* This is a hack. I dunno if we can count on SWIG calling the module "m" */ @@ -462,3 +507,36 @@ SubversionException = _core.SubversionException %} #endif + +#ifdef SWIGRUBY +%include svn_diff.h +%rename(svn_stream_read) svn_stream_read_; +%rename(svn_stream_write) svn_stream_write_; +%rename(svn_stream_close) svn_stream_close_; +%inline %{ +svn_error_t * +svn_stream_read_ (svn_stream_t *stream, char *buffer, + apr_size_t *len, apr_pool_t *pool) +{ + return svn_stream_read(stream, buffer, len); +} +svn_error_t * +svn_stream_write_ (svn_stream_t *stream, const char *data, + apr_size_t *len, apr_pool_t *pool) +{ + return svn_stream_write(stream, data, len); +} +svn_error_t * +svn_stream_close_ (svn_stream_t *stream, apr_pool_t *pool) +{ + return svn_stream_close(stream); +} +%} +%extend svn_auth_provider_object_t +{ + ~svn_auth_provider_object_t(svn_auth_provider_object_t *obj) + { + /* do nothing */ + } +} +#endif Index: subversion/bindings/swig/svn_types.i =================================================================== --- subversion/bindings/swig/svn_types.i (revision 12622) +++ subversion/bindings/swig/svn_types.i (working copy) @@ -33,6 +33,9 @@ %typemap(perl5, in, numinputs=0) SWIGTYPE **OUTPARAM ($*1_type temp) { $1 = ($1_ltype)&temp; } +%typemap(ruby, in, numinputs=0) SWIGTYPE **OUTPARAM ($*1_type temp) { + $1 = ($1_ltype)&temp; +} %typemap(python, argout, fragment="t_output_helper") SWIGTYPE **OUTPARAM { $result = t_output_helper($result, @@ -42,6 +45,9 @@ ST(argvi) = sv_newmortal(); SWIG_MakePtr(ST(argvi++), (void *)*$1, $*1_descriptor,0); } +%typemap(ruby, argout, fragment="output_helper") SWIGTYPE **OUTPARAM { + $result = output_helper($result, SWIG_NewPointerObj(*$1, $*1_descriptor, 0)); +} %typemap(java, in) SWIGTYPE **OUTPARAM ($*1_type temp) { $1 = ($1_ltype)&temp; @@ -69,6 +75,24 @@ %apply const char * { const char *MAY_BE_NULL }; #endif +%typemap(ruby, in) const char* MAY_BE_NULL +{ + if (NIL_P($input)) { + $1 = NULL; + } else { + $1 = StringValuePtr($input); + } +} + +%typemap(ruby, out) const char * +{ + if ($1) { + $result = rb_str_new2($1); + } else { + $result = Qnil; + } +} + %typemap(java, in) const char *MAY_BE_NULL { /* ### WHEN IS THIS USED? */ $1 = 0; @@ -126,6 +150,29 @@ } } +%typemap(ruby, out) svn_error_t * +{ + if ($1) { + svn_error_t *error = $1; + VALUE message; + + message = rb_str_new2(error->message ? error->message : ""); + + while (error->child) { + error = error->child; + if (error->message) { + rb_str_concat(message, rb_str_new2("\n")); + rb_str_concat(message, rb_str_new2(error->message)); + } + } + svn_error_clear(error); + + rb_exc_raise(svn_swig_rb_svn_error_new(INT2NUM(error->apr_err), + message)); + } + $result = Qnil; +} + %typemap(java, out) svn_error_t * %{ $result = ($1 != NULL) ? svn_swig_java_convert_error(jenv, $1) : NULL; %} @@ -150,6 +197,9 @@ %typemap(perl5,in,numinputs=0) svn_filesize_t * (svn_filesize_t temp) "$1 = &temp;"; +%typemap(ruby,in,numinputs=0) svn_filesize_t * (svn_filesize_t temp) + "$1 = &temp;"; + /* We have to use APR_INT64_T_FMT because SWIG won't convert the SVN_FILESIZE_T_FMT to the actual value only APR_INT64_T_FMT */ #if APR_INT64_T_FMT == "ld" @@ -173,6 +223,8 @@ sv_setpv((SV*)ST(argvi++), temp); }; +%typemap(ruby,argout,fragment="output_helper") svn_filesize_t * + "$result = output_helper($result, LL2NUM((apr_int64_t) (*$1)));"; #endif /* ----------------------------------------------------------------------- @@ -253,6 +305,11 @@ %typemap(perl5, default) apr_pool_t *pool(apr_pool_t *_global_pool) { _global_pool = $1 = svn_swig_pl_make_pool (ST(items-1)); } +%typemap(ruby, arginit) apr_pool_t *pool (apr_pool_t *_global_pool) { + /* Assume that the pool here is the last argument in the list */ + SWIG_ConvertPtr(argv[argc - 1], (void **)&$1, $1_descriptor, 1); + _global_pool = $1; +} #ifdef SWIGPERL %apply apr_pool_t *pool { @@ -356,6 +413,10 @@ argvi++; } +%typemap(ruby, in) svn_stream_t * { + $1 = svn_swig_rb_make_stream($input, _global_pool); +} + %typemap(java, in) svn_stream_t *out %{ $1 = svn_swig_java_outputstream_to_stream(jenv, $input, _global_pool); %} @@ -432,6 +493,61 @@ SWIG_croak("unknown opt_revision_t type"); } +%typemap(ruby, in) svn_opt_revision_t * (svn_opt_revision_t rev) { + $1 = &rev; + switch (TYPE($input)) { + case T_NIL: + rev.kind = svn_opt_revision_unspecified; + break; + case T_FIXNUM: + rev.kind = svn_opt_revision_number; + rev.value.number = NUM2LONG($input); + break; + case T_STRING: + if (RTEST(rb_reg_match(rb_reg_new("^BASE$", + strlen("^BASE$"), + RE_OPTION_IGNORECASE), + $input))) + rev.kind = svn_opt_revision_base; + else if (RTEST(rb_reg_match(rb_reg_new("^HEAD$", + strlen("^HEAD$"), + RE_OPTION_IGNORECASE), + $input))) + rev.kind = svn_opt_revision_head; + else if (RTEST(rb_reg_match(rb_reg_new("^WORKING$", + strlen("^WORKING$"), + RE_OPTION_IGNORECASE), + $input))) + rev.kind = svn_opt_revision_working; + else if (RTEST(rb_reg_match(rb_reg_new("^COMMITTED$", + strlen("^COMMITTED$"), + RE_OPTION_IGNORECASE), + $input))) + rev.kind = svn_opt_revision_committed; + else if (RTEST(rb_reg_match(rb_reg_new("^PREV$", + strlen("^PREV$"), + RE_OPTION_IGNORECASE), + $input))) + rev.kind = svn_opt_revision_previous; + else + rb_raise(rb_eArgError, + "invalid value: %s", + StringValuePtr($input)); + break; + default: + if (rb_obj_is_kind_of($input, + rb_const_get(rb_cObject, rb_intern("Time")))) { + rev.kind = svn_opt_revision_date; + rev.value.date = NUM2LONG(rb_funcall($input, rb_intern("to_i"), 0)); + } else { + rb_raise(rb_eArgError, + "invalid type: %s", + rb_class2name(CLASS_OF($input))); + } + break; + } +} + /* ----------------------------------------------------------------------- apr_hash_t **dirents svn_client_ls() @@ -465,6 +581,9 @@ $result = $1 ? JNI_TRUE : JNI_FALSE; %} +%typemap(ruby, in) svn_boolean_t "$1 = RTEST($input);"; +%typemap(ruby, out) svn_boolean_t "$result = $1 ? Qtrue : Qfalse;"; + /* ----------------------------------------------------------------------- Handle python thread locking. @@ -482,9 +601,30 @@ #endif } + +/* ----------------------------------------------------------------------- + handle config and fs_config in svn_{fs,repos}_create +*/ + + +%typemap(ruby, in) apr_hash_t *config (apr_hash_t *temp) +{ + if (NIL_P($input)) { + $1 = NULL; + } else { + $1 = svn_swig_rb_hash_to_apr_hash_swig_type($input, "svn_config_t *", _global_pool); + } +} +%typemap(ruby, in) apr_hash_t *fs_config +{ + if (NIL_P($input)) { + $1 = NULL; + } else { + $1 = svn_swig_rb_hash_to_apr_hash($input, _global_pool); + } +} /* ----------------------------------------------------------------------- */ -%include svn_types.h %{ #include "svn_types.h" #include "svn_time.h" @@ -500,4 +640,9 @@ #ifdef SWIGPERL #include "swigutil_pl.h" #endif + +#ifdef SWIGRUBY +#include "swigutil_rb.h" +#endif %} +%include svn_types.h Index: subversion/bindings/swig/ruby/test/run-test.sh.in =================================================================== --- subversion/bindings/swig/ruby/test/run-test.sh.in (revision 0) +++ subversion/bindings/swig/ruby/test/run-test.sh.in (revision 0) @@ -0,0 +1,17 @@ +#!/bin/sh + +for x in test/@top_builddir@/subversion/lib*/.libs +do + LD_LIBRALY_PATH=$x:$LD_LIBRALY_PATH +done + +(cd test/@top_builddir@/subversion/bindings/swig/ruby/; \ + mkdir -p ext/svn; \ + cd ext/svn; \ + ln -fs ../../.libs ./ext) + +env LD_LIBRALY_PATH=$LD_LIBRALY_PATH @RUBY@ -w \ + -I test/@top_srcdir@/subversion/bindings/swig/ruby \ + -I test/@top_builddir@/subversion/bindings/swig/ruby/ext \ + test/runner.rb \ + $@ Property changes on: subversion/bindings/swig/ruby/test/run-test.sh.in ___________________________________________________________________ Name: svn:executable + * Index: subversion/bindings/swig/ruby/test/my-assertions.rb =================================================================== --- subversion/bindings/swig/ruby/test/my-assertions.rb (revision 0) +++ subversion/bindings/swig/ruby/test/my-assertions.rb (revision 0) @@ -0,0 +1,21 @@ +require "test/unit/assertions" + +module Test + module Unit + module Assertions + + def assert_true(boolean, message=nil) + _wrap_assertion do + assert_equal(true, boolean, message) + end + end + + def assert_false(boolean, message=nil) + _wrap_assertion do + assert_equal(false, boolean, message) + end + end + + end + end +end Index: subversion/bindings/swig/ruby/test/util.rb =================================================================== --- subversion/bindings/swig/ruby/test/util.rb (revision 0) +++ subversion/bindings/swig/ruby/test/util.rb (revision 0) @@ -0,0 +1,64 @@ +require "fileutils" + +require "svn/core" +require "svn/client" +require "svn/repos" + +module SvnTestUtil + + def setup_basic + @author = ENV["USER"] || "sample-user" + @pool = Svn::Core::Pool.new(nil) + @repos_path = "test/repos" + @repos_uri = "file://#{File.expand_path(@repos_path)}" + @wc_path = "test/wc" + setup_repository(@repos_path) + @repos = Svn::Repos.open(@repos_path, @pool) + @fs = @repos.fs + make_context("").checkout(@repos_uri, @wc_path) + end + + def teardown_basic + @pool.destroy + teardown_repository(@repos_path) + FileUtils.rm_rf(@wc_path) + end + + def setup_repository(path, config={}, fs_config={}) + FileUtils.mkdir_p(File.dirname(path)) + Svn::Core.run_app do |pool| + Svn::Repos.create(path, config, fs_config, pool) + end + end + + def teardown_repository(path) + Svn::Core.run_app do |pool| + Svn::Repos.delete(path, pool) + end + end + + def youngest_rev + @fs.youngest_rev + end + + def root(rev=nil) + @fs.root(rev) + end + + def prop(name, rev=nil) + @fs.prop(name, rev) + end + + def make_context(log) + ctx = Svn::Client::Context.new(@pool) + ctx.log_msg_func = Proc.new do |items| + [true, log] + end + ctx.add_username_prompt_provider(0) do |cred, realm, may_save, pool| + cred.username = @author + cred.may_save = false + end + ctx + end + +end Index: subversion/bindings/swig/ruby/test/test_client.rb =================================================================== --- subversion/bindings/swig/ruby/test/test_client.rb (revision 0) +++ subversion/bindings/swig/ruby/test/test_client.rb (revision 0) @@ -0,0 +1,35 @@ +require "my-assertions" +require "util" + +require "svn/core" + +class TestSvnClient < Test::Unit::TestCase + include SvnTestUtil + + def setup + setup_basic + end + + def teardown + teardown_basic + end + + def test_commit + log = "sample log" + ctx = make_context(log) + Svn::Client.checkout(@repos_uri, @wc_path, "HEAD", true, ctx, @pool) + Svn::Client.mkdir(["#{@wc_path}/new_dir"], ctx, @pool) + assert_equal(0, youngest_rev) + Svn::Client.commit([@wc_path], false, ctx, @pool) + assert_equal(1, youngest_rev) + end + + def test_not_new + assert_raise(NoMethodError) do + Svn::Client::CommitItem.new + end + assert_raise(NoMethodError) do + Svn::Client::CommitInfo.new + end + end +end Index: subversion/bindings/swig/ruby/test/test_core.rb =================================================================== --- subversion/bindings/swig/ruby/test/test_core.rb (revision 0) +++ subversion/bindings/swig/ruby/test/test_core.rb (revision 0) @@ -0,0 +1,32 @@ +require "my-assertions" +require "util" + +require "svn/core" + +class TestSvnCore < Test::Unit::TestCase + include SvnTestUtil + + def setup + @repos_path = "test/repos" + setup_repository(@repos_path) + end + + def teardown + teardown_repository(@repos_path) + end + + def test_binary_mime_type? + assert(Svn::Core.binary_mime_type?("image/png")) + assert(!Svn::Core.binary_mime_type?("text/plain")) + end + + def test_new_pool + + end + + def test_not_new_auth_provider_object + assert_raise(NoMethodError) do + Svn::Core::AuthProviderObject.new + end + end +end Index: subversion/bindings/swig/ruby/test/test_fs.rb =================================================================== --- subversion/bindings/swig/ruby/test/test_fs.rb (revision 0) +++ subversion/bindings/swig/ruby/test/test_fs.rb (revision 0) @@ -0,0 +1,37 @@ +require "my-assertions" +require "util" + +require "svn/core" +require "svn/fs" +require "svn/repos" +require "svn/client" + +class TestSvnFs < Test::Unit::TestCase + include SvnTestUtil + + def setup + setup_basic + end + + def teardown + teardown_basic + end + + def test_prop + log = "sample log" + ctx = make_context(log) + ctx.checkout(@repos_uri, @wc_path) + ctx.mkdir(["#{@wc_path}/new_dir"]) + past_time = Time.new + info = ctx.commit([@wc_path]) + + assert_equal(@author, info.author) + assert_equal(@fs.youngest_rev, info.revision) + assert(past_time <= info.date) + assert(info.date <= Time.now) + + assert_equal(@author, prop(Svn::Core::PROP_REVISION_AUTHOR)) + assert_equal(log, prop(Svn::Core::PROP_REVISION_LOG)) + end + +end Index: subversion/bindings/swig/ruby/test/test_repos.rb =================================================================== --- subversion/bindings/swig/ruby/test/test_repos.rb (revision 0) +++ subversion/bindings/swig/ruby/test/test_repos.rb (revision 0) @@ -0,0 +1,78 @@ +require "my-assertions" +require "util" + +require "svn/core" +require "svn/fs" +require "svn/repos" +require "svn/client" + +class TestSvnRepos < Test::Unit::TestCase + include SvnTestUtil + + def setup + setup_basic + end + + def teardown + teardown_basic + end + + def test_path + assert_equal(@repos_path, @repos.path) + + assert_equal(File.join(@repos_path, "db"), @repos.db_env) + + assert_equal(File.join(@repos_path, "conf"), @repos.conf_dir) + + assert_equal(File.join(@repos_path, "conf", "svnserve.conf"), + @repos.svnserve_conf) + + assert_equal(File.join(@repos_path, "locks"), @repos.lock_dir) + + + hooks_dir = File.join(@repos_path, "hooks") + assert_equal(File.join(hooks_dir, "start-commit"), + @repos.start_commit_hook) + assert_equal(File.join(hooks_dir, "pre-commit"), + @repos.pre_commit_hook) + assert_equal(File.join(hooks_dir, "post-commit"), + @repos.post_commit_hook) + + assert_equal(File.join(hooks_dir, "pre-revprop-change"), + @repos.pre_revprop_change_hook) + assert_equal(File.join(hooks_dir, "post-revprop-change"), + @repos.post_revprop_change_hook) + + + search_path = @repos_path + assert_equal(@repos_path, Svn::Repos.find_root_path(search_path, @pool)) + search_path = "#{@repos_path}/XXX" + assert_equal(@repos_path, Svn::Repos.find_root_path(search_path, @pool)) + + search_path = "not-found" + assert_equal(nil, Svn::Repos.find_root_path(search_path, @pool)) + end + + def test_transaction + log = "sample log" + ctx = make_context(log) + ctx.checkout(@repos_uri, @wc_path) + ctx.mkdir(["#{@wc_path}/new_dir"]) + + prev_rev = @repos.youngest_rev + past_date = Time.now + @repos.transaction_for_commit(@author, log) do |txn| + txn.abort + end + assert_equal(prev_rev, @repos.youngest_rev) + assert_equal(prev_rev, @repos.dated_revision(past_date)) + + prev_rev = @repos.youngest_rev + @repos.transaction_for_commit(@author, log) do |txn| + end + assert_equal(prev_rev + 1, @repos.youngest_rev) + assert_equal(prev_rev, @repos.dated_revision(past_date)) + assert_equal(prev_rev + 1, @repos.dated_revision(Time.now)) + end + +end Index: subversion/bindings/swig/ruby/test/runner.rb =================================================================== --- subversion/bindings/swig/ruby/test/runner.rb (revision 0) +++ subversion/bindings/swig/ruby/test/runner.rb (revision 0) @@ -0,0 +1,5 @@ +#!/usr/bin/env ruby + +require "test/unit" + +exit Test::Unit::AutoRunner.run(false, File.dirname($0)) Property changes on: subversion/bindings/swig/ruby/test/runner.rb ___________________________________________________________________ Name: svn:executable + * Index: subversion/bindings/swig/ruby/test/test_info.rb =================================================================== --- subversion/bindings/swig/ruby/test/test_info.rb (revision 0) +++ subversion/bindings/swig/ruby/test/test_info.rb (revision 0) @@ -0,0 +1,209 @@ +require "util" + +require "svn/info" + +class SvnInfoTest < Test::Unit::TestCase + include SvnTestUtil + + def setup + setup_basic + end + + def teardown + teardown_basic + end + + def test_info + file = "hello.txt" + path = File.join(@wc_path, file) + log = "test commit\nnew line" + FileUtils.touch(path) + + ctx = make_context(log) + ctx.add(path) + commit_info = ctx.commit([@wc_path]) + + info = make_info(commit_info.revision) + assert_equal(@author, info.author) + assert_equal(commit_info.date, info.date) + assert_equal(commit_info.revision, info.revision) + assert_equal(log, info.log) + end + + def test_dirs_changed + dir = "new_dir" + file = "new.txt" + dir_path = File.join(@wc_path, dir) + file_path = File.join(dir_path, file) + log = "added dir" + + ctx = make_context(log) + ctx.mkdir(dir_path) + FileUtils.touch(file_path) + ctx.add(file_path) + commit_info = ctx.commit(@wc_path) + + info = make_info(commit_info.revision) + assert_equal(["/", "#{dir}/"], info.changed_dirs) + assert_equal(commit_info.revision, info.revision) + assert_equal(log, info.log) + end + + def test_changed + dir = "changed_dir" + tmp_dir = "changed_tmp_dir" + dir_path = File.join(@wc_path, dir) + tmp_dir_path = File.join(@wc_path, tmp_dir) + + log = "added 2 dirs\nanded 5 files" + ctx = make_context(log) + + ctx.mkdir([dir_path, tmp_dir_path]) + + file1 = "changed1.txt" + file2 = File.join(dir, "changed2.txt") + file3 = "changed3.txt" + file4 = File.join(dir, "changed4.txt") + file5 = "changed5.txt" + file1_path = File.join(@wc_path, file1) + file2_path = File.join(@wc_path, file2) + file3_path = File.join(@wc_path, file3) + file4_path = File.join(@wc_path, file4) + file5_path = File.join(@wc_path, file5) + FileUtils.touch(file1_path) + FileUtils.touch(file2_path) + FileUtils.touch(file3_path) + FileUtils.touch(file4_path) + FileUtils.touch(file5_path) + ctx.add(file1_path) + ctx.add(file2_path) + ctx.add(file3_path) + ctx.add(file4_path) + ctx.add(file5_path) + + commit_info = ctx.commit(@wc_path) + + info = make_info(commit_info.revision) + assert_equal([].sort, info.updated_dirs.sort) + assert_equal([].sort, info.deleted_dirs.sort) + assert_equal(["#{dir}/", "#{tmp_dir}/"].sort, info.added_dirs.sort) + + + file6 = File.join(dir, "changed6.txt") + file7 = "changed7.txt" + file8 = File.join(dir, "changed8.txt") + file6_path = File.join(@wc_path, file6) + file7_path = File.join(@wc_path, file7) + file8_path = File.join(@wc_path, file8) + File.open(file1_path, "w") {|f| f.puts "changed"} + File.open(file2_path, "w") {|f| f.puts "changed"} + File.open(file3_path, "w") {|f| f.puts "changed"} + + log = "changed 3 files\ndeleted 2 files\nadded 3 files" + ctx = make_context(log) + ctx.rm_f([file4_path, file5_path]) + FileUtils.touch(file6_path) + FileUtils.touch(file7_path) + FileUtils.touch(file8_path) + ctx.add(file6_path) + ctx.add(file7_path) + ctx.add(file8_path) + ctx.rm(tmp_dir_path) + + commit_info = ctx.commit(@wc_path) + + info = make_info(commit_info.revision) + assert_equal([file1, file2, file3].sort, info.updated_files.sort) + assert_equal([file4, file5].sort, info.deleted_files.sort) + assert_equal([file6, file7, file8].sort, info.added_files.sort) + assert_equal([].sort, info.updated_dirs.sort) + assert_equal(["#{tmp_dir}/"].sort, info.deleted_dirs.sort) + assert_equal([].sort, info.added_dirs.sort) + end + + def _test_diff + log = "diff" + + file1 = "diff1.txt" + file2 = "diff2.txt" + file3 = "diff3.txt" + FileUtils.touch("#{@wc}/#{file1}") + File.open("#{@wc}/#{file2}", "w") {|f| f.puts "changed"} + FileUtils.touch("#{@wc}/#{file3}") + `svn add #{@wc}/#{file1}` + `svn add #{@wc}/#{file2}` + `svn add #{@wc}/#{file3}` + `svn propset AAA BBB #{@wc}/#{file1}` + commit(log) + + file4 = "diff4.txt" + file5 = "diff5.txt" + File.open("#{@wc}/#{file1}", "w") {|f| f.puts "changed"} + File.open("#{@wc}/#{file2}", "w") {|f| f.puts "removed\nadded"} + FileUtils.touch("#{@wc}/#{file4}") + `svn add #{@wc}/#{file4}` + `svn propdel AAA #{@wc}/#{file1}` + `svn propset XXX YYY #{@wc}/#{file4}` + `svn copy #{@wc}/#{file3} #{@wc}/#{file5}` + commit(log) + + info = make_info + keys = info.diffs.keys.sort + file5_key = keys.last + assert_equal(4, info.diffs.size) + assert_equal([file1, file2, file4].sort, keys[0..-2]) + assert_match(/\A#{file5}/, file5_key) + assert(info.diffs[file1].has_key?(:modified)) + assert(info.diffs[file2].has_key?(:modified)) + assert(info.diffs[file4].has_key?(:added)) + assert(info.diffs[file4].has_key?(:property_changed)) + assert(info.diffs[file5_key].has_key?(:copied)) + assert_equal(1, info.diffs[file1][:modified][:added]) + assert_equal(0, info.diffs[file1][:modified][:deleted]) + assert_equal(2, info.diffs[file2][:modified][:added]) + assert_equal(1, info.diffs[file2][:modified][:deleted]) + assert_equal(0, info.diffs[file4][:added][:added]) + assert_equal(0, info.diffs[file4][:added][:deleted]) + assert_equal(0, info.diffs[file5_key][:copied][:added]) + assert_equal(0, info.diffs[file5_key][:copied][:deleted]) + assert_equal(@rev, info.revision) + assert_equal(log, info.log) + end + + def _test_sha256 + log = "sha256" + + file1 = "diff1.txt" + file2 = "diff2.txt" + file3 = "diff3.txt" + file1_content = "added file1" + file2_content = "added file2" + file3_content = "added file3" + all_content = file1_content + file2_content + file3_content + File.open("#{@wc}/#{file1}", "w") {|f| f.print file1_content} + File.open("#{@wc}/#{file2}", "w") {|f| f.print file2_content} + File.open("#{@wc}/#{file3}", "w") {|f| f.print file3_content} + `svn add #{@wc}/#{file1}` + `svn add #{@wc}/#{file2}` + `svn add #{@wc}/#{file3}` + commit(log) + + info = make_info + assert_equal(3, info.sha256.size) + assert_equal(Digest::SHA256.hexdigest(file1_content), + info.sha256[file1][:sha256]) + assert_equal(Digest::SHA256.hexdigest(file2_content), + info.sha256[file2][:sha256]) + assert_equal(Digest::SHA256.hexdigest(file3_content), + info.sha256[file3][:sha256]) + assert_equal(Digest::SHA256.hexdigest(all_content), + info.entire_sha256) + assert_equal(@rev, info.revision) + assert_equal(log, info.log) + end + + def make_info(rev=nil) + Svn::Info.new(@repos_path, rev || @fs.youngest_rev, @pool) + end + +end Index: subversion/bindings/swig/ruby/test/test_util.rb =================================================================== --- subversion/bindings/swig/ruby/test/test_util.rb (revision 0) +++ subversion/bindings/swig/ruby/test/test_util.rb (revision 0) @@ -0,0 +1,52 @@ +require "my-assertions" + +require "svn/util" + +class SvnUtilTest < Test::Unit::TestCase + + def test_to_ruby_boolean + assert(Svn::Util.to_ruby_boolean(1)) + assert(!Svn::Util.to_ruby_boolean(0)) + end + + def test_to_ruby_const_name + assert_equal("ABC", Svn::Util.to_ruby_const_name("abc")) + assert_equal("ABC_DEF", Svn::Util.to_ruby_const_name("abc_def")) + end + + def test_to_ruby_class_name + assert_equal("Abc", Svn::Util.to_ruby_class_name("abc")) + assert_equal("AbcDef", Svn::Util.to_ruby_class_name("abc_def")) + end + + def test_time + now = Time.now.gmtime + str = now.strftime("%Y-%m-%dT%H:%M:%S.") + "#{now.usec}Z" + + assert_equal(Time.at(now.to_i, 0), Svn::Util.string_to_time(str)) + Svn::Core.run_app do |pool| + assert_equal(now, Svn::Util.string_to_time(str, pool)) + end + + apr_time = now.to_i * 1000000 + now.usec + assert_equal(apr_time, Svn::Util.to_apr_time(now)) + assert_equal(apr_time, Svn::Util.to_apr_time(apr_time)) + end + + def test_set_pool + obj = Object.new + class << obj + attr_accessor :pool + end + + obj.pool = nil + assert_nil(obj.pool) + + Svn::Core.run_app do |pool| + Svn::Util.set_pool(pool) do + obj + end + assert_equal(pool, obj.pool) + end + end +end Index: subversion/bindings/swig/ruby/svn/util.rb =================================================================== --- subversion/bindings/swig/ruby/svn/util.rb (revision 0) +++ subversion/bindings/swig/ruby/svn/util.rb (revision 0) @@ -0,0 +1,85 @@ +require "time" + +module Svn + module Util + + MILLION = 1000000 + + module_function + def to_ruby_class_name(name) + name.split("_").collect{|x| "#{x[0,1].upcase}#{x[1..-1]}"}.join("") + end + + def to_ruby_const_name(name) + name.upcase + end + + def to_ruby_boolean(value) + not value.zero? + end + + def to_apr_time(value) + if value.is_a?(Time) + value.to_i * MILLION + value.usec + else + value + end + end + + def string_to_time(str, pool=nil) + if pool + sec, usec = Core.time_from_cstring(str, pool).divmod(MILLION) + Time.at(sec, usec) + else + Time.parse(str).localtime + end + end + + def set_pool(pool) + obj = yield + obj.pool = pool + obj + end + + def set_constants(ext_mod, target_mod=self) + target_name = nil + ext_mod.constants.each do |const| + target_name = nil + case const + when /^SVN_(?:#{target_mod.name.split("::").last.upcase}_)?/ + target_name = $POSTMATCH + when /^SWIG_SVN_/ + target_name = "SWIG_#{$POSTMATCH}" + when /^Svn_(?:#{target_mod.name.split("::").last.downcase}_)?(.+)_t$/ + target_name = to_ruby_class_name($1) + when /^Svn_(?:#{target_mod.name.split("::").last.downcase}_)?/ + target_name = to_ruby_const_name($POSTMATCH) +# else +# puts const + end + unless target_name.nil? + target_mod.const_set(target_name, ext_mod.const_get(const)) + end + end + end + + def set_methods(ext_mod, target_mod=self) + target_name = nil + ext_mod.public_methods(false).each do |meth| + target_name = nil + case meth + when /^svn_(?:#{target_mod.name.split("::").last.downcase}_)?/ + target_name = $POSTMATCH + when /^apr_/ + target_name = meth + end + unless target_name.nil? + target_id = target_name.intern + target_proc = ext_mod.method(meth).to_proc + target_mod.__send__(:define_method, target_id, target_proc) + target_mod.__send__(:module_function, target_id) + end + end + end + end +end Index: subversion/bindings/swig/ruby/svn/ext/fs.rb =================================================================== --- subversion/bindings/swig/ruby/svn/ext/fs.rb (revision 0) +++ subversion/bindings/swig/ruby/svn/ext/fs.rb (revision 0) @@ -0,0 +1 @@ +require "svn/fs" Index: subversion/bindings/swig/ruby/svn/ext/delta.rb =================================================================== --- subversion/bindings/swig/ruby/svn/ext/delta.rb (revision 0) +++ subversion/bindings/swig/ruby/svn/ext/delta.rb (revision 0) @@ -0,0 +1 @@ +require "svn/delta" Index: subversion/bindings/swig/ruby/svn/ext/wc.rb =================================================================== --- subversion/bindings/swig/ruby/svn/ext/wc.rb (revision 0) +++ subversion/bindings/swig/ruby/svn/ext/wc.rb (revision 0) @@ -0,0 +1 @@ +require "svn/wc" Index: subversion/bindings/swig/ruby/svn/fs_additional.rb =================================================================== --- subversion/bindings/swig/ruby/svn/fs_additional.rb (revision 0) +++ subversion/bindings/swig/ruby/svn/fs_additional.rb (revision 0) @@ -0,0 +1,283 @@ +require "tempfile" + +module Svn + module Fs + class << self + def dir?(root, path, pool) + Util.to_ruby_boolean(is_dir(root, path, pool)) + end + end + + FileSystem = SWIG::TYPE_p_svn_fs_t + class FileSystem + + class << self + def new(config, pool) + Util.set_pool(pool) do + Fs.new(config, pool) + end + end + + def create(path, config, pool) + Util.set_pool(pool) do + Fs.create(path, config, pool) + end + end + + def open(path, config, pool) + Util.set_pool(pool) do + Fs.open(path, config, pool) + end + end + end + + attr_accessor :pool + + def open_txn(name) + Util.set_pool(@pool) do + Fs.open_txn(self, name, @pool) + end + end + + def transaction(rev=nil) + txn = nil + Util.set_pool(@pool) do + txn = Fs.begin_txn(self, rev || youngest_rev, @pool) + end + + if block_given? + yield(txn) + txn.commit if transactions.include?(txn.name) + else + txn + end + end + + def youngest_rev + Fs.youngest_rev(self, @pool) + end + + def prop(name, rev=nil) + Fs.revision_prop(self, rev || youngest_rev, name, @pool) + end + + def transactions + Fs.list_transactions(self, @pool) + end + + def root(rev=nil) + Util.set_pool(@pool) do + Fs.revision_root(self, rev || youngest_rev, @pool) + end + end + end + + + Transaction = SWIG::TYPE_p_svn_fs_txn_t + class Transaction + + attr_accessor :pool + + def name + Fs.txn_name(self, @pool) + end + + def prop(name) + Fs.txn_prop(self, name, @pool) + end + + def base_revision + Fs.txn_base_revision(self) + end + + def root + Util.set_pool(@pool) do + Fs.txn_root(self, @pool) + end + end + + def proplist + Fs.txn_proplist(self, @pool) + end + + def abort + Fs.abort_txn(self, @pool) + end + + def commit + result = Fs.commit(self, @pool) + if result.is_a?(Array) + result + else + [nil, result] + end + end + end + + + Root = SWIG::TYPE_p_svn_fs_root_t + class Root + attr_accessor :pool + attr_reader :editor + + def revision + Fs.revision_root_revision(self) + end + + def fs + Util.set_pool(@pool) do + Fs.root_fs(self) + end + end + + def node_id(path) + Fs.node_id(self, path, @pool) + end + + def node_created_rev(path) + Fs.node_created_rev(self, path, @pool) + end + + def node_proplist(path) + Fs.node_proplist(self, path, @pool) + end + alias node_prop_list node_proplist + + def dir?(path) + Fs.dir?(self, path, @pool) + end + + def check_path(path) + Fs.check_path(self, path, @pool) + end + + def file_length(path) + Fs.file_length(self, path, @pool) + end + + def file_contents(path) + stream = Fs.file_contents(self, path, @pool) + if block_given? + begin + yield stream + ensure + stream.close + end + else + stream + end + end + + def close + Fs.close_root(self) + end + + def dir_entries(path) + Fs.dir_entries(self, path, @pool) + end + + def editor=(editor) + @editor = editor + @svn_editor, @baton = Delta.make_editor(@editor, @pool) + end + + def dir_delta(src_path, src_entry, tgt_root, tgt_path, + text_deltas=false, recurse=true, + entry_props=false, ignore_ancestry=false, + &authz_read_func) + authz_read_func = Proc.new{true} if authz_read_func.nil? + Repos.dir_delta(self, src_path, src_entry, + tgt_root, tgt_path, + @svn_editor, @baton, authz_read_func, + text_deltas, recurse, entry_props, + ignore_ancestry, @pool) + end + end + + + Id = SWIG::TYPE_p_svn_fs_id_t + class Id + def to_s(pool=nil) + if pool + unparse(pool) + else + "\#<#{self.class}:#{__id__}>" + end + end + + def unparse(pool) + Fs.unparse_id(self, pool) + end + end + + + class FileDiff + + def initialize(root1, path1, label1, root2, path2, label2, pool) + @tempfile1 = nil + @tempfile2 = nil + + @root1 = root1 + @path1 = path1 + @label1 = label1 + @root2 = root2 + @path2 = path2 + @label2 = label2 + + @pool = Core::Pool.new(pool) + end + + def binary? + if @path1 + prop = @root1.node_prop(@path1, Core::PROP_MIME_TYPE, @pool) + prop and Core.binary_mime_type?(prop) + elsif @path2 + prop = @root2.node_prop(@path2, Core::PROP_MIME_TYPE, @pool) + prop and Core.binary_mime_type?(prop) + else + false + end + end + + def files + if @tempfile1 + [@tempfile1, @tempfile2] + else + @tempfile1 = Tempfile.new("svn_fs") + @tempfile2 = Tempfile.new("svn_fs") + + dump_contents(@tempfile1, @root1, @path1) + @pool.clear + dump_contents(@tempfile2, @root2, @path2) + @pool.clear + + [@tempfile1, @tempfile2] + end + end + + def diff + files + Core.diff_file_diff(@tempfile1.path, @tempfile2.path, @pool) + end + + private + def dump_contents(tempfile, root, path) + unless path.nil? + begin + tempfile.open + root.file_contents(path) do |stream| + Util.set_pool(@pool) do + stream + end + tempfile.print(stream.read) + end + ensure + tempfile.close + end + end + end + + end + end +end + Index: subversion/bindings/swig/ruby/svn/core_additional.rb =================================================================== --- subversion/bindings/swig/ruby/svn/core_additional.rb (revision 0) +++ subversion/bindings/swig/ruby/svn/core_additional.rb (revision 0) @@ -0,0 +1,111 @@ +Svn::Core.apr_initialize +at_exit {Svn::Core.apr_terminate} + +module Svn + module Core + class << self + + alias pool_destroy apr_pool_destroy + alias pool_clear apr_pool_clear + + def run_app(*args) + Pool.new(nil) do |pool| + yield(pool, *args) + end + end + + def binary_mime_type?(mime_type) + Util.to_ruby_boolean(mime_type_is_binary(mime_type)) + end + end + + + AuthCredSSLClientCert = AuthCredSslClientCert + AuthCredSSLClientCertPw = AuthCredSslClientCertPw + AuthCredSSLServerTrust = AuthCredSslServerTrust + + + Pool = SWIG::TYPE_p_apr_pool_t + + class Pool + class << self + def new(parent=nil) + pool = Core.pool_create(parent) + if block_given? + result = yield pool + pool.destroy + result + else + pool + end + end + end + + def clear + Core.pool_clear(self) + end + + def destroy + Core.pool_destroy(self) + end + end + + + Stream = SWIG::TYPE_p_svn_stream_t + + class Stream + CHUNK_SIZE = Core::STREAM_CHUNK_SIZE + + attr_accessor :pool + + def read(len=nil) + if len.nil? + read_all + else + buf = "" + while len > CHUNK_SIZE + buf << _read(CHUNK_SIZE) + len -= CHUNK_SIZE + end + buf << _read(len) + buf + end + end + + def close + Core.stream_close(self, @pool) + end + + private + def _read(size) + Core.stream_read(self, size, @pool) + end + + def read_all + buf = "" + while chunk = _read(CHUNK_SIZE) + buf << chunk + end + buf + end + end + + + AuthBaton = SWIG::TYPE_p_svn_auth_baton_t + class AuthBaton + class << self + def open(providers, pool) + Core.auth_open(providers, pool) + end + end + end + + + class AuthProviderObject + class << self + undef new + end + end + end +end + Index: subversion/bindings/swig/ruby/svn/repos_additional.rb =================================================================== --- subversion/bindings/swig/ruby/svn/repos_additional.rb (revision 0) +++ subversion/bindings/swig/ruby/svn/repos_additional.rb (revision 0) @@ -0,0 +1,82 @@ +module Svn + module Repos + + class << self + alias_method :_create, :create + alias_method :_open, :open + end + alias_method :_create, :create + alias_method :_open, :open + + module_function + def open(path, pool) + Util.set_pool(pool) do + _open(path, pool) + end + end + + def create(path, config, fs_config, pool) + Util.set_pool(pool) do + _create(path, nil, nil, config, fs_config, pool) + end + end + + ReposCore = SWIG::TYPE_p_svn_repos_t + class ReposCore + class << self + def def_simple_delegate(*ids) + ids.each do |id| + module_eval(<<-EOC, __FILE__, __LINE__) + def #{id.to_s} + Repos.#{id.to_s}(self, @pool) + end + EOC + end + end + end + + attr_accessor :pool + + def_simple_delegate :path, :db_env, :conf_dir + def_simple_delegate :svnserve_conf, :lock_dir + def_simple_delegate :start_commit_hook + def_simple_delegate :pre_commit_hook, :post_commit_hook + def_simple_delegate :pre_revprop_change_hook, :post_revprop_change_hook + + + def fs + @fs ||= Util.set_pool(@pool) do + Repos.fs(self) + end + end + + def youngest_rev + fs.youngest_rev + end + + def dated_revision(date) + Repos.dated_revision(self, Util.to_apr_time(date), @pool) + end + + def transaction_for_commit(author, log, rev=nil) + txn = nil + args = [self, rev || youngest_rev, author, log, @pool] + Util.set_pool(@pool) do + txn = Repos.fs_begin_txn_for_commit(*args) + end + + if block_given? + yield(txn) + commit(txn) if fs.transactions.include?(txn.name) + else + txn + end + end + + def commit(txn) + Repos.fs_commit_txn(self, txn, @pool) + end + + end + end +end Index: subversion/bindings/swig/ruby/svn/delta_additional.rb =================================================================== --- subversion/bindings/swig/ruby/svn/delta_additional.rb (revision 0) +++ subversion/bindings/swig/ruby/svn/delta_additional.rb (revision 0) @@ -0,0 +1,281 @@ +module Svn + module Delta + + class << self + alias make_editor swig_rb_make_editor + end + + remove_const(:Editor) + class Editor + + def set_target_revision(target_revision, pool) + end + + def open_root(base_revision, dir_pool) + end + + def delete_entry(path, revision, parent_baton, pool) + end + + def add_directory(path, parent_baton, + copyfrom_path, copyfrom_revision, dir_pool) + end + + def open_directory(path, parent_baton, base_revision, dir_pool) + end + + def change_dir_prop(dir_baton, name, value, pool) + end + + def close_directory(dir_baton) + end + + def absent_directory(path, parent_baton, pool) + end + + def add_file(path, parent_baton, + copyfrom_path, copyfrom_revision, file_pool) + end + + def open_file(path, parent_baton, base_revision, file_pool) + end + + def apply_textdelta(file_baton, base_checksum, pool) + end + + def change_file_prop(file_baton, name, value, pool) + end + + def close_file(file_baton, text_checksum) + end + + def absent_file(path, parent_baton, pool) + end + + def close_edit(baton) + end + + def abort_edit(baton) + end + end + + class ChangedDirsEditor < Editor + attr_reader :changed_dirs + + def initialize + @changed_dirs = [] + end + + def open_root(base_revision, dir_pool) + [true, ''] + end + + def delete_entry(path, revision, parent_baton, pool) + dir_changed(parent_baton) + end + + def add_directory(path, parent_baton, + copyfrom_path, copyfrom_revision, dir_pool) + dir_changed(parent_baton) + [true, path] + end + + def open_directory(path, parent_baton, base_revision, dir_pool) + [true, path] + end + + def change_dir_prop(dir_baton, name, value, pool) + dir_changed(dir_baton) + end + + def add_file(path, parent_baton, + copyfrom_path, copyfrom_revision, file_pool) + dir_changed(parent_baton) + end + + def open_file(path, parent_baton, base_revision, file_pool) + dir_changed(parent_baton) + end + + private + def dir_changed(baton) + if baton[0] + # the directory hasn't been printed yet. do it. + @changed_dirs << "#{baton[1]}/" + baton[0] = nil + end + end + end + + class ChangedEditor < Editor + + attr_reader :added_files, :added_dirs + attr_reader :deleted_files, :deleted_dirs + attr_reader :updated_files, :updated_dirs + + def initialize(root, base_root) + @root = root + @base_root = base_root + @added_files = [] + @added_dirs = [] + @deleted_files = [] + @deleted_dirs = [] + @updated_files = [] + @updated_dirs = [] + end + + def open_root(base_revision, dir_pool) + [true, ''] + end + + def delete_entry(path, revision, parent_baton, pool) + if @base_root.dir?("/#{path}") + @deleted_dirs << "#{path}/" + else + @deleted_files << path + end + end + + def add_directory(path, parent_baton, + copyfrom_path, copyfrom_revision, dir_pool) + @added_dirs << "#{path}/" + [false, path] + end + + def open_directory(path, parent_baton, base_revision, dir_pool) + [true, path] + end + + def change_dir_prop(dir_baton, name, value, pool) + if dir_baton[0] + @updated_dirs << "#{dir_baton[1]}/" + dir_baton[0] = false + end + end + + def add_file(path, parent_baton, + copyfrom_path, copyfrom_revision, file_pool) + @added_files << path + [nil, nil, nil] + end + + def open_file(path, parent_baton, base_revision, file_pool) + [nil, nil, path] + end + + def apply_textdelta(file_baton, base_checksum, pool) + file_baton[0] = :update + nil + end + + def change_file_prop(file_baton, name, value, pool) + file_baton[1] = :update + end + + def close_file(file_baton, text_checksum) + text_mod, prop_mod, path = file_baton + # test the path. it will be nil if we added this file. + if path + if [text_mod, prop_mod] != [nil, nil] + @updated_files << path + end + end + end + end + + class DiffEditor < Svn::Delta::Editor + + def initialize(root, base_root) + @root = root + @base_root = base_root + @entries = {} + end + + def delete_entry(path, revision, parent_baton, pool) + unless @base_root.dir?("/#{path}") + do_diff(path, nil, pool) + end + end + + def add_file(path, parent_baton, + copyfrom_path, copyfrom_revision, file_pool) + do_diff(nil, path, file_pool) + [nil, nil, nil] + end + + def open_file(path, parent_baton, base_revision, file_pool) + [nil, nil, path] + end + + def apply_textdelta(file_baton, base_checksum, pool) + txt_mod, prop_mod, path = file_baton + if path.nil? + nil + else + do_diff(path, path, pool) + end + end + + private + def do_diff(base_path, path, pool) + if base_path.nil? + type = :added + label = path + elsif path.nil? + type = :deleted + label = base_path + else + type = :modified + label = path + end + + @entries[label] ||= {} + entry = { + :type => type, + :body => "", + :added => 0, + :deleted => 0, + } + @entries[label][type] = entry + + base_rev = @base_root.revision + rev = @root.revision + base_date = @base_root.fs.prop(Core::PROP_REVISION_DATE, base_rev) + date = @root.fs.prop(Core::PROP_REVISION_DATE, rev) + base_label = "#{label}\t#{base_date} (rev #{base_rev})" + label = "#{label}\t#{date} (rev #{rev})" + differ = Fs::FileDiff.new(@base_root, base_path, base_label, + @root, path, label, pool) + + in_content = false + diff = differ.diff + if diff + Core.diff_file_output_unified(STDOUT, + diff, + File.join(Dir.pwd, "test/wc", base_path), + File.join(Dir.pwd, "test/wc", path), + base_label, label, + pool) + p Core.diff_contains_conflicts(diff) + p Core.diff_contains_diffs(diff) + end +# .each do |line| +# if /^@@/ =~ line +# in_content = true +# else +# if in_content +# case line +# when /^-/ +# entry[:deleted] += 1 +# when /^\+/ +# entry[:added] += 1 +# end +# end +# end +# entry[:body] << line +# end + end + + end + end +end Index: subversion/bindings/swig/ruby/svn/client_additional.rb =================================================================== --- subversion/bindings/swig/ruby/svn/client_additional.rb (revision 0) +++ subversion/bindings/swig/ruby/svn/client_additional.rb (revision 0) @@ -0,0 +1,131 @@ +module Svn + module Client + + class CommitItem + class << self + undef new + end + end + + class CommitInfo + class << self + undef new + end + + attr_accessor :pool + + alias _date date + def date + Util.string_to_time(_date, @pool) + end + end + + + Context = Ctx + class Context + class << self + undef new + def new(pool) + obj = Client.create_context(pool) + obj.__send__("initialize", pool) + obj + end + end + + alias _initialize initialize + def initialize(pool) + @pool = pool + @providers = [] + end + undef _initialize + + def checkout(url, path, revision="HEAD", recurse=true) + Client.checkout(url, path, revision, recurse, self, @pool) + end + + def checkout2(url, path, peg_revision=nil, revision="HEAD", recurse=true) + Client.checkout2(url, path, peg_revision, revision, recurse, self, @pool) + end + + def mkdir(paths) + paths = [paths] unless paths.is_a?(Array) + Client.mkdir(normalize_path(paths), self, @pool) + end + + def commit(targets, recurse=true) + targets = [targets] unless targets.is_a?(Array) + Util.set_pool(@pool) do + Client.commit(targets, !recurse, self, @pool) + end + end + + def add(path, recurse=true) + Client.add(path, recurse, self, @pool) + end + + def delete(paths, force=false) + paths = [paths] unless paths.is_a?(Array) + Client.delete(paths, force, self, @pool) + end + alias remove delete + alias rm remove + + def rm_f(paths) + rm(paths, true) + end + + def add_simple_prompt_provider(retry_limit, prompt=Proc.new) + args = [retry_limit, @pool] + klass = Core::AuthCredSimple + add_prompt_provider("simple", args, prompt, klass) + end + + def add_username_prompt_provider(retry_limit, prompt=Proc.new) + args = [retry_limit, @pool] + klass = Core::AuthCredUsername + add_prompt_provider("username", args, prompt, klass) + end + + def add_ssl_server_trust_prompt_provider(prompt=Proc.new) + args = [@pool] + klass = Core::AuthCredSSLServerTrust + add_prompt_provider("ssl_server_trust", args, prompt, klass) + end + + def add_ssl_client_cert_prompt_provider(retry_limit, prompt=Proc.new) + args = [retry_limit, @pool] + klass = Core::AuthCredSSLClientCert + add_prompt_provider("ssl_client_cert", args, prompt, klass) + end + + def add_ssl_client_cert_pw_prompt_provider(retry_limit, prompt=Proc.new) + args = [retry_limit, @pool] + klass = Core::AuthCredSSLClientCertPw + add_prompt_provider("ssl_client_cert_pw", args, prompt, klass) + end + + private + def add_prompt_provider(name, args, prompt, cred_class) + real_prompt = Proc.new do |*prompt_args| + cred = cred_class.new + prompt.call(cred, *prompt_args) + cred + end + pro = Client.__send__("get_#{name}_prompt_provider", real_prompt, *args) + @providers << pro + update_auth_baton + end + + def update_auth_baton + self.auth_baton = Core::AuthBaton.open(@providers, @pool) + end + + def normalize_path(paths) + paths = [paths] unless paths.is_a?(Array) + paths.collect do |path| + path.chomp(File::SEPARATOR) + end + end + end + end +end Index: subversion/bindings/swig/ruby/svn/error.rb =================================================================== --- subversion/bindings/swig/ruby/svn/error.rb (revision 0) +++ subversion/bindings/swig/ruby/svn/error.rb (revision 0) @@ -0,0 +1,39 @@ +require "svn/ext/_core" + +module Svn + class Error < StandardError + + TABLE = {} + + Ext::Core.constants.each do |const_name| + if /^SVN_ERR_(.*)/ =~ const_name + value = Ext::Core.const_get(const_name) + module_eval(<<-EOC, __FILE__, __LINE__) + class #{$1} < Error + def initialize(message) + super(#{value}, message) + end + end + EOC + TABLE[value] = const_get($1) + end + end + + class << self + def new_corresponding_error(code, message) + if TABLE.has_key?(code) + TABLE[code].new(message) + else + new(code, message) + end + end + end + + attr_reader :code, :message + def initialize(code, message) + @code = code + @message = message + super(message) + end + end +end Index: subversion/bindings/swig/ruby/svn/info.rb =================================================================== --- subversion/bindings/swig/ruby/svn/info.rb (revision 0) +++ subversion/bindings/swig/ruby/svn/info.rb (revision 0) @@ -0,0 +1,205 @@ +require "English" +require "time" +require "digest/sha2" + +require "nkf" +begin + require "uconv" +rescue LoadError + module Uconv + class Error < StandardError + end + def self.u8toeuc(str) + raise Error + end + end +end + +require "svn/core" +require "svn/repos" +require "svn/fs" +require "svn/delta" + +module Svn + class Info + + attr_reader :author, :log, :date, :changed_dirs + attr_reader :added_files, :deleted_files, :updated_files + attr_reader :added_dirs, :deleted_dirs, :updated_dirs + attr_reader :path, :revision, :diffs + attr_reader :sha256, :entire_sha256 + + def initialize(path, rev, pool) + @path = path + @revision = Integer(rev) + @pool = pool + @repos = Repos.open(@path, @pool) + @fs = @repos.fs + @root = @fs.root(@revision) + get_info + get_dirs_changed + get_changed + get_diff + get_sha256 + end + + private + def get_info + @author = force_to_utf8(prop(Core::PROP_REVISION_AUTHOR)) + @date = Util.string_to_time(prop(Core::PROP_REVISION_DATE), @pool) + @log = force_to_utf8(prop(Core::PROP_REVISION_LOG)) + end + + def get_dirs_changed + editor = traverse(Delta::ChangedDirsEditor) + @changed_dirs = editor.changed_dirs + end + + def get_changed + editor = traverse(Delta::ChangedEditor, true) + @added_files = editor.added_files + @added_dirs = editor.added_dirs + @deleted_files = editor.deleted_files + @deleted_dirs = editor.deleted_dirs + @updated_files = editor.updated_files + @updated_dirs = editor.updated_dirs + end + + def get_diff + editor = traverse(Delta::DiffEditor, true) + @diffs = {} + + change_lang("C") do + @diff = svnlook("diff") + end + @diffs = {} + last_target = nil + in_content = in_header = false + @diff.each do |line| + case line + when /^(Modified|Added|Deleted|Copied|Property changes on):\s+(.+)/ + in_content = false + in_header = nil + last_target = get_diff_handle_start($2.chomp, normalize_type($1)) + when /^@@/ + in_content = true + in_header = false + else + if in_content + case line + when /^-/ + last_target[:deleted] += 1 + when /^\+/ + last_target[:added] += 1 + end + end + end + + if in_content or in_header + last_target[:body] << line + end + + in_header = true if in_header.nil? + + end + + @diffs.each do |key, values| + values.each do |type, value| + value[:body] = force_to_utf8(value[:body]) + end + end + end + + NORMALIZE_TYPE_TABLE = { + "property_changes_on" => :property_changed + } + + def normalize_type(type_info_str) + normalized = type_info_str.gsub(/ /, '_').downcase + if NORMALIZE_TYPE_TABLE.has_key?(normalized) + NORMALIZE_TYPE_TABLE[normalized] + else + normalized.intern + end + end + + def get_diff_handle_start(target, type) + @diffs[target] ||= {} + @diffs[target][type] = { + :type => type, + :body => "", + :added => 0, + :deleted => 0, + } + @diffs[target][type] + end + + def get_sha256 + sha = Digest::SHA256.new + @sha256 = {} + [ + @added_files, +# @deleted_files, + @updated_files, + ].each do |files| + files.each do |file| + content = svnlook("cat", file) + sha << content + @sha256[file] = { + :file => file, + :revision => @revision, + :sha256 => Digest::SHA256.hexdigest(content), + } + end + end + @entire_sha256 = sha.hexdigest + end + + def svnlook(command, *others) + `svnlook #{command} #{@path} -r #{@revision} #{others.join(' ')}` + end + + def prop(name) + @fs.prop(name, @revision) + end + + def directory_path?(path) + path[-1] == ?/ + end + + def force_to_utf8(str) + str = str.to_s + begin + # check UTF-8 or not + Uconv.u8toeuc(str) + str + rescue Uconv::Error + NKF.nkf("-w", str) + end + end + + def change_lang(lang) + orig = ENV["LANG"] + begin + ENV["LANG"] = lang + yield + ensure + ENV["LANG"] = orig + end + end + + def traverse(editor_class, pass_root=false) + base_rev = @revision - 1 + base_root = @fs.root(base_rev) + if pass_root + editor = editor_class.new(@root, base_root) + else + editor = editor_class.new + end + base_root.editor = editor + base_root.dir_delta("", "", @root, "") + editor + end + + end +end Index: subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h =================================================================== --- subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h (revision 0) +++ subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h (revision 0) @@ -0,0 +1,131 @@ +#ifndef SVN_SWIG_SWIGUTIL_RB_H +#define SVN_SWIG_SWIGUTIL_RB_H + +#include +#include + +#include +#include +#include +#include + +#include "svn_types.h" +#include "svn_string.h" +#include "svn_delta.h" +#include "svn_client.h" +#include "svn_repos.h" + +#include +#ifndef SWIG_NewPointerObj +#define SWIG_NewPointerObj SWIG_Ruby_NewPointerObj +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include + +/* If this file is being included outside of a wrapper file, then need to + create stubs for some of the SWIG types. */ + +/* if SWIGEXPORT is defined, then we're in a wrapper. otherwise, we need + the prototypes and type definitions. */ +#ifndef SWIGEXPORT +#define SVN_NEED_SWIG_TYPES +#endif + +#ifdef SVN_NEED_SWIG_TYPES + +typedef struct _unnamed swig_type_info; +VALUE SWIG_NewPointerObj(void *, swig_type_info *, int own); +swig_type_info *SWIG_TypeQuery(const char *name); +int SWIG_ConvertPtr(VALUE, void **, swig_type_info *, int flags); + +#endif /* SVN_NEED_SWIG_TYPES */ + + +VALUE svn_swig_rb_svn_error_new(VALUE code, VALUE message); + +VALUE svn_swig_rb_apr_hash_to_hash_svn_string(apr_hash_t *hash); +VALUE svn_swig_rb_apr_hash_to_hash_swig_type(apr_hash_t *hash, + const char *type_name); + +VALUE svn_swig_rb_apr_array_to_array_string(const apr_array_header_t *ary); + +apr_hash_t *svn_swig_rb_hash_to_apr_hash(VALUE hash, apr_pool_t *pool); +apr_hash_t *svn_swig_rb_hash_to_apr_hash_swig_type(VALUE hash, + const char *typename, + apr_pool_t *pool); + +apr_array_header_t *svn_swig_rb_strings_to_apr_array(VALUE strings, + apr_pool_t *pool); +apr_array_header_t * +svn_swig_rb_array_to_auth_provider_object_apr_array(VALUE array, + apr_pool_t *pool); + +void svn_swig_rb_make_editor(const svn_delta_editor_t **editor, + void **edit_baton, + VALUE rb_editor, + apr_pool_t *pool); + +svn_error_t *svn_swig_rb_repos_authz_func(svn_boolean_t *allowed, + svn_fs_root_t *root, + const char *path, + void *baton, + apr_pool_t *pool); + +svn_error_t *svn_swig_rb_get_commit_log_func(const char **log_msg, + const char **tmp_file, + apr_array_header_t *commit_items, + void *baton, + apr_pool_t *pool); + +/* auth provider callbacks */ +svn_error_t *svn_swig_rb_auth_simple_prompt_func( + svn_auth_cred_simple_t **cred, + void *baton, + const char *realm, + const char *username, + svn_boolean_t may_save, + apr_pool_t *pool); + +svn_error_t *svn_swig_rb_auth_username_prompt_func( + svn_auth_cred_username_t **cred, + void *baton, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool); + +svn_error_t *svn_swig_rb_auth_ssl_server_trust_prompt_func( + svn_auth_cred_ssl_server_trust_t **cred, + 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); + +svn_error_t *svn_swig_rb_auth_ssl_client_cert_prompt_func( + svn_auth_cred_ssl_client_cert_t **cred, + void *baton, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool); + +svn_error_t *svn_swig_rb_auth_ssl_client_cert_pw_prompt_func( + svn_auth_cred_ssl_client_cert_pw_t **cred, + void *baton, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool); + +apr_file_t *svn_swig_rb_make_file(VALUE file, apr_pool_t *pool); +svn_stream_t *svn_swig_rb_make_stream(VALUE io, apr_pool_t *pool); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SVN_SWIG_SWIGUTIL_RB_H */ Property changes on: subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h ___________________________________________________________________ Name: svn:keywords + Id Index: subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.c =================================================================== --- subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.c (revision 0) +++ subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.c (revision 0) @@ -0,0 +1,1128 @@ +#include "ruby.h" +#include "st.h" + +#include "swigutil_rb.h" + +static VALUE mSvn = Qnil; +static VALUE mSvnCore = Qnil; +static VALUE cSvnError = Qnil; +static VALUE cSvnCoreStream = Qnil; + +#define RB_SVN_ERROR (rb_svn_error()) + +#define DEFINE_ID(key, name) \ +static ID id_ ## key = 0; \ +static ID \ +rb_id_ ## key (void) \ +{ \ + if (!id_ ## key) { \ + id_ ## key = rb_intern(name); \ + } \ + return id_ ## key; \ +} + +DEFINE_ID(code, "code") +DEFINE_ID(message, "message") +DEFINE_ID(call, "call") +DEFINE_ID(read, "read") +DEFINE_ID(write, "write") +DEFINE_ID(eqq, "===") +DEFINE_ID(new_corresponding_error, "new_corresponding_error") +DEFINE_ID(set_target_revision, "set_target_revision") +DEFINE_ID(open_root, "open_root") +DEFINE_ID(delete_entry, "delete_entry") +DEFINE_ID(add_directory, "add_directory") +DEFINE_ID(open_directory, "open_directory") +DEFINE_ID(change_dir_prop, "change_dir_prop") +DEFINE_ID(close_directory, "close_directory") +DEFINE_ID(absent_directory, "absent_directory") +DEFINE_ID(add_file, "add_file") +DEFINE_ID(open_file, "open_file") +DEFINE_ID(apply_textdelta, "apply_textdelta") +DEFINE_ID(change_file_prop, "change_file_prop") +DEFINE_ID(absent_file, "absent_file") +DEFINE_ID(close_file, "close_file") +DEFINE_ID(close_edit, "close_edit") +DEFINE_ID(abort_edit, "abort_edit") + +typedef void *(*r2c_func)(VALUE value, void *ctx, apr_pool_t *pool); +typedef VALUE (*c2r_func)(void *value, void *ctx); +typedef struct hash_to_apr_hash_data_t +{ + apr_hash_t *apr_hash; + r2c_func func; + void *ctx; + apr_pool_t *pool; +} hash_to_apr_hash_data_t; + + +static VALUE +rb_ary_aref1(VALUE ary, VALUE arg) +{ + VALUE args[1] = {arg}; + return rb_ary_aref(1, args, ary); +} + +static VALUE +rb_ary_aref_n(VALUE ary, int n) +{ + return rb_ary_aref1(ary, INT2NUM(n)); +} + + +static VALUE +rb_svn(void) +{ + if (NIL_P(mSvn)) { + mSvn = rb_const_get(rb_cObject, rb_intern("Svn")); + } + return mSvn; +} + +static VALUE +rb_svn_core(void) +{ + if (NIL_P(mSvnCore)) { + mSvnCore = rb_const_get(rb_svn(), rb_intern("Core")); + } + return mSvnCore; +} + +static VALUE +rb_svn_error(void) +{ + if (NIL_P(cSvnError)) { + cSvnError = rb_const_get(rb_svn(), rb_intern("Error")); + } + return cSvnError; +} + +static VALUE +rb_svn_core_stream(void) +{ + if (NIL_P(cSvnCoreStream)) { + cSvnCoreStream = rb_const_get(rb_svn_core(), rb_intern("Stream")); + } + return cSvnCoreStream; +} + +VALUE +svn_swig_rb_svn_error_new(VALUE code, VALUE message) +{ + return rb_funcall(rb_svn_error(), + rb_id_new_corresponding_error(), + 2, code, message); +} + + +/* C -> Ruby */ +static VALUE +c2r_swig_type(void *value, void *ctx) +{ + return SWIG_NewPointerObj(value, SWIG_TypeQuery((char *)ctx), 1); +} + +static VALUE +c2r_string(const char *cstr) +{ + if (cstr) { + return rb_str_new2(cstr); + } else { + return Qnil; + } +} + +static VALUE +c2r_commit_item(svn_client_commit_item_t item) +{ + return c2r_swig_type(&item, (void *)"svn_client_commit_item_t *"); +} + +static VALUE +c2r_svn_string(void *value, void *ctx) +{ + const svn_string_t *s = (svn_string_t *)value; + + return c2r_string(s->data); +} + + + +/* Ruby -> C */ +static void * +r2c_string(VALUE value, void *ctx, apr_pool_t *pool) +{ + return (void *)apr_pstrdup(pool, StringValuePtr(value)); +} + +static void * +r2c_swig_type(VALUE value, void *ctx, apr_pool_t *pool) +{ + void **result = apr_palloc(pool, sizeof(void *)); + SWIG_ConvertPtr(value, result, SWIG_TypeQuery((char *)ctx), 1); + return *result; +} + + + +/* apr_array_t -> Ruby Array */ +#define DEFINE_APR_ARRAY_TO_ARRAY(return_type, name, converter, type) \ +return_type \ +name(const apr_array_header_t *apr_ary) \ +{ \ + VALUE ary = rb_ary_new(); \ + int i; \ + \ + for (i = 0; i < apr_ary->nelts; i++) { \ + rb_ary_push(ary, converter(APR_ARRAY_IDX(apr_ary, i, type))); \ + } \ + \ + return ary; \ +} + +/* +DEFINE_APR_ARRAY_TO_ARRAY(VALUE, svn_swig_rb_apr_array_to_rb_array_rev, + INT2NUM, svn_revnum_t) +*/ +DEFINE_APR_ARRAY_TO_ARRAY(VALUE, svn_swig_rb_apr_array_to_array_string, + c2r_string, const char *) +DEFINE_APR_ARRAY_TO_ARRAY(static VALUE, c2r_commit_item_array, + c2r_commit_item, svn_client_commit_item_t) + + + +/* Ruby Array -> apr_array_t */ +#define DEFINE_ARRAY_TO_APR_ARRAY(type, name, converter, context) \ +apr_array_header_t * \ +name(VALUE array, apr_pool_t *pool) \ +{ \ + int i, len; \ + apr_array_header_t *apr_ary; \ + \ + Check_Type(array, T_ARRAY); \ + len = RARRAY(array)->len; \ + apr_ary = apr_array_make(pool, len, sizeof(type)); \ + apr_ary->nelts = len; \ + for (i = 0; i < len; i++) { \ + VALUE value; \ + type val; \ + value = rb_ary_aref_n(array, i); \ + val = converter(value, context, pool); \ + APR_ARRAY_IDX(apr_ary, i, type) = val; \ + } \ + return apr_ary; \ +} + +DEFINE_ARRAY_TO_APR_ARRAY(const char *, svn_swig_rb_strings_to_apr_array, + r2c_string, NULL) +DEFINE_ARRAY_TO_APR_ARRAY(svn_auth_provider_object_t *, + svn_swig_rb_array_to_auth_provider_object_apr_array, + r2c_swig_type, (void *)"svn_auth_provider_object_t *") + + + +/* apr_hash_t -> Ruby Hash */ +static VALUE +c2r_hash(apr_hash_t *hash, + c2r_func func, + void *ctx) +{ + apr_hash_index_t *hi; + VALUE r_hash = rb_hash_new(); + + for (hi = apr_hash_first(NULL, hash); hi; hi = apr_hash_next(hi)) { + const void *key; + void *val; + VALUE v = Qnil; + + apr_hash_this(hi, &key, NULL, &val); + if (val) { + v = (*func)(val, ctx); + } + rb_hash_aset(r_hash, c2r_string(key), v); + } + + return r_hash; +} + +VALUE +svn_swig_rb_apr_hash_to_hash_svn_string(apr_hash_t *hash) +{ + return c2r_hash(hash, c2r_svn_string, NULL); +} + +VALUE +svn_swig_rb_apr_hash_to_hash_swig_type(apr_hash_t *hash, const char *type_name) +{ + return c2r_hash(hash, c2r_swig_type, (void *)type_name); +} + + + +/* Ruby Hash -> apr_hash_t */ +static int +r2c_hash_i(VALUE key, VALUE value, hash_to_apr_hash_data_t *data) +{ + if (key != Qundef) { + void *val = data->func(value, data->ctx, data->pool); + apr_hash_set(data->apr_hash, StringValuePtr(key), APR_HASH_KEY_STRING, val); + } + return ST_CONTINUE; +} + +static apr_hash_t * +r2c_hash(VALUE hash,r2c_func func, void *ctx, apr_pool_t *pool) +{ + if (NIL_P(hash)) { + return NULL; + } else { + apr_hash_t *apr_hash; + hash_to_apr_hash_data_t data = { + NULL, + func, + ctx, + pool + }; + + apr_hash = apr_hash_make(pool); + data.apr_hash = apr_hash; + rb_hash_foreach(hash, r2c_hash_i, (VALUE)&data); + + return apr_hash; + } +} + + +apr_hash_t * +svn_swig_rb_hash_to_apr_hash(VALUE hash, apr_pool_t *pool) +{ + return r2c_hash(hash, r2c_string, NULL, pool); +} + +apr_hash_t * +svn_swig_rb_hash_to_apr_hash_swig_type(VALUE hash, const char *typename, apr_pool_t *pool) +{ + return r2c_hash(hash, r2c_swig_type, (void *)typename, pool); +} + + +/* +static VALUE +convert_svn_client_commit_item_t(void *value, void *ctx) +{ + VALUE ary; + VALUE path, kind, url, rev, cf_url, state; + svn_client_commit_item_t *item = value; + + ary = rb_ary_new2(6); + + path = c2r_string(item->path); + url = c2r_string(item->url); + cf_url = c2r_string(item->copyfrom_url); + + kind = INT2NUM(item->kind); + rev = INT2NUM(item->revision); + state = INT2NUM(item->state_flags); + + if (path && kind && url && rev && cf_url && state) { + rb_ary_push(ary, path); + rb_ary_push(ary, kind); + rb_ary_push(ary, url); + rb_ary_push(ary, rev); + rb_ary_push(ary, cf_url); + rb_ary_push(ary, state); + return ary; + } else { + return Qnil; + } +} +*/ + +static VALUE +callback(VALUE info) +{ + return rb_apply(rb_ary_aref_n(info, 0), + (ID)rb_ary_aref_n(info, 1), + rb_ary_aref1(info, + rb_range_new(INT2NUM(2), + INT2NUM(-1), + FALSE))); +} + + +static VALUE +callback_rescue(VALUE error) +{ + svn_error_t **err = (svn_error_t **)error; + VALUE message; + + message = rb_funcall(ruby_errinfo, rb_id_message(), 0); + *err = svn_error_create(NUM2INT(rb_funcall(ruby_errinfo, rb_id_code(), 0)), + NULL, + StringValuePtr(message)); + return Qnil; +} + + +typedef struct { + VALUE editor; + VALUE baton; +} item_baton; + +static item_baton * +make_baton(apr_pool_t *pool, VALUE editor, VALUE baton) +{ + item_baton *newb = apr_palloc(pool, sizeof(*newb)); + + newb->editor = editor; + newb->baton = baton; + + return newb; +} + + +static svn_error_t * +set_target_revision(void *edit_baton, + svn_revnum_t target_revision, + apr_pool_t *pool) +{ + item_baton *ib = edit_baton; + svn_error_t *err = SVN_NO_ERROR; + VALUE args; + + args = rb_ary_new3(4, + ib->editor, + rb_id_set_target_revision(), + INT2NUM(target_revision), + c2r_swig_type((void *)pool, (void *)"apr_pool_t *")); + rb_rescue2(callback, args, + callback_rescue, (VALUE)&err, + RB_SVN_ERROR, NULL); + return err; +} + +static svn_error_t * +open_root(void *edit_baton, + svn_revnum_t base_revision, + apr_pool_t *dir_pool, + void **root_baton) +{ + item_baton *ib = edit_baton; + svn_error_t *err = SVN_NO_ERROR; + VALUE args; + VALUE result; + + args = rb_ary_new3(4, + ib->editor, + rb_id_open_root(), + INT2NUM(base_revision), + c2r_swig_type((void *)dir_pool, (void *)"apr_pool_t *")); + result = rb_rescue2(callback, args, + callback_rescue, (VALUE)&err, + RB_SVN_ERROR, NULL); + *root_baton = make_baton(dir_pool, ib->editor, result); + return err; +} + +static svn_error_t * +delete_entry(const char *path, + svn_revnum_t revision, + void *parent_baton, + apr_pool_t *pool) +{ + item_baton *ib = parent_baton; + svn_error_t *err = SVN_NO_ERROR; + VALUE args; + + args = rb_ary_new3(6, + ib->editor, + rb_id_delete_entry(), + c2r_string(path), + INT2NUM(revision), + ib->baton, + c2r_swig_type((void *)pool, (void *)"apr_pool_t *")); + rb_rescue2(callback, args, + callback_rescue, (VALUE)&err, + RB_SVN_ERROR, NULL); + return err; +} + +static svn_error_t * +add_directory(const char *path, + void *parent_baton, + const char *copyfrom_path, + svn_revnum_t copyfrom_revision, + apr_pool_t *dir_pool, + void **child_baton) +{ + item_baton *ib = parent_baton; + svn_error_t *err = SVN_NO_ERROR; + VALUE args; + VALUE result; + + args = rb_ary_new3(7, + ib->editor, + rb_id_add_directory(), + c2r_string(path), + ib->baton, + c2r_string(copyfrom_path), + INT2NUM(copyfrom_revision), + c2r_swig_type((void *)dir_pool, (void *)"apr_pool_t *")); + result = rb_rescue2(callback, args, + callback_rescue, (VALUE)&err, + RB_SVN_ERROR, NULL); + *child_baton = make_baton(dir_pool, ib->editor, result); + return err; +} + +static svn_error_t * +open_directory(const char *path, + void *parent_baton, + svn_revnum_t base_revision, + apr_pool_t *dir_pool, + void **child_baton) +{ + item_baton *ib = parent_baton; + svn_error_t *err = SVN_NO_ERROR; + VALUE args; + VALUE result; + + args = rb_ary_new3(6, + ib->editor, + rb_id_open_directory(), + c2r_string(path), + ib->baton, + INT2NUM(base_revision), + c2r_swig_type((void *)dir_pool, (void *)"apr_pool_t *")); + result = rb_rescue2(callback, args, + callback_rescue, (VALUE)&err, + RB_SVN_ERROR, NULL); + *child_baton = make_baton(dir_pool, ib->editor, result); + return err; +} + +static svn_error_t * +change_dir_prop(void *dir_baton, + const char *name, + const svn_string_t *value, + apr_pool_t *pool) +{ + item_baton *ib = dir_baton; + svn_error_t *err = SVN_NO_ERROR; + VALUE args; + + args = rb_ary_new3(6, + ib->editor, + rb_id_change_dir_prop(), + ib->editor, + c2r_string(name), + value ? rb_str_new(value->data, value->len) : Qnil, + c2r_swig_type((void *)pool, (void *)"apr_pool_t *")); + rb_rescue2(callback, args, + callback_rescue, (VALUE)&err, + RB_SVN_ERROR, NULL); + return err; +} + +static svn_error_t * +close_baton(void *baton, ID method_id) +{ + item_baton *ib = baton; + svn_error_t *err = SVN_NO_ERROR; + VALUE args; + + args = rb_ary_new3(3, + ib->editor, + method_id, + ib->baton); + rb_rescue2(callback, args, + callback_rescue, (VALUE)&err, + RB_SVN_ERROR, NULL); + return err; +} + +static svn_error_t * +close_directory(void *dir_baton, apr_pool_t *pool) +{ + return close_baton(dir_baton, rb_id_close_directory()); +} + +static svn_error_t * +absent_directory(const char *path, void *parent_baton, apr_pool_t *pool) +{ + item_baton *ib = parent_baton; + svn_error_t *err = SVN_NO_ERROR; + VALUE args; + + args = rb_ary_new3(5, + ib->editor, + rb_id_absent_directory(), + c2r_string(path), + ib->baton, + c2r_swig_type((void *)pool, (void *)"apr_pool_t *")); + rb_rescue2(callback, args, + callback_rescue, (VALUE)&err, + RB_SVN_ERROR, NULL); + return err; +} + +static svn_error_t * +add_file(const char *path, + void *parent_baton, + const char *copyfrom_path, + svn_revnum_t copyfrom_revision, + apr_pool_t *file_pool, + void **file_baton) +{ + item_baton *ib = parent_baton; + svn_error_t *err = SVN_NO_ERROR; + VALUE args; + VALUE result; + + args = rb_ary_new3(7, + ib->editor, + rb_id_add_file(), + c2r_string(path), + ib->baton, + c2r_string(copyfrom_path), + INT2NUM(copyfrom_revision), + c2r_swig_type((void *)file_pool, (void *)"apr_pool_t *")); + result = rb_rescue2(callback, args, + callback_rescue, (VALUE)&err, + RB_SVN_ERROR, NULL); + *file_baton = make_baton(file_pool, ib->editor, result); + return err; +} + +static svn_error_t * +open_file(const char *path, + void *parent_baton, + svn_revnum_t base_revision, + apr_pool_t *file_pool, + void **file_baton) +{ + item_baton *ib = parent_baton; + svn_error_t *err = SVN_NO_ERROR; + VALUE args; + VALUE result; + + args = rb_ary_new3(6, + ib->editor, + rb_id_open_file(), + c2r_string(path), + ib->baton, + INT2NUM(base_revision), + c2r_swig_type((void *)file_pool, (void *)"apr_pool_t *")); + result = rb_rescue2(callback, args, + callback_rescue, (VALUE)&err, + RB_SVN_ERROR, NULL); + *file_baton = make_baton(file_pool, ib->editor, result); + return err; +} + +static svn_error_t * +window_handler(svn_txdelta_window_t *window, void *baton) +{ + VALUE handler = (VALUE)baton; + VALUE args; + VALUE result; + svn_error_t *err = SVN_NO_ERROR; + + args = rb_ary_new3(3, + handler, + rb_id_call(), + window ? + c2r_swig_type((void *)window, (void *)"svn_txdelta_window_t *") : + Qnil); + result = rb_rescue2(callback, args, + callback_rescue, (VALUE)&err, + RB_SVN_ERROR, NULL); + return err; +} + +static svn_error_t * +apply_textdelta(void *file_baton, + const char *base_checksum, + apr_pool_t *pool, + svn_txdelta_window_handler_t *handler, + void **h_baton) +{ + item_baton *ib = file_baton; + svn_error_t *err = SVN_NO_ERROR; + VALUE args; + VALUE result; + + args = rb_ary_new3(5, + ib->editor, + rb_id_apply_textdelta(), + ib->baton, + c2r_string(base_checksum), + c2r_swig_type((void *)pool, (void *)"apr_pool_t *")); + result = rb_rescue2(callback, args, + callback_rescue, (VALUE)&err, + RB_SVN_ERROR, NULL); + if (NIL_P(result)) { + *handler = svn_delta_noop_window_handler; + *h_baton = NULL; + } else { + *handler = window_handler; + *h_baton = (void *)result; + } + + return err; +} + +static svn_error_t * +change_file_prop(void *file_baton, + const char *name, + const svn_string_t *value, + apr_pool_t *pool) +{ + item_baton *ib = file_baton; + svn_error_t *err = SVN_NO_ERROR; + VALUE args; + + args = rb_ary_new3(6, + ib->editor, + rb_id_change_file_prop(), + ib->baton, + c2r_string(name), + value ? rb_str_new(value->data, value->len) : Qnil, + c2r_swig_type((void *)pool, (void *)"apr_pool_t *")); + rb_rescue2(callback, args, + callback_rescue, (VALUE)&err, + RB_SVN_ERROR, NULL); + + return err; +} + +static svn_error_t * +close_file(void *file_baton, + const char *text_checksum, + apr_pool_t *pool) +{ + item_baton *ib = file_baton; + svn_error_t *err = SVN_NO_ERROR; + VALUE args; + + args = rb_ary_new3(4, + ib->editor, + rb_id_close_file(), + ib->baton, + c2r_string(text_checksum)); + rb_rescue2(callback, args, + callback_rescue, (VALUE)&err, + RB_SVN_ERROR, NULL); + + return err; +} + +static svn_error_t * +absent_file(const char *path, + void *parent_baton, + apr_pool_t *pool) +{ + item_baton *ib = parent_baton; + svn_error_t *err = SVN_NO_ERROR; + VALUE args; + + args = rb_ary_new3(4, + ib->editor, + rb_id_absent_file(), + c2r_string(path), + ib->baton, + c2r_swig_type((void *)pool, (void *)"apr_pool_t *")); + rb_rescue2(callback, args, + callback_rescue, (VALUE)&err, + RB_SVN_ERROR, NULL); + + return err; +} + +static svn_error_t * +close_edit(void *edit_baton, apr_pool_t *pool) +{ + return close_baton(edit_baton, rb_id_close_edit()); +} + +static svn_error_t * +abort_edit(void *edit_baton, apr_pool_t *pool) +{ + return close_baton(edit_baton, rb_id_abort_edit()); +} + +void +svn_swig_rb_make_editor(const svn_delta_editor_t **editor, + void **edit_baton, + VALUE rb_editor, + apr_pool_t *pool) +{ + svn_delta_editor_t *thunk_editor = svn_delta_default_editor(pool); + + thunk_editor->set_target_revision = set_target_revision; + thunk_editor->open_root = open_root; + thunk_editor->delete_entry = delete_entry; + thunk_editor->add_directory = add_directory; + thunk_editor->open_directory = open_directory; + thunk_editor->change_dir_prop = change_dir_prop; + thunk_editor->close_directory = close_directory; + thunk_editor->absent_directory = absent_directory; + thunk_editor->add_file = add_file; + thunk_editor->open_file = open_file; + thunk_editor->apply_textdelta = apply_textdelta; + thunk_editor->change_file_prop = change_file_prop; + thunk_editor->close_file = close_file; + thunk_editor->absent_file = absent_file; + thunk_editor->close_edit = close_edit; + thunk_editor->abort_edit = abort_edit; + + *editor = thunk_editor; + *edit_baton = make_baton(pool, rb_editor, Qnil); +} + + +svn_error_t * +svn_swig_rb_repos_authz_func(svn_boolean_t *allowed, + svn_fs_root_t *root, + const char *path, + void *baton, + apr_pool_t *pool) +{ + VALUE proc = (VALUE)baton; + svn_error_t *err = SVN_NO_ERROR; + + *allowed = TRUE; + + if (!NIL_P(proc)) { + VALUE args; + VALUE result; + + args = rb_ary_new3(5, + proc, + rb_id_call(), + c2r_swig_type((void *)root, (void *)"svn_fs_root_t *"), + c2r_string(path), + c2r_swig_type((void *)pool, (void *)"apr_pool_t *")); + result = rb_rescue2(callback, args, + callback_rescue, (VALUE)&err, + RB_SVN_ERROR, NULL); + + *allowed = RTEST(result); + } + return err; +} + +svn_error_t * +svn_swig_rb_get_commit_log_func(const char **log_msg, + const char **tmp_file, + apr_array_header_t *commit_items, + void *baton, + apr_pool_t *pool) +{ + VALUE proc = (VALUE)baton; + svn_error_t *err = SVN_NO_ERROR; + + if (!NIL_P(proc)) { + VALUE args; + VALUE result; + VALUE is_message; + VALUE value; + char *ret; + + args = rb_ary_new3(3, + proc, + rb_id_call(), + c2r_commit_item_array(commit_items)); + result = rb_rescue2(callback, args, + callback_rescue, (VALUE)&err, + RB_SVN_ERROR, NULL); + + is_message = rb_ary_aref_n(result, 0); + value = rb_ary_aref_n(result, 1); + + Check_Type(value, T_STRING); + ret = (char *)r2c_string(value, NULL, pool); + if (RTEST(is_message)) { + *log_msg = ret; + } else { + *tmp_file = ret; + } + } + return err; +} + + + +/* auth provider callbacks */ +svn_error_t * +svn_swig_rb_auth_simple_prompt_func(svn_auth_cred_simple_t **cred, + void *baton, + const char *realm, + const char *username, + svn_boolean_t may_save, + apr_pool_t *pool) +{ + VALUE proc = (VALUE)baton; + svn_auth_cred_simple_t *new_cred = NULL; + svn_error_t *err = SVN_NO_ERROR; + + if (!NIL_P(proc)) { + VALUE args; + VALUE result; + + args = rb_ary_new3(6, + proc, + rb_id_call(), + rb_str_new2(realm), + rb_str_new2(username), + RTEST(may_save) ? Qtrue : Qfalse, + c2r_swig_type((void *)pool, (void *)"apr_pool_t *")); + result = rb_rescue2(callback, args, + callback_rescue, (VALUE)&err, + RB_SVN_ERROR, NULL); + + if (!NIL_P(result)) { + svn_auth_cred_simple_t *tmp_cred = NULL; + + SWIG_ConvertPtr(result, (void **)&tmp_cred, + SWIG_TypeQuery("svn_auth_cred_simple_t *"), 1); + new_cred = apr_pcalloc(pool, sizeof (*new_cred)); + new_cred->username = tmp_cred->username ? \ + apr_pstrdup(pool, tmp_cred->username) : NULL; + new_cred->password = tmp_cred->password ? \ + apr_pstrdup(pool, tmp_cred->password) : NULL; + new_cred->may_save = tmp_cred->may_save; + } + } + + *cred = new_cred; + return err; +} + +svn_error_t * +svn_swig_rb_auth_username_prompt_func(svn_auth_cred_username_t **cred, + void *baton, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool) +{ + VALUE proc = (VALUE)baton; + svn_auth_cred_username_t *new_cred = NULL; + svn_error_t *err = SVN_NO_ERROR; + + if (!NIL_P(proc)) { + VALUE args; + VALUE result; + + args = rb_ary_new3(5, + proc, + rb_id_call(), + rb_str_new2(realm), + RTEST(may_save) ? Qtrue : Qfalse, + c2r_swig_type((void *)pool, (void *)"apr_pool_t *")); + result = rb_rescue2(callback, args, + callback_rescue, (VALUE)&err, + RB_SVN_ERROR, NULL); + + if (!NIL_P(result)) { + svn_auth_cred_username_t *tmp_cred = NULL; + + SWIG_ConvertPtr(result, (void **)&tmp_cred, + SWIG_TypeQuery("svn_auth_cred_username_t *"), 1); + new_cred = apr_pcalloc(pool, sizeof (*new_cred)); + new_cred->username = tmp_cred->username ? \ + apr_pstrdup(pool, tmp_cred->username) : NULL; + new_cred->may_save = tmp_cred->may_save; + } + } + + *cred = new_cred; + return err; +} + +svn_error_t * +svn_swig_rb_auth_ssl_server_trust_prompt_func( + svn_auth_cred_ssl_server_trust_t **cred, + 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) +{ + VALUE proc = (VALUE)baton; + svn_auth_cred_ssl_server_trust_t *new_cred = NULL; + svn_error_t *err = SVN_NO_ERROR; + + if (!NIL_P(proc)) { + VALUE args; + VALUE result; + + args = rb_ary_new3(7, + proc, + rb_id_call(), + rb_str_new2(realm), + UINT2NUM(failures), + c2r_swig_type((void *)cert_info, + (void *)"svn_auth_ssl_server_cert_info_t *"), + RTEST(may_save) ? Qtrue : Qfalse, + c2r_swig_type((void *)pool, (void *)"apr_pool_t *")); + result = rb_rescue2(callback, args, + callback_rescue, (VALUE)&err, + RB_SVN_ERROR, NULL); + + if (!NIL_P(result)) { + svn_auth_cred_ssl_server_trust_t *tmp_cred = NULL; + + SWIG_ConvertPtr(result, (void **)&tmp_cred, + SWIG_TypeQuery("svn_auth_cred_ssl_server_trust_t *"), 1); + new_cred = apr_pcalloc(pool, sizeof (*new_cred)); + *new_cred = *tmp_cred; + } + } + + *cred = new_cred; + return err; +} + +svn_error_t * +svn_swig_rb_auth_ssl_client_cert_prompt_func( + svn_auth_cred_ssl_client_cert_t **cred, + void *baton, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool) +{ + VALUE proc = (VALUE)baton; + svn_auth_cred_ssl_client_cert_t *new_cred = NULL; + svn_error_t *err = SVN_NO_ERROR; + + if (!NIL_P(proc)) { + VALUE args; + VALUE result; + + args = rb_ary_new3(5, + proc, + rb_id_call(), + rb_str_new2(realm), + RTEST(may_save) ? Qtrue : Qfalse, + c2r_swig_type((void *)&pool, (void *)"apr_pool_t *")); + result = rb_rescue2(callback, args, + callback_rescue, (VALUE)&err, + RB_SVN_ERROR, NULL); + + if (!NIL_P(result)) { + svn_auth_cred_ssl_client_cert_t *tmp_cred = NULL; + + SWIG_ConvertPtr(result, (void **)&tmp_cred, + SWIG_TypeQuery("svn_auth_cred_ssl_client_cert_t *"), 1); + new_cred = apr_pcalloc(pool, sizeof (*new_cred)); + new_cred->cert_file = tmp_cred->cert_file ? \ + apr_pstrdup(pool, tmp_cred->cert_file) : NULL; + new_cred->may_save = tmp_cred->may_save; + } + } + + *cred = new_cred; + return err; +} + +svn_error_t * +svn_swig_rb_auth_ssl_client_cert_pw_prompt_func( + svn_auth_cred_ssl_client_cert_pw_t **cred, + void *baton, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool) +{ + VALUE proc = (VALUE)baton; + svn_auth_cred_ssl_client_cert_pw_t *new_cred = NULL; + svn_error_t *err = SVN_NO_ERROR; + + if (!NIL_P(proc)) { + VALUE args; + VALUE result; + + args = rb_ary_new3(5, + proc, + rb_id_call(), + rb_str_new2(realm), + RTEST(may_save) ? Qtrue : Qfalse, + c2r_swig_type((void *)pool, (void *)"apr_pool_t *")); + result = rb_rescue2(callback, args, + callback_rescue, (VALUE)&err, + RB_SVN_ERROR, NULL); + + if (!NIL_P(result)) { + svn_auth_cred_ssl_client_cert_pw_t *tmp_cred = NULL; + + SWIG_ConvertPtr(result, (void **)&tmp_cred, + SWIG_TypeQuery("svn_auth_cred_ssl_client_cert_pw_t *"), 1); + new_cred = apr_pcalloc(pool, sizeof (*new_cred)); + new_cred->password = tmp_cred->password ? \ + apr_pstrdup(pool, tmp_cred->password) : NULL; + new_cred->may_save = tmp_cred->may_save; + } + } + + *cred = new_cred; + return err; +} + + +apr_file_t * +svn_swig_rb_make_file(VALUE file, apr_pool_t *pool) +{ + apr_file_t *apr_file = NULL; + + apr_file_open(&apr_file, StringValuePtr(file), + APR_CREATE | APR_READ | APR_WRITE, + APR_OS_DEFAULT, + pool); + + return apr_file; +} + + +static svn_error_t * +read_handler_rbio (void *baton, char *buffer, apr_size_t *len) +{ + VALUE result; + VALUE io = (VALUE)baton; + svn_error_t *err = SVN_NO_ERROR; + + result = rb_funcall(io, rb_id_read(), 1, INT2NUM(*len)); + memcpy(buffer, StringValuePtr(result), RSTRING(result)->len); + *len = RSTRING(result)->len; + + return err; +} + +static svn_error_t * +write_handler_rbio (void *baton, const char *data, apr_size_t *len) +{ + VALUE io = (VALUE)baton; + svn_error_t *err = SVN_NO_ERROR; + + rb_funcall(io, rb_id_write(), 1, rb_str_new(data, *len)); + + return err; +} + +svn_stream_t * +svn_swig_rb_make_stream(VALUE io, apr_pool_t *pool) +{ + svn_stream_t *stream; + + if (RTEST(rb_funcall(rb_svn_core_stream(), rb_id_eqq(), 1, io))) { + SWIG_ConvertPtr(io, (void **)&stream, + SWIG_TypeQuery("svn_stream_t *"), 1); + } else { + stream = svn_stream_create((void *)io, pool); + svn_stream_set_read(stream, read_handler_rbio); + svn_stream_set_write(stream, write_handler_rbio); + } + + return stream; +} + Property changes on: subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.c ___________________________________________________________________ Name: svn:keywords + Id Index: subversion/bindings/swig/svn_string.i =================================================================== --- subversion/bindings/swig/svn_string.i (revision 12622) +++ subversion/bindings/swig/svn_string.i (working copy) @@ -55,7 +55,14 @@ $result = &PL_sv_undef; argvi++; } - +%typemap(ruby,argout,fragment="output_helper") RET_STRING +{ + if (*$1) { + $result = output_helper($result, rb_str_new((*$1)->data, (*$1)->len)); + } else { + $result = output_helper($result, Qnil); + } +} %typemap(java,out) RET_STRING { /* FIXME: This is just a stub -- implement JNI code for returning a string! */ $output = NULL; @@ -137,6 +144,16 @@ $1 = NULL; } } +%typemap(ruby,in) const svn_string_t * (svn_string_t value) +{ + if (NIL_P($input)) { + $1 = NULL; + } else { + value.data = StringValuePtr($input); + value.len = RSTRING($input)->len; + $1 = &value; + } +} /* when storing an svn_string_t* into a structure, we must allocate the svn_string_t structure on the heap. */ @@ -146,6 +163,9 @@ %typemap(perl5,memberin) const svn_string_t * { $1 = svn_string_dup($input, _global_pool); } +%typemap(ruby,memberin) const svn_string_t * { + $1 = svn_string_dup($input, _global_pool); +} %typemap(python,out) svn_string_t * { $result = PyString_FromStringAndSize($1->data, $1->len); @@ -154,6 +174,9 @@ $result = sv_2mortal(newSVpv($1->data, $1->len)); ++argvi; } +%typemap(ruby,out) svn_string_t * { + $result = rb_str_new($1->data, $1->len); +} /* ----------------------------------------------------------------------- define a way to return a 'const char *' @@ -185,6 +208,15 @@ ++argvi; } +%typemap(ruby,argout,fragment="output_helper") const char **OUTPUT +{ + if (*$1) { + $result = output_helper($result, rb_str_new2(*$1)); + } else { + $result = output_helper($result, Qnil); + } +} + /* ----------------------------------------------------------------------- define a general INPUT param of an array of const char * items. */ @@ -200,6 +232,10 @@ _global_pool); } +%typemap(ruby,in) const apr_array_header_t *STRINGLIST { + $1 = svn_swig_rb_strings_to_apr_array($input, _global_pool); +} + %typemap(jni) const apr_array_header_t *STRINGLIST "jobjectArray" %typemap(jtype) const apr_array_header_t *STRINGLIST "java.lang.String[]" %typemap(jstype) const apr_array_header_t *STRINGLIST "java.lang.String[]" Index: subversion/bindings/swig/svn_fs.i =================================================================== --- subversion/bindings/swig/svn_fs.i (revision 12622) +++ subversion/bindings/swig/svn_fs.i (working copy) @@ -93,6 +93,10 @@ %typemap(perl5,argout) apr_hash_t **entries_p { ST(argvi++) = svn_swig_pl_convert_hash(*$1, SWIGTYPE_p_svn_fs_dirent_t); } +%typemap(ruby,in,numinputs=0) apr_hash_t **entries_p = apr_hash_t **OUTPUT; +%typemap(ruby,argout) apr_hash_t **entries_p { + $result = svn_swig_rb_apr_hash_to_hash_swig_type(*$1, "svn_fs_dirent_t *"); +} /* ----------------------------------------------------------------------- and except for svn_fs_paths_changed, which returns svn_fs_path_change_t @@ -129,7 +133,6 @@ /* ----------------------------------------------------------------------- */ -%include svn_fs.h %{ #include "svn_md5.h" #include "svn_fs.h" @@ -145,4 +148,9 @@ #ifdef SWIGPERL #include "swigutil_pl.h" #endif + +#ifdef SWIGRUBY +#include "swigutil_rb.h" +#endif %} +%include svn_fs.h Index: subversion/bindings/swig/svn_wc.i =================================================================== --- subversion/bindings/swig/svn_wc.i (revision 12622) +++ subversion/bindings/swig/svn_wc.i (working copy) @@ -118,7 +118,6 @@ /* ----------------------------------------------------------------------- */ -%include svn_wc.h %{ #include "svn_wc.h" @@ -133,4 +132,9 @@ #ifdef SWIGPERL #include "swigutil_pl.h" #endif + +#ifdef SWIGRUBY +#include "swigutil_rb.h" +#endif %} +%include svn_wc.h Index: subversion/bindings/swig/svn_client.i =================================================================== --- subversion/bindings/swig/svn_client.i (revision 12622) +++ subversion/bindings/swig/svn_client.i (working copy) @@ -126,6 +126,14 @@ $2 = $input; /* our function is the baton. */ } +%typemap(ruby,in) (svn_client_get_commit_log_t log_msg_func) +{ + if (arg1) { + $1 = svn_swig_rb_get_commit_log_func; + arg1->log_msg_baton = (void *)$input; + } +} + %typemap(java,in) (svn_client_get_commit_log_t log_msg_func, void *log_msg_baton) { @@ -186,6 +194,11 @@ $1 = svn_swig_py_auth_simple_prompt_func; $2 = $input; } +%typemap(ruby, in) (svn_auth_simple_prompt_func_t prompt_func, + void *prompt_baton) { + $1 = svn_swig_rb_auth_simple_prompt_func; + $2 = (void *) $input; +} %typemap(perl5, in) (svn_auth_username_prompt_func_t prompt_func, void *prompt_baton) { @@ -198,6 +211,11 @@ $1 = svn_swig_py_auth_username_prompt_func; $2 = $input; } +%typemap(ruby, in) (svn_auth_username_prompt_func_t prompt_func, + void *prompt_baton) { + $1 = svn_swig_rb_auth_username_prompt_func; + $2 = (void *) $input; +} %typemap(perl5, in) (svn_auth_ssl_server_trust_prompt_func_t prompt_func, void *prompt_baton) { @@ -210,6 +228,11 @@ $1 = svn_swig_py_auth_ssl_server_trust_prompt_func; $2 = $input; } +%typemap(ruby, in) (svn_auth_ssl_server_trust_prompt_func_t prompt_func, + void *prompt_baton) { + $1 = svn_swig_rb_auth_ssl_server_trust_prompt_func; + $2 = (void *) $input; +} %typemap(perl5, in) (svn_auth_ssl_client_cert_prompt_func_t prompt_func, void *prompt_baton) { @@ -222,6 +245,11 @@ $1 = svn_swig_py_auth_ssl_client_cert_prompt_func; $2 = $input; } +%typemap(ruby, in) (svn_auth_ssl_client_cert_prompt_func_t prompt_func, + void *prompt_baton) { + $1 = svn_swig_rb_auth_ssl_client_cert_prompt_func; + $2 = (void *) $input; +} %typemap(perl5, in) (svn_auth_ssl_client_cert_pw_prompt_func_t prompt_func, void *prompt_baton) { @@ -234,6 +262,11 @@ $1 = svn_swig_py_auth_ssl_client_cert_pw_prompt_func; $2 = $input; } +%typemap(ruby, in) (svn_auth_ssl_client_cert_pw_prompt_func_t prompt_func, + void *prompt_baton) { + $1 = svn_swig_rb_auth_ssl_client_cert_pw_prompt_func; + $2 = (void *) $input; +} /* ----------------------------------------------------------------------- * For all the various functions that set a callback baton create a reference @@ -280,6 +313,45 @@ argvi++; } +%runtime %{ + #include + #include + + static apr_pool_t * + _svn_client_pool(void) + { + static apr_pool_t *__svn_client_pool = NULL; + if (!__svn_client_pool) { + apr_pool_create(&__svn_client_pool, NULL); + } + return __svn_client_pool; + } + + static apr_pool_t * + _svn_client_config_pool(void) + { + static apr_pool_t *__svn_client_config_pool = NULL; + if (!__svn_client_config_pool) { + apr_pool_create(&__svn_client_config_pool, _svn_client_pool()); + } + return __svn_client_config_pool; + } +%} + +%typemap(ruby, argout) apr_hash_t *config { + if ($1) + apr_pool_clear(_svn_client_config_pool()); +} + +%typemap(ruby, in) apr_hash_t *config { + $1 = svn_swig_rb_hash_to_apr_hash_swig_type($input, "svn_config_t *", + _svn_client_config_pool()); +} + +%typemap(ruby, out) apr_hash_t *config { + $result = svn_swig_rb_apr_hash_to_hash_swig_type($1, "svn_config_t *"); +} + /* ----------------------------------------------------------------------- * override default typemap for svn_client_commit_info_t for perl. Some calls * never allocate and fill the commit_info struct. This lets us return @@ -352,6 +424,27 @@ #ifdef SWIGPERL #include "swigutil_pl.h" #endif + +#ifdef SWIGRUBY +#include "swigutil_rb.h" +#endif %} %include svn_client.h + +#ifdef SWIGRUBY +%extend svn_client_commit_item_t +{ + ~svn_client_commit_item_t(svn_client_commit_item_t *item) + { + /* do nothing */ + } +} +%extend svn_client_commit_info_t +{ + ~svn_client_commit_info_t(svn_client_commit_info_t *info) + { + /* do nothing */ + } +} +#endif Index: subversion/bindings/swig/svn_repos.i =================================================================== --- subversion/bindings/swig/svn_repos.i (revision 12622) +++ subversion/bindings/swig/svn_repos.i (working copy) @@ -48,7 +48,9 @@ }; %apply const char *MAY_BE_NULL { - const char *src_entry + const char *src_entry, + const char *unused_1, + const char *unused_2 }; /* svn_repos_db_logfiles() */ @@ -120,6 +122,11 @@ $2 = $input; /* our function is the baton. */ } +%typemap(ruby, in) (svn_repos_authz_func_t authz_read_func, void *authz_read_baton) { + $1 = svn_swig_rb_repos_authz_func; + $2 = (void *)$input; +} + /* ----------------------------------------------------------------------- handle config and fs_config in svn_repos_create */ @@ -143,7 +150,6 @@ /* ----------------------------------------------------------------------- */ -%include svn_repos.h %{ #include "svn_repos.h" @@ -158,4 +164,9 @@ #ifdef SWIGPERL #include "swigutil_pl.h" #endif + +#ifdef SWIGRUBY +#include "swigutil_rb.h" +#endif %} +%include svn_repos.h Index: subversion/bindings/swig/svn_delta.i =================================================================== --- subversion/bindings/swig/svn_delta.i (revision 12622) +++ subversion/bindings/swig/svn_delta.i (working copy) @@ -60,9 +60,15 @@ svn_delta_make_editor(&$1, &$2, $input, _global_pool); } +#ifdef SWIGRUBY +void svn_swig_rb_make_editor(const svn_delta_editor_t **editor, + void **edit_baton, + VALUE rb_editor, + apr_pool_t *pool); +#endif + /* ----------------------------------------------------------------------- */ -%include svn_delta.h %{ #include "svn_md5.h" #include "svn_delta.h" @@ -78,7 +84,12 @@ #ifdef SWIGPERL #include "swigutil_pl.h" #endif + +#ifdef SWIGRUBY +#include "swigutil_rb.h" +#endif %} +%include svn_delta.h /* ----------------------------------------------------------------------- editor callback invokers Index: subversion/bindings/swig/apr.i =================================================================== --- subversion/bindings/swig/apr.i (revision 12622) +++ subversion/bindings/swig/apr.i (working copy) @@ -23,8 +23,23 @@ Actually, core.i wraps a few, key functions. */ +#ifdef SWIGRUBY +#pragma SWIG nowarn=801 +#endif + %include typemaps.i +/* for SWIG bug */ +%typemap(ruby, argout, fragment="output_helper") long long *OUTPUT, long long &OUTPUT +{ + $result = output_helper($result, LL2NUM(*$1)); +} +%typemap(ruby, argout, fragment="output_helper") unsigned long long *OUTPUT, unsigned long long &OUTPUT +{ + $result = output_helper($result, ULL2NUM(*$1)); +} + + /* ----------------------------------------------------------------------- This is default in SWIG 1.3.17 and is a really good idea */ @@ -75,6 +90,9 @@ %typemap(perl5,in,numinputs=0) apr_hash_t **OUTPUT (apr_hash_t *temp) "$1 = &temp;"; +%typemap(ruby,in,numinputs=0) apr_hash_t **OUTPUT (apr_hash_t *temp) + "$1 = &temp;"; + /* ----------------------------------------------------------------------- create an OUTPUT argument defn for an apr_hash_t ** which is storing property values @@ -93,6 +111,11 @@ argvi++; } +%typemap(ruby,in,numinputs=0) apr_hash_t **PROPHASH = apr_hash_t **OUTPUT; +%typemap(ruby,argout) apr_hash_t **PROPHASH { + $result = svn_swig_rb_apr_hash_to_hash_svn_string(*$1); +} + /* ----------------------------------------------------------------------- apr_array_header_t ** */ @@ -109,6 +132,9 @@ $result = svn_swig_pl_array_to_list(*$1); ++argvi; } +%typemap(ruby, argout) apr_array_header_t **OUTPUT_OF_CONST_CHAR_P { + $result = svn_swig_rb_apr_array_to_array_string(*$1); +} /* ----------------------------------------------------------------------- Handle an apr_hash_t ** in Java @@ -168,6 +194,10 @@ $1 = svn_swig_pl_make_file($input, _global_pool); } +%typemap(ruby, in) apr_file_t * { + $1 = svn_swig_rb_make_file($input, _global_pool); +} + /* ----------------------------------------------------------------------- apr_file_t ** is always an OUT param */ @@ -185,4 +215,9 @@ SWIG_MakePtr(ST(argvi++), (void *)*$1, $*1_descriptor,0); } +%typemap(ruby, argout, fragment="output_helper") apr_file_t ** { + $result = output_helper($result, + SWIG_NewPointerObj((void *)*$1, $*1_descriptor, 1)); +} + /* ----------------------------------------------------------------------- */ Index: tools/examples/svnlook.rb =================================================================== --- tools/examples/svnlook.rb (revision 0) +++ tools/examples/svnlook.rb (revision 0) @@ -0,0 +1,414 @@ +#!/usr/bin/env ruby + +require "svn/core" +require "svn/fs" +require "svn/delta" +require "svn/repos" + +def basename(path) + path.chomp("/") +end + +class SvnLook + def initialize(pool, path, rev, txn) + @pool = pool + @fs = Svn::Repos.open(path, @pool).fs + + if txn + @txn = @fs.open_txn(txn, @pool) + else + @txn = nil + rev ||= @fs.youngest_rev(@pool) + end + @rev = rev + end + + def run(cmd, *args) + dispatch(cmd, *args) + end + + private + def dispatch(cmd, *args) + if respond_to?("cmd_#{cmd}", true) + begin + __send__("cmd_#{cmd}", *args) + rescue ArgumentError + puts $!.message + puts $@ + puts("invalid argument for #{cmd}: #{args.join(' ')}") + end + else + puts("unknown command: #{cmd}") + end + end + + def cmd_default + cmd_info + cmd_tree + end + + def cmd_author + puts(property(Svn::Core::PROP_REVISION_AUTHOR) || "") + end + + def cmd_cat + end + + def cmd_changed + print_tree(ChangedEditor, nil, true) + end + + def cmd_date + if @txn + puts + else + date = property(Svn::Core::PROP_REVISION_DATE) + if date + time = str_to_time(date, @pool) + puts time.strftime('%Y-%m-%d %H:%M(%Z)') + else + puts + end + end + end + + def cmd_diff + print_tree(DiffEditor, nil, true) + end + + def cmd_dirs_changed + print_tree(DirsChangedEditor) + end + + def cmd_ids + print_tree(Editor, 0, true) + end + + def cmd_info + cmd_author + cmd_date + cmd_log(true) + end + + def cmd_log(print_size=false) + log = property(Svn::Core::PROP_REVISION_LOG) || '' + puts log.length if print_size + puts log + end + + def cmd_tree + print_tree(Editor, 0) + end + + def property(name) + if @txn + @txn.prop(name, @pool) + else + @fs.revision_prop(@rev, name, @pool) + end + end + + def print_tree(editor_class, base_rev=nil, pass_root=false) + if base_rev.nil? + if @txn + base_rev = @txn.base_revision + else + base_rev = @rev - 1 + end + end + + if @txn + root = @txn.root(@pool) + else + root = @fs.revision_root(@rev, @pool) + end + + base_root = @fs.revision_root(base_rev, @pool) + + if pass_root + editor = editor_class.new(root, base_root) + else + editor = editor_class.new + end + + e_ptr, e_baton = Svn::Delta.make_editor(editor, @pool) + + Svn::Repos.dir_delta(base_root, '', '', root, '', + e_ptr, e_baton, method(:authz_cb), + false, true, false, false, @pool) + end + + def authz_cb(root, path, pool) + true + end + + def str_to_time(str, pool) + sec, usec = Svn::Core.time_from_cstring(str, pool).divmod(1000000) + time = Time.at(sec, usec) + ## or + # date = Time.parse(str).localtime + time + end + + class Editor < Svn::Delta::Editor + def initialize(root=nil, base_root=nil) + @root = root + # base_root ignored + + @indent = "" + end + + def open_root(base_revision, dir_pool) + puts "/#{id('/', dir_pool)}" + @indent << ' ' + end + + def add_directory(path, *args) + puts "#{@indent}#{basename(path)}/#{id(path, args[-1])}" + @indent << ' ' + end + + alias open_directory add_directory + + def close_directory(baton) + @indent.chop! + end + + def add_file(path, *args) + puts "#{@indent}#{basename(path)}#{id(path, args[-1])}" + end + + alias open_file add_file + + private + def id(path, pool) + if @root + fs_id = @root.node_id(path, pool) + " <#{fs_id.unparse(pool)}>" + else + "" + end + end + end + + class DirsChangedEditor < Svn::Delta::Editor + def open_root(base_revision, dir_pool) + [true, ''] + end + + def delete_entry(path, revision, parent_baton, pool) + dir_changed(parent_baton) + end + + def add_directory(path, parent_baton, + copyfrom_path, copyfrom_revision, dir_pool) + dir_changed(parent_baton) + [true, path] + end + + def open_directory(path, parent_baton, base_revision, dir_pool) + [true, path] + end + + def change_dir_prop(dir_baton, name, value, pool) + dir_changed(dir_baton) + end + + def add_file(path, parent_baton, + copyfrom_path, copyfrom_revision, file_pool) + dir_changed(parent_baton) + end + + def open_file(path, parent_baton, base_revision, file_pool) + dir_changed(parent_baton) + end + + private + def dir_changed( baton) + if baton[0] + # the directory hasn't been printed yet. do it. + puts baton[1] + '/' + baton[0] = nil + end + end + end + + class ChangedEditor < Svn::Delta::Editor + def initialize(root, base_root) + @root = root + @base_root = base_root + end + + def open_root(base_revision, dir_pool) + [true, ''] + end + + def delete_entry(path, revision, parent_baton, pool) + print "D #{path}" + if @base_root.dir?('/' + path, pool) + puts "/" + else + puts + end + end + + def add_directory(path, parent_baton, + copyfrom_path, copyfrom_revision, dir_pool) + puts "A #{path}/" + [false, path] + end + + def open_directory(path, parent_baton, base_revision, dir_pool) + [true, path] + end + + def change_dir_prop(dir_baton, name, value, pool) + if dir_baton[0] + # the directory hasn't been printed yet. do it. + puts "_U #{dir_baton[1]}/" + dir_baton[0] = false + end + end + + def add_file(path, parent_baton, + copyfrom_path, copyfrom_revision, file_pool) + puts "A #{path}" + ['_', ' ', nil] + end + + def open_file(path, parent_baton, base_revision, file_pool) + ['_', ' ', path] + end + + def apply_textdelta(file_baton, base_checksum, pool) + file_baton[0] = 'U' + nil + end + + def change_file_prop(file_baton, name, value, pool) + file_baton[1] = 'U' + end + + def close_file(file_baton, text_checksum) + text_mod, prop_mod, path = file_baton + # test the path. it will be None if we added this file. + if path + status = text_mod + prop_mod + # was there some kind of change? + if status != '_ ' + puts "#{status} #{path}" + end + end + end + end + + class DiffEditor < Svn::Delta::Editor + + def initialize(root, base_root) + @root = root + @base_root = base_root + end + + def delete_entry(path, revision, parent_baton, pool) + unless @base_root.dir?('/' + path, pool) + do_diff(path, nil, pool) + end + end + + def add_file(path, parent_baton, + copyfrom_path, copyfrom_revision, file_pool) + do_diff(nil, path, file_pool) + ['_', ' ', nil, file_pool] + end + + def open_file(path, parent_baton, base_revision, file_pool) + ['_', ' ', path, file_pool] + end + + def apply_textdelta(file_baton, base_checksum, pool) + if file_baton[2].nil? + nil + else + do_diff(file_baton[2], file_baton[2], file_baton[3]) + end + end + + private + def do_diff(base_path, path, pool) + if base_path.nil? + puts("Added: #{path}") + label = path + elsif path.nil? + puts("Removed: #{base_path}") + label = base_path + else + puts "Modified: #{path}" + label = path + end + + puts "===============================================================" + + "===============" + opts = [ + "--label=#{label} (original)", + "--label=#{label} (new)", + "-u", + ] + differ = Svn::Fs::FileDiff.new(@base_root, base_path, + @root, path, pool, opts) + differ.io.each do |line| + puts line + end + puts + end + end +end + +def usage + messages = [ + "usage: #{$0} REPOS_PATH rev REV [COMMAND] - inspect revision REV", + " #{$0} REPOS_PATH txn TXN [COMMAND] - inspect transaction TXN", + " #{$0} REPOS_PATH [COMMAND] - inspect the youngest revision", + "", + "REV is a revision number > 0.", + "TXN is a transaction name.", + "", + "If no command is given, the default output (which is the same as", + "running the subcommands `info' then `tree') will be printed.", + "", + "COMMAND can be one of: ", + "", + " author: print author.", + " changed: print full change summary: all dirs & files changed.", + " date: print the timestamp (revisions only).", + " diff: print GNU-style diffs of changed files and props.", + " dirs-changed: print changed directories.", + " ids: print the tree, with nodes ids.", + " info: print the author, data, log_size, and log message.", + " log: print log message.", + " tree: print the tree.", + ] + puts(messages.join("\n")) + exit(1) +end + +if ARGV.empty? + usage +end + +path = ARGV.shift +cmd = ARGV.shift +rev = nil +txn = nil +case cmd +when "rev" + rev = Integer(ARGV.shift) + cmd = ARGV.shift +when "txn" + txn = ARGV.shift + cmd = ARGV.shift +end +cmd ||= "default" + +Svn::Core.run_app do |pool| + SvnLook.new(pool, path, rev, txn).run(cmd.gsub(/-/, '_')) +end Property changes on: tools/examples/svnlook.rb ___________________________________________________________________ Name: svn:executable + * Index: tools/examples/svnshell.rb =================================================================== --- tools/examples/svnshell.rb (revision 0) +++ tools/examples/svnshell.rb (revision 0) @@ -0,0 +1,291 @@ +#!/usr/bin/env ruby + +require "readline" +require "shellwords" +require "time" + +require "svn/fs" +require "svn/core" +require "svn/repos" + +class SvnShell + + WORDS = [] + + class << self + def method_added(name) + if /^do_(.*)$/ =~ name.to_s + WORDS << $1 + end + end + end + + def initialize(pool, path) + @pool = pool + @taskpool = Svn::Core.pool_create(@pool) + @fs = Svn::Repos.open(path, @pool).fs + @in_rev_mode = true + @rev = @fs.youngest_rev(@pool) + @txn = nil + @root = @fs.revision_root(@rev, @pool) + @path = "/" + @exited = false + end + + def run + while !@exited and buf = Readline.readline(prompt, true) + cmd, *args = Shellwords.shellwords(buf) + next if /\A\s*\z/ =~ cmd + dispatch(cmd, *args) + end + end + + private + def prompt + if @in_rev_mode + mode = "rev" + info = @rev + else + mode = "txn" + info = @txn + end + "<#{mode}: #{info} #{@path}>$ " + end + + def dispatch(cmd, *args) + if respond_to?("do_#{cmd}", true) + begin + __send__("do_#{cmd}", *args) + rescue ArgumentError + puts $!.message + puts $@ + puts("invalid argument for #{cmd}: #{args.join(' ')}") + end + else + puts("unknown command: #{cmd}") + end + end + + def do_cat(path) + new_path = parse_path(path) + case @root.check_path(new_path, @taskpool) + when Svn::Core::NODE_NONE + puts "Path '#{new_path}' does not exist." + when Svn::Core::NODE_DIR + puts "Path '#{new_path}' is not a file." + else + @root.file_contents(new_path, @taskpool) do |stream| + puts stream.read(@root.file_length(new_path, @taskpool)) + end + end + end + + def do_cd(path) + new_path = parse_path(path) + if @root.check_path(new_path, @taskpool) == Svn::Core::NODE_DIR + @path = new_path + else + puts "Path '#{new_path}' is not a valid filesystem directory." + end + @taskpool.clear + end + + def do_ls(*paths) + paths << @path if paths.empty? + paths.each do |path| + new_path = parse_path(path) + case @root.check_path(new_path, @taskpool) + when Svn::Core::NODE_DIR + parent = new_path + entries = @root.dir_entries(parent, @taskpool) + when Svn::Core::NODE_FILE + parts = path_to_parts(new_path) + name = parts.pop + parent = parts_to_path(parts) + puts "#{parent}:#{name}" + tmp = @root.dir_entries(parent, @taskpool) + if tmp[name].nil? + return + else + entries = {name => tmp[name]} + end + else + puts "Path '#{new_path}' not found." + return + end + + puts " REV AUTHOR NODE-REV-ID SIZE DATE NAME" + puts "----------------------------------------------------------------------------" + + entries.keys.sort.each do |entry| + fullpath = parent + '/' + entry + size = '' + if @root.dir?(fullpath, @taskpool) + name = entry + '/' + else + size = @root.file_length(fullpath, @taskpool).to_i.to_s + name = entry + end + + node_id = entries[entry].id.to_s(@taskpool) + created_rev = @root.node_created_rev(fullpath, @taskpool) + author = @fs.revision_prop(created_rev, + Svn::Core::PROP_REVISION_AUTHOR, + @taskpool).to_s + date = @fs.revision_prop(created_rev, + Svn::Core::PROP_REVISION_DATE, + @taskpool) + args = [ + created_rev, author[0,8], + node_id, size, format_date(date, @taskpool), name + ] + puts "%6s %8s <%10s> %8s %17s %s" % args + @taskpool.clear + end + end + end + + def do_lstxns + txns = @fs.list_transactions(@taskpool) + txns.sort + counter = 0 + txns.each do |txn| + counter = counter + 1 + puts "%8s " % txn + if counter == 6 + puts + counter = 0 + end + end + puts + @taskpool.clear + end + + def do_pcat(path=nil) + catpath = path || @path + if @root.check_path(catpath, @taskpool) == Svn::Core::NODE_NONE + puts "Path '#{catpath}' does not exist." + return + end + + plist = @root.node_proplist(catpath, @taskpool) + return if plist.nil? + + plist.each do |key, value| + puts "K #{key.size}" + puts key + puts "P #{value.size}" + puts value + end + puts 'PROPS-END' + end + + def do_setrev(rev) + begin + newroot = @fs.revision_root(Integer(rev), @pool) + rescue Svn::Error + puts "Error setting the revision to '#{rev}': #{$!.message}" + return + end + @root.close + @root = newroot + @rev = rev + @in_rev_mode = true + path_landing + end + + def do_settxn(name) + new_root = nil + begin + txn = @fs.open_txn(name, @pool) + new_root = txn.root(@pool) + rescue Svn::Error + puts "Error setting the transaction to '#{name}': #{$!.message}" + return + end + @root.close + @root = new_root + @txn = name + @in_rev_mode = false + path_landing + end + + def do_youngest + rev = @fs.youngest_rev(@taskpool) + puts rev + @taskpool.clear + end + + def do_exit + @exited = true + end + + def path_to_parts(path) + path.split(/\/+/) + end + + def parts_to_path(parts) + normalized_parts = parts.reject{|part| part.empty?} + "/#{normalized_parts.join('/')}" + end + + def parse_path(path) + if path[0,1] != "/" and @path != "/" + path = "#{@path}/#{path}" + end + parts = path_to_parts(path) + + normalized_parts = [] + parts.each do |part| + case part + when "." + # ignore + when ".." + normalized_parts.pop + else + normalized_parts << part + end + end + parts_to_path(normalized_parts) + end + + def path_landing + found = false + new_path = @path + until found + case @root.check_path(new_path, @taskpool) + when Svn::Core::NODE_DIR + found = true + else + parts = path_to_parts(new_path) + parts.pop + new_path = parts_to_path(parts) + end + end + @path = new_path + @taskpool.clear + end + + def format_date(date_str, pool) + sec, usec = Svn::Core.time_from_cstring(date_str, pool).divmod(1000000) + date = Time.at(sec, usec) + ## or + # date = Time.parse(date_str).localtime + date.strftime("%b %d %H:%M(%Z)") + end + +end + + + +Readline.completion_proc = Proc.new do |word| + SvnShell::WORDS.grep(/^#{Regexp.quote(word)}/) +end + +if ARGV.size != 1 + puts "#{$0} REPOS_PATH" + exit(1) +end +Svn::Core.run_app(ARGV.shift) do |pool, path| + SvnShell.new(pool, path).run +end Property changes on: tools/examples/svnshell.rb ___________________________________________________________________ Name: svn:executable + *