- Lot of fixes and support for the UI!!!
authordmacvicar <dmacvicar@e0cc52ee-31ee-0310-8b87-e83c4596d67c>
Wed, 19 Sep 2007 15:50:57 +0000 (15:50 +0000)
committerdmacvicar <dmacvicar@e0cc52ee-31ee-0310-8b87-e83c4596d67c>
Wed, 19 Sep 2007 15:50:57 +0000 (15:50 +0000)
git-svn-id: http://svn.opensuse.org/svn/yast/trunk/ruby-bindings@40969 e0cc52ee-31ee-0310-8b87-e83c4596d67c

48 files changed:
CMakeLists.txt [new file with mode: 0644]
MAINTAINER [new file with mode: 0644]
README [new file with mode: 0644]
RPMNAME [new file with mode: 0644]
TODO [new file with mode: 0644]
VERSION.cmake [new file with mode: 0644]
cmake/modules/FindRuby.cmake [new file with mode: 0644]
cmake/modules/FindYast.cmake [new file with mode: 0644]
examples/ruby/Bar.rb [new file with mode: 0644]
examples/ruby/Foo.rb [new file with mode: 0644]
examples/ruby/libycp.rb [new file with mode: 0644]
examples/ruby/logger.rb [new file with mode: 0644]
examples/ruby/module-arch.rb [new file with mode: 0644]
examples/ruby/module-storage.rb [new file with mode: 0644]
examples/ruby/module-timezone.rb [new file with mode: 0644]
examples/ruby/ruby_from_ycp.ycp [new file with mode: 0644]
examples/ruby/ruby_from_ycp_2.ycp [new file with mode: 0644]
examples/ruby/scr.rb [new file with mode: 0644]
examples/ruby/scr.ycp [new file with mode: 0644]
examples/ruby/ui.rb [new file with mode: 0644]
modules/MyModule.ycp [new file with mode: 0644]
package/yast2-ruby-bindings.changes [new file with mode: 0644]
src/CMakeLists.txt [new file with mode: 0644]
src/ruby/CMakeLists.txt [new file with mode: 0644]
src/ruby/Makefile.am [new file with mode: 0644]
src/ruby/RubyLogger.cc [new file with mode: 0644]
src/ruby/RubyLogger.h [new file with mode: 0644]
src/ruby/Y2CCRuby.cc [new file with mode: 0644]
src/ruby/Y2CCRuby.h [new file with mode: 0644]
src/ruby/Y2RubyComponent.cc [new file with mode: 0644]
src/ruby/Y2RubyComponent.h [new file with mode: 0644]
src/ruby/Y2RubyTypeConv.cc [new file with mode: 0644]
src/ruby/Y2RubyTypeConv.h [new file with mode: 0644]
src/ruby/Y2RubyTypePath.cc [new file with mode: 0644]
src/ruby/Y2RubyTypePath.h [new file with mode: 0644]
src/ruby/Y2RubyTypeTerm.cc [new file with mode: 0644]
src/ruby/Y2RubyTypeTerm.h [new file with mode: 0644]
src/ruby/YCP.cc [new file with mode: 0644]
src/ruby/YCP.rb [new file with mode: 0644]
src/ruby/YRuby.cc [new file with mode: 0644]
src/ruby/YRuby.h [new file with mode: 0644]
src/ruby/YRubyNamespace.cc [new file with mode: 0644]
src/ruby/YRubyNamespace.h [new file with mode: 0644]
src/ruby/YaPI.pm.in [new file with mode: 0644]
src/ruby/yast.rb [new file with mode: 0644]
src/swig/CMakeLists.txt [new file with mode: 0644]
src/swig/yast.i [new file with mode: 0644]
yast2-ruby-bindings.spec.in [new file with mode: 0644]

diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..a61d065
--- /dev/null
@@ -0,0 +1,98 @@
+PROJECT(yast2-ruby-bindings)
+
+SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O3 -Wall -Woverloaded-virtual" )
+SET( CMAKE_C_FLAGS   "${CMAKE_C_FLAGS}   -g -O3 -Wall" )
+
+# Library
+IF ( DEFINED LIB )
+  SET ( LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${LIB}" )
+ELSE ( DEFINED  LIB )
+  IF (CMAKE_SIZEOF_VOID_P MATCHES "8")
+    SET( LIB_SUFFIX "64" )
+  ENDIF(CMAKE_SIZEOF_VOID_P MATCHES "8")
+  SET ( LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}" )
+ENDIF ( DEFINED  LIB )
+MESSAGE(STATUS "Libraries will be installed in ${LIB_INSTALL_DIR}" )
+
+# where to look first for cmake modules, before ${CMAKE_ROOT}/Modules/ is checked
+SET(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH})
+#SET(CMAKE_MODULE_PATH ${CMAKE_INSTALL_PREFIX}/share/cmake/Modules ${CMAKE_MODULE_PATH})
+
+SET( PACKAGE "yast2-ruby-bindings" )
+INCLUDE(${CMAKE_SOURCE_DIR}/VERSION.cmake)
+SET ( VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}" )
+
+SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Language Bindings for YaST")
+SET(CPACK_PACKAGE_VENDOR "Novell Inc.")
+#SET(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/ReadMe.txt")
+#SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/Copyright.txt")
+SET(CPACK_PACKAGE_VERSION_MAJOR ${VERSION_MAJOR})
+SET(CPACK_PACKAGE_VERSION_MINOR ${VERSION_MINOR})
+SET(CPACK_PACKAGE_VERSION_PATCH ${VERSION_PATCH})
+
+SET( CPACK_GENERATOR "TBZ2")
+SET( CPACK_SOURCE_GENERATOR "TBZ2")
+SET( CPACK_SOURCE_PACKAGE_FILE_NAME "${PACKAGE}-${VERSION}" )
+
+# The following components are regex's to match anywhere (unless anchored)
+# in absolute path + filename to find files or directories to be excluded
+# from source tarball.
+SET (CPACK_SOURCE_IGNORE_FILES
+"/CVS/;/.svn/;/.libs/;/.deps/;/.git/;.swp$;.#;/#;/build/"
+"~$"
+"\\\\.cvsignore$"
+"/package"
+"Makefile\\\\.in$"
+)
+
+INCLUDE(CPack)
+
+#FIND_PACKAGE(SWIG REQUIRED)
+
+FIND_PROGRAM(SWIG_EXECUTABLE
+  NAMES swig-1.3 swig
+  PATHS ${SWIG_DIR} ${SWIG_DIR}/.. ${SWIG_DIR}/../../bin /usr/bin /usr/local/bin ${CMAKE_INSTALL_PREFIX}/bin
+)
+
+IF ( NOT SWIG_EXECUTABLE )
+  MESSAGE( FATAL "SWIG not found." )
+ELSE ( NOT SWIG_EXECUTABLE )
+  MESSAGE( STATUS "SWIG found at ${SWIG_EXECUTABLE}" )
+ENDIF ( NOT SWIG_EXECUTABLE )
+
+FIND_PACKAGE(Yast REQUIRED)
+FIND_PACKAGE(Ruby REQUIRED)
+
+#RUBY_RUBY_LIB_PATH
+MESSAGE( STATUS "Ruby vendor arch dir: ${RUBY_VENDORARCH_DIR}" )
+
+####################################################################
+# RPM SPEC                                                         #
+####################################################################
+MESSAGE(STATUS "Writing spec file...")
+CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/${PACKAGE}.spec.in ${CMAKE_BINARY_DIR}/package/${PACKAGE}.spec @ONLY)
+
+ADD_SUBDIRECTORY(src)
+
+INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
+
+ADD_CUSTOM_TARGET( svncheck
+  COMMAND cd $(CMAKE_SOURCE_DIR) && ! LC_ALL=C svn status --show-updates --quiet | grep -v '^Status against revision'
+)
+
+SET( AUTOBUILD_COMMAND
+  COMMAND ${CMAKE_MAKE_PROGRAM} package_source
+  COMMAND ${CMAKE_COMMAND} -E copy ${CPACK_SOURCE_PACKAGE_FILE_NAME}.tar.bz2 ${CMAKE_BINARY_DIR}/package
+  COMMAND ${CMAKE_COMMAND} -E remove ${CPACK_SOURCE_PACKAGE_FILE_NAME}.tar.bz2
+  COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/package/${PACKAGE}.changes" "${CMAKE_BINARY_DIR}/package/${PACKAGE}.changes"
+)
+
+ADD_CUSTOM_TARGET( srcpackage_local
+  ${AUTOBUILD_COMMAND}
+)
+
+ADD_CUSTOM_TARGET( srcpackage
+  COMMAND ${CMAKE_MAKE_PROGRAM} svncheck
+  ${AUTOBUILD_COMMAND}
+)
+
diff --git a/MAINTAINER b/MAINTAINER
new file mode 100644 (file)
index 0000000..e0dbd2d
--- /dev/null
@@ -0,0 +1,2 @@
+Duncan Mac-Vicar <dmacvicar@suse.de>
+
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..93ff4bc
--- /dev/null
+++ b/README
@@ -0,0 +1,17 @@
+/*---------------------------------------------------------------------\
+|                                                                      |
+|                      __   __    ____ _____ ____                      |
+|                      \ \ / /_ _/ ___|_   _|___ \                     |
+|                       \ V / _` \___ \ | |   __) |                    |
+|                        | | (_| |___) || |  / __/                     |
+|                        |_|\__,_|____/ |_| |_____|                    |
+|                                                                      |
+|                                                                      |
+| ruby language support                              (C) Novell Inc.   |
+\----------------------------------------------------------------------/
+
+Author: Duncan Mac-Vicar <dmacvicar@suse.de>
+
+Based on yast2-perl-bindings by
+  Martin Vidner <mvidner@suse.cz>
+  Stefan Hundhammer <sh@suse.de>
diff --git a/RPMNAME b/RPMNAME
new file mode 100644 (file)
index 0000000..57030e1
--- /dev/null
+++ b/RPMNAME
@@ -0,0 +1 @@
+yast2-ruby-bindings
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..b9c5e92
--- /dev/null
+++ b/TODO
@@ -0,0 +1,6 @@
+What is missing.
+
+- UI support
+- Conversion of some types from ruby to YCP like hash and arrays
+- handling of abort signal from the interpreter.
+- implement y2log functions
diff --git a/VERSION.cmake b/VERSION.cmake
new file mode 100644 (file)
index 0000000..672b177
--- /dev/null
@@ -0,0 +1,3 @@
+SET(VERSION_MAJOR "0")
+SET(VERSION_MINOR "2")
+SET(VERSION_PATCH "0")
\ No newline at end of file
diff --git a/cmake/modules/FindRuby.cmake b/cmake/modules/FindRuby.cmake
new file mode 100644 (file)
index 0000000..0ee5da2
--- /dev/null
@@ -0,0 +1,84 @@
+# - Find Ruby
+# This module finds if Ruby is installed and determines where the include files
+# and libraries are. It also determines what the name of the library is. This
+# code sets the following variables:
+#
+#  RUBY_INCLUDE_PATH = path to where ruby.h can be found
+#  RUBY_EXECUTABLE   = full path to the ruby binary
+
+# Copyright (c) 2002 Kitware, Inc., Insight Consortium.  All rights reserved.
+# See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
+
+
+if(RUBY_LIBRARY AND RUBY_INCLUDE_PATH)
+   # Already in cache, be silent
+   set(RUBY_FIND_QUIETLY TRUE)
+endif (RUBY_LIBRARY AND RUBY_INCLUDE_PATH)
+
+#   RUBY_ARCHDIR=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"archdir"@:>@)'`
+#   RUBY_SITEARCHDIR=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"sitearchdir"@:>@)'`
+#   RUBY_SITEDIR=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"sitelibdir"@:>@)'`
+#   RUBY_LIBDIR=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"libdir"@:>@)'`
+#   RUBY_LIBRUBYARG=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"LIBRUBYARG_SHARED"@:>@)'`
+
+FIND_PROGRAM(RUBY_EXECUTABLE NAMES ruby ruby1.8 ruby18 )
+
+EXECUTE_PROCESS(COMMAND ${RUBY_EXECUTABLE} -r rbconfig -e "print Config::CONFIG['archdir']"
+   OUTPUT_VARIABLE RUBY_ARCH_DIR)
+
+EXECUTE_PROCESS(COMMAND ${RUBY_EXECUTABLE} -r rbconfig -e "print Config::CONFIG['libdir']"
+   OUTPUT_VARIABLE RUBY_POSSIBLE_LIB_PATH)
+
+EXECUTE_PROCESS(COMMAND ${RUBY_EXECUTABLE} -r rbconfig -e "print Config::CONFIG['rubylibdir']"
+   OUTPUT_VARIABLE RUBY_RUBY_LIB_PATH)
+
+# site_ruby
+EXECUTE_PROCESS(COMMAND ${RUBY_EXECUTABLE} -r rbconfig -e "print Config::CONFIG['sitearchdir']"
+   OUTPUT_VARIABLE RUBY_SITEARCH_DIR)
+
+EXECUTE_PROCESS(COMMAND ${RUBY_EXECUTABLE} -r rbconfig -e "print Config::CONFIG['sitelibdir']"
+   OUTPUT_VARIABLE RUBY_SITELIB_DIR)
+
+
+# vendor_ruby
+EXECUTE_PROCESS(COMMAND ${RUBY_EXECUTABLE} -r vendor-specific -e "print '-rvendor-specific'"
+   OUTPUT_VARIABLE RUBY_VENDOR_ARG)
+
+IF(RUBY_VENDOR_ARG)
+    EXECUTE_PROCESS(COMMAND ${RUBY_EXECUTABLE} -r rbconfig -e "print Config::CONFIG['vendorarchdir']"
+       OUTPUT_VARIABLE RUBY_VENDORARCH_DIR)
+
+    EXECUTE_PROCESS(COMMAND ${RUBY_EXECUTABLE} -r rbconfig -e "print Config::CONFIG['vendorlibdir']"
+       OUTPUT_VARIABLE RUBY_VENDORLIB_DIR)
+ELSE(RUBY_VENDOR_ARG)
+    # fall back to site*dir
+    EXECUTE_PROCESS(COMMAND ${RUBY_EXECUTABLE} -r rbconfig -e "print Config::CONFIG['sitearchdir']"
+       OUTPUT_VARIABLE RUBY_VENDORARCH_DIR)
+
+    EXECUTE_PROCESS(COMMAND ${RUBY_EXECUTABLE} -r rbconfig -e "print Config::CONFIG['sitelibdir']"
+       OUTPUT_VARIABLE RUBY_VENDORLIB_DIR)
+ENDIF(RUBY_VENDOR_ARG)
+
+# this is not needed if you use "print" inside the ruby statements
+# remove the new lines from the output by replacing them with empty strings
+#STRING(REPLACE "\n" "" RUBY_ARCH_DIR "${RUBY_ARCH_DIR}")
+#STRING(REPLACE "\n" "" RUBY_POSSIBLE_LIB_PATH "${RUBY_POSSIBLE_LIB_PATH}")
+#STRING(REPLACE "\n" "" RUBY_RUBY_LIB_PATH "${RUBY_RUBY_LIB_PATH}")
+
+
+FIND_PATH(RUBY_INCLUDE_PATH
+   NAMES ruby.h
+  PATHS
+   ${RUBY_ARCH_DIR}
+  /usr/lib/ruby/1.8/i586-linux-gnu/ )
+
+FIND_LIBRARY(RUBY_LIBRARY
+  NAMES ruby ruby1.8
+  PATHS ${RUBY_POSSIBLE_LIB_PATH}
+  )
+
+MARK_AS_ADVANCED(
+  RUBY_EXECUTABLE
+  RUBY_LIBRARY
+  RUBY_INCLUDE_PATH
+  )
diff --git a/cmake/modules/FindYast.cmake b/cmake/modules/FindYast.cmake
new file mode 100644 (file)
index 0000000..a9de452
--- /dev/null
@@ -0,0 +1,53 @@
+
+if(YAST_INCLUDE_DIR AND YAST_LIBRARY AND YAST_YCP_LIBRARY)
+  # Already in cache, be silent
+  set(YAST_FIND_QUIETLY TRUE)  
+endif(YAST_INCLUDE_DIR AND YAST_LIBRARY AND YAST_YCP_LIBRARY)
+
+set(YAST_LIBRARY)
+set(YAST_INCLUDE_DIR)
+set(YAST_YCP_LIBRARY)
+
+FIND_PATH(YAST_INCLUDE_DIR Y2.h
+  ${CMAKE_INSTALL_PREFIX}/include/YaST2
+  /usr/include/YaST2
+  /usr/local/include/YaST2
+)
+
+SET(YAST_PLUGIN_DIR ${LIB_INSTALL_DIR}/YaST2/plugin)
+
+FIND_LIBRARY(YAST_LIBRARY NAMES y2
+  PATHS
+  ${LIB_INSTALL_DIR}
+  /usr/local/lib
+)
+
+FIND_LIBRARY(YAST_YCP_LIBRARY NAMES ycp
+  PATHS
+  ${LIB_INSTALL_DIR}
+  /usr/local/lib
+)
+
+FIND_LIBRARY(YAST_PLUGIN_WFM_LIBRARY NAMES py2wfm
+  PATHS
+  ${YAST_PLUGIN_DIR}
+  /usr/lib
+  /usr/local/lib
+)
+
+FIND_LIBRARY(YAST_PLUGIN_SCR_LIBRARY NAMES py2scr
+  PATHS
+  ${YAST_PLUGIN_DIR}
+  ${LIB_INSTALL_DIR}
+  /usr/local/lib
+)
+
+if(YAST_INCLUDE_DIR AND YAST_LIBRARY AND YAST_YCP_LIBRARY)
+   MESSAGE( STATUS "YaST2 found: includes in ${YAST_INCLUDE_DIR}, library in ${YAST_LIBRARY}")
+   MESSAGE( STATUS "             plugins in ${YAST_PLUGIN_DIR}")
+   set(YAST_FOUND TRUE)
+else(YAST_INCLUDE_DIR AND YAST_LIBRARY AND YAST_YCP_LIBRARY)
+   MESSAGE( STATUS "YaST2 not found")
+endif(YAST_INCLUDE_DIR AND YAST_LIBRARY AND YAST_YCP_LIBRARY)
+
+MARK_AS_ADVANCED(YAST_INCLUDE_DIR YAST_LIBRARY YAST_YCP_LIBRARY YAST_PLUGIN_WFM_LIBRARY ${YAST_PLUGIN_DIR})
\ No newline at end of file
diff --git a/examples/ruby/Bar.rb b/examples/ruby/Bar.rb
new file mode 100644 (file)
index 0000000..e2d3ec1
--- /dev/null
@@ -0,0 +1,8 @@
+require 'yast'
+
+module Bar
+  def self.try
+    m = YaST::Module.new("SCR")
+    return m.Execute(".target.bash", "firefox").class.to_s
+  end
+end
diff --git a/examples/ruby/Foo.rb b/examples/ruby/Foo.rb
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/examples/ruby/libycp.rb b/examples/ruby/libycp.rb
new file mode 100644 (file)
index 0000000..bbaf9a8
--- /dev/null
@@ -0,0 +1,45 @@
+require 'ryast'
+include Ryast
+
+def get_ns(name)
+  import = Import.new(name)
+  ns = import.name_space
+  puts ns.filename
+  #ns.initialize
+
+  return ns
+end
+
+component = Y2ComponentBroker.get_namespace_component("Arch");
+#puts component.methods
+#Y2Namespace *ns = c->import(RSTRING (namespace_name)->ptr);
+nsname = "Arch"
+fncname = "sparc32"
+ns = get_ns(nsname)
+#/**/
+#t = Type.from_signature("bool()")
+#puts t
+sym = ns.table.find(fncname);
+puts sym.class
+if (sym.nil?)
+  raise ("No such symbol #{nsname}::#{fncname}")
+elsif (sym.sentry.is_variable or sym.sentry.reference? )
+  # set the variable
+  #ret_yv = YCP_getset_variable (aTHX_ ns_name, sym_te->sentry (), args);
+else
+  fnccall = ns.create_function_call(fncname, nil)
+  if fnccall.nil?
+    raise("No such function #{nsname}::#{fncname}")
+  end
+end
+exit
+
+h = ns.lookup_symbol("sparc32")
+puts h.class
+exit
+function = ns.create_function_call("sparc32",0)
+#call->appendParameter (v);
+function.finish_parameters
+res = function.evaluate_call
+puts res
+exit
diff --git a/examples/ruby/logger.rb b/examples/ruby/logger.rb
new file mode 100644 (file)
index 0000000..06f9125
--- /dev/null
@@ -0,0 +1,5 @@
+require 'yast'
+include YaST
+#ui = YaST::Module.new("UI")
+
+y2milestone("Hello")
\ No newline at end of file
diff --git a/examples/ruby/module-arch.rb b/examples/ruby/module-arch.rb
new file mode 100644 (file)
index 0000000..a813d97
--- /dev/null
@@ -0,0 +1,6 @@
+require 'yast'
+
+m = YaST::Module.new("Arch")
+puts m.sparc32
+puts m.arch_short
+puts m.is_xen
diff --git a/examples/ruby/module-storage.rb b/examples/ruby/module-storage.rb
new file mode 100644 (file)
index 0000000..a47825e
--- /dev/null
@@ -0,0 +1,7 @@
+require 'yast'
+
+m = YaST::Module.new("Storage")
+dp = m.GetDiskPartition("/dev/sda1")
+dp.each do | key, value |
+  puts "#{key} #{value}"
+end
\ No newline at end of file
diff --git a/examples/ruby/module-timezone.rb b/examples/ruby/module-timezone.rb
new file mode 100644 (file)
index 0000000..09fca27
--- /dev/null
@@ -0,0 +1,12 @@
+require 'yast'
+
+m = YaST::Module.new("Timezone")
+zonemap = m.get_zonemap()
+puts zonemap.class
+zonemap.each do | element |
+  element.each do | key, value |
+    value.each do | k, v |
+      puts "#{k} #{v}"
+    end
+  end
+end
diff --git a/examples/ruby/ruby_from_ycp.ycp b/examples/ruby/ruby_from_ycp.ycp
new file mode 100644 (file)
index 0000000..b61bd3c
--- /dev/null
@@ -0,0 +1,13 @@
+
+{
+    import "Duncan";
+    string result = (string) multiply_by_eight(10);
+    UI::OpenDialog(
+         `VBox(
+         `Label(result),
+         `PushButton("OK")
+         )
+         );
+    UI::UserInput();
+    UI::CloseDialog();
+}
\ No newline at end of file
diff --git a/examples/ruby/ruby_from_ycp_2.ycp b/examples/ruby/ruby_from_ycp_2.ycp
new file mode 100644 (file)
index 0000000..733d4d9
--- /dev/null
@@ -0,0 +1,14 @@
+
+{
+    import "Foo";
+    //integer result = (integer) Foo::multiply_by_eight(6);
+    integer result2 = (integer) Foo::sum(2,3);
+    UI::OpenDialog(
+         `VBox(
+         `Label(sformat("%1", result2)),
+         `PushButton("OK")
+         )
+         );
+    UI::UserInput();
+    UI::CloseDialog();
+}
\ No newline at end of file
diff --git a/examples/ruby/scr.rb b/examples/ruby/scr.rb
new file mode 100644 (file)
index 0000000..dc027f7
--- /dev/null
@@ -0,0 +1,10 @@
+require 'yast'
+
+m = YaST::Module.new("SCR")
+modules = m.Read(".proc.modules")
+modules.each do | k, v |
+  puts "#{k}:"
+  v.each do | a, b |
+    puts "    #{a} - #{b}"
+  end
+end
\ No newline at end of file
diff --git a/examples/ruby/scr.ycp b/examples/ruby/scr.ycp
new file mode 100644 (file)
index 0000000..a51ec9d
--- /dev/null
@@ -0,0 +1,12 @@
+{
+    import "Bar";
+    string result = (string) Bar::try();
+    UI::OpenDialog(
+         `VBox(
+         `Label(result),
+         `PushButton("OK")
+         )
+         );
+    UI::UserInput();
+    UI::CloseDialog();
+}
\ No newline at end of file
diff --git a/examples/ruby/ui.rb b/examples/ruby/ui.rb
new file mode 100644 (file)
index 0000000..7f7fcad
--- /dev/null
@@ -0,0 +1,22 @@
+require 'yast'
+ui = YaST::Module.new("UI")
+YaST::Ui::init("qt")
+include YaST::Ui
+
+t = HBox( Label("Welcome to Ruby!"), PushButton("Push me") )
+
+puts "#{t.to_s} #{t.class}"
+
+ui.OpenDialog(t)
+ui.UserInput()
+
+# You can also use downcase, as the symbols are aliased
+t = hbox( label("Welcome to Ruby!"), pushbutton("Push me") )
+
+puts "#{t.to_s} #{t.class}"
+
+ui.OpenDialog(t)
+ui.UserInput()
+
+
+exit
\ No newline at end of file
diff --git a/modules/MyModule.ycp b/modules/MyModule.ycp
new file mode 100644 (file)
index 0000000..17fb7f1
--- /dev/null
@@ -0,0 +1,14 @@
+{
+   module "MyModule";
+
+   global void nothing()
+   {
+    y2milestone("doing nothing, returning nothing");
+   }
+
+   global integer half( integer value )
+   {
+      return value / 2;
+   } 
+}
+         
\ No newline at end of file
diff --git a/package/yast2-ruby-bindings.changes b/package/yast2-ruby-bindings.changes
new file mode 100644 (file)
index 0000000..c512e79
--- /dev/null
@@ -0,0 +1,18 @@
+-------------------------------------------------------------------
+Wed Sep 19 16:42:35 CEST 2007 - dmacvicar@suse.de
+
+- Lot of improvements, examples and
+  support for the YaST UI
+- 0.2.0
+
+-------------------------------------------------------------------
+Thu Aug  9 12:01:39 CEST 2007 - dmacvicar@suse.de
+
+- Fix build on 64 bits
+- Use ruby vendor arch dir
+
+-------------------------------------------------------------------
+Mon Aug  6 13:33:43 CEST 2007 - dmacvicar@suse.de
+
+- Initial release 0.1.0 
+
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644 (file)
index 0000000..2a3b5fa
--- /dev/null
@@ -0,0 +1,2 @@
+#ADD_SUBDIRECTORY(swig)
+ADD_SUBDIRECTORY(ruby)
\ No newline at end of file
diff --git a/src/ruby/CMakeLists.txt b/src/ruby/CMakeLists.txt
new file mode 100644 (file)
index 0000000..11e9caf
--- /dev/null
@@ -0,0 +1,53 @@
+
+ADD_DEFINITIONS(-DY2LOG=\\\"Ruby\\\")
+
+SET(yast_ruby_module_SRCS
+  YCP.cc
+  Y2RubyTypeConv.cc
+  RubyLogger.cc
+  RubyLogger.h
+)
+
+SET(ruby_yast_plugin_SRCS
+  RubyLogger.cc
+  Y2CCRuby.cc
+  Y2RubyComponent.cc
+  YRuby.cc
+  YRubyNamespace.cc
+  Y2RubyTypeConv.cc
+  Y2RubyTypePath.cc
+  Y2RubyTypeTerm.cc
+)
+  
+SET(ruby_yast_plugin_HEADERS
+  RubyLogger.h
+  Y2CCRuby.h
+  Y2RubyComponent.h
+  YRuby.h
+  YRubyNamespace.h
+  Y2RubyTypePath.h
+  Y2RubyTypeTerm.h
+)
+  
+INCLUDE_DIRECTORIES( ${RUBY_INCLUDE_PATH} )
+INCLUDE_DIRECTORIES( ${YAST_INCLUDE_DIR} )
+  
+ADD_LIBRARY( yastx SHARED ${yast_ruby_module_SRCS})
+SET_TARGET_PROPERTIES( yastx PROPERTIES PREFIX "" )
+TARGET_LINK_LIBRARIES( yastx ${YAST_LIBRARY} )
+TARGET_LINK_LIBRARIES( yastx ${YAST_YCP_LIBRARY} )
+TARGET_LINK_LIBRARIES( yastx ${YAST_PLUGIN_WFM_LIBRARY} )
+TARGET_LINK_LIBRARIES( yastx ${YAST_PLUGIN_SCR_LIBRARY} )
+TARGET_LINK_LIBRARIES( yastx ${RUBY_LIBRARY} )
+INSTALL(TARGETS yastx LIBRARY DESTINATION ${RUBY_VENDORARCH_DIR} )
+
+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/yast.rb DESTINATION ${RUBY_VENDORLIB_DIR} )
+
+ADD_LIBRARY( py2lang_ruby SHARED ${ruby_yast_plugin_SRCS})
+TARGET_LINK_LIBRARIES( py2lang_ruby ${YAST_LIBRARY} )
+TARGET_LINK_LIBRARIES( py2lang_ruby ${YAST_YCP_LIBRARY} )
+TARGET_LINK_LIBRARIES( py2lang_ruby ${YAST_PLUGIN_WFM_LIBRARY} )
+TARGET_LINK_LIBRARIES( py2lang_ruby ${RUBY_LIBRARY} )
+INSTALL(TARGETS py2lang_ruby LIBRARY DESTINATION ${YAST_PLUGIN_DIR} )
+
+#SET_TARGET_PROPERTIES( y2lang_ruby PROPERTIES PREFIX "" )
\ No newline at end of file
diff --git a/src/ruby/Makefile.am b/src/ruby/Makefile.am
new file mode 100644 (file)
index 0000000..77063ad
--- /dev/null
@@ -0,0 +1,93 @@
+#
+# Makefile.am for perl-bindings/src
+#
+
+AM_CXXFLAGS = -DY2LOG=\"Perl\" -DMODULEDIR=\"$(moduledir)\"
+
+MY_PERL_VENDORARCH = $(subst /usr,$(prefix),$(PERL_VENDORARCH))
+perlpmdir = $(MY_PERL_VENDORARCH)/YaST
+perlsodir = $(MY_PERL_VENDORARCH)/auto/YaST/YCP
+
+perlpm_DATA = YCP.pm
+
+# plugin, libtool forces 'lib' prefix
+plugin_LTLIBRARIES = libpy2lang_perl.la
+noinst_LTLIBRARIES = liby2lang_perl.la
+perlso_LTLIBRARIES = libYCP.la
+
+# binary part of the Perl module
+libYCP_la_SOURCES =                            \
+       $(liby2lang_perl_la_SOURCES)            \
+       YCP.cc                                  \
+       PerlLogger.cc PerlLogger.h
+
+
+# are there enough yast libraries?
+# check with y2base, integrate them like y2pm does
+# Originally, of the py2* there was only py2plugin here
+# with the assumption that it would bring in the other plugins.
+# But it does not work.
+libYCP_la_LDFLAGS = $(PERL_LDFLAGS)            \
+               -L$(libdir) -L$(plugindir)      \
+               -Xlinker --whole-archive        \
+               -lpy2scr                        \
+               -lpy2wfm                        \
+               -lscr -lyui                     \
+               -lycp -ly2                      \
+               -Xlinker --no-whole-archive     \
+               ${ZYPP_LIBS} -ly2util           \
+               -version-info 2:0
+
+
+libpy2lang_perl_la_LDFLAGS = -version-info 2:0
+
+liby2lang_perl_la_LDFLAGS = -version-info 2:0
+
+
+# the yast libraries are apparently necessary when we're loaded by perl.
+libpy2lang_perl_la_LIBADD = $(PERL_LDFLAGS)    \
+               -L$(libdir) -L$(plugindir)      \
+               -lycp -ly2 ${ZYPP_LIBS} -ly2util
+#libpy2lang_perl_la_LIBADD = $(PERL_LDFLAGS)
+
+liby2lang_perl_la_LIBADD  = $(PERL_LDFLAGS)
+
+
+liby2lang_perl_la_SOURCES =                            \
+       YPerl.cc YPerl.h                        \
+       perlxsi.c
+
+# Auto-generated stub for dynamic loading of Perl modules.
+# And also register the interface to the YCP module
+## which is linked in already and won't be in the standard Perl location.
+#
+# This results in a linker warning:
+# *** Warning: Linking the shared library libpy2lang_perl.la against the
+# *** static library /usr/lib/perl5/.../DynaLoader.a is not portable!
+#
+# According to mls@suse.de this warning can safely be disregarded:
+# The SuSE DynaLoader is compiled with -fPIC for just this situation.
+#
+# -- sh@suse.de 2003-07-24
+perlxsi.c:
+       perl -MExtUtils::Embed -e xsinit -- -o perlxsi.c -std
+# See "man perlembed"
+
+
+# the plugin adds the liby2 component interface
+
+libpy2lang_perl_la_SOURCES =                           \
+       $(liby2lang_perl_la_SOURCES)                    \
+       Y2CCPerl.cc Y2CCPerl.h                  \
+       YPerlNamespace.cc YPerlNamespace.h      \
+       Y2PerlComponent.cc Y2PerlComponent.h    
+
+CLEANFILES = \
+       perlxsi.c
+
+INCLUDES = -I$(srcdir)/include -I$(includedir) ${ZYPP_CFLAGS}
+
+# generated from YaPI.pm.in by configure
+nodist_module_DATA = YaPI.pm
+
+EXTRA_DIST = $(perlpm_DATA) $(module_DATA)
diff --git a/src/ruby/RubyLogger.cc b/src/ruby/RubyLogger.cc
new file mode 100644 (file)
index 0000000..e6a168c
--- /dev/null
@@ -0,0 +1,52 @@
+/*---------------------------------------------------------------------\
+|                                                                      |
+|                      __   __    ____ _____ ____                      |
+|                      \ \ / /_ _/ ___|_   _|___ \                     |
+|                       \ V / _` \___ \ | |   __) |                    |
+|                        | | (_| |___) || |  / __/                     |
+|                        |_|\__,_|____/ |_| |_____|                    |
+|                                                                      |
+|                                                                      |
+| ruby language support                              (C) Novell Inc.   |
+\----------------------------------------------------------------------/
+
+Author: Duncan Mac-Vicar <dmacvicar@suse.de>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version
+2 of the License, or (at your option) any later version.
+
+*/
+
+#include "RubyLogger.h"
+#include <ycp/ExecutionEnvironment.h>
+
+extern ExecutionEnvironment ee;
+
+void
+RubyLogger::error (string error_message)
+{
+  y2_logger (LOG_ERROR,"Ruby",ee.filename ().c_str ()
+             ,ee.linenumber (),"","%s", error_message.c_str ());
+}
+
+
+void
+RubyLogger::warning (string warning_message)
+{
+  y2_logger (LOG_ERROR,"Ruby",ee.filename ().c_str ()
+             ,ee.linenumber (),"","%s", warning_message.c_str ());
+}
+
+RubyLogger*
+RubyLogger::instance ()
+{
+  if ( ! m_rubylogger )
+  {
+    m_rubylogger = new RubyLogger ();
+  }
+  return m_rubylogger;
+}
+
+RubyLogger* RubyLogger::m_rubylogger = NULL;
diff --git a/src/ruby/RubyLogger.h b/src/ruby/RubyLogger.h
new file mode 100644 (file)
index 0000000..af82b0f
--- /dev/null
@@ -0,0 +1,44 @@
+/*---------------------------------------------------------------------\
+|                                                                      |
+|                      __   __    ____ _____ ____                      |
+|                      \ \ / /_ _/ ___|_   _|___ \                     |
+|                       \ V / _` \___ \ | |   __) |                    |
+|                        | | (_| |___) || |  / __/                     |
+|                        |_|\__,_|____/ |_| |_____|                    |
+|                                                                      |
+|                                                                      |
+| ruby language support                              (C) Novell Inc.   |
+\----------------------------------------------------------------------/
+
+Author: Duncan Mac-Vicar <dmacvicar@suse.de>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version
+2 of the License, or (at your option) any later version.
+
+*/
+
+#ifndef RubyLogger_h
+#define RubyLogger_h
+
+#include "ycp/y2log.h"
+
+/**
+ * @short A class to provide logging for Ruby bindings errors and warning
+ */
+class RubyLogger : public Logger
+{
+    static RubyLogger* m_rubylogger;
+
+public:
+    void error (string message);
+    void warning (string message);
+
+    static RubyLogger* instance ();
+};
+
+#endif // ifndef RubyLogger_h
+
+
+// EOF
diff --git a/src/ruby/Y2CCRuby.cc b/src/ruby/Y2CCRuby.cc
new file mode 100644 (file)
index 0000000..3dcefe6
--- /dev/null
@@ -0,0 +1,65 @@
+/*---------------------------------------------------------------------\
+|                                                                      |
+|                      __   __    ____ _____ ____                      |
+|                      \ \ / /_ _/ ___|_   _|___ \                     |
+|                       \ V / _` \___ \ | |   __) |                    |
+|                        | | (_| |___) || |  / __/                     |
+|                        |_|\__,_|____/ |_| |_____|                    |
+|                                                                      |
+|                                                                      |
+| ruby language support                              (C) Novell Inc.   |
+\----------------------------------------------------------------------/
+
+Author: Duncan Mac-Vicar <dmacvicar@suse.de>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version
+2 of the License, or (at your option) any later version.
+
+*/
+
+#include "Y2CCRuby.h"
+#include <ycp/pathsearch.h>
+#define y2log_component "Y2Ruby"
+#include <ycp/y2log.h>
+
+// This is very important: We create one global variable of
+// Y2CCRuby. Its constructor will register it automatically to
+// the Y2ComponentBroker, so that will be able to find it.
+// This all happens before main() is called!
+
+Y2CCRuby g_y2ccruby;
+
+Y2Component *Y2CCRuby::provideNamespace (const char *name)
+{
+  y2debug ("Y2CCRuby::provideNamespace %s", name);
+  if (strcmp (name, "Ruby") == 0)
+  {
+    // low level functions
+
+    // leave implementation to later
+    return 0;
+  }
+  else
+  {
+    // is there a ruby module?
+    // must be the same in Y2CCRuby and Y2RubyComponent
+    string module = YCPPathSearch::find (YCPPathSearch::Module, string (name) + ".rb");
+    
+    if (!module.empty ())
+    {
+      y2milestone("Find module result: '%s'", module.c_str());
+      if (!cruby)
+      {
+        y2milestone("new ruby component");
+        cruby = new Y2RubyComponent();
+      }
+      y2milestone("returning existing ruby component");
+      return cruby;
+    }
+
+    // let someone else try creating the namespace
+    return 0;
+  }
+}
diff --git a/src/ruby/Y2CCRuby.h b/src/ruby/Y2CCRuby.h
new file mode 100644 (file)
index 0000000..7db90ec
--- /dev/null
@@ -0,0 +1,77 @@
+/*---------------------------------------------------------------------\
+|                                                                      |
+|                      __   __    ____ _____ ____                      |
+|                      \ \ / /_ _/ ___|_   _|___ \                     |
+|                       \ V / _` \___ \ | |   __) |                    |
+|                        | | (_| |___) || |  / __/                     |
+|                        |_|\__,_|____/ |_| |_____|                    |
+|                                                                      |
+|                                                                      |
+| ruby language support                              (C) Novell Inc.   |
+\----------------------------------------------------------------------/
+
+Author: Duncan Mac-Vicar <dmacvicar@suse.de>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version
+2 of the License, or (at your option) any later version.
+
+*/
+
+#ifndef _Y2CCRuby_h
+#define _Y2CCRuby_h
+
+#include "Y2RubyComponent.h"
+
+/**
+ * @short Y2ComponentCreator that creates Ruby-from-YCP bindings.
+ *
+ * A Y2ComponentCreator is an object that can create components.
+ * It receives a component name and - if it knows how to create
+ * such a component - returns a newly created component of this
+ * type. Y2CCRuby can create components with the name "Ruby".
+ */
+class Y2CCRuby : public Y2ComponentCreator
+{
+private:
+    Y2Component *cruby;
+
+public:
+    /**
+     * Creates a Ruby component creator
+     */
+    Y2CCRuby() : Y2ComponentCreator( Y2ComponentBroker::BUILTIN ),
+       cruby (0) {};
+
+    ~Y2CCRuby () {
+       if (cruby)
+           delete cruby;
+    }
+
+    /**
+     * Returns true, since the Ruby component is a YaST2 server.
+     */
+    bool isServerCreator() const { return true; };
+
+    /**
+     * Creates a new Ruby component.
+     */
+    Y2Component *create( const char * name ) const
+    {
+       // create as many as requested, they all share the static YRuby anyway
+       if ( ! strcmp( name, "ruby") ) return new Y2RubyComponent();
+       else return 0;
+    }
+
+    /**
+     * always returns the same component, deletes it finally
+     */
+    Y2Component *provideNamespace (const char *name);
+
+};
+
+#endif // ifndef _Y2CCRuby_h
+
+
+// EOF
diff --git a/src/ruby/Y2RubyComponent.cc b/src/ruby/Y2RubyComponent.cc
new file mode 100644 (file)
index 0000000..c6dbcde
--- /dev/null
@@ -0,0 +1,75 @@
+/*---------------------------------------------------------------------\
+|                                                                      |
+|                      __   __    ____ _____ ____                      |
+|                      \ \ / /_ _/ ___|_   _|___ \                     |
+|                       \ V / _` \___ \ | |   __) |                    |
+|                        | | (_| |___) || |  / __/                     |
+|                        |_|\__,_|____/ |_| |_____|                    |
+|                                                                      |
+|                                                                      |
+| ruby language support                              (C) Novell Inc.   |
+\----------------------------------------------------------------------/
+
+Author: Duncan Mac-Vicar <dmacvicar@suse.de>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version
+2 of the License, or (at your option) any later version.
+
+*/
+
+#define y2log_component "Y2Ruby"
+#include <ycp/y2log.h>
+#include <ycp/pathsearch.h>
+
+#include "Y2RubyComponent.h"
+#include "YRuby.h"
+#include "YRubyNamespace.h"
+using std::string;
+
+
+Y2RubyComponent::Y2RubyComponent()
+{
+  // Actual creation of a Ruby interpreter is postponed until one of the
+  // YRuby static methods is used. They handle that.
+
+  y2milestone( "Creating Y2RubyComponent" );
+}
+
+
+Y2RubyComponent::~Y2RubyComponent()
+{
+  y2milestone( "Destroying Y2RubyComponent" );
+  YRuby::destroy();
+}
+
+
+void Y2RubyComponent::result( const YCPValue & )
+{}
+
+
+Y2Namespace *Y2RubyComponent::import (const char* name)
+{
+  y2milestone("Creating namespace for import '%s'", name);
+  // TODO where to look for it
+  // must be the same in Y2CCRuby and Y2RubyComponent
+  string module = YCPPathSearch::find (YCPPathSearch::Module, string (name) + ".rb");
+  if (module.empty ())
+  {
+    y2internal ("Couldn't find %s after Y2CCRuby pointed to us", name);
+    return NULL;
+  }
+  y2milestone("Found in '%s'", module.c_str());
+  module.erase (module.size () - 3 /* strlen (".pm") */);
+  YCPList args;
+  args->add (YCPString(/*module*/ name));
+  args->add (YCPString(/*module*/ module));
+  // load it
+  YRuby::loadModule (args);
+  y2milestone("Module '%s' loaded", name);
+  // introspect, create data structures for the interpreter
+  Y2Namespace *ns = new YRubyNamespace (name);
+
+  return ns;
+}
diff --git a/src/ruby/Y2RubyComponent.h b/src/ruby/Y2RubyComponent.h
new file mode 100644 (file)
index 0000000..a9826ec
--- /dev/null
@@ -0,0 +1,76 @@
+/*---------------------------------------------------------------------\
+|                                                                      |
+|                      __   __    ____ _____ ____                      |
+|                      \ \ / /_ _/ ___|_   _|___ \                     |
+|                       \ V / _` \___ \ | |   __) |                    |
+|                        | | (_| |___) || |  / __/                     |
+|                        |_|\__,_|____/ |_| |_____|                    |
+|                                                                      |
+|                                                                      |
+| ruby language support                              (C) Novell Inc.   |
+\----------------------------------------------------------------------/
+
+Author: Duncan Mac-Vicar <dmacvicar@suse.de>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version
+2 of the License, or (at your option) any later version.
+
+*/
+
+#ifndef Y2RubyComponent_h
+#define Y2RubyComponent_h
+
+#include "Y2.h"
+
+
+/**
+ * @short YaST2 Component: Ruby bindings
+ */
+class Y2RubyComponent : public Y2Component
+{
+public:
+    /**
+     * Constructor.
+     */
+    Y2RubyComponent();
+
+    /**
+     * Destructor.
+     */
+    ~Y2RubyComponent();
+
+    /**
+     * The name of this component.
+     */
+    string name() const { return "ruby"; }
+
+    /**
+     * Is called by the generic frontend when the session is finished.
+     */
+    void result( const YCPValue & result );
+
+    /**
+     * Implements the Ruby:: functions.
+     **/
+// not yet, prototype the transparent bindings first
+//    YCPValue evaluate( const YCPValue & val );
+
+    /**
+     * Try to import a given namespace. This method is used
+     * for transparent handling of namespaces (YCP modules)
+     * through whole YaST.
+     * @param name_space the name of the required namespace
+     * @return on errors, NULL should be returned. The
+     * error reporting must be done by the component itself
+     * (typically using y2log). On success, the method
+     * should return a proper instance of the imported namespace
+     * ready to be used. The returned instance is still owned
+     * by the component, any other part of YaST will try to
+     * free it. Thus, it's possible to share the instance.
+     */
+    Y2Namespace *import (const char* name);
+};
+
+#endif // Y2RubyComponent_h
diff --git a/src/ruby/Y2RubyTypeConv.cc b/src/ruby/Y2RubyTypeConv.cc
new file mode 100644 (file)
index 0000000..5e1c172
--- /dev/null
@@ -0,0 +1,203 @@
+/*---------------------------------------------------------------------\
+|                                                                      |
+|                      __   __    ____ _____ ____                      |
+|                      \ \ / /_ _/ ___|_   _|___ \                     |
+|                       \ V / _` \___ \ | |   __) |                    |
+|                        | | (_| |___) || |  / __/                     |
+|                        |_|\__,_|____/ |_| |_____|                    |
+|                                                                      |
+|                                                                      |
+| ruby language support                              (C) Novell Inc.   |
+\----------------------------------------------------------------------/
+
+Author: Duncan Mac-Vicar <dmacvicar@suse.de>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version
+2 of the License, or (at your option) any later version.
+
+*/
+
+#include <ycp/y2log.h>
+
+#include <ycp/YCPValue.h>
+#include <ycp/YCPBoolean.h>
+#include <ycp/YCPList.h>
+#include <ycp/YCPMap.h>
+#include <ycp/YCPString.h>
+#include <ycp/YCPInteger.h>
+#include <ycp/YCPTerm.h>
+#include <ycp/YCPFloat.h>
+#include <ycp/YCPElement.h>
+#include <ycp/YCPSymbol.h>
+#include <ycp/YCPPath.h>
+#include <ycp/YCPVoid.h>
+#include <ycp/Import.h>
+
+#include "Y2RubyTypePath.h"
+#include "Y2RubyTypeTerm.h"
+
+#include "Y2RubyTypeConv.h"
+
+#define IS_A(obj,klass) ((rb_obj_is_kind_of((obj),(klass))==Qtrue)?1:0)
+
+static YCPMap rbhash_2_ycpmap( VALUE value )
+{
+  YCPMap map;
+  VALUE keys = rb_funcall(value, rb_intern("keys"), 0);
+  int n = NUM2LONG(rb_funcall(keys, rb_intern("size"), 0));
+  for ( int i=0; i<n; ++i)
+  {
+    VALUE rkey = rb_funcall(keys, rb_intern("at"), 1, i);
+    YCPValue ykey = rbvalue_2_ycpvalue(rkey);
+    YCPValue yvalue = rbvalue_2_ycpvalue( rb_funcall(value, rb_intern("[]"), 1, rkey) );
+    map.add(ykey, yvalue);
+  }
+  return map;
+}
+
+static YCPList rbarray_2_ycplist( VALUE value )
+{
+  YCPList list;
+  int n = NUM2LONG(rb_funcall(value, rb_intern("size"), 0));
+  for ( int i=0; i<n; ++i)
+  {
+    VALUE element = rb_funcall(value, rb_intern("[]"), 1, i);
+    list.add( rbvalue_2_ycpvalue(element) );
+  }
+  return list;
+}
+
+
+/**
+ * Converts a YCPValue into a Ruby Value
+ * Supports neested lists using recursion.
+ */
+extern "C" VALUE
+ycpvalue_2_rbvalue( YCPValue ycpval )
+{
+  // TODO
+  // YT_BYTEBLOCK YT_PATH YT_SYMBOL YT_LIST YT_TERM YT_MAP YT_CODE YT_RETURN YT_BREAK YT_ENTRY YT_ERROR  YT_REFERENCE YT_EXTERNA
+  if (ycpval->isVoid())
+  {
+    return Qnil;
+  }
+  else if (ycpval->isBoolean())
+  {
+    return ycpval->asBoolean()->value() ? Qtrue : Qfalse;
+  }
+  else if (ycpval->isString())
+  {
+    return rb_str_new2(ycpval->asString()->value().c_str());
+  }
+  else if (ycpval->isPath())
+  {
+    // FIXME implement a ruby class for YCPPath
+    return rb_str_new2(ycpval->asPath()->asString()->value().c_str());
+  }
+  else if (ycpval->isTerm())
+  {
+    return ryast_rterm_from_yterm(ycpval->asTerm());
+  }
+  else if (ycpval->isInteger())
+  {
+    return INT2NUM( ycpval->asInteger()->value() );
+  }
+  else if (ycpval->isFloat())
+  {
+    return rb_float_new(ycpval->asFloat()->value());
+  }
+  else if ( ycpval->isMap() )
+  {
+    VALUE rbhash;
+    rbhash = rb_hash_new();
+    YCPMap map = ycpval->asMap();
+    y2internal("map size %d\n", (int) map.size());
+
+    for ( YCPMapIterator it = map.begin(); it != map.end(); ++it )
+    {
+      YCPValue key = it.key();
+      YCPValue value = it.value();
+      rb_hash_aset(rbhash, ycpvalue_2_rbvalue(key), ycpvalue_2_rbvalue(value) );
+    }
+    return rbhash;
+  }
+  else if (ycpval->isList())
+  {
+    VALUE rblist;
+    rblist = rb_ary_new();
+    YCPList list = ycpval->asList();
+    y2internal("list size %d\n",list.size());
+    for (int i=0; i < list.size(); i++)
+    {
+      rb_ary_push( rblist, ycpvalue_2_rbvalue(list.value(i)));
+    }
+    return rblist;
+  }
+  else if (ycpval->isSymbol())
+  {
+    YCPSymbol symbol = ycpval->asSymbol();
+    return rb_intern(symbol->symbol_cstr());
+  }
+  rb_raise( rb_eTypeError, "Conversion of YCP type %s not supported", ycpval->toString().c_str() );
+  return Qnil;
+}
+
+// isEmpty size add remove (value n) toString
+YCPValue
+rbvalue_2_ycpvalue( VALUE value )
+{
+  VALUE klass = rb_funcall( value, rb_intern("class"), 0);
+  //std::cout << RSTRING( rb_funcall( klass, rb_intern("to_s"), 0))->ptr << " | " << RSTRING(rb_funcall( value, rb_intern("inspect"), 0))->ptr << std::endl;
+  //y2internal("type: '%d'", TYPE(value));
+  // TODO conver integers, and add support for lists, ah, and boleans!
+  switch (TYPE(value))
+  {
+  case T_NIL:
+    return YCPVoid();
+  case T_STRING:
+    return YCPString(RSTRING (value)->ptr);
+    break;
+  case T_TRUE:
+    return YCPBoolean(true);
+    break;
+  case T_FALSE:
+    return YCPBoolean(false);
+    break;
+  case T_FIXNUM:
+    return YCPInteger(NUM2LONG(value));
+    break;
+  case T_FLOAT:
+    return YCPFloat(NUM2DBL(value));
+    break;
+  case T_ARRAY:
+    return rbarray_2_ycplist(value);
+    break;
+  case T_HASH:
+    return rbhash_2_ycpmap(value);
+    break;
+  case T_SYMBOL:
+    return YCPSymbol(rb_id2name(rb_to_id(value)));
+  //case T_DATA:
+  //  rb_raise( rb_eRuntimeError, "Object");
+    break;
+  default:
+    string class_name(RSTRING(rb_funcall(rb_funcall(value, rb_intern("class"), 0), rb_intern("to_s"), 0))->ptr);
+    /* get the Term class object */
+    if ( class_name == "YaST::Term" )
+    {
+      return ryast_yterm_from_rterm(value);
+    }
+    rb_raise( rb_eTypeError, "Conversion of Ruby type not supported");
+    return YCPValue();
+  }
+}
+
+YCPValue
+rbvalue_2_ycppath( VALUE value )
+{
+  VALUE stringrep = rb_funcall(value, rb_intern("to_s"), 0);
+  return  YCPPath(RSTRING(stringrep)->ptr);
+}
+
diff --git a/src/ruby/Y2RubyTypeConv.h b/src/ruby/Y2RubyTypeConv.h
new file mode 100644 (file)
index 0000000..d440d98
--- /dev/null
@@ -0,0 +1,50 @@
+/*---------------------------------------------------------------------\
+|                                                                      |
+|                      __   __    ____ _____ ____                      |
+|                      \ \ / /_ _/ ___|_   _|___ \                     |
+|                       \ V / _` \___ \ | |   __) |                    |
+|                        | | (_| |___) || |  / __/                     |
+|                        |_|\__,_|____/ |_| |_____|                    |
+|                                                                      |
+|                                                                      |
+| ruby language support                              (C) Novell Inc.   |
+\----------------------------------------------------------------------/
+
+Author: Duncan Mac-Vicar <dmacvicar@suse.de>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version
+2 of the License, or (at your option) any later version.
+
+*/
+
+#ifndef Y2RUBYTYPECONV_H
+#define Y2RUBYTYPECONV_H
+
+#include <ycp/YCPValue.h>
+#include "ruby.h"
+
+/**
+ * Converts a YCPValue into a Ruby Value
+ * Supports neested lists and maps using recursion.
+ */
+extern "C" VALUE
+ycpvalue_2_rbvalue( YCPValue ycpval );
+
+/**
+ * Converts a Ruby Value into a YCPValue
+ * Supports neested lists and maps using recursion.
+ */
+YCPValue
+rbvalue_2_ycpvalue( VALUE value );
+
+
+/**
+ * excplicitly create a YCPPath from a Ruby value
+ */
+YCPValue
+rbvalue_2_ycppath( VALUE value );
+
+#endif
+
diff --git a/src/ruby/Y2RubyTypePath.cc b/src/ruby/Y2RubyTypePath.cc
new file mode 100644 (file)
index 0000000..78bfad1
--- /dev/null
@@ -0,0 +1,75 @@
+#include "YRuby.h"
+#include "Y2RubyTypePath.h"
+#include <ycp/YCPPath.h>
+
+static VALUE ryast_cPath;
+
+struct ryast_Path_Wrapper
+{
+  YCPPath path;
+};
+
+//-----------------------------------------------------------------------------
+// internal used only
+
+static void
+ryast_path_set_path( VALUE self, const YCPPath & path )
+{
+  ryast_Path_Wrapper *wrapper;
+  Data_Get_Struct(self, ryast_Path_Wrapper, wrapper);
+  wrapper->path = path;
+}
+
+static void
+ryast_path_mark (ryast_Path_Wrapper *r)
+{
+
+}
+
+static void
+ryast_path_free (ryast_Path_Wrapper *r)
+{
+  delete r;
+}
+
+static VALUE
+ryast_path_initialize( int argc, VALUE *argv, VALUE self )
+{
+    ryast_Path_Wrapper *wrapper;
+    Data_Get_Struct(self, ryast_Path_Wrapper, wrapper);
+    
+    // we should be using rb_scan_args here but I couldn't get it to work.
+
+//     if (argc > 0) {
+//   Check_Type( argv[0], T_STRING);
+//   version = StringValuePtr( argv[0] );
+//     }
+//     if (argc > 1) {
+//   Check_Type( argv[1], T_STRING);
+//   release = StringValuePtr( argv[1] );
+//     }
+//     if (argc > 2) {
+//   Check_Type( argv[2], T_FIXNUM);
+//   epoch = FIX2INT( argv[2] );
+//     }
+    wrapper->path = YCPPath();
+    return self;
+}
+
+
+static VALUE
+ryast_path_allocate(VALUE klass)
+{
+  // create struct
+  ryast_Path_Wrapper *wrapper = new ryast_Path_Wrapper();
+  // wrap and return struct
+  return Data_Wrap_Struct (klass, ryast_path_mark, ryast_path_free, wrapper);
+}
+
+void
+ryast_path_init( VALUE super )
+{
+  ryast_cPath = rb_define_class_under( super, "Path", rb_cObject );
+  rb_define_alloc_func( ryast_cPath, ryast_path_allocate );
+  rb_define_method( ryast_cPath, "initialize", RB_METHOD( ryast_path_initialize ), -1 );
+}
diff --git a/src/ruby/Y2RubyTypePath.h b/src/ruby/Y2RubyTypePath.h
new file mode 100644 (file)
index 0000000..5e33aaf
--- /dev/null
@@ -0,0 +1,30 @@
+/*---------------------------------------------------------------------\
+|                                                                      |
+|                      __   __    ____ _____ ____                      |
+|                      \ \ / /_ _/ ___|_   _|___ \                     |
+|                       \ V / _` \___ \ | |   __) |                    |
+|                        | | (_| |___) || |  / __/                     |
+|                        |_|\__,_|____/ |_| |_____|                    |
+|                                                                      |
+|                                                                      |
+| ruby language support                              (C) Novell Inc.   |
+\----------------------------------------------------------------------/
+
+Author: Duncan Mac-Vicar <dmacvicar@suse.de>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version
+2 of the License, or (at your option) any later version.
+
+*/
+
+#ifndef Y2RUBY_TYPE_PATH_H
+#define Y2RUBY_TYPE_PATH_H
+
+#include <ruby.h>
+
+void ryast_path_init( VALUE super );
+
+#endif
+
diff --git a/src/ruby/Y2RubyTypeTerm.cc b/src/ruby/Y2RubyTypeTerm.cc
new file mode 100644 (file)
index 0000000..8518d07
--- /dev/null
@@ -0,0 +1,171 @@
+/*---------------------------------------------------------------------\
+|                                                                      |
+|                      __   __    ____ _____ ____                      |
+|                      \ \ / /_ _/ ___|_   _|___ \                     |
+|                       \ V / _` \___ \ | |   __) |                    |
+|                        | | (_| |___) || |  / __/                     |
+|                        |_|\__,_|____/ |_| |_____|                    |
+|                                                                      |
+|                                                                      |
+| ruby language support                              (C) Novell Inc.   |
+\----------------------------------------------------------------------/
+
+Author: Duncan Mac-Vicar <dmacvicar@suse.de>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version
+2 of the License, or (at your option) any later version.
+
+*/
+
+#include "YRuby.h"
+#include "Y2RubyTypeConv.h"
+#include "Y2RubyTypeTerm.h"
+#include <ycp/YCPTerm.h>
+
+static VALUE ryast_cTerm;
+
+struct ryast_Term_Wrapper
+{
+  ryast_Term_Wrapper()
+    : term("init-me")
+  {
+
+  }
+  YCPTerm term;
+};
+
+//-----------------------------------------------------------------------------
+// internal used only
+
+static void
+ryast_term_set_term( VALUE self, const YCPTerm & term )
+{
+  ryast_Term_Wrapper *wrapper;
+  Data_Get_Struct(self, ryast_Term_Wrapper, wrapper);
+  wrapper->term = term;
+}
+
+VALUE
+ryast_rterm_from_yterm( const YCPTerm &term )
+{
+  VALUE rterm_obj = rb_funcall( ryast_cTerm, rb_intern("new"), 0);
+  ryast_term_set_term( rterm_obj, term );
+  return rterm_obj;
+}
+
+YCPTerm
+ryast_yterm_from_rterm( VALUE term )
+{
+  ryast_Term_Wrapper *wrapper;
+  Data_Get_Struct(term, ryast_Term_Wrapper, wrapper);
+  return wrapper->term;
+}
+
+static void
+ryast_term_mark (ryast_Term_Wrapper *r)
+{
+
+}
+
+static void
+ryast_term_free (ryast_Term_Wrapper *r)
+{
+  delete r;
+}
+
+static VALUE
+ryast_term_initialize( int argc, VALUE *argv, VALUE self )
+{
+    ryast_Term_Wrapper *wrapper;
+    Data_Get_Struct(self, ryast_Term_Wrapper, wrapper);
+    
+    // we should be using rb_scan_args here but I couldn't get it to work.
+
+    // we need at least the name to create a YCPTerm
+    Check_Type( argv[0], T_STRING);
+    wrapper->term = YCPTerm( RSTRING(argv[0])->ptr );
+    // add the remaining YCPTerm arguments
+    if (argc > 1)
+    {
+      int i=1;
+      for ( ; i<argc; ++i )
+      {
+        wrapper->term->add(rbvalue_2_ycpvalue(argv[i]));
+      }
+    }
+    return self;
+}
+
+static VALUE
+ryast_term_add( int argc, VALUE *argv, VALUE self )
+{
+    ryast_Term_Wrapper *wrapper;
+    Data_Get_Struct(self, ryast_Term_Wrapper, wrapper);
+    
+    // we should be using rb_scan_args here but I couldn't get it to work.
+
+    // add the remaining YCPTerm arguments
+    if (argc > 0)
+    {
+      int i=0;
+      for ( ; i<argc; ++i )
+      {
+        //std::cout << "Hi! term::add: " << TYPE(argv[i]) << std::endl;
+        YCPValue value = rbvalue_2_ycpvalue(argv[i]);
+        wrapper->term->add(value);
+      }
+    }
+    return self;
+}
+
+static VALUE
+ryast_term_allocate(VALUE klass)
+{
+  // create struct
+  ryast_Term_Wrapper *wrapper = new ryast_Term_Wrapper();
+  // wrap and return struct
+  return Data_Wrap_Struct (klass, ryast_term_mark, ryast_term_free, wrapper);
+}
+
+
+static VALUE
+ryast_term_to_s(VALUE self)
+{
+  ryast_Term_Wrapper *wrapper;
+  Data_Get_Struct(self, ryast_Term_Wrapper, wrapper);
+  return rb_str_new2(wrapper->term.toString().c_str());
+}
+
+static VALUE
+ryast_term_name(VALUE self)
+{
+  ryast_Term_Wrapper *wrapper;
+  Data_Get_Struct(self, ryast_Term_Wrapper, wrapper);
+  return rb_str_new2(wrapper->term.name().c_str());
+}
+
+// static VALUE
+// ryast_term_set_name(VALUE self, VALUE name)
+// {
+//   Check_Type(name, T_STRING);
+//   ryast_Term_Wrapper *wrapper;
+//   Data_Get_Struct(self, ryast_Term_Wrapper, wrapper);
+//   
+//   wrapper->term->setName(RSTRING(name)->ptr);
+//   return self;
+// }
+
+void
+ryast_term_init( VALUE super )
+{
+  //std::cout << "Hi! term" << std::endl;
+  ryast_cTerm = rb_define_class_under( super, "Term", rb_cObject );
+  rb_define_alloc_func( ryast_cTerm, ryast_term_allocate );
+  rb_define_method( ryast_cTerm, "initialize", RB_METHOD( ryast_term_initialize ), -1 );
+  rb_define_method( ryast_cTerm, "add", RB_METHOD( ryast_term_add ), -1 );
+  //rb_define_method( ryast_cTerm, "name=", RB_METHOD( ryast_term_set_name ), 1 );
+  rb_define_method( ryast_cTerm, "name", RB_METHOD( ryast_term_name ), 0 );
+  rb_define_method( ryast_cTerm, "to_s", RB_METHOD( ryast_term_to_s ), 0 );
+}
diff --git a/src/ruby/Y2RubyTypeTerm.h b/src/ruby/Y2RubyTypeTerm.h
new file mode 100644 (file)
index 0000000..ecf54c4
--- /dev/null
@@ -0,0 +1,36 @@
+/*---------------------------------------------------------------------\
+|                                                                      |
+|                      __   __    ____ _____ ____                      |
+|                      \ \ / /_ _/ ___|_   _|___ \                     |
+|                       \ V / _` \___ \ | |   __) |                    |
+|                        | | (_| |___) || |  / __/                     |
+|                        |_|\__,_|____/ |_| |_____|                    |
+|                                                                      |
+|                                                                      |
+| ruby language support                              (C) Novell Inc.   |
+\----------------------------------------------------------------------/
+
+Author: Duncan Mac-Vicar <dmacvicar@suse.de>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version
+2 of the License, or (at your option) any later version.
+
+*/
+
+#ifndef Y2RUBY_TYPE_TERM_H
+#define Y2RUBY_TYPE_TERM_H
+
+#include <ruby.h>
+
+void ryast_term_init( VALUE super );
+
+VALUE
+ryast_rterm_from_yterm( const YCPTerm &term );
+
+YCPTerm
+ryast_yterm_from_rterm( VALUE term );
+
+#endif
+
diff --git a/src/ruby/YCP.cc b/src/ruby/YCP.cc
new file mode 100644 (file)
index 0000000..a2188df
--- /dev/null
@@ -0,0 +1,473 @@
+/*---------------------------------------------------------------------\
+|                                                                      |
+|                      __   __    ____ _____ ____                      |
+|                      \ \ / /_ _/ ___|_   _|___ \                     |
+|                       \ V / _` \___ \ | |   __) |                    |
+|                        | | (_| |___) || |  / __/                     |
+|                        |_|\__,_|____/ |_| |_____|                    |
+|                                                                      |
+|                                                                      |
+| ruby language support                              (C) Novell Inc.   |
+\----------------------------------------------------------------------/
+
+Author: Duncan Mac-Vicar <dmacvicar@suse.de>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version
+2 of the License, or (at your option) any later version.
+
+*/
+
+// #include <y2/Y2Namespace.h>
+// #include <y2/Y2Component.h>
+#include <y2/Y2ComponentCreator.h>
+
+#include <yui/YUIComponent.h>
+#include <wfm/Y2WFMComponent.h>
+
+#include <y2/Y2ComponentBroker.h>
+#include <y2/Y2Namespace.h>
+#include <y2/Y2Component.h>
+#include <y2/Y2Function.h>
+
+#include <ycp/pathsearch.h>
+#include <ycp/y2log.h>
+#include <ycp/YBlock.h>
+#include <ycp/YExpression.h>
+#include <ycp/YStatement.h>
+#include <ycp/YCPValue.h>
+#include <ycp/YCPBoolean.h>
+#include <ycp/YCPList.h>
+#include <ycp/YCPMap.h>
+#include <ycp/YCPString.h>
+#include <ycp/YCPInteger.h>
+#include <ycp/YCPFloat.h>
+#include <ycp/YCPElement.h>
+#include <ycp/Import.h>
+#include <ycp/y2log.h>
+
+#include "ruby.h"
+
+//#include "YRuby.h"
+#include "RubyLogger.h"
+#include "Y2RubyTypePath.h"
+#include "Y2RubyTypeTerm.h"
+#include "Y2RubyTypeConv.h"
+#include "YRuby.h"
+
+static VALUE rb_mYaST;
+static VALUE rb_mUi;
+static VALUE rb_cBroker;
+
+// make the compiler happy when
+// calling rb_define_method()
+typedef VALUE (ruby_method)(...);
+
+// more useful macros
+#define RB_FINALIZER(func) ((void (*)(...))func)
+
+// this macro saves us from typing
+// (ruby_method*) & method_name
+// in rb_define_method
+#define RB_METHOD(func) ((VALUE (*)(...))func)
+
+Y2Component *owned_uic = 0;
+Y2Component *owned_wfmc = 0;
+
+static
+Y2Namespace *
+getNs (const char * ns_name, const char * func_name)
+{
+  Import import(ns_name);  // has a static cache
+  Y2Namespace *ns = import.nameSpace();
+  if (ns == NULL)
+  {
+    y2error ("... for a Ruby call of %s", func_name);
+  }
+  else
+  {
+    ns->initialize ();
+  }
+  return ns;
+}
+
+void init_wfm()
+{
+    y2milestone("init_wfm");
+//     if (Y2WFMComponent::instance () == 0)
+//     {
+      y2milestone("WFM init");
+      owned_wfmc = Y2ComponentBroker::createClient ("wfm");
+      if (owned_wfmc == 0)
+      {
+          y2error ("Cannot create WFM component");
+      }
+//     }
+}
+
+static VALUE
+rb_init_ui( int argc, VALUE *argv, VALUE self )
+{
+  const char *ui_name = "ncurses";
+
+  if (argc == 1)
+  {
+    Check_Type(argv[0], T_STRING);
+    ui_name = RSTRING(argv[0])->ptr;
+  }
+  else if (argc != 0)
+  {
+    y2error ("Zero or one arguments required (ui name, default %s", ui_name);
+    return Qnil;
+  }
+
+  Y2Component *c = YUIComponent::uiComponent ();
+  if (c == 0)
+  {
+    y2debug ("UI component not created yet, creating %s", ui_name);
+
+    c = Y2ComponentBroker::createServer (ui_name);
+    if (c == 0)
+    {
+      y2error ("Cannot create component %s", ui_name);
+      return Qnil;
+    }
+
+    if (YUIComponent::uiComponent () == 0)
+    {
+      y2error ("Component %s is not a UI", ui_name);
+      return Qnil;
+    }
+    else
+    {
+      // got it - initialize, remember
+      c->setServerOptions (0, NULL);
+      owned_uic = c;
+    }
+  }
+  else
+  {
+    y2debug ("UI component already present: %s", c->name ().c_str ());
+  }
+  return Qnil;
+}
+
+typedef struct brokerinfo
+{
+  Y2Component *component;
+  Y2Namespace *ns;
+}
+BrokerInfo;
+
+//  mark for garbage collection
+// NOT USED, ONLY WHEN USING DATA_WRAP_STRUCT
+void
+yast_module_mark( BrokerInfo *info )
+{}
+
+// dispose a namespace
+// NOT USED, ONLY WHEN USING DATA_WRAP_STRUCT
+void
+yast_module_free( BrokerInfo *info )
+{
+  //std::cout << "Deleting namespace." << std::endl;
+  //delete info;
+}
+
+VALUE
+yast_module_allocate( VALUE klass )
+{
+  BrokerInfo *info = new BrokerInfo;
+  return Data_Wrap_Struct( klass, yast_module_mark, RB_FINALIZER(yast_module_free), info );
+}
+
+VALUE
+yast_module_initialize( VALUE self, VALUE param_ns_name )
+{
+  Check_Type(param_ns_name, T_STRING);
+  rb_iv_set(self, "@namespace_name", param_ns_name);
+  init_wfm();
+  return self;
+}
+
+
+VALUE
+yast_module_name( VALUE self )
+{
+  return rb_iv_get(self, "@namespace_name");
+}
+
+VALUE
+yast_module_methods( VALUE self )
+{
+  return Qnil;
+}
+
+//forward declaration
+YCPValue ycp_call_builtin ( const string &module_name, const string &func_name, int argc, VALUE *argv );
+
+VALUE
+yast_module_proxy_method( int argc, VALUE *argv, VALUE self )
+{
+  VALUE symbol = argv[0];
+  VALUE namespace_name = rb_iv_get(self, "@namespace_name");
+
+  // the func name (1st argument, is a symbol
+  // lets convert it to string
+  VALUE symbol_str = rb_funcall(symbol, rb_intern("to_s"), 0);
+  y2internal("Dynamic Proxy: [%s::%s] with [%d] params\n", RSTRING(namespace_name)->ptr, RSTRING(symbol_str)->ptr, argc);
+
+  //Check_Type(argv[0], T_STRING);
+  //y2internal(RSTRING (symbol)->ptr);
+  //Data_Get_Struct( self, class Y2Namespace, ns );
+  //ns = gNameSpaces[self];
+
+  // get the name of the module
+  //VALUE namespace_name = rb_funcall(self, rb_intern("name"), 0);
+  
+
+  // SCR
+  // this is a hack before the builtin namespaces get a uniform interface:
+  if (! strcmp (RSTRING (namespace_name)->ptr, "SCR"))
+  {
+    y2internal("Dynamic Proxy: [%s::%s] is a call to SCR\n", RSTRING(namespace_name)->ptr, RSTRING(symbol_str)->ptr);
+    YCPValue res;
+    res = ycp_call_builtin( RSTRING(namespace_name)->ptr, RSTRING(symbol_str)->ptr, argc, argv);
+    return ycpvalue_2_rbvalue(res);
+    //return Qnil;
+  }
+
+  Y2Component *c;
+  c = Y2ComponentBroker::getNamespaceComponent(RSTRING (namespace_name)->ptr);
+  if (c == NULL)
+  {
+    y2internal("No component can provide namespace %s\n", RSTRING (namespace_name)->ptr);
+    rb_raise( rb_eRuntimeError, "No component can provide namespace %s\n", RSTRING (namespace_name)->ptr);
+    return 2;
+  }
+  y2internal("component name %s\n", c->name().c_str());
+
+  // import the namespace
+  //Y2Namespace *ns = c->import(RSTRING (namespace_name)->ptr);
+  Y2Namespace *ns = getNs( RSTRING (namespace_name)->ptr, RSTRING(symbol_str)->ptr);
+  if (ns == NULL)
+  {
+    rb_raise( rb_eRuntimeError, "Component cannot import namespace %s", RSTRING (namespace_name)->ptr );
+    return Qnil;
+  }
+  else
+  {
+    y2internal("Namespace created from %s\n", ns->filename().c_str());
+  }
+  // ensure it is an initialized namespace
+  //ns->initialize ();
+  y2internal("Namespace %s initialized\n", RSTRING (namespace_name)->ptr);
+
+  TableEntry *sym_te = ns->table()->find (RSTRING(symbol_str)->ptr);
+
+  if (sym_te == NULL)
+  {
+    y2error ("No such symbol %s::%s", RSTRING (namespace_name)->ptr, RSTRING(symbol_str)->ptr);
+    rb_raise( rb_eNameError, "YCP symbol '%s' not found in namespace '%s'", RSTRING(symbol_str)->ptr, RSTRING (namespace_name)->ptr );
+    return Qnil;
+  }
+
+  if (sym_te->sentry ()->isVariable () ||
+      sym_te->sentry ()->isReference ())
+  {
+    // set the variable
+    //ret_yv = YCP_getset_variable (aTHX_ ns_name, sym_te->sentry (), args);
+  }
+  else
+  { // no indent yet
+    Y2Function* call = ns->createFunctionCall (RSTRING(symbol_str)->ptr, 0 /*Type::fromSignature("list<string>()")*/);
+
+    if (call == NULL)
+    {
+      y2internal ("Cannot create function call %s\n", RSTRING(symbol_str)->ptr);
+      return 4;
+    }
+
+    // add the parameters
+    for (int i=1; i<argc; i++)
+    {
+      VALUE arg = argv[i];
+      YCPValue v = rbvalue_2_ycpvalue(arg);
+      //y2internal ("Appending parameter %s\n", v->toString());
+      call->appendParameter (v);
+    }
+    call->finishParameters ();
+
+    YCPValue res = call->evaluateCall ();
+    delete call;
+    y2internal ("Call succeded\n");
+    //y2internal ("Result: %i\n", res->asList()->size());
+    return ycpvalue_2_rbvalue(res);
+  }
+  return Qnil;
+}
+
+YCPValue ycp_call_builtin ( const string &module_name, const string &func_name, int argc, VALUE *argv )
+{
+  // access directly the statically declared builtins
+  extern StaticDeclaration static_declarations;
+  string qualified_name_s = module_name + "::" + func_name;
+  const char *qualified_name = qualified_name_s.c_str ();
+
+  y2milestone("Qualified name '%s'", qualified_name);
+  /*
+    this does not work across namespaces
+      TableEntry *bi_te = static_declarations.symbolTable ()->find (qualified_name);
+      if (bi_te == NULL)
+      {
+    y2error ("No such builtin %s",qualified_name);
+    return YCPNull ();
+      }
+  
+      SymbolEntry *bi_se = bi_te->sentry ();
+      assert (bi_se != NULL);
+      assert (bi_se->isBuiltin ());
+      declaration_t bi_dt = bi_se->declaration ();
+  */
+  declaration_t *bi_dt = static_declarations.findDeclaration (qualified_name);
+  if (bi_dt == NULL)
+  {
+    y2error ("No such builtin '%s'", qualified_name);
+    return YCPNull ();
+  }
+  y2milestone("builtin '%s' found.", module_name.c_str());
+  // construct a builtin call using the proper overloaded builtin
+  YEBuiltin *bi_call = new YEBuiltin (bi_dt);
+
+  // attach the parameters:
+
+  // we would like to know the destination type so that we could
+  // convert eg a Ruby scalar to a YCP symbol, but because the
+  // builtins may be overloaded, let's say we want Any
+
+  // maybe a special exceptional hack to make Path for the 1st argument?
+
+  // go through the actual parameters
+  int j;
+  for (j = 1; j < argc; ++j)
+  {
+    y2milestone("builtin param '%d'", j-1);
+    // convert the value according to the expected type:
+    constTypePtr param_tp = (j == 0)? Type::Path : Type::Any;
+    y2milestone("builtin param '%d' 1", j-1);
+    
+    // convert the first argument to a path
+    YCPValue param_v;
+    if ( j == 1 )
+      param_v = rbvalue_2_ycppath(argv[j]);
+    else
+      param_v = rbvalue_2_ycpvalue(argv[j] /*, param_tp */);
+    
+    y2milestone("builtin param '%d' 2", j-1);
+    if (param_v.isNull ())
+    {
+      // an error has already been reported, now refine it.
+      // Can't know parameter name?
+      y2error ("... when passing parameter #%u to builtin %s",
+        j, qualified_name);
+      return YCPNull ();
+    }
+    // Such YConsts without a specific type produce invalid
+    // bytecode. (Which is OK here)
+    // The actual parameter's YCode becomes owned by the function call?
+    YConst *param_c = new YConst (YCode::ycConstant, param_v);
+    // for attaching the parameter, must get the real type so that it matches
+    constTypePtr act_param_tp = Type::vt2type (param_v->valuetype ());
+    // Attach the parameter
+    // Returns NULL if OK, Type::Error if excessive argument
+    // Other errors (bad code, bad type) shouldn't happen
+    constTypePtr err_tp = bi_call->attachParameter (param_c, act_param_tp);
+    if (err_tp != NULL)
+    {
+        if (err_tp->isError ())
+        {
+          // TODO really need to know the place in Ruby code
+          // where we were called from.
+          y2error ("Excessive parameter to builtin %s", qualified_name);
+        }
+        else
+        {
+          y2internal ("attachParameter returned %s", err_tp->toString ().c_str ());
+        }
+        return YCPNull ();
+    }
+  } // for each actual parameter
+
+  // now must check if we got fewer parameters than needed
+  // or there was another error while resolving the overload
+  constTypePtr err_tp = bi_call->finalize (RubyLogger::instance ());
+  if (err_tp != NULL)
+  {
+    // apparently the error was already reported?
+    y2error ("Error type %s when finalizing builtin %s",
+    err_tp->toString ().c_str (), qualified_name);
+    return YCPNull ();
+  }
+
+  // go call it now!
+  y2debug ("Ruby is calling builtin %s", qualified_name);
+  YCPValue ret_yv = bi_call->evaluate (false /* no const subexpr elim */);
+  delete bi_call;
+
+  return ret_yv;
+}
+
+
+
+//y2_logger_helper
+
+//y2_logger (level, comp, file, line, function, "%s", message);
+
+static VALUE
+rb_y2_logger( int argc, VALUE *argv, VALUE self )
+{
+  Check_Type(argv[0], T_FIXNUM);
+  Check_Type(argv[1], T_STRING);
+  Check_Type(argv[2], T_STRING);
+  Check_Type(argv[3], T_FIXNUM);
+  Check_Type(argv[4], T_STRING);
+
+  int i;
+  for ( i = 5; i < argc; i++)
+  {
+    Check_Type(argv[i], T_STRING);
+  }
+  y2_logger((loglevel_t)NUM2INT(argv[0]),RSTRING(argv[1])->ptr,RSTRING(argv[2])->ptr,NUM2INT(argv[3]),"",RSTRING(argv[5])->ptr);
+  return Qnil;
+}
+
+extern "C"
+{
+  void
+  Init_yastx()
+  {
+    YCPPathSearch::initialize ();
+
+    for ( list<string>::const_iterator it = YCPPathSearch::searchListBegin (YCPPathSearch::Module); it != YCPPathSearch::searchListEnd (YCPPathSearch::Module) ; ++it )
+    {
+      y2internal("%s\n", (*it).c_str() );
+    }
+
+    rb_mYaST = rb_define_module("YaST");
+    rb_mUi = rb_define_module_under(rb_mYaST, "Ui");
+    rb_define_singleton_method( rb_mUi, "init", RB_METHOD(rb_init_ui), -1);
+    
+    rb_define_method( rb_mYaST, "y2_logger", RB_METHOD(rb_y2_logger), -1);
+    
+    rb_cBroker = rb_define_class_under( rb_mYaST, "Module", rb_cObject);
+    //rb_define_singleton_method( rb_cBroker, "new", RB_METHOD(module_new), 1);
+    rb_define_alloc_func(rb_cBroker, yast_module_allocate);
+    rb_define_method(rb_cBroker, "initialize", RB_METHOD(yast_module_initialize), 1);
+    rb_define_method( rb_cBroker, "method_missing", RB_METHOD(yast_module_proxy_method), -1);
+    rb_define_method( rb_cBroker, "name", RB_METHOD(yast_module_name), -1);
+
+    ryast_path_init(rb_mYaST);
+    ryast_term_init(rb_mYaST);
+  }
+}
diff --git a/src/ruby/YCP.rb b/src/ruby/YCP.rb
new file mode 100644 (file)
index 0000000..fb820b5
--- /dev/null
@@ -0,0 +1,520 @@
+#! /usr/bin/perl -w
+# Martin Vidner
+# $Id: YCP.pm 33405 2006-10-13 13:12:42Z mvidner $
+
+=head1 NAME
+
+YaST::YCP - a binary interface between Perl and YCP
+
+=head1 SYNOPSIS
+
+ use YaST::YCP qw(:DATA :LOGGING);
+
+ YaST::YCP::Import ("SCR");
+ my $m = SCR->Read (".sysconfig.displaymanager.DISPLAYMANAGER");
+ SCR->Write (".sysconfig.kernel.CRASH_OFTEN", Boolean (1));
+
+=head1 DATA TYPES
+
+YaST has a richer and stricter data type system than Perl.
+
+Note that the stdio-communicating agents, based on the modules
+L<YaST::SCRAgent|YaST::SCRAgent> and L<ycp|ycp>, have a similar but
+not the same data type mapping.
+
+When the language binding knows what type to expect, eg. when passing
+an argument to a YCP function, it will convert a Perl scalar to the
+desired type.
+
+On the other hand, if the type is not known, expressed
+in YCP as C<any>, scalars will be passed as strings. If you want
+a specific data type, use one of the data classes like
+L<YaST::YCP::Integer|/Integer>. Of course these work also when
+the type is known.
+
+=over 4
+
+=item void
+
+Has only one value, C<nil>, which is represented as C<undef>.
+Any data type can have C<nil> as a value.
+
+=item any
+
+A union of all data types. Any data type can be assigned to it.
+
+=item string, integer, float, boolean
+
+B<YCP to Perl:> Becomes a scalar
+
+B<Perl to YCP:> Any scalar will become a string
+(even if it looks like a number).
+Use L</String>, L</Integer>, L</Float> or L</Boolean>
+if you want a specific data type.
+
+=item list E<lt>TE<gt>
+
+B<YCP to Perl:> A list becomes a reference to an array.
+(Note that it refers to a B<copy>.)
+
+B<Perl to YCP:> A reference to an array becomes a list.
+I<This was different before SL9.1 Beta1:>
+Perl functions returning multiple values should not return a list
+but a reference to it. YCP will always set a scalar calling context,
+even if the result is assigned to a list.
+
+=item map E<lt>T1, T2E<gt>
+
+B<YCP to Perl:> A map becomes a reference to a hash.
+(Note that it refers to a B<copy>.)
+
+B<Perl to YCP:> A reference to a hash becomes a map.
+
+=item path
+
+B<YCP to Perl:> NOT IMPLEMENTED YET.
+
+B<Perl to YCP:> If a path is expected, a scalar like C<".foo.bar">
+will be converted to C<.foo.bar>.
+Otherwise use L</Path> (which is NOT IMPLEMENTED YET).
+
+=item symbol
+
+B<YCP to Perl:> Becomes a L</Symbol>.
+
+B<Perl to YCP:> If a symbol is expected, a scalar like C<"foo">
+will be converted to C<`foo>.
+Otherwise use L</Symbol>.
+
+=item term
+
+B<YCP to Perl:> Becomes a L</Term>.
+
+B<Perl to YCP:> Use L</Term>.
+
+=item byteblock
+
+B<YCP to Perl:> Becomes a scalar.
+
+B<Perl to YCP:> If a byteblock is expected, a scalar like C<"\0\1">
+will be converted to C<#[0001]>.
+Otherwise use L</Byteblock>.
+
+=item locale, block E<lt>TE<gt>, ...
+
+Not implemented.
+
+=back
+
+=head1 YaST::YCP
+
+The DATA tag (in C<use YaST::YCP qw(:DATA)>) imports the data
+constructor functions such as Boolean, Symbol or Term.
+
+=cut
+
+package YaST::YCP;
+use strict;
+use warnings;
+use diagnostics;
+
+require Exporter;
+our @ISA = qw(Exporter);
+my @e_data = qw(Boolean Byteblock Integer Float String Symbol Term);
+my @e_logging = qw(y2debug y2milestone y2warning y2error y2security y2internal);
+our @EXPORT_OK = (@e_data, @e_logging, "sformat");
+our %EXPORT_TAGS = ( DATA => [@e_data], LOGGING => [@e_logging] );
+
+=head2 debug
+
+ $olddebug = YaST::YCP::debug (1);
+ YaST::YCP::...
+ YaST::YCP::debug ($olddebug);
+
+Enables miscellaneous unscpecified debugging
+
+=cut
+
+my $debug = 0;
+sub debug (;$)
+{
+    my $param = shift;
+    if (defined $param)
+    {
+       $debug = $param;
+    }
+    return $debug;
+}
+
+
+## calls boot_YaST__YCP
+require XSLoader;
+XSLoader::load ('YaST::YCP');
+
+=head2 init_ui
+
+ YaST::YCP::init_ui ();
+ YaST::YCP::init_ui "qt";
+
+Initializes the user interface, "ncurses" (the default) or "qt".
+
+=cut
+
+# ensure that the ncurses window is closed
+# and wfm and its agents are closed (#39519)
+END {
+    close_components (); # XS
+}
+
+=head2 Import
+
+ YaST::YCP::Import "Namespace";
+ Namespace->foo ("bar");
+
+Imports a YaST namespace (in YCP or Perl or any supported language).
+Equivalent to YCP C<import>, similar to Perl C<use>.
+
+If C<Namespace> is in YCP, its constructor is executed later than if
+it were imported from YCP. This can have subtle effects, for example
+in testsuites. To get closer to the YCP import behavior, call
+C<Import> from a C<BEGIN> block.
+
+=cut
+
+sub Import ($)
+{
+    my $package = shift;
+    print "Importing $package\n" if debug;
+
+    no strict;
+    # let it get our autoload
+    *{"${package}::AUTOLOAD"} = \&YaST::YCP::Autoload::AUTOLOAD;
+}
+
+=head2 logging
+
+These functions go via liby2util and thus use log.conf.
+See also ycp::y2milestone.
+
+The multiple arguments are simply joined by a space.
+
+ y2debug ($message, $message2, ...)
+ y2milestone ($message, $message2, ...)
+ y2warning ($message, $message2, ...)
+ y2error ($message, $message2, ...)
+ y2security ($message, $message2, ...)
+ y2internal ($message, $message2, ...)
+
+=cut
+
+sub y2_logger_helper ($@)
+{
+    my $level = shift;
+    # look _two_ frames up for the subroutine
+    # when called from the main script, it will be undef
+    my ($package, $filename, $line, $subroutine) = caller (2);
+    # look _one_ frame up for file and line
+    # (is it because of optimization?)
+    ($package, $filename, $line) = caller (1);
+    # this is a XS:
+    y2_logger ($level, "Perl", $filename, $line, $subroutine || "",
+              join (" ", @_));
+}
+
+sub y2debug (@)                { y2_logger_helper (0, @_); }
+sub y2milestone (@)    { y2_logger_helper (1, @_); }
+sub y2warning (@)      { y2_logger_helper (2, @_); }
+sub y2error (@)                { y2_logger_helper (3, @_); }
+sub y2security (@)     { y2_logger_helper (4, @_); }
+sub y2internal (@)     { y2_logger_helper (5, @_); }
+
+=head2 sformat
+
+Implements the sformat YCP builtin:
+
+C<sformat ('%2 %% %1', "a", "b")> returns C<'b % a'>
+
+It is useful mainly for messages marked for translation.
+
+=cut
+
+sub sformat ($@)
+{
+    # don't shift
+    # now the % indices can be used for @_
+    my $format = $_[0];
+
+    # g: global, replace all occurences
+    # e: expression, not a string
+    $format =~ s{%([1-9%])}{
+       ($1 eq '%') ? '%' : $_[$1]
+    }ge;
+
+    return $format;
+}
+
+# shortcuts for the data types
+# for POD see packages below
+
+sub Boolean ($)
+{
+    return new YaST::YCP::Boolean (@_);
+}
+
+sub Byteblock ($)
+{
+    return new YaST::YCP::Byteblock (@_);
+}
+
+sub Integer ($)
+{
+    return new YaST::YCP::Integer (@_);
+}
+
+sub Float ($)
+{
+    return new YaST::YCP::Float (@_);
+}
+
+sub String ($)
+{
+    return new YaST::YCP::String (@_);
+}
+
+sub Symbol ($)
+{
+    return new YaST::YCP::Symbol (@_);
+}
+
+sub Term ($@)
+{
+    return new YaST::YCP::Term (@_);
+}
+
+# by defining AUTOLOAD in a separate package, undefined functions in
+# the main one will be detected
+package YaST::YCP::Autoload;
+use strict;
+use warnings;
+use diagnostics;
+
+# cannot rely on UNIVERSAL::AUTOLOAD getting automatically called
+# http://www.rocketaware.com/perl/perldelta/Deprecated_Inherited_C_AUTOLOAD.htm
+
+# Gets called instead of all functions in Import'ed modules
+# It assumes a normal function, not a class or instance method
+sub AUTOLOAD
+{
+    our $AUTOLOAD;
+
+    # strip $self on the way from Perl to YCP,
+    # just as it is added in the reverse direction
+    my $himself = shift;
+    print "$himself $AUTOLOAD (", join (", ", @_), ")\n" if YaST::YCP::debug;
+
+    my @components = split ("::", $AUTOLOAD);
+    my $func = pop (@components);
+    return YaST::YCP::call_ycp (join ("::", @components), $func, @_);
+}
+
+=head2 Boolean
+
+ $b = YaST::YCP::Boolean (1);
+ $b->value (0);
+ print $b->value, "\n";
+ SCR::Write (".foo", $b);
+
+=cut
+
+package YaST::YCP::Boolean;
+use strict;
+use warnings;
+use diagnostics;
+
+# a Boolean is just a blessed reference to a scalar
+
+sub new
+{
+    my $class = shift;
+    my $val = shift;
+    return bless \$val, $class
+}
+
+# get/set
+sub value
+{
+    # see "Constructors and Instance Methods" in perltoot
+    my $self = shift;
+    if (@_) { $$self = shift; }
+    return $$self;
+}
+
+=head2 Byteblock
+
+A chunk of binary data.
+
+ use YaST::YCP qw(:DATA);
+
+ read ($dev_random_fh, $r, 100);
+ $b = Byteblock ($r);
+ $b->value ("Hello\0world\0");
+ print $b->value, "\n";
+ return $b;
+
+=cut
+
+package YaST::YCP::Byteblock;
+use strict;
+use warnings;
+use diagnostics;
+
+# a Byteblock is just a blessed reference to a scalar
+# just like Boolean, so use it!
+
+our @ISA = qw (YaST::YCP::Boolean);
+
+=head2 Integer
+
+An explicitly typed integer, useful to put in heterogenous data structures.
+
+ use YaST::YCP qw(:DATA);
+
+ $i = Integer ("42 and more");
+ $i->value ("43, actually");
+ print $i->value, "\n";
+ return [ $i ];
+
+=cut
+
+package YaST::YCP::Integer;
+use strict;
+use warnings;
+use diagnostics;
+
+
+# an Integer is just a blessed reference to a scalar
+# just like Boolean, so use it!
+
+our @ISA = qw (YaST::YCP::Boolean);
+
+=head2 Float
+
+An explicitly typed float, useful to put in heterogenous data structures.
+
+ use YaST::YCP qw(:DATA);
+
+ $f = Float ("3.41 is PI");
+ $f->value ("3.14 is PI");
+ print $f->value, "\n";
+ return [ $f ];
+
+=cut
+
+package YaST::YCP::Float;
+use strict;
+use warnings;
+use diagnostics;
+
+
+# a Float is just a blessed reference to a scalar
+# just like Boolean, so use it!
+
+our @ISA = qw (YaST::YCP::Boolean);
+
+=head2 Path
+
+Not implemented yet.
+
+=cut
+
+=head2 String
+
+An explicitly typed string, useful to put in heterogenous data structures.
+
+ use YaST::YCP qw(:DATA);
+
+ $s = String (42);
+ $s->value (1 + 1);
+ print $s->value, "\n";
+ return [ $s ];
+
+=cut
+
+package YaST::YCP::String;
+use strict;
+use warnings;
+use diagnostics;
+
+# a String is just a blessed reference to a scalar
+# just like Boolean, so use it!
+
+our @ISA = qw (YaST::YCP::Boolean);
+
+=head2 Symbol
+
+ use YaST::YCP qw(:DATA);
+
+ $s = Symbol ("next");
+ $s->value ("back");
+ print $s->value, "\n";
+ return Term ("id", $s);
+
+=cut
+
+package YaST::YCP::Symbol;
+use strict;
+use warnings;
+use diagnostics;
+
+
+# a Symbol is just a blessed reference to a scalar
+# just like Boolean, so use it!
+
+our @ISA = qw (YaST::YCP::Boolean);
+
+=head2 Term
+
+ $t = new YaST::YCP::Term("CzechBox", "Accept spam", new YaST::YCP::Boolean(0));
+ $t->name ("CheckBox");
+ print $t->args->[0], "\n";
+ UIx::OpenDialog ($t);
+
+=cut
+
+package YaST::YCP::Term;
+use strict;
+use warnings;
+use diagnostics;
+
+# a Term has a name and arguments
+
+sub new
+{
+    my $class = shift;
+    my $name = shift;
+    my $args = [ @_ ];
+    return bless { name => $name, args => $args }, $class
+}
+
+# get/set
+sub name
+{
+    # see "Constructors and Instance Methods" in perltoot
+    my $self = shift;
+    if (@_) { $self->{name} = shift; }
+    return $self->{name};
+}
+
+# get/set
+sub args
+{
+    # see "Constructors and Instance Methods" in perltoot
+    my $self = shift;
+    if (@_) { @{ $self->{args} } = @_; }
+    # HACK:
+    # because I don't want to process multiple return values,
+    # I return it as a reference
+    return $self->{args};
+}
+
+1;
diff --git a/src/ruby/YRuby.cc b/src/ruby/YRuby.cc
new file mode 100644 (file)
index 0000000..07ab59a
--- /dev/null
@@ -0,0 +1,173 @@
+/*---------------------------------------------------------------------\
+|                                                                      |
+|                      __   __    ____ _____ ____                      |
+|                      \ \ / /_ _/ ___|_   _|___ \                     |
+|                       \ V / _` \___ \ | |   __) |                    |
+|                        | | (_| |___) || |  / __/                     |
+|                        |_|\__,_|____/ |_| |_____|                    |
+|                                                                      |
+|                                                                      |
+| ruby language support                              (C) Novell Inc.   |
+\----------------------------------------------------------------------/
+
+Author: Duncan Mac-Vicar <dmacvicar@suse.de>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version
+2 of the License, or (at your option) any later version.
+
+*/
+
+#include <stdlib.h>
+#include <list>
+#include <iosfwd>
+#include <sstream>
+#include <iomanip>
+
+// Ruby stuff
+#include <ruby.h>
+
+
+#define y2log_component "Y2Ruby"
+#include <ycp/y2log.h>
+#include <ycp/pathsearch.h>
+
+
+#include <ycp/YCPBoolean.h>
+#include <ycp/YCPByteblock.h>
+#include <ycp/YCPFloat.h>
+#include <ycp/YCPInteger.h>
+#include <ycp/YCPList.h>
+#include <ycp/YCPMap.h>
+#include <ycp/YCPPath.h>
+#include <ycp/YCPString.h>
+#include <ycp/YCPSymbol.h>
+#include <ycp/YCPTerm.h>
+#include <ycp/YCPVoid.h>
+#include <ycp/YCPCode.h>
+#include <ycp/YCPExternal.h>
+
+#include "YRuby.h"
+
+#define DIM(ARRAY)     ( sizeof( ARRAY )/sizeof( ARRAY[0] ) )
+
+#include "Y2RubyTypeConv.h"
+
+static void prependModulePath()
+{
+  YCPPathSearch::initialize ();
+
+  list<string>::const_iterator
+      b = YCPPathSearch::searchListBegin (YCPPathSearch::Module),
+      e = YCPPathSearch::searchListEnd (YCPPathSearch::Module),
+      i;
+      
+  // count the number of directories to prepend
+//   int n = 0;
+//   for (i = b; i != e; ++i)
+//   {
+//     // do something
+//   }
+}
+
+YRuby * YRuby::_yRuby = 0;
+
+YRuby::YRuby()
+{
+  y2milestone( "Initializing ruby interpreter." );
+  ruby_init();
+  //ruby_options(argc - 1, ++argv);
+  ruby_script("yast");
+  ruby_init_loadpath();
+}
+
+YRuby::~YRuby()
+{
+    y2milestone( "Shutting down ruby interpreter." );
+    ruby_finalize();
+}
+
+
+YRuby *
+YRuby::yRuby()
+{
+  if ( ! _yRuby )
+    _yRuby = new YRuby();
+
+  return _yRuby;
+}
+
+
+YCPValue
+YRuby::destroy()
+{
+  if ( _yRuby )
+    delete _yRuby;
+
+  _yRuby = 0;
+
+  return YCPVoid();
+}
+
+/**
+ * Loads a module.
+ */
+YCPValue
+YRuby::loadModule( YCPList argList )
+{
+  YRuby::yRuby();
+  //y2milestone("loadModule 1");
+  if ( argList->size() != 2 || ! argList->value(0)->isString() || ! argList->value(1)->isString() )
+    return YCPError( "Ruby::loadModule() / Ruby::Use() : Bad arguments: String expected!" );
+  //y2milestone("loadModule 2");
+  string module_name = argList->value(0)->asString()->value();
+  string module_path = argList->value(1)->asString()->value();
+  //y2milestone("loadModule 3: '%s'", module_name.c_str());
+  string require_module = "require(\"" + module_path + "\")";
+  //y2milestone("loadModule 3.5");
+  VALUE result = rb_eval_string((require_module).c_str());
+  if ( result == Qfalse )
+    return YCPError( "Ruby::loadModule() / Can't load ruby module '" + module_path + "'" );
+  //y2milestone("loadModule 4");
+  return YCPVoid();
+}
+
+
+
+/**
+ * @param argList arguments start 1!, 0 is dummy
+ */
+YCPValue
+YRuby::callInner (string module_name, string function, bool method,
+                  YCPList argList, constTypePtr wanted_result_type)
+{
+  VALUE module = rb_funcall( rb_mKernel, rb_intern("const_get"), 1, rb_str_new2(module_name.c_str()) );
+  if (module == Qnil)
+  {
+    y2error ("The Ruby module '%s' is not provided by its rb file", module_name.c_str());
+    return YCPVoid();
+  }
+  
+  // first element of the list is ignored
+  int size = argList.size();
+  
+  // make rooms for size-1 arguments to
+  // the ruby function
+  VALUE values[size-1];
+  int i=0;
+  for ( ; i < size-1; ++i )
+  {
+    // get the
+    YCPValue v = argList->value(i+1);
+    y2milestone("Adding argument %d of type %s", i, v->valuetype_str());
+    values[i] = ycpvalue_2_rbvalue(v);
+  }
+
+  y2milestone( "Wll call function '%s' in module '%s' with '%d' arguments", function.c_str(), module_name.c_str(), size-1);
+  VALUE result = rb_funcall2( module, rb_intern(function.c_str()), size-1, values );
+  //VALUE result = rb_funcall( module, rb_intern(function.c_str()), 2, INT2NUM(2), INT2NUM(3) );
+  y2milestone( "Called function '%s' in module '%s'", function.c_str(), module_name.c_str());
+  return rbvalue_2_ycpvalue(result);
+}
+
diff --git a/src/ruby/YRuby.h b/src/ruby/YRuby.h
new file mode 100644 (file)
index 0000000..89dd7b1
--- /dev/null
@@ -0,0 +1,107 @@
+/*---------------------------------------------------------------------\
+|                                                                      |
+|                      __   __    ____ _____ ____                      |
+|                      \ \ / /_ _/ ___|_   _|___ \                     |
+|                       \ V / _` \___ \ | |   __) |                    |
+|                        | | (_| |___) || |  / __/                     |
+|                        |_|\__,_|____/ |_| |_____|                    |
+|                                                                      |
+|                                                                      |
+| ruby language support                              (C) Novell Inc.   |
+\----------------------------------------------------------------------/
+
+Author: Duncan Mac-Vicar <dmacvicar@suse.de>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version
+2 of the License, or (at your option) any later version.
+
+*/
+
+#ifndef YRuby_h
+#define YRuby_h
+
+// Ruby stuff
+#include <ruby.h>
+
+#include <ycp/YCPList.h>
+#include <ycp/Type.h>
+
+// make the compiler happy when
+// calling rb_define_method()
+typedef VALUE (ruby_method)(...);
+
+// more useful macros
+#define RB_FINALIZER(func) ((void (*)(...))func)
+
+// this macro saves us from typing
+// (ruby_method*) & method_name
+// in rb_define_method
+#define RB_METHOD(func) ((VALUE (*)(...))func)
+
+class YRuby
+{
+public:
+
+    /**
+     * Load a Ruby module - equivalent to "use" in Ruby.
+     *
+     * Returns a YCPError on failure, YCPVoid on success.
+     **/
+    static YCPValue loadModule( YCPList argList );
+
+    /**
+     * Access the static (singleton) YRuby object. Create it if it isn't
+     * created yet.
+     *
+     * Returns 0 on error.
+     **/
+    static YRuby * yRuby();
+
+    /**
+     * Destroy the static (singleton) YRuby object and unload the embedded Ruby
+     * interpreter.
+     *
+     * Returns YCPVoid().
+     **/
+    static YCPValue destroy();
+
+    
+protected:
+
+    /**
+     * Protected constructor. Use one of the static methods rather than
+     * instantiate an object of this class yourself.
+     **/
+    YRuby();
+
+    /**
+     * Protected constructor. Use one of the static methods rather than
+     * instantiate an object of this class yourself.
+     **/
+    
+    /**
+     * Destructor.
+     **/
+    ~YRuby();
+
+    /**
+     * Returns the internal embedded Ruby interpreter.
+     **/
+
+
+public:
+    /**
+     * Generic Ruby call.
+     **/
+    YCPValue callInner (string module, string function, bool method,
+                       YCPList argList, constTypePtr wanted_result_type);
+    
+protected:
+    
+public:
+    static YRuby *     _yRuby;
+};
+
+#endif // YRuby_h
diff --git a/src/ruby/YRubyNamespace.cc b/src/ruby/YRubyNamespace.cc
new file mode 100644 (file)
index 0000000..b338745
--- /dev/null
@@ -0,0 +1,314 @@
+/*---------------------------------------------------------------------\
+|                                                                      |
+|                      __   __    ____ _____ ____                      |
+|                      \ \ / /_ _/ ___|_   _|___ \                     |
+|                       \ V / _` \___ \ | |   __) |                    |
+|                        | | (_| |___) || |  / __/                     |
+|                        |_|\__,_|____/ |_| |_____|                    |
+|                                                                      |
+|                                                                      |
+| ruby language support                              (C) Novell Inc.   |
+\----------------------------------------------------------------------/
+
+Author: Duncan Mac-Vicar <dmacvicar@suse.de>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version
+2 of the License, or (at your option) any later version.
+
+*/
+
+#include "YRubyNamespace.h"
+
+// Ruby stuff
+#include <ruby.h>
+
+#define y2log_component "Y2Ruby"
+#include <ycp/y2log.h>
+
+#include <ycp/YCPElement.h>
+#include <ycp/Type.h>
+#include <ycp/YCPVoid.h>
+//#include <YCP.h>
+#include "YRuby.h"
+#include <stdio.h>
+
+/**
+ * using this instead of plain strcmp
+ * enables embedding argument names into the typeinfo
+ */
+static bool firstWordIs (const char *where, const char *what)
+{
+  size_t n = strlen (what);
+  return !strncmp (where, what, n) &&
+         (where[n] == '\0' || isspace (where[n]));
+}
+
+/**
+ * The definition of a function that is implemented in Ruby
+ */
+class Y2RubyFunctionCall : public Y2Function
+{
+  //! module name
+  string m_module_name;
+  //! function name, excluding module name
+  string m_local_name;
+  //! function type
+  constFunctionTypePtr m_type;
+  //! data prepared for the inner call
+  YCPList m_call;
+
+public:
+  Y2RubyFunctionCall (const string &module_name,
+                      const string &local_name,
+                      constFunctionTypePtr function_type
+                     ) :
+      m_module_name (module_name),
+      m_local_name (local_name),
+      m_type (function_type)
+  {
+    // placeholder, formerly function name
+    m_call->add (YCPVoid ());
+  }
+
+  //! if true, the ruby function is passed the module name
+  virtual bool isMethod () = 0;
+
+  //! called by YEFunction::evaluate
+  virtual YCPValue evaluateCall ()
+  {
+    return YRuby::yRuby()->callInner ( m_module_name,
+                                       m_local_name,
+                                       isMethod (),
+                                       m_call,
+                                       m_type->returnType() );
+  }
+  /**
+  * Attaches a parameter to a given position to the call.
+  * @return false if there was a type mismatch
+  */
+  virtual bool attachParameter (const YCPValue& arg, const int position)
+  {
+    m_call->set (position+1, arg);
+    return true;
+  }
+
+  /**
+   * What type is expected for the next appendParameter (val) ?
+   * (Used when calling from Ruby, to be able to convert from the
+   * simple type system of Ruby to the elaborate type system of YCP)
+   * @return Type::Any if number of parameters exceeded
+   */
+  virtual constTypePtr wantedParameterType () const
+  {
+    // -1 for the function name
+    int params_so_far = m_call->size ()-1;
+    return m_type->parameterType (params_so_far);
+  }
+
+  /**
+   * Appends a parameter to the call.
+   * @return false if there was a type mismatch
+   */
+  virtual bool appendParameter (const YCPValue& arg)
+  {
+    y2internal("Adding parameter to function %s::%s of type %s", m_module_name.c_str(), m_local_name.c_str(), arg->valuetype_str());
+    m_call->add (arg);
+    return true;
+  }
+
+  /**
+   * Signal that we're done adding parameters.
+   * @return false if there was a parameter missing
+   */
+  virtual bool finishParameters ()
+  {
+    return true;
+  }
+
+
+  virtual bool reset ()
+  {
+    m_call = YCPList ();
+    // placeholder, formerly function name
+    m_call->add (YCPVoid ());
+    return true;
+  }
+
+  /**
+   * Something for remote namespaces
+   */
+  virtual string name () const
+  {
+    return m_local_name;
+  }
+};
+
+class Y2RubySubCall : public Y2RubyFunctionCall
+{
+public:
+  Y2RubySubCall (const string &module_name,
+                 const string &local_name,
+                 constFunctionTypePtr function_type
+                ) :
+      Y2RubyFunctionCall (module_name, local_name, function_type)
+  {}
+  virtual bool isMethod ()
+  {
+    return false;
+  }
+};
+
+class Y2RubyMethodCall : public Y2RubyFunctionCall
+{
+public:
+  Y2RubyMethodCall (const string &module_name,
+                    const string &local_name,
+                    constFunctionTypePtr function_type
+                   ) :
+      Y2RubyFunctionCall (module_name, local_name, function_type)
+  {}
+  virtual bool isMethod ()
+  {
+    return true;
+  }
+};
+
+\f
+
+YRubyNamespace::YRubyNamespace (string name)
+    : m_name (name),
+    m_all_methods (true)
+{
+  y2milestone("Creating namespace for '%s'", name.c_str());
+
+  //y2milestone("loadModule 3.5");
+  //VALUE result = rb_eval_string((require_module).c_str());
+  
+  VALUE module = rb_funcall( rb_mKernel, rb_intern("const_get"), 1, rb_str_new2(name.c_str()) );
+  if (module == Qnil)
+  {
+    y2error ("The Ruby module '%s' is not provided by its rb file", name.c_str());
+    return;
+  }
+  y2milestone("The module '%s' was found", name.c_str());
+
+  // we will perform operator- to determine the module methods
+  VALUE moduleklassmethods = rb_funcall( rb_cModule, rb_intern("methods"), 0);
+  VALUE mymodulemethods = rb_funcall( module, rb_intern("methods"), 0);
+  VALUE methods = rb_funcall( mymodulemethods, rb_intern("-"), 1, moduleklassmethods );
+      
+  if (methods == Qnil)
+  {
+    y2error ("Can't see methods in module '%s'", name.c_str());
+    return;
+  }
+  
+  int i;
+  for(i = 0; i < RARRAY(methods)->len; i++)
+  {
+    VALUE current = RARRAY(methods)->ptr[i];
+    y2milestone("New method: '%s'", RSTRING(current)->ptr);
+    
+    constTypePtr sym_tp = Type::Unspec;
+    //sym_tp = parseTypeinfo (*sym_ti)
+    if (sym_tp->isError ())
+    {
+      y2error ("Cannot parse $TYPEINFO{%s}", RSTRING(current)->ptr);
+      continue;
+    }
+    if (sym_tp->isUnspec ())
+    {
+      //sym_tp = new FunctionType (Type::Any, new FunctionType(Type::Any) );
+      // figure out arity.
+      y2milestone("1.");
+      Check_Type(module,T_MODULE);
+      VALUE methodobj = rb_funcall( module, rb_intern("method"), 1, current );
+      //VALUE methodobj = rb_funcall( module, rb_intern("send"), 2, rb_str_new2("method"), current );
+      if ( methodobj == Qnil )
+      {
+        y2error ("Cannot access method object '%s'", RSTRING(current)->ptr);
+        continue;
+      }
+      y2milestone("2.");
+      string signature = "any( ";
+      VALUE rbarity = rb_funcall( methodobj, rb_intern("arity"), 0);
+      y2milestone("3.");
+      int arity = NUM2INT(rbarity);
+      for ( int k=0; k < arity; ++k )
+      {
+        signature += "any";
+        if ( k < (arity - 1) )
+            signature += ",";
+      }
+      signature += ")";
+      y2internal("going to parse signature: '%s'", signature.c_str());
+      sym_tp = Type::fromSignature(signature);
+    }
+    
+    constFunctionTypePtr fun_tp = (constFunctionTypePtr) sym_tp;
+
+    // symbol entry for the function
+    SymbolEntry *fun_se = new SymbolEntry ( this,
+                                            i,// position. arbitrary numbering. must stay consistent when?
+                                            RSTRING(current)->ptr, // passed to Ustring, no need to strdup
+                                            SymbolEntry::c_function,
+                                            sym_tp);
+    fun_se->setGlobal (true);
+    // enter it to the symbol table
+    enterSymbol (fun_se, 0);
+    y2milestone("method: '%s' added", RSTRING(current)->ptr);
+    y2milestone("%s", symbolsToString().c_str());
+  }
+}
+
+YRubyNamespace::~YRubyNamespace ()
+{}
+
+const string YRubyNamespace::filename () const
+{
+  // TODO improve
+  return ".../" + m_name;
+}
+
+// this is for error reporting only?
+string YRubyNamespace::toString () const
+{
+  y2error ("TODO");
+  return "{\n"
+         "/* this namespace is provided in Ruby */\n"
+         "}\n";
+}
+
+// called when running and the import statement is encountered
+// does initialization of variables
+// constructor is handled separately after this
+YCPValue YRubyNamespace::evaluate (bool cse)
+{
+  // so we don't need to do anything
+  y2debug ("Doing nothing");
+  return YCPNull ();
+}
+
+// It seems that this is the standard implementation. why would we
+// ever want it to be different?
+Y2Function* YRubyNamespace::createFunctionCall (const string name, constFunctionTypePtr required_type)
+{
+  y2debug ("Creating function call for %s", name.c_str ());
+  TableEntry *func_te = table ()->find (name.c_str (), SymbolEntry::c_function);
+  if (func_te)
+  {
+    constTypePtr t = required_type ? required_type : (constFunctionTypePtr)func_te->sentry()->type ();
+    if (m_all_methods)
+    {
+      return new Y2RubyMethodCall (m_name, name, t);
+    }
+    else
+    {
+      return new Y2RubySubCall (m_name, name, t);
+    }
+  }
+  y2error ("No such function %s", name.c_str ());
+  return NULL;
+}
diff --git a/src/ruby/YRubyNamespace.h b/src/ruby/YRubyNamespace.h
new file mode 100644 (file)
index 0000000..0b8eb92
--- /dev/null
@@ -0,0 +1,58 @@
+/*---------------------------------------------------------------------\
+|                                                                      |
+|                      __   __    ____ _____ ____                      |
+|                      \ \ / /_ _/ ___|_   _|___ \                     |
+|                       \ V / _` \___ \ | |   __) |                    |
+|                        | | (_| |___) || |  / __/                     |
+|                        |_|\__,_|____/ |_| |_____|                    |
+|                                                                      |
+|                                                                      |
+| ruby language support                              (C) Novell Inc.   |
+\----------------------------------------------------------------------/
+
+Author: Duncan Mac-Vicar <dmacvicar@suse.de>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version
+2 of the License, or (at your option) any later version.
+
+*/
+
+#include <ruby.h>
+#include <y2/Y2Namespace.h>
+#include <y2/Y2Function.h>
+#include <ycp/YStatement.h>
+
+/**
+ * YaST interface to a Ruby module
+ */
+class YRubyNamespace : public Y2Namespace
+{
+private:
+    string m_name;             //! this namespace's name, eg. XML::Writer
+    bool m_all_methods;                //! add the class name to all calls
+public:
+    /**
+     * Construct an interface. The module must be already loaded
+     * @param name eg "XML::Writer"
+     */
+    YRubyNamespace (string name);
+
+    virtual ~YRubyNamespace ();
+
+    //! what namespace do we implement
+    virtual const string name () const { return m_name; }
+    //! used for error reporting
+    virtual const string filename () const;
+
+    //! unparse. useful  only for YCP namespaces??
+    virtual string toString () const;
+    //! called when evaluating the import statement
+    // constructor is handled separately
+    virtual YCPValue evaluate (bool cse = false);
+
+    virtual Y2Function* createFunctionCall (const string name, constFunctionTypePtr requiredType);
+private:
+    VALUE _module_instance;
+};
diff --git a/src/ruby/YaPI.pm.in b/src/ruby/YaPI.pm.in
new file mode 100644 (file)
index 0000000..c991d30
--- /dev/null
@@ -0,0 +1,246 @@
+# -*- perl -*-
+# @configure_input@
+# $Id: YaPI.pm.in 33405 2006-10-13 13:12:42Z mvidner $
+
+package YaPI;
+
+=head1 NAME
+
+YaPI - common functions for modules implementing YaST API
+
+=cut
+
+BEGIN {
+    # substituted by configure
+    my $modules = '@yast2dir4perl@/modules';
+    # unconditional 'use lib' could override a "use lib ." that
+    # we do during compilation, #197099
+    grep { $_ eq $modules } @INC or unshift(@INC, $modules);
+}
+use strict;
+
+use Exporter;
+our @ISA = qw(Exporter);
+our @EXPORT = qw(textdomain __);
+
+use YaST::YCP;
+use ycp;
+
+use Locale::gettext ("!textdomain");
+use POSIX ();     # Needed for setlocale()
+
+POSIX::setlocale(&POSIX::LC_MESSAGES, "");
+
+our %TYPEINFO;
+my %__error = ();
+my $VERSION = "";
+our @CAPABILITIES = ();
+
+=head2 base functions
+
+These are to be used by modules that use YaPI as their base class.
+
+  use YaPI;
+  our @ISA = ("YaPI");
+
+=head3 Interface
+
+Returns a reference to a list of hashes describing the functions
+in the current package. The information is taken from TYPEINFO.
+
+  [
+   {
+      functionName => "contains",
+      return => "boolean",
+      argument => [ "string", ["list", "string"]],
+   },
+   ...
+  ]
+
+=cut
+
+BEGIN { $TYPEINFO{Interface} = ["function", "any"]; }
+sub Interface {
+    my $self = shift;
+    my @ret = ();
+
+    no strict "refs";
+    my %TI = %{"${self}::TYPEINFO"};
+
+    foreach my $k (keys %TYPEINFO) {
+        $TI{$k} = $TYPEINFO{$k};
+    }
+
+    foreach my $funcName (sort keys %TI) {
+        my @dummy = @{$TI{$funcName}};
+        my $hash = {};
+
+        $hash->{'functionName'} = $funcName;
+        $hash->{'return'}       = $dummy[1];
+        splice(@dummy, 0, 2);
+        $hash->{'argument'} = \@dummy;
+        push @ret, $hash;
+    }
+    return \@ret;
+}
+
+=head3 Version
+
+Returns the version of the current package.
+
+=cut
+
+BEGIN { $TYPEINFO{Version} = ["function", "string"]; }
+sub Version {
+    my $self = shift;
+    no strict "refs";
+    return ${"${self}::VERSION"};
+}
+
+=head3 Supports
+
+Greps C<@CAPABILITIES> of the current package.
+
+  if (YaPI::Foo->Supports ("frobnicate")) {...}
+
+=cut
+
+BEGIN { $TYPEINFO{Supports} = ["function", "boolean", "string"]; }
+sub Supports {
+    my $self = shift;
+    my $cap  = shift;
+
+    no strict "refs";
+    my @c = @{"${self}::CAPABILITIES"};
+    foreach my $k (@CAPABILITIES) {
+        push @c, $k;
+    }
+
+    return !!grep( ($_ eq $cap), @c);
+}
+
+
+=head3 SetError
+
+Logs an error and remembers it for L</Error>.
+
+Error map:
+
+  {
+    code               # mandatory, an uppercase short string
+    summary
+    description
+  # if all of the following are missing, caller () is used
+    package
+    file
+    line
+  }
+
+=cut
+
+BEGIN { $TYPEINFO{SetError} = ["function", "boolean", ["map", "string", "any" ]]; }
+sub SetError {
+    my $self = shift;
+    %__error = @_;
+    if( !$__error{package} && !$__error{file} && !$__error{line})
+    {
+        @__error{'package','file','line'} = caller();
+    }
+    if ( defined $__error{summary} ) {
+        y2error($__error{code}."[".$__error{line}.":".$__error{file}."] ".$__error{summary});
+    } else {
+        y2error($__error{code});
+    }
+    if(defined $__error{description} && $__error{description} ne "") {
+        y2error("Description: ".$__error{description});
+    }
+
+    return undef;
+}
+
+=head3 Error
+
+Returns the error set by L</SetError>
+
+=cut
+
+BEGIN { $TYPEINFO{Error} = ["function", ["map", "string", "any"] ]; }
+sub Error {
+    my $self = shift;
+    return \%__error;
+}
+
+=head2 i18n
+
+C<< use YaPI; >>
+
+C<< textdomain "mydomain"; >>
+
+Just use a double underscore to mark text to be translated: C<__("my text")>.
+Both C<textdomain> and C<__> are exported to the calling package.
+
+These must not be used any longer because they collide with symbols
+exported by this module:
+
+ #  use Locale::gettext;    # textdomain
+ #  sub _ { ... }
+
+These don't hurt but aren't necessary:
+
+ #  use POSIX ();
+ #  POSIX::setlocale(LC_MESSAGES, "");    # YaPI calls it itself now
+
+=head3 textdomain
+
+Calls Locale::gettext::textdomain
+and also
+remembers an association between the calling package and the
+domain. Later calls of __ use this domain as an argument to dgettext.
+
+=cut
+
+# See also bug 38613 where untranslated texts were seen because
+# a random textdomain was used instead of the proper one.
+my %textdomains;
+
+sub textdomain
+{
+    my $domain = shift;
+    my ($package, $filename, $line) = caller;
+
+    if (defined ($textdomains{package}))
+    {
+       if ($textdomains{package} ne $domain)
+       {
+           y2error ("Textdomain '$domain' overrides old textdomain '$textdomains{package}' in package $package, $filename:$line");
+       }
+    }
+    $textdomains{$package} = $domain;
+    return Locale::gettext::textdomain ($domain);
+}
+
+=head3 __ (double underscore)
+
+Calls Locale::gettext::dgettext, supplying the textdomain of the calling
+package (set by calling textdomain).
+
+Note: the single underscore function (_) will be removed because it
+is automaticaly exported to main:: which causes namespace conflicts.
+
+=cut
+
+# bug 39954: __ better than _
+sub __ {
+    my $msgid = shift;
+    my $package = caller;
+    my $domain = $textdomains{$package};
+    return Locale::gettext::dgettext ($domain, $msgid);
+}
+
+# Compatibility by partial typeglob assignment:
+# &_ will call &__ but $_ will not be $__ which would happen
+# if we just asigned *_ = *__.
+# Cannot just call __ from _ because of "caller".
+*_ = \&__;
+
+1;
diff --git a/src/ruby/yast.rb b/src/ruby/yast.rb
new file mode 100644 (file)
index 0000000..e205cc0
--- /dev/null
@@ -0,0 +1,141 @@
+# -----------------------------------------------------------------------\
+# |                                                                      |
+# |                      __   __    ____ _____ ____                      |
+# |                      \ \ / /_ _/ ___|_   _|___ \                     |
+# |                       \ V / _` \___ \ | |   __) |                    |
+# |                        | | (_| |___) || |  / __/                     |
+# |                        |_|\__,_|____/ |_| |_____|                    |
+# |                                                                      |
+# |                                                                      |
+# | ruby language support                              (C) Novell Inc.   |
+# \----------------------------------------------------------------------/
+#
+# Author: Duncan Mac-Vicar <dmacvicar@suse.de>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version
+# 2 of the License, or (at your option) any later version.
+#
+
+ENV['LD_LIBRARY_PATH'] = "/usr/lib/YaST2/plugin"
+
+# Load the native part (.so)
+require 'yastx'
+
+module YaST
+  module Ui
+    #my @e_logging = qw(y2debug y2milestone y2warning y2error y2security y2internal);
+    
+  # Define symbols for the UI
+  ui_terms = [ :BarGraph, :Bottom, :CheckBox, :ColoredLabel, :ComboBox, :Date,
+    :DownloadProgress, :DumbTab, :DummySpecialWidget, :Empty, :Frame, :HBox, :HBoxvHCenter,
+    :HMultiProgressMeter, :HSpacing, :HSquash, :HStretch, :HVCenter, :HVSquash,
+    :HVStretch, :HWeight, :Heading, :IconButton, :Image, :IntField, :Label, :Left, :LogView,
+    :MarginBox, :MenuButton, :MinHeight, :MinSize, :MinWidth, :MultiLineEdit,
+    :MultiSelectionBox, :PackageSelector, :PatternSelector, :PartitionSplitter,
+    :Password, :PkgSpecial, :ProgressBar, :PushButton, :RadioButton,
+    :RadioButtonGroup, :ReplacePoint, :RichText, :Right, :SelectionBox, :Slider, :Table,
+    :TextEntry, :Time, :Top, :Tree, :VBox, :VCenter, :VMultiProgressMeter, :VSpacing,
+    :VSquash, :VStretch, :VWeight, :Wizard,
+    :id, :opt ]
+       
+#     buffer = String.new
+#     buffer << "["
+#     ui_terms.each do |t|
+#       buffer << " :" << t.to_s.downcase << ","
+#     end
+#     buffer <<  " ]"
+#     puts buffer
+    # If the method name contains underscores, convert to camel case
+#     while method =~ /([^_]*)_(.)(.*)/ 
+#            method = $1 + $2.upcase + $3
+#        end
+         
+    # for each symbol define a util function that will create a term
+    ui_terms.each do | term_name |
+      define_method(term_name) do | *args |
+        t = YaST::Term.new(term_name.to_s)
+        args.each do |arg|
+          t.add(arg)
+        end
+        return t
+      end
+      alias_method term_name.to_s.downcase, term_name
+    end
+
+  end # end Ui module
+end
+
+module YaST
+
+  class TermBuilder
+    # blank slate
+    instance_methods.each { |m| undef_method m unless (m =~ /^__|instance_eval$/)}
+    
+    def initialize(&block)
+        @term = nil
+        @term = instance_eval(&block)
+    end
+    
+    def method_missing(name, *args, &block )
+  #    puts "hi #{name.to_s} | #{args}"
+      term = nil
+      elements = block ? nil : args
+      @__to_s = nil # invalidate to_s cache
+      term = YaST::Term.new(name.to_s)
+      if not elements.nil?
+        elements.each do | e |
+          term.add(e)
+        end
+        return term
+      else
+        r = instance_eval(&block)
+        puts term.class
+        term.add(r) if not r.nil?
+      end
+      return term
+    end
+    
+    def to_s
+      return @term.to_s
+    end
+    
+    def term
+      return @term
+    end
+    
+  end
+
+  def y2_logger_helper(*args)
+    level = args.shift
+    
+    caller[0] =~ /(.+):(\d+):in `([^']+)'/
+    y2_logger(level,"Ruby",$1,$2.to_i,"",args[0])
+  end
+  
+  def y2debug(*args)
+    y2_logger_helper(0, args)
+  end
+  
+  def y2milestone(*args)
+    y2_logger_helper(1, args)
+  end
+  
+  def y2warning(*args)
+    y2_logger_helper(2, args)
+  end
+  
+  def y2error(*args)
+    y2_logger_helper(3, args)
+  end
+  
+  def y2security(*args)
+    y2_logger_helper(4, args)
+  end
+  
+  def y2internal(*args)
+    y2_logger_helper(5, args)
+  end
+  
+end # module YaST
\ No newline at end of file
diff --git a/src/swig/CMakeLists.txt b/src/swig/CMakeLists.txt
new file mode 100644 (file)
index 0000000..2ecda54
--- /dev/null
@@ -0,0 +1,30 @@
+
+SET( SWIG_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/yast_wrap.cxx" )
+SET( SWIG_INPUT "${CMAKE_CURRENT_SOURCE_DIR}/yast.i" )
+
+ADD_CUSTOM_COMMAND (
+   OUTPUT  ${CMAKE_CURRENT_BINARY_DIR}/yast_wrap.cxx
+   COMMAND ${CMAKE_COMMAND} -E echo_append "Creating wrapper code..."
+#COMMAND ${SWIG_EXECUTABLE} -c++ -ruby -autorename -xmlout parse.xml -I/usr/include swig/zypp.i
+   COMMAND ${SWIG_EXECUTABLE} -I${YAST_INCLUDE_DIR} -c++ -ruby -autorename -xmlout ${CMAKE_CURRENT_BINARY_DIR}/parse.xml -o ${CMAKE_CURRENT_BINARY_DIR}/yast_wrap.cxx -I${ZYPP_INCLUDE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/yast.i
+   COMMAND ${CMAKE_COMMAND} -E echo "Done."
+   WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+   DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/yast.i
+)
+
+ADD_CUSTOM_TARGET( glue
+   DEPENDS ${SWIG_OUTPUT}
+)
+
+ADD_LIBRARY( ryast SHARED "${CMAKE_CURRENT_BINARY_DIR}/yast_wrap.cxx" )
+SET_TARGET_PROPERTIES( ryast PROPERTIES PREFIX "" )
+ADD_DEPENDENCIES( ryast glue )
+
+INCLUDE_DIRECTORIES( ${RUBY_INCLUDE_PATH} )
+INCLUDE_DIRECTORIES( ${YAST_INCLUDE_DIR} )
+TARGET_LINK_LIBRARIES( ryast ${YAST_LIBRARY} )
+TARGET_LINK_LIBRARIES( ryast ${YAST_YCP_LIBRARY} )
+TARGET_LINK_LIBRARIES( ryast ${YAST_PLUGIN_WFM_LIBRARY} )
+TARGET_LINK_LIBRARIES( ryast ${RUBY_LIBRARY} )
+
+INSTALL(TARGETS ryast LIBRARY DESTINATION ${RUBY_ARCH_DIR} )
\ No newline at end of file
diff --git a/src/swig/yast.i b/src/swig/yast.i
new file mode 100644 (file)
index 0000000..5562714
--- /dev/null
@@ -0,0 +1,218 @@
+%module ryast
+%include std_string.i
+%include stl.i
+ %{
+/* Includes the header in the wrapper code */
+#include <ycp/YCPCode.h>
+#include <ycp/YCPElement.h>
+#include <ycp/YCPExternal.h>
+#include <ycp/YCPValue.h>
+#include <ycp/YCPBoolean.h>
+#include <ycp/YCPList.h>
+#include <ycp/YCPMap.h>
+#include <ycp/YCPString.h>
+#include <ycp/YCPInteger.h>
+#include <ycp/YCPFloat.h>
+
+#include <y2/Y2ComponentBroker.h>
+#include <y2/Y2Namespace.h>
+#include <y2/Y2Component.h>
+#include <y2/Y2Function.h>
+#include <y2/SymbolEntry.h>
+#include <y2util/Ustring.h>
+#include <Y2.h>
+
+#include <ycp/Import.h>
+
+#include <ycp/pathsearch.h>
+
+#define y2log_component "Y2Ruby"
+
+//static swig_type_info _swigt__p_YCPValue;
+
+ %}
+
+#ifdef SWIGRUBY
+//%include "ruby.i"
+#endif
+
+%rename("+") "operator+";
+%rename("<<") "operator<<";
+%rename("!=") "operator!=";
+%rename("!") "operator!";
+%rename("==") "operator==";
+    
+%typemap(in) int YCPValue {
+  // TODO conver integers, and add support for lists, ah, and boleans!
+  switch (TYPE($1))
+  {
+    case T_STRING:
+    $result = YCPString(RSTRING ($1)->ptr);
+    break;
+    case T_TRUE:
+    $result = YCPBoolean(true);
+    break;
+    case T_FALSE:
+    $result = YCPBoolean(false);
+    break;
+    case T_FIXNUM:
+    $result = YCPInteger(NUM2LONG($1));
+    break;
+    case T_FLOAT:
+    $result = YCPFloat(NUM2DBL($1));
+    break;
+    case T_ARRAY:
+    // FIXME
+    break;
+    case T_HASH:
+    // FIXME
+    break;
+    case T_DATA:
+    rb_raise( rb_eRuntimeError, "Object");
+    break;
+  }
+  std::cout << TYPE($1) << std::endl;
+  rb_raise( rb_eRuntimeError, "Conversion of Ruby type not supported");
+  $result = YCPValue();
+}
+
+%typemap(out) YCPValue {
+  // TODO
+  // YT_BYTEBLOCK YT_PATH YT_SYMBOL YT_LIST YT_TERM YT_MAP YT_CODE YT_RETURN YT_BREAK YT_ENTRY YT_ERROR  YT_REFERENCE YT_EXTERNA
+  if($1->isVoid())
+  {
+    $result = Qnil;
+  }
+  else if($1->isBoolean())
+  {
+    $result = $1->asBoolean()->value() ? Qtrue : Qfalse;
+  }
+  else if($1->isString())
+  {
+    $result = rb_str_new2($1->asString()->value().c_str());
+  }
+  else if($1->isInteger())
+  {
+    $result = INT2NUM( $1->asInteger()->value() );
+  }
+  else if( $1->isMap() )
+  {
+    VALUE rbhash;
+    rbhash = rb_hash_new();
+    YCPMap map = $1->asMap();
+    printf("map size %d\n", map.size());
+    
+    for ( YCPMapIterator it = map.begin(); it != map.end(); ++it )
+    {
+      YCPValue *key = new YCPValue(it.key());
+      YCPValue *value = new YCPValue(it.value());
+      VALUE rkey = SWIG_NewPointerObj(key, SWIGTYPE_p_YCPValue, 1);
+      VALUE rvalue = SWIG_NewPointerObj(value, SWIGTYPE_p_YCPValue, 1);
+      rb_hash_aset(rbhash, rkey, rvalue );
+    }
+    $result = rbhash;
+  }
+  else if($1->isList())
+  {
+    VALUE rblist;
+    rblist = rb_ary_new();
+    YCPList list = $1->asList();
+    printf("list size %d\n",list.size()); 
+    for (int i=0; i < list.size(); i++)
+    {
+      YCPValue *value = new YCPValue(list.value(i));
+       VALUE rvalue = SWIG_NewPointerObj(value, SWIGTYPE_p_YCPValue, 1);
+      rb_ary_push( rblist, rvalue);
+    }
+    $result = rblist;
+  }
+  rb_raise( rb_eRuntimeError, "Conversion of YCP type %s not supported", $1->toString().c_str() );
+  $result = Qnil;
+}
+
+//%include "y2util/RepDef.h"
+%include "y2/Y2ComponentBroker.h"
+%include "y2/Y2Namespace.h"
+%include "y2/Y2Component.h"
+%include "y2/Y2Function.h"
+
+%ignore SymbolEntryPtr::_nameHash;
+%ignore SymbolEntry::emptyUstring;
+    
+class SymbolEntry
+{
+public:
+    //static UstringHash* _nameHash;
+    //static Ustring emptyUstring;
+public:
+    typedef enum {
+       c_unspec = 0,           //  0 unspecified local symbol (sets m_global = false)
+       c_global,               //  1 unspecified global symbol (translates to c_unspec, sets m_global = true)
+       c_module,               //  2 a module identifier
+       c_variable,             //  3 a variable
+       c_reference,            //  4 a reference to a variable
+       c_function,             //  5 a defined function
+       c_builtin,              //  6 a builtin function
+       c_typedef,              //  7 a type
+       c_const,                //  8 a constant (a read-only c_variable)
+       c_namespace,            //  9 a namespace identifier
+       c_self,                 // 10 the current namespace (namespace prefix used in namespace definition)
+       c_predefined,           // 11 a predefined namespace identifier
+       c_filename              // 12 a filename (used in conjunction with TableEntry to store definition locations)
+    } category_t;
+public:
+    // create symbol beloging to namespace (at position)
+    SymbolEntry (const Y2Namespace* name_space, unsigned int position, const char *name, category_t cat, constTypePtr type);
+
+    virtual ~SymbolEntry ();
+
+    // symbols link to the defining namespace
+    const Y2Namespace *nameSpace () const;
+    void setNamespace (const Y2Namespace *name_space);
+    virtual bool onlyDeclared () const { return false; }
+
+    unsigned int position () const;
+    void setPosition (unsigned int position);
+
+    bool isGlobal () const;
+    void setGlobal (bool global);
+
+    bool isModule () const { return m_category == c_module; }
+    bool isVariable () const { return m_category == c_variable; }
+    bool isReference () const { return m_category == c_reference; }
+    bool isFunction () const { return m_category == c_function; }
+    bool isBuiltin () const { return m_category == c_builtin; }
+    bool isNamespace () const { return m_category == c_namespace; }
+    bool isSelf () const { return m_category == c_self; }
+    bool isFilename () const { return m_category == c_filename; }
+    bool isPredefined () const { return m_category == c_predefined; }
+
+    bool likeNamespace () const { return isModule() || isNamespace() || isSelf(); }
+
+    const char *name () const;
+    category_t category () const;
+    void setCategory (category_t cat);
+    constTypePtr type () const;
+    string catString () const;
+    void setType (constTypePtr type);
+    YCPValue setValue (YCPValue value);
+    YCPValue value () const;
+    
+    void push ();
+    void pop ();
+
+    virtual string toString (bool with_type = true) const;
+};
+
+template < typename T >
+class Ptr {
+  public:
+  T *operator->();
+};
+%template (SymbolEntryPtr) Ptr<SymbolEntry>;
+
+%include "ycp/SymbolTable.h"
+
+%include "ycp/Import.h"
+%include "ycp/Type.h"
+%include "ycp/TypePtr.h"
diff --git a/yast2-ruby-bindings.spec.in b/yast2-ruby-bindings.spec.in
new file mode 100644 (file)
index 0000000..b481aae
--- /dev/null
@@ -0,0 +1,57 @@
+
+
+#
+# spec file for package yast2-ruby-bindings (Version 0.1)
+#
+# norootforbuild
+
+Name:           yast2-ruby-bindings
+Version:        @VERSION@
+Release:        0
+License:        GPL
+Group:          System/YaST
+BuildRoot:      %{_tmppath}/%{name}-%{version}-build
+Source0:        yast2-ruby-bindings-%{version}.tar.bz2
+prefix:         /usr
+
+BuildRequires: swig cmake gcc-c++ libxcrypt-devel yast2-core-devel yast2-devtools
+# libzypp-devel is missing .la requires
+BuildRequires: ruby-devel
+Requires:      yast2-core >= 2.13.28
+Requires:      ruby
+Summary:       Ruby bindings for the YaST platform.
+
+%description
+The bindings allow YaST modules to be written using the Ruby language and also
+Ruby scripts can use YaST agents, APIs and modules.
+
+Author
+
+%prep
+%setup -n yast2-ruby-bindings-%{version}
+%build
+mkdir build
+cd build
+cmake -DCMAKE_INSTALL_PREFIX=%{prefix} \
+      -DLIB=%{_lib} \
+      -DCMAKE_C_FLAGS="%{optflags}" \
+      -DCMAKE_CXX_FLAGS="%{optflags}" \
+      -DCMAKE_BUILD_TYPE=Release \
+      -DCMAKE_SKIP_RPATH=1 \
+      ..
+make %{?jobs:-j %jobs}
+
+%install
+cd build
+make install DESTDIR=$RPM_BUILD_ROOT
+cd ..
+
+%clean
+rm -rf "$RPM_BUILD_ROOT"
+
+@CLEAN@
+
+%files
+%defattr (-, root, root)
+%{_libdir}/YaST2/plugin/libpy2lang_ruby.so
+%{_libdir}/ruby/vendor_ruby/%{rb_ver}/%{rb_arch}/yast.so