summaryrefslogtreecommitdiff
path: root/ext/dl
diff options
context:
space:
mode:
authorttate <ttate@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2002-04-02 10:56:13 +0000
committerttate <ttate@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2002-04-02 10:56:13 +0000
commit7d711b817e62eb6c8dee01ea2283fcb1ad90f8ac (patch)
treeb1b54ab76217775071de4e3669674aa7887aa6b9 /ext/dl
parent64b6406445e53f187d2982f87becff8065edd0cc (diff)
Add ruby-dl
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@2324 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'ext/dl')
-rw-r--r--ext/dl/Changes166
-rw-r--r--ext/dl/MANIFEST24
-rw-r--r--ext/dl/README186
-rw-r--r--ext/dl/README.html247
-rw-r--r--ext/dl/depend45
-rw-r--r--ext/dl/dl.c655
-rw-r--r--ext/dl/dl.def70
-rw-r--r--ext/dl/dl.h282
-rw-r--r--ext/dl/extconf.rb197
-rw-r--r--ext/dl/h2rb500
-rw-r--r--ext/dl/handle.c207
-rw-r--r--ext/dl/install.rb49
-rw-r--r--ext/dl/lib/dl/import.rb228
-rw-r--r--ext/dl/lib/dl/win32.rb26
-rw-r--r--ext/dl/mkcall.rb68
-rw-r--r--ext/dl/mkcallback.rb83
-rw-r--r--ext/dl/mkcbtable.rb42
-rw-r--r--ext/dl/ptr.c1075
-rw-r--r--ext/dl/sample/drives.rb70
-rw-r--r--ext/dl/sample/getch.rb5
-rw-r--r--ext/dl/sample/libc.rb84
-rw-r--r--ext/dl/sample/msgbox.rb19
-rw-r--r--ext/dl/sample/msgbox2.rb18
-rw-r--r--ext/dl/sample/stream.rb87
-rw-r--r--ext/dl/sym.c771
-rw-r--r--ext/dl/test/libtest.def29
-rw-r--r--ext/dl/test/test.c251
-rw-r--r--ext/dl/test/test.rb272
-rw-r--r--ext/dl/type.rb115
29 files changed, 5871 insertions, 0 deletions
diff --git a/ext/dl/Changes b/ext/dl/Changes
new file mode 100644
index 0000000000..d02dd9ed42
--- /dev/null
+++ b/ext/dl/Changes
@@ -0,0 +1,166 @@
+1.0.2 -> 1.1.0
+---------------
+* Use inline assembler for gcc on ix86 as a default.
+* Add DL::Importable module (defined in "dl/import.rb").
+
+1.0.1 -> 1.0.2
+--------------
+* Fix an installation problem, thanks to Nakada.
+* Handle#to_i, to_ptr is added.
+* lib/dl/win32.rb is added for Win32API compatibility.
+
+1.0 -> 1.0.1
+-------------
+* Fix PtrData#to_s.
+* Add PtrData#to_str(size).
+
+0.9 -> 1.0 (stable)
+-------------------
+* PtrData object keeps size data if possible.
+* PtrData#size is added.
+* LoadLibary(), FreeLibrary() and GetProcAddress() are used on
+ windows instead of dlopen(),dlclose() and dlsym().
+
+0.8.1 -> 0.9
+------------
+* You can use inline assembler to constracting the function calls by
+ building the library using --with-asm options for extconf.rb.
+ But now this can work with GNU assembler on i386 machines. If you try
+ this mechanism, you can get rid of the limit of number of parameters.
+
+0.8 -> 0.8.1
+-------------
+* rb_dlsym_call() calls xmalloc() or dlmalloc() instread of
+ alloca() because of portability.
+
+* 'h2rb' get to parse the enumeration type.
+
+0.7.1 -> 0.8
+-------------
+* If <type> of Symbol.new(addr, name = nil, type = nil) is nil,
+ it returns a DataPtr object.
+
+* PtrData#[] returns the memory image, and PtrData#[]= is used
+ to copy the memory image to specified area.
+
+* Handle#sym(symbol_name) returns a PtrData object, and
+ Handle#sym(symbol_name, type_spec) returns a Symbol object.
+
+* We can use the number following a type specifier to represent the
+ length of an array. For example, if you'd like to represent the
+ following structure, we use 'C1024I' when using PtrData#struct! or
+ PtrData#union!.
+
+ struct {
+ char name[1024];
+ int age;
+ }
+
+0.6 -> 0.7
+-----------
+* type `ANY_FUNC' is removed, it is because the method for
+ calling the function obtained by dlsym() was changed.
+
+* `char', `short' and `float' types are supported, if you'd
+ like to use those type exactly and run extconf.rb with the
+ option `--with-type-{char,short,float}'.
+
+* `DL.sizeof(type)' returns the size of the <type> with the
+ alignment. so `DL.sizeof("C") + DL.sizeof("L")' is not equal
+ to `DL.sizeof("CL")'. it is assumed that the latter returns
+ the enough size for the following structure:
+ struct foo { char x; long y; }
+ but it may not equal to `sizeof(struct foo)' of C.
+
+* new methods:
+ - PtrData#define_data_type
+ - PtrData#struct!
+ - PtrData#union!
+ - PtrData#data_type
+ - PtrData#[]
+ - PtrData#[]=
+ - Symbol.new
+
+0.5 -> 0.6
+-----------
+* DL.set_callback is changed.
+ - set_callback(type,num,proc) [old style]
+ - set_callback(type,num){...} [new style]
+
+* the handle object don't call dlclose() at the time of GC.
+ if you need to call dlclose(), use Handle#enable_close.
+
+* new methods:
+ - PtrData#{+,-}
+ - PtrData#{+@,-@} (= PtrData#{ptr,ref})
+ - DL.malloc
+ - DL.strdup
+ - MemorySpace.each
+
+* some memory leaks are fixed.
+
+0.4 -> 0.5
+----------
+* new methods:
+ - DL.dlopen (= DL::Handle.new)
+ - PtrData#ref
+ - PtrData#==
+ - PtrData#eql?
+ - String#to_ptr
+ - Handle#enable_close
+ - Handle#disable_close
+
+* PtrData#ptr returns the pointed value (*ptr).
+
+* PtrData#ref returns the reference (&ptr).
+ `ptr.ref.ptr == ptr' must be true.
+
+(experiment)
+* the callback function is supported.
+
+* new methods:
+ - DL.set_callback
+ - DL.get_callback
+
+
+0.3 -> 0.4
+----------
+* Symbol#call supports the mechanism for converting any object
+ to PtrData object automatically using 'to_ptr' method, if the
+ argument type is 'P' or 'p'.
+
+* new methods are added.
+ - Array#to_ptr
+ - IO#to_ptr
+ - Symbol#to_ptr
+ - Symbol#[]
+
+* new constant is added.
+ - DL::FREE is a symbol object for representing the function 'free'.
+
+* the specification of PtrData#free was changed.
+
+* new internal functions are added.
+ - rb_dlptr2cptr()
+ - rb_dlsym2csym()
+ - rb_dlsym_new()
+
+* 'dl.h' is new file.
+
+* 'extconf.rb' and 'depend' ware modified so that we can build
+ the library in a directory except 'srcdir' (by N. Nakada).
+
+* (experimental) 'h2rb' is a new script which converts C header
+ files into ruby scripts.
+
+0.2 -> 0.3
+----------
+* many useful functions ware added (by Akinori Musha).
+
+* the type of 'long' was supported (by Akinori Musha).
+
+* some methods of the PtrData ware added for handling pointer data.
+
+* bug fix for mutable int and long.
+
+* the type of array was supported.
diff --git a/ext/dl/MANIFEST b/ext/dl/MANIFEST
new file mode 100644
index 0000000000..a98d6f5b34
--- /dev/null
+++ b/ext/dl/MANIFEST
@@ -0,0 +1,24 @@
+.cvsignore
+Changes
+MANIFEST
+README
+README.html
+depend
+dl.c
+dl.h
+dl.def
+extconf.rb
+h2rb
+handle.c
+mkcall.rb
+mkcallback.rb
+mkcbtable.rb
+ptr.c
+sym.c
+type.rb
+test/test.c
+test/test.rb
+sample/drives.rb
+sample/getch.rb
+sample/libc.rb
+sample/msgbox.rb
diff --git a/ext/dl/README b/ext/dl/README
new file mode 100644
index 0000000000..2463e48bb7
--- /dev/null
+++ b/ext/dl/README
@@ -0,0 +1,186 @@
+ Ruby/DL
+
+ an interface to dynamic linking loader
+-------------------------------------------------------------------------------
+
+Ruby/DL
+
+`Ruby/DL' provides an interface to the dynamic linking loader.
+-------------------------------------------------------------------------------
+
+Installing
+
+ $ ruby extconf.rb # to create the Makefile
+ $ make # to build the library 'dl.so'
+ $ make libtest.so # to build the C library 'libtest.so' for the test script
+ $ make test # to run the test script
+ $ make install # to install the library
+ $ make clean # to remove the created files without Makefile
+ $ make distclean # to remove the all created files
+
+-------------------------------------------------------------------------------
+
+Functions and Classes
+
+after loading the `dl' library, we get access to the module called `DL'. the DL
+module has the following constants, functions and classes.
+
+Constants
+
+VERSION
+MAJOR_VERSION
+MINOR_VERSION
+PATCH_VERSION
+RTLD_GLOBAL
+RTLD_LAZY
+RTLD_NOW
+MAX_ARG
+MAX_CBARG
+MAX_CBENT
+
+Functions
+
+handle = dlopen(lib){|handle| ... }
+ is quite equal to `Handle.new(lib)'
+sym = set_callback(cbtype, entry){|args| ... }
+sym = set_callback(cbtype, entry, proc)
+ makes entry-th pre-defined function to call the proc or given block. the
+ entry-th pre-defined function is specified by cbtype and entry. cbtype is a
+ prototype of the callback. see also the section `Type specifiers' about
+ cbtype.
+sym = get_callback(cbtype, entry)
+ returns the Proc object which is given by the above function
+ `set_callback'.
+ptr = malloc(size, [free = nil])
+ allocates the size bytes, and returns the pointer as a PtrData object ptr.
+ptr = strdup(str)
+ returns a PtrData object ptr which represents the pointer to a new string
+ which is a duplicate of the string str.
+size = sizeof(type)
+ returns the size of type. `sizeof("C") + sizeof("L")' is not equal to
+ `sizeof("CL")'. the latter is assumed to returns the enough size of the
+ structure `struct foo { char c; long l; }', but the size may not equal to
+ `sizeof(foo)' of C.
+
+class Handle
+
+handle = Handle.new(lib){|handle| ... }
+ opens a library lib and returns a Handle object handle. if a block is
+ given, the handle is automatically closed as the block ends.
+Handle#close
+ closes the handle opened by the above Handle.new(lib).
+sym = Handle#sym(func, prototype = "0")
+sym = Handle#[func, prototype = nil]
+ obtains the pointer to a function called func and returns a Symbol object
+ or a DataPtr object. prototype is a string which consists of type
+ specifiers, it indicates the function's prototype. see also the section
+ `Type specifiers'.
+
+class Symbol
+
+sym = Symbol.new(addr, type = nil, name = nil)
+ creates the Symbol object sym with the type type if type is not nil. addr
+ is the address where the function is allocated. If type is nil, it returns
+ a DataPtr object.
+Symbol::char2type(char)
+ takes a character char that represents a type and returns the type
+ specifier of the C language.
+str = Symbol#proto()
+ returns the function prototype.
+str = Symbol#name()
+ Returns the function name.
+str = Symbol#cproto()
+str = Symbol#to_s()
+ returns the prototype of the C language.
+str = Symbol#inspect()
+ returns the inspectable string.
+r,rs = Symbol#call(arg1,arg2,...,argN)
+r,rs = Symbol#[](arg1,arg2,...,argN)
+ calls the function with parameters arg1, arg2, ..., argN. and the result
+ consists of the return value r and parameters rs. rs is an array.
+ptr = Symbol#to_ptr
+ returns the corresponding PtrData object ptr.
+
+class PtrData
+
+ptr = PtrData.new(addr, [free = nil])
+ returns the PtrData object representing the pointer which indicates the
+ address addr. GC frees the memory using the free function.
+PtrData#free=(sym)
+ if you specify a symbol object sym, GC frees the memory using the function
+ represented by sym.
+sym = PtrData#free
+ returns a symbol object sym which is used when GC frees the memory. it
+ usually configured by `PtrData#free=' or `PtrData.new'.
+size = PtrData#size, PtrData#size=(size)
+ gets and sets allocated size of the memory.
+ary = PtrData#to_a(type, [size])
+ returns an array of the type which specified with type. type must be one of
+ 'S','P','I','L','D' and 'F'.
+str = PtrData#to_s([len])
+ returns a string which length is len. if len is omitted, the end of the
+ string is '\0'.
+ptr = PtrData#ptr,+@
+ returns the pointed value as a PtrData object ptr.
+ptr = PtrData#ref,-@
+ returns the reference as a PtrData object ptr.
+ptr = PtrData#+
+ returns the PtrData object
+ptr = PtrData#-
+ returns the PtrData object
+PtrData#struct!(type, *members)
+ defines the data type to get access to a structure member with a symbol.
+ (see also PtrData#[])
+PtrData#union!(type, *members)
+ defines the data type to get access to a union member with a symbol. (see
+ also PtrData#[])
+val = PtrData#[key], PtrData#[key, num = 0]
+ if the key is a string or symbol, this method returns the value of the
+ structure/union member which has the type defined by PtrData#
+ {struct!,union!}. if the key is a integer value and this object represents
+ the pointer ptr, it returns the value of `(ptr + key).to_s(num)'
+PtrData#[key,num]=val, PtrData#[key]=val
+ if the key is a string or symbol, this method substitute the value of the
+ structure/union member with val. if the key is a integer value and val is a
+ string, this method copies num bytes of val to the memory area ptr using
+ memcpy(3).
+
+-------------------------------------------------------------------------------
+
+Type specifiers
+
+the prototype consists of the following type specifiers, first element of
+prototype represents the type of return value, and remaining elements represent
+the type of each argument.
+
+ C : a character (char)
+ c : a pointer to a character (char *)
+ H : a short integer (short)
+ h : a pointer to a short integer (short *)
+ I : an integer (char, short, int)
+ i : a pointer to an integer (char *, short *, int *)
+ L : a long integer (long)
+ l : a pointer to a long integer (long *)
+ F : a real (float)
+ f : a pointer to a real (float *)
+ D : a real (double)
+ d : a pointer to a real (double *)
+ S : an immutable string (const char *)
+ s : a mutable string (char *)
+ A : an array (const type[])
+ a : a mutable array (type[])
+ P : a pointer (void *)
+ p : a mutable object (void *)
+ 0 : void function (this must be a first character of the prototype)
+
+the cbtype consists of type specifiers 0, I, L, D and P.
+for example:
+
+ DL.set_callback('IPP',0){|ptr1,ptr2|
+ str1 = ptr1.ptr.to_s
+ str2 = ptr2.ptr.to_s
+ return str1 <=> str2
+ }
+
+-------------------------------------------------------------------------------
+ttate@kt.jaist.ac.jp
diff --git a/ext/dl/README.html b/ext/dl/README.html
new file mode 100644
index 0000000000..b248cce1f3
--- /dev/null
+++ b/ext/dl/README.html
@@ -0,0 +1,247 @@
+<html>
+<head><title>Ruby/DL</title></head>
+<body>
+<center>
+ <h2>Ruby/DL</h2>
+ an interface to dynamic linking loader
+</center>
+
+<hr>
+<h2>Ruby/DL</h2>
+
+`Ruby/DL' provides an interface to the dynamic linking loader.
+
+<hr>
+<h2>Installing</h2>
+
+<blockquote>
+<pre>
+$ ruby extconf.rb # to create the Makefile
+$ make # to build the library 'dl.so'
+$ make libtest.so # to build the C library 'libtest.so' for the test script
+$ make test # to run the test script
+$ make install # to install the library
+$ make clean # to remove the created files without Makefile
+$ make distclean # to remove the all created files
+</pre>
+</blockquote>
+
+<hr>
+<h2>Functions and Classes</h2>
+
+after loading the `dl' library, we get access to the module called `DL'.
+the DL module has the following constants, functions and classes.
+
+<h2>Constants</h2>
+
+VERSION<br>
+MAJOR_VERSION<br>
+MINOR_VERSION<br>
+PATCH_VERSION<br>
+RTLD_GLOBAL<br>
+RTLD_LAZY<br>
+RTLD_NOW<br>
+MAX_ARG<br>
+MAX_CBARG<br>
+MAX_CBENT<br>
+
+<h2>Functions</h2>
+
+<dl>
+ <dt>handle = dlopen(lib){|handle| ... }</dt>
+ <dd>is quite equal to `Handle.new(lib)'
+
+ <dt>sym = set_callback(cbtype, entry){|args| ... }
+ <dt>sym = set_callback(cbtype, entry, proc)
+ <dd>makes <u>entry</u>-th pre-defined function to call the <u>proc</u>
+ or given block.
+ the <u>entry</u>-th pre-defined function is specified by
+ <u>cbtype</u> and <u>entry</u>.
+ <u>cbtype</u> is a prototype of the callback.
+ see also the section `Type specifiers' about <u>cbtype</u>.
+
+ <dt>sym = get_callback(cbtype, entry)
+ <dd>returns the Proc object which is given by the above function `set_callback'.
+
+ <dt>ptr = malloc(size, [free = nil])
+ <dd>allocates the <u>size</u> bytes, and returns the pointer as a
+ PtrData object <u>ptr</u>.
+
+ <dt>ptr = strdup(str)
+ <dd>returns a PtrData object <u>ptr</u> which represents the pointer to
+ a new string which is a duplicate of the string <u>str</u>.
+
+ <dt>size = sizeof(type)
+ <dd>returns the size of <u>type</u>. `sizeof("C") + sizeof("L")' is not
+ equal to `sizeof("CL")'. the latter is assumed to returns the
+ enough size of the structure `struct foo { char c; long l; }',
+ but the size may not equal to `sizeof(foo)' of C.
+</dl>
+
+<h2>class Handle</h2>
+
+<dl>
+ <dt>handle = Handle.new(lib){|handle| ... }</dt>
+ <dd>opens a library <u>lib</u> and returns a Handle object
+ <u>handle</u>. if a block is given, the handle is
+ automatically closed as the block ends.
+
+ <dt>Handle#close
+ <dd>closes the handle opened by the above Handle.new(lib).
+
+ <dt>sym = Handle#sym(func, prototype = "0")
+ <dt>sym = Handle#[func, prototype = nil]
+ <dd>obtains the pointer to a function called <u>func</u> and returns
+ a Symbol object or a DataPtr object.
+ <u>prototype</u> is a string which consists of type specifiers,
+ it indicates the function's prototype.
+ see also the section `Type specifiers'.
+</dl>
+
+<h2>class Symbol</h2>
+
+<dl>
+ <dt>sym = Symbol.new(addr, type = nil, name = nil)
+ <dd>creates the Symbol object <u>sym</u> with the type <u>type</u>
+ if <u>type</u> is not nil. <u>addr</u> is the address where the
+ function is allocated. If <u>type</u> is nil, it returns a DataPtr
+ object.
+
+ <dt>Symbol::char2type(char)
+ <dd>takes a character <u>char</u> that represents a type and returns
+ the type specifier of the C language.
+
+ <dt>str = Symbol#proto()
+ <dd>returns the function prototype.
+
+ <dt>str = Symbol#name()
+ <dd>Returns the function name.
+
+ <dt>str = Symbol#cproto()
+ <dt>str = Symbol#to_s()
+ <dd>returns the prototype of the C language.
+
+ <dt>str = Symbol#inspect()
+ <dd>returns the inspectable string.
+
+ <dt>r,rs = Symbol#call(arg1,arg2,...,argN)
+ <dt>r,rs = Symbol#[](arg1,arg2,...,argN)
+ <dd>calls the function with parameters arg1, arg2, ..., argN.
+ and the result consists of the return value <u>r</u> and
+ parameters <u>rs</u>. <u>rs</u> is an array.
+
+ <dt>ptr = Symbol#to_ptr
+ <dd>returns the corresponding PtrData object <u>ptr</u>.
+</dl>
+
+<h2>class PtrData</h2>
+
+<dl>
+ <dt>ptr = PtrData.new(addr, [free = nil])
+ <dd>returns the PtrData object representing the pointer which
+ indicates the address <u>addr</u>.
+ GC frees the memory using the <u>free</u> function.
+
+ <dt>PtrData#free=(sym)
+ <dd>if you specify a symbol object <u>sym</u>, GC frees the memory
+ using the function represented by <u>sym</u>.
+
+ <dt>sym = PtrData#free
+ <dd>returns a symbol object <u>sym</u> which is used when GC frees
+ the memory. it usually configured by `PtrData#free=' or `PtrData.new'.
+
+ <dt>size = PtrData#size, PtrData#size=(size)
+ <dd>gets and sets allocated size of the memory.
+
+ <dt>ary = PtrData#to_a(type, [size])
+ <dd>returns an array of the type which specified with <u>type</u>.
+ <u>type</u> must be one of 'S','P','I','L','D' and 'F'.
+
+ <dt>str = PtrData#to_s([len])
+ <dd>returns a string which length is <u>len</u>. if <u>len</u>
+ is omitted, the end of the string is '\0'.
+
+ <dt>ptr = PtrData#ptr,+@
+ <dd>returns the pointed value as a PtrData object <u>ptr</u>.
+
+ <dt>ptr = PtrData#ref,-@
+ <dd>returns the reference as a PtrData object <u>ptr</u>.
+
+ <dt>ptr = PtrData#+
+ <dd>returns the PtrData object
+
+ <dt>ptr = PtrData#-
+ <dd>returns the PtrData object
+
+ <dt>PtrData#struct!(type, *members)
+ <dd>defines the data type to get access to a structure member with a symbol.
+ (see also PtrData#[])
+
+ <dt>PtrData#union!(type, *members)
+ <dd>defines the data type to get access to a union member with a symbol.
+ (see also PtrData#[])
+
+ <dt>val = PtrData#[key], PtrData#[key, num = 0]
+ <dd>if the <u>key</u> is a string or symbol, this method returns the
+ value of the structure/union member which has the type defined by
+ PtrData#{struct!,union!}.
+ if the <u>key</u> is a integer value and this object represents
+ the pointer <u>ptr</u>, it returns the value of
+ `(<u>ptr</u> + <u>key</u>).to_s(num)'
+
+ <dt>PtrData#[key,num]=val, PtrData#[key]=val
+ <dd>if the <u>key</u> is a string or symbol, this method substitute
+ the value of the structure/union member with <u>val</u>.
+ if the <u>key</u> is a integer value and <u>val</u> is a string,
+ this method copies <u>num</u> bytes of <u>val</u> to the memory
+ area <u>ptr</u> using memcpy(3).
+</dl>
+
+<hr>
+<h2>Type specifiers</h2>
+
+the <u>prototype</u> consists of the following type specifiers,
+first element of <u>prototype</u> represents the type of return value,
+and remaining elements represent the type of each argument.
+
+<blockquote>
+ C : a character (char)<br>
+ c : a pointer to a character (char *)<br>
+ H : a short integer (short)<br>
+ h : a pointer to a short integer (short *)<br>
+ I : an integer (char, short, int)<br>
+ i : a pointer to an integer (char *, short *, int *)<br>
+ L : a long integer (long)<br>
+ l : a pointer to a long integer (long *)<br>
+ F : a real (float)<br>
+ f : a pointer to a real (float *)<br>
+ D : a real (double)<br>
+ d : a pointer to a real (double *)<br>
+ S : an immutable string (const char *)<br>
+ s : a mutable string (char *)<br>
+ A : an array (const type[])<br>
+ a : a mutable array (type[])<br>
+ P : a pointer (void *)<br>
+ p : a mutable object (void *)<br>
+ 0 : void function
+ (this must be a first character of the <u>prototype</u>)<br>
+</blockquote>
+
+the <u>cbtype</u> consists of type specifiers 0, I, L, D and P.
+<br>
+for example:
+<blockquote>
+<pre>
+DL.set_callback('IPP',0){|ptr1,ptr2|
+ str1 = ptr1.ptr.to_s
+ str2 = ptr2.ptr.to_s
+ return str1 <=> str2
+}
+</pre>
+</blockquote>
+
+<hr>
+<i>ttate@kt.jaist.ac.jp</i>
+
+</body>
+</html> \ No newline at end of file
diff --git a/ext/dl/depend b/ext/dl/depend
new file mode 100644
index 0000000000..50ef5cebbf
--- /dev/null
+++ b/ext/dl/depend
@@ -0,0 +1,45 @@
+RUBY = $(RUBY_INSTALL_NAME)$(EXEEXT)
+CLEANFILES = test/test.o
+DISTCLEANFILES = call.func callback.func cbtable.func dlconfig.rb dlconfig.h \
+ test/libtest.so test/*~ *~ mkmf.log
+
+libtest.so: test/libtest.so
+
+test/libtest.so: test/test.o test/libtest.def
+ $(RUBY) -rftools -e 'ARGV.each{|d|File.mkpath(File.dirname(d))}' $@
+ `$(RUBY) -e 'print ARGV.join(" ").gsub(/dl\\.def/,"test/libtest.def")' $(LDSHARED)` $(LDFLAGS) test/test.o -o test/libtest.so
+
+test/test.o: test/test.c
+ @$(RUBY) -rftools -e 'File.mkpath(*ARGV)' test
+ $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
+
+test:: dl.so libtest.so .force
+ $(RUBY) -I. -I$(srcdir)/lib $(srcdir)/test/test.rb
+
+.force:
+
+.PHONY: .force test
+
+allclean: distclean
+ @rm -f $(CLEANFILES) $(DISTCLEANFILES)
+
+$(OBJS): dlconfig.h
+
+sym.o: call.func
+
+dl.o: callback.func cbtable.func
+
+call.func: mkcall.rb dlconfig.rb
+ @echo "Generating call.func"
+ @$(RUBY) $< > $@
+
+callback.func: mkcallback.rb dlconfig.rb
+ @echo "Generating callback.func"
+ @$(RUBY) $< > $@
+
+cbtable.func: mkcbtable.rb dlconfig.rb
+ @echo "Generating cbtable.func"
+ @$(RUBY) $< > $@
+
+debug:
+ $(MAKE) CFLAGS+=-DDEBUG
diff --git a/ext/dl/dl.c b/ext/dl/dl.c
new file mode 100644
index 0000000000..b8e7381f83
--- /dev/null
+++ b/ext/dl/dl.c
@@ -0,0 +1,655 @@
+/*
+ * $Id$
+ */
+
+#include <ruby.h>
+#include <rubyio.h>
+#include "dl.h"
+
+VALUE rb_mDL;
+VALUE rb_eDLError;
+VALUE rb_eDLTypeError;
+
+static VALUE DLFuncTable;
+static void *rb_dl_func_table[MAX_CALLBACK_TYPE][MAX_CALLBACK];
+static ID id_call;
+
+#include "callback.func"
+
+static void
+init_dl_func_table(){
+#include "cbtable.func"
+};
+
+void *
+dlmalloc(size_t size)
+{
+ DEBUG_CODE2({
+ void *ptr;
+
+ printf("dlmalloc(%d)",size);
+ ptr = xmalloc(size);
+ printf(":0x%x\n",ptr);
+ return ptr;
+ },
+ {
+ return xmalloc(size);
+ });
+};
+
+void *
+dlrealloc(void *ptr, size_t size)
+{
+ DEBUG_CODE({
+ printf("dlrealloc(0x%x,%d)\n",ptr,size);
+ });
+ return xrealloc(ptr, size);
+};
+
+void
+dlfree(void *ptr)
+{
+ DEBUG_CODE({
+ printf("dlfree(0x%x)\n",ptr);
+ });
+ xfree(ptr);
+};
+
+char*
+dlstrdup(const char *str)
+{
+ char *newstr;
+
+ newstr = (char*)dlmalloc(strlen(str));
+ strcpy(newstr,str);
+
+ return newstr;
+};
+
+size_t
+dlsizeof(const char *cstr)
+{
+ size_t size;
+ int i, len, n, dlen;
+ char *d;
+
+ len = strlen(cstr);
+ size = 0;
+ for( i=0; i<len; i++ ){
+ n = 1;
+ if( isdigit(cstr[i+1]) ){
+ dlen = 1;
+ while( isdigit(cstr[i+dlen]) ){ dlen ++; };
+ dlen --;
+ d = ALLOCA_N(char, dlen + 1);
+ strncpy(d, cstr + i + 1, dlen);
+ d[dlen] = '\0';
+ n = atoi(d);
+ }
+ else{
+ dlen = 0;
+ };
+
+ switch( cstr[i] ){
+ case 'I':
+ DLALIGN(0,size,INT_ALIGN);
+ case 'i':
+ size += sizeof(int) * n;
+ break;
+ case 'L':
+ DLALIGN(0,size,LONG_ALIGN);
+ case 'l':
+ size += sizeof(long) * n;
+ break;
+ case 'F':
+ DLALIGN(0,size,FLOAT_ALIGN);
+ case 'f':
+ size += sizeof(float) * n;
+ break;
+ case 'D':
+ DLALIGN(0,size,DOUBLE_ALIGN);
+ case 'd':
+ size += sizeof(double) * n;
+ break;
+ case 'C':
+ case 'c':
+ size += sizeof(char) * n;
+ break;
+ case 'H':
+ DLALIGN(0,size,SHORT_ALIGN);
+ case 'h':
+ size += sizeof(short) * n;
+ break;
+ case 'P':
+ DLALIGN(0,size,VOIDP_ALIGN);
+ case 'p':
+ size += sizeof(void*) * n;
+ break;
+ default:
+ rb_raise(rb_eDLTypeError, "unexpected type '%c'", cstr[i]);
+ break;
+ };
+ i += dlen;
+ };
+
+ return size;
+};
+
+static float *
+c_farray(VALUE v, long *size)
+{
+ int i, len;
+ float *ary;
+ VALUE e;
+
+ len = RARRAY(v)->len;
+ *size = sizeof(float) * len;
+ ary = dlmalloc(*size);
+ for( i=0; i < len; i++ ){
+ e = rb_ary_entry(v, i);
+ switch( TYPE(e) ){
+ case T_FLOAT:
+ ary[i] = (float)(RFLOAT(e)->value);
+ break;
+ case T_NIL:
+ ary[i] = 0.0;
+ break;
+ default:
+ rb_raise(rb_eDLTypeError, "unexpected type of the element #%d", i);
+ break;
+ };
+ };
+
+ return ary;
+};
+
+static double *
+c_darray(VALUE v, long *size)
+{
+ int i, len;
+ double *ary;
+ VALUE e;
+
+ len = RARRAY(v)->len;
+ *size = sizeof(double) * len;
+ ary = dlmalloc(*size);
+ for( i=0; i < len; i++ ){
+ e = rb_ary_entry(v, i);
+ switch( TYPE(e) ){
+ case T_FLOAT:
+ ary[i] = (double)(RFLOAT(e)->value);
+ break;
+ case T_NIL:
+ ary[i] = 0.0;
+ break;
+ default:
+ rb_raise(rb_eDLTypeError, "unexpected type of the element #%d", i);
+ break;
+ };
+ };
+
+ return ary;
+};
+
+static long *
+c_larray(VALUE v, long *size)
+{
+ int i, len;
+ long *ary;
+ VALUE e;
+
+ len = RARRAY(v)->len;
+ *size = sizeof(long) * len;
+ ary = dlmalloc(*size);
+ for( i=0; i < len; i++ ){
+ e = rb_ary_entry(v, i);
+ switch( TYPE(e) ){
+ case T_FIXNUM:
+ case T_BIGNUM:
+ ary[i] = (long)(NUM2INT(e));
+ break;
+ case T_NIL:
+ ary[i] = 0;
+ break;
+ default:
+ rb_raise(rb_eDLTypeError, "unexpected type of the element #%d", i);
+ break;
+ };
+ };
+
+ return ary;
+};
+
+static int *
+c_iarray(VALUE v, long *size)
+{
+ int i, len;
+ int *ary;
+ VALUE e;
+
+ len = RARRAY(v)->len;
+ *size = sizeof(int) * len;
+ ary = dlmalloc(*size);
+ for( i=0; i < len; i++ ){
+ e = rb_ary_entry(v, i);
+ switch( TYPE(e) ){
+ case T_FIXNUM:
+ case T_BIGNUM:
+ ary[i] = (int)(NUM2INT(e));
+ break;
+ case T_NIL:
+ ary[i] = 0;
+ break;
+ default:
+ rb_raise(rb_eDLTypeError, "unexpected type of the element #%d", i);
+ break;
+ };
+ };
+
+ return ary;
+};
+
+static short *
+c_harray(VALUE v, long *size)
+{
+ int i, len;
+ short *ary;
+ VALUE e;
+
+ len = RARRAY(v)->len;
+ *size = sizeof(short) * len;
+ ary = dlmalloc(*size);
+ for( i=0; i < len; i++ ){
+ e = rb_ary_entry(v, i);
+ switch( TYPE(e) ){
+ case T_FIXNUM:
+ case T_BIGNUM:
+ ary[i] = (short)(NUM2INT(e));
+ break;
+ case T_NIL:
+ ary[i] = 0;
+ break;
+ default:
+ rb_raise(rb_eDLTypeError, "unexpected type of the element #%d", i);
+ break;
+ };
+ };
+
+ return ary;
+};
+
+static char *
+c_carray(VALUE v, long *size)
+{
+ int i, len;
+ char *ary;
+ VALUE e;
+
+ len = RARRAY(v)->len;
+ *size = sizeof(char) * len;
+ ary = dlmalloc(*size);
+ for( i=0; i < len; i++ ){
+ e = rb_ary_entry(v, i);
+ switch( TYPE(e) ){
+ case T_FIXNUM:
+ case T_BIGNUM:
+ ary[i] = (char)(NUM2INT(e));
+ break;
+ case T_NIL:
+ ary[i] = 0;
+ break;
+ default:
+ rb_raise(rb_eDLTypeError, "unexpected type of the element #%d", i);
+ break;
+ };
+ };
+
+ return ary;
+};
+
+static void *
+c_parray(VALUE v, long *size)
+{
+ int i, len;
+ void **ary;
+ VALUE e;
+
+ len = RARRAY(v)->len;
+ *size = sizeof(void*) * len;
+ ary = dlmalloc(*size);
+ for( i=0; i < len; i++ ){
+ e = rb_ary_entry(v, i);
+ switch( TYPE(e) ){
+ case T_STRING:
+ {
+ char *str, *src;
+ src = STR2CSTR(e);
+ str = dlstrdup(src);
+ ary[i] = (void*)str;
+ };
+ break;
+ case T_NIL:
+ ary[i] = NULL;
+ break;
+ case T_DATA:
+ if( rb_obj_is_kind_of(e, rb_cDLPtrData) ){
+ struct ptr_data *pdata;
+ Data_Get_Struct(e, struct ptr_data, pdata);
+ ary[i] = (void*)(pdata->ptr);
+ }
+ else{
+ rb_raise(rb_eDLTypeError, "unexpected type of the element #%d", i);
+ };
+ break;
+ default:
+ rb_raise(rb_eDLTypeError, "unexpected type of the element #%d", i);
+ break;
+ };
+ };
+
+ return ary;
+};
+
+void *
+rb_ary2cary(char t, VALUE v, long *size)
+{
+ int len;
+ VALUE val0;
+
+ if( TYPE(v) != T_ARRAY ){
+ rb_raise(rb_eDLTypeError, "an array is expected.");
+ };
+
+ len = RARRAY(v)->len;
+ if( len == 0 ){
+ return NULL;
+ };
+
+ if( !size ){
+ size = ALLOCA_N(long,1);
+ };
+
+ val0 = rb_ary_entry(v,0);
+ switch( TYPE(val0) ){
+ case T_FIXNUM:
+ case T_BIGNUM:
+ switch( t ){
+ case 'C': case 'c':
+ return (void*)c_carray(v,size);
+ case 'H': case 'h':
+ return (void*)c_harray(v,size);
+ case 'I': case 'i':
+ return (void*)c_iarray(v,size);
+ case 'L': case 'l': case 0:
+ return (void*)c_larray(v,size);
+ default:
+ rb_raise(rb_eDLTypeError, "type mismatch");
+ };
+ case T_STRING:
+ return (void*)c_parray(v,size);
+ case T_FLOAT:
+ switch( t ){
+ case 'F': case 'f':
+ return (void*)c_farray(v,size);
+ case 'D': case 'd': case 0:
+ return (void*)c_darray(v,size);
+ };
+ rb_raise(rb_eDLTypeError, "type mismatch");
+ case T_DATA:
+ if( rb_obj_is_kind_of(val0, rb_cDLPtrData) ){
+ return (void*)c_parray(v,size);
+ };
+ rb_raise(rb_eDLTypeError, "type mismatch");
+ default:
+ rb_raise(rb_eDLTypeError, "unsupported type");
+ };
+};
+
+VALUE
+rb_str_to_ptr(VALUE self)
+{
+ char *ptr;
+ int len;
+
+ len = RSTRING(self)->len;
+ ptr = (char*)dlmalloc(len + 1);
+ memcpy(ptr, STR2CSTR(self), len);
+ ptr[len] = '\0';
+ return rb_dlptr_new((void*)ptr,len,dlfree);
+};
+
+VALUE
+rb_ary_to_ptr(int argc, VALUE argv[], VALUE self)
+{
+ void *ptr;
+ VALUE t;
+ long size;
+
+ switch( rb_scan_args(argc, argv, "01", &t) ){
+ case 1:
+ ptr = rb_ary2cary(STR2CSTR(t)[0], self, &size);
+ break;
+ case 0:
+ ptr = rb_ary2cary(0, self, &size);
+ break;
+ };
+ return ptr ? rb_dlptr_new(ptr, size, dlfree) : Qnil;
+};
+
+VALUE
+rb_io_to_ptr(VALUE self)
+{
+ OpenFile *fptr;
+ FILE *fp;
+
+ GetOpenFile(self, fptr);
+ fp = fptr->f;
+
+ return fp ? rb_dlptr_new(fp, sizeof(FILE), 0) : Qnil;
+};
+
+VALUE
+rb_dl_dlopen(int argc, VALUE argv[], VALUE self)
+{
+ return rb_dlhandle_s_new(argc, argv, rb_cDLHandle);
+};
+
+VALUE
+rb_dl_malloc(VALUE self, VALUE size)
+{
+ void *ptr;
+ long s;
+
+ s = DLNUM2LONG(size);
+ ptr = dlmalloc((size_t)s);
+ memset(ptr,0,(size_t)s);
+ return rb_dlptr_new(ptr, s, dlfree);
+};
+
+VALUE
+rb_dl_strdup(VALUE self, VALUE str)
+{
+ void *p;
+
+ str = rb_String(str);
+ return rb_dlptr_new(strdup(STR2CSTR(str)), RSTRING(str)->len, dlfree);
+};
+
+static VALUE
+rb_dl_sizeof(VALUE self, VALUE str)
+{
+ return INT2NUM(dlsizeof(STR2CSTR(str)));
+};
+
+static VALUE
+rb_dl_callback_type(VALUE str)
+{
+ char *type;
+ int len;
+ int i;
+ long ftype;
+
+ ftype = 0;
+ type = STR2CSTR(str);
+ len = RSTRING(str)->len;
+
+ if( len - 1 > MAX_CBARG ){
+ rb_raise(rb_eDLError, "maximum number of the argument is %d.", MAX_CBARG);
+ };
+
+ for( i = len - 1; i > 0; i-- ){
+ switch( type[i] ){
+ case 'P':
+ CBPUSH_P(ftype);
+ break;
+ case 'I':
+ CBPUSH_I(ftype);
+ break;
+ case 'L':
+ CBPUSH_L(ftype);
+ break;
+ case 'F':
+ CBPUSH_F(ftype);
+ break;
+ case 'D':
+ CBPUSH_D(ftype);
+ default:
+ rb_raise(rb_eDLError, "unsupported type `%c'", type[i]);
+ break;
+ };
+ };
+
+ switch( type[0] ){
+ case '0':
+ CBPUSH_0(ftype);
+ break;
+ case 'P':
+ CBPUSH_P(ftype);
+ break;
+ case 'I':
+ CBPUSH_I(ftype);
+ break;
+ case 'L':
+ CBPUSH_L(ftype);
+ break;
+ case 'F':
+ CBPUSH_F(ftype);
+ break;
+ case 'D':
+ CBPUSH_D(ftype);
+ break;
+ default:
+ rb_raise(rb_eDLError, "unsupported type `%c'", type[i]);
+ break;
+ };
+
+ return INT2NUM(ftype);
+};
+
+VALUE
+rb_dl_set_callback(int argc, VALUE argv[], VALUE self)
+{
+ VALUE types, num, proc;
+ VALUE key;
+ VALUE entry;
+ void *func;
+
+ char func_name[1024];
+ extern dln_sym();
+
+ switch( rb_scan_args(argc, argv, "21", &types, &num, &proc) ){
+ case 2:
+ proc = rb_f_lambda();
+ break;
+ case 3:
+ break;
+ default:
+ rb_bug("rb_dl_set_callback");
+ };
+
+ key = rb_dl_callback_type(types);
+ entry = rb_hash_aref(DLFuncTable, key);
+ if( entry == Qnil ){
+ entry = rb_hash_new();
+ rb_hash_aset(DLFuncTable, key, entry);
+ };
+
+ func = rb_dl_func_table[NUM2INT(key)][NUM2INT(num)];
+ if( func ){
+ rb_hash_aset(entry, num, proc);
+ snprintf(func_name, 1023, "rb_dl_func%d_%d", NUM2INT(key), NUM2INT(num));
+ return rb_dlsym_new(func, func_name, STR2CSTR(types));
+ }
+ else{
+ return Qnil;
+ };
+};
+
+VALUE
+rb_dl_get_callback(VALUE self, VALUE types, VALUE num)
+{
+ VALUE key;
+ VALUE entry;
+
+ key = rb_dl_callback_type(types);
+ entry = rb_hash_aref(DLFuncTable, key);
+ if( entry == Qnil ){
+ return Qnil;
+ };
+ return rb_hash_aref(entry, num);
+};
+
+void
+Init_dl()
+{
+ void Init_dlptr();
+ void Init_dlsym();
+ void Init_dlhandle();
+
+ id_call = rb_intern("call");
+
+ rb_mDL = rb_define_module("DL");
+
+ rb_eDLError = rb_define_class_under(rb_mDL, "DLError", rb_eStandardError);
+ rb_eDLTypeError = rb_define_class_under(rb_mDL, "DLTypeError", rb_eDLError);
+
+ DLFuncTable = rb_hash_new();
+ init_dl_func_table();
+ rb_define_const(rb_mDL, "FuncTable", DLFuncTable);
+
+ rb_define_const(rb_mDL, "RTLD_GLOBAL", INT2NUM(RTLD_GLOBAL));
+ rb_define_const(rb_mDL, "RTLD_LAZY", INT2NUM(RTLD_LAZY));
+ rb_define_const(rb_mDL, "RTLD_NOW", INT2NUM(RTLD_NOW));
+
+ rb_define_const(rb_mDL, "ALIGN_INT", INT2NUM(ALIGN_INT));
+ rb_define_const(rb_mDL, "ALIGN_LONG", INT2NUM(ALIGN_LONG));
+ rb_define_const(rb_mDL, "ALIGN_FLOAT", INT2NUM(ALIGN_FLOAT));
+ rb_define_const(rb_mDL, "ALIGN_SHORT", INT2NUM(ALIGN_SHORT));
+ rb_define_const(rb_mDL, "ALIGN_DOUBLE",INT2NUM(ALIGN_DOUBLE));
+ rb_define_const(rb_mDL, "ALIGN_VOIDP", INT2NUM(ALIGN_VOIDP));
+
+ rb_define_const(rb_mDL, "VERSION", rb_tainted_str_new2(DL_VERSION));
+ rb_define_const(rb_mDL, "MAJOR_VERSION", INT2NUM(DL_MAJOR_VERSION));
+ rb_define_const(rb_mDL, "MINOR_VERSION", INT2NUM(DL_MINOR_VERSION));
+ rb_define_const(rb_mDL, "PATCH_VERSION", INT2NUM(DL_PATCH_VERSION));
+ rb_define_const(rb_mDL, "MAX_ARG", INT2NUM(MAX_ARG));
+ rb_define_const(rb_mDL, "MAX_CBARG", INT2NUM(MAX_CBARG));
+ rb_define_const(rb_mDL, "MAX_CBENT", INT2NUM(MAX_CBENT));
+
+ rb_define_module_function(rb_mDL, "dlopen", rb_dl_dlopen, -1);
+ rb_define_module_function(rb_mDL, "set_callback", rb_dl_set_callback, -1);
+ rb_define_module_function(rb_mDL, "get_callback", rb_dl_get_callback, 2);
+ rb_define_module_function(rb_mDL, "malloc", rb_dl_malloc, 1);
+ rb_define_module_function(rb_mDL, "strdup", rb_dl_strdup, 1);
+ rb_define_module_function(rb_mDL, "sizeof", rb_dl_sizeof, 1);
+
+ Init_dlptr();
+ Init_dlsym();
+ Init_dlhandle();
+
+ rb_define_const(rb_mDL, "FREE", rb_dlsym_new(dlfree, "free", "0P"));
+
+ rb_define_method(rb_cString, "to_ptr", rb_str_to_ptr, 0);
+ rb_define_method(rb_cArray, "to_ptr", rb_ary_to_ptr, -1);
+ rb_define_method(rb_cIO, "to_ptr", rb_io_to_ptr, 0);
+};
diff --git a/ext/dl/dl.def b/ext/dl/dl.def
new file mode 100644
index 0000000000..75da180a7a
--- /dev/null
+++ b/ext/dl/dl.def
@@ -0,0 +1,70 @@
+EXPORTS
+Init_dl
+dlfree
+dlmalloc
+dlrealloc
+dlstrdup
+rb_ary2cary
+rb_ary_to_ptr
+rb_dl_dlopen
+rb_dl_get_callback
+rb_dl_malloc
+rb_dl_set_callback
+rb_dl_strdup
+rb_eDLError
+rb_eDLTypeError
+rb_io_to_ptr
+rb_mDL
+rb_str_to_ptr
+Init_dlhandle
+dlhandle_free
+rb_cDLHandle
+rb_dlhandle_close
+rb_dlhandle_disable_close
+rb_dlhandle_enable_close
+rb_dlhandle_init
+rb_dlhandle_s_new
+rb_dlhandle_sym
+Init_dlptr
+dlptr_free
+dlptr_init
+rb_cDLPtrData
+rb_dlmem_each
+rb_dlptr2cptr
+rb_dlptr_alloc
+rb_dlptr_aref
+rb_dlptr_aset
+rb_dlptr_cmp
+rb_dlptr_define_data_type
+rb_dlptr_define_struct
+rb_dlptr_define_union
+rb_dlptr_eql
+rb_dlptr_free_get
+rb_dlptr_free_set
+rb_dlptr_get_data_type
+rb_dlptr_inspect
+rb_dlptr_minus
+rb_dlptr_new
+rb_dlptr_null_p
+rb_dlptr_plus
+rb_dlptr_ptr
+rb_dlptr_ref
+rb_dlptr_to_array
+rb_dlptr_to_i
+rb_dlptr_to_s
+rb_dlptr_to_str
+rb_mDLMemorySpace
+Init_dlsym
+dlsym_free
+rb_cDLSymbol
+rb_dlsym2csym
+rb_dlsym_call
+rb_dlsym_cproto
+rb_dlsym_initialize
+rb_dlsym_inspect
+rb_dlsym_name
+rb_dlsym_new
+rb_dlsym_proto
+rb_dlsym_s_new
+rb_dlsym_to_ptr
+rb_s_dlsym_char2type
diff --git a/ext/dl/dl.h b/ext/dl/dl.h
new file mode 100644
index 0000000000..27290fc12b
--- /dev/null
+++ b/ext/dl/dl.h
@@ -0,0 +1,282 @@
+/* -*- C -*-
+ * $Id$
+ */
+
+#ifndef RUBY_DL_H
+#define RUBY_DL_H
+
+#include <ruby.h>
+#include <dlconfig.h>
+
+#if defined(HAVE_DLFCN_H)
+# include <dlfcn.h>
+#else
+# if defined(HAVE_WINDOWS_H)
+# include <windows.h>
+# define dlclose(ptr) FreeLibrary((HINSTANCE)ptr)
+# define dlopen(name,flag) ((void*)LoadLibrary(name))
+# define dlerror() "unknown error"
+# define dlsym(handle,name) ((void*)GetProcAddress(handle,name))
+# define RTLD_LAZY -1
+# define RTLD_NOW -1
+# define RTLD_GLOBAL -1
+# endif
+#endif
+
+#ifdef DEBUG
+#define DEBUG_CODE(b) {printf("DEBUG:%d\n",__LINE__);b;}
+#define DEBUG_CODE2(b1,b2) {printf("DEBUG:%d\n",__LINE__);b1;}
+#else
+#define DEBUG_CODE(b)
+#define DEBUG_CODE2(b1,b2) b2
+#endif
+
+#define DL_VERSION "1.1.0"
+#define DL_MAJOR_VERSION 1
+#define DL_MINOR_VERSION 1
+#define DL_PATCH_VERSION 0
+
+#define VOID_DLTYPE 0x00
+#define CHAR_DLTYPE 0x01
+#define SHORT_DLTYPE 0x02
+#define INT_DLTYPE 0x03
+#define LONG_DLTYPE 0x04
+#define FLOAT_DLTYPE 0x05
+#define DOUBLE_DLTYPE 0x06
+#define VOIDP_DLTYPE 0x07
+
+#define ARG_TYPE(x,i) (((x) & (0x07 << ((i)*3))) >> ((i)*3))
+#define PUSH_ARG(x,t) do{x <<= 3; x |= t;}while(0)
+#define PUSH_0(x) PUSH_ARG(x,VOID_DLTYPE)
+
+#if SIZEOF_INT == SIZEOF_LONG
+# define PUSH_I(x) PUSH_ARG(x,LONG_DLTYPE)
+# define ANY2I(x) x.l
+# define DLINT(x) (long)x
+#else
+# define PUSH_I(x) PUSH_ARG(x,INT_DLTYPE)
+# define ANY2I(x) x.i
+# define DLINT(x) (int)x
+#endif
+#define PUSH_L(x) PUSH_ARG(x,LONG_DLTYPE)
+#define ANY2L(x) x.l
+#define DLLONG(x) (long)x
+
+#if defined(WITH_TYPE_FLOAT)
+# if SIZEOF_FLOAT == SIZEOF_DOUBLE
+# define PUSH_F(x) PUSH_ARG(x,DOUBLE_DLTYPE)
+# define ANY2F(x) (x.d)
+# define DLFLOAT(x) ((double)x)
+# else
+# define PUSH_F(x) PUSH_ARG(x,FLOAT_DLTYPE)
+# define ANY2F(x) (x.f)
+# define DLFLOAT(x) ((float)x)
+# endif
+#else
+# define PUSH_F(x) PUSH_ARG(x,DOUBLE_DLTYPE)
+# define ANY2F(x) (x.d)
+# define DLFLOAT(x) ((double)x)
+#endif
+#define PUSH_D(x) PUSH_ARG(x,DOUBLE_DLTYPE)
+#define ANY2D(x) (x.d)
+#define DLDOUBLE(x) ((double)x)
+
+#if SIZEOF_INT == SIZEOF_VOIDP && SIZEOF_INT != SIZEOF_LONG
+# define PUSH_P(x) PUSH_ARG(x,INT_DLTYPE)
+# define ANY2P(x) (x.i)
+# define DLVOIDP(x) ((int)x)
+#elif SIZEOF_LONG == SIZEOF_VOIDP
+# define PUSH_P(x) PUSH_ARG(x,LONG_DLTYPE)
+# define ANY2P(x) (x.l)
+# define DLVOIDP(x) ((long)x)
+#else
+# define PUSH_P(x) PUSH_ARG(x,VOIDP_DLTYPE)
+# define ANY2P(x) (x.p)
+# define DLVOIDP(x) ((void*)p)
+#endif
+
+#if defined(WITH_TYPE_CHAR)
+# define PUSH_C(x) PUSH_ARG(x,CHAR_DLTYPE)
+# define ANY2C(x) (x.c)
+# define DLCHAR(x) ((char)x)
+#else
+# define PUSH_C(x) PUSH_I(x)
+# define ANY2C(x) ANY2I(x)
+# define DLCHAR(x) DLINT(x)
+#endif
+
+#if defined(WITH_TYPE_SHORT)
+# define PUSH_H(x) PUSH_ARG(x,SHORT_DLTYPE)
+# define ANY2H(x) (x.h)
+# define DLSHORT(x) ((short)x)
+#else
+# define PUSH_H(x) PUSH_I(x)
+# define ANY2H(x) ANY2I(x)
+# define DLSHORT(x) DLINT(x)
+#endif
+
+#define PUSH_S(x) PUSH_P(x)
+#define ANY2S(x) ANY2P(x)
+#define DLSTR(x) DLVOIDP(x)
+
+#define CBPUSH_0(x) PUSH_0(x)
+#define CBPUSH_C(x) PUSH_C(x)
+#define CBPUSH_H(x) PUSH_H(x)
+#define CBPUSH_I(x) PUSH_I(x)
+#define CBPUSH_L(x) PUSH_L(x)
+#define CBPUSH_F(x) PUSH_F(x)
+#define CBPUSH_D(x) PUSH_D(x)
+#if defined(WITH_CBTYPE_VOIDP)
+# define CBPUSH_P(x) PUSH_ARG(x,VOIDP_DLTYPE)
+#else
+# define CBPUSH_P(x) PUSH_P(x)
+#endif
+
+
+#if defined(USE_INLINE_ASM)
+# if defined(__i386__) && defined(__GNUC__)
+# define ASM_START(type)
+# define ASM_END(type)
+# define ASM_PUSH_C(x) asm volatile ("pushl %0" :: "g" (x));
+# define ASM_PUSH_H(x) asm volatile ("pushl %0" :: "g" (x));
+# define ASM_PUSH_I(x) asm volatile ("pushl %0" :: "g" (x));
+# define ASM_PUSH_L(x) asm volatile ("pushl %0" :: "g" (x));
+# define ASM_PUSH_P(x) asm volatile ("pushl %0" :: "g" (x));
+# define ASM_PUSH_F(x) asm volatile ("flds %0"::"g"(x));\
+ asm volatile ("subl $4,%esp");\
+ asm volatile ("fstps (%esp)");
+# define ASM_PUSH_D(x) asm volatile ("fldl %0"::"g"(x));\
+ asm volatile ("subl $8,%esp");\
+ asm volatile ("fstpl (%esp)")
+# else
+# error --with-asm is not supported on this machine
+# endif
+#else
+# define ASM_START(type)
+# define ASM_END(type)
+# define ASM_PUSH_C(x)
+# define ASM_PUSH_I(x)
+# define ASM_PUSH_L(x)
+# define ASM_PUSH_P(x)
+# define ASM_PUSH_F(x)
+# define ASM_PUSH_D(x)
+#endif
+
+extern VALUE rb_mDL;
+extern VALUE rb_mDLMemorySpace;
+extern VALUE rb_cDLHandle;
+extern VALUE rb_cDLSymbol;
+extern VALUE rb_cDLPtrData;
+extern VALUE rb_cDLStructData;
+
+extern VALUE rb_eDLError;
+extern VALUE rb_eDLTypeError;
+
+#if defined(LONG2NUM) && (SIZEOF_LONG == SIZEOF_VOIDP)
+# define DLLONG2NUM(x) LONG2NUM((long)x)
+# define DLNUM2LONG(x) (long)(NUM2LONG(x))
+#else
+# define DLLONG2NUM(x) INT2NUM((long)x)
+# define DLNUM2LONG(x) (long)(NUM2INT(x))
+#endif
+
+typedef struct { char c; void *x; } s_voidp;
+typedef struct { char c; short x; } s_short;
+typedef struct { char c; int x; } s_int;
+typedef struct { char c; long x; } s_long;
+typedef struct { char c; float x; } s_float;
+typedef struct { char c; double x; } s_double;
+
+#define ALIGN_VOIDP (sizeof(s_voidp) - sizeof(void *))
+#define ALIGN_SHORT (sizeof(s_short) - sizeof(short))
+#define ALIGN_INT (sizeof(s_int) - sizeof(int))
+#define ALIGN_LONG (sizeof(s_long) - sizeof(long))
+#define ALIGN_FLOAT (sizeof(s_float) - sizeof(float))
+#define ALIGN_DOUBLE (sizeof(s_double) - sizeof(double))
+
+/* for compatibility */
+#define VOIDP_ALIGN ALIGN_VOIDP
+#define SHORT_ALIGN ALIGN_SHORT
+#define INT_ALIGN ALIGN_INT
+#define LONG_ALIGN ALIGN_LONG
+#define FLOAT_ALIGN ALIGN_FLOAT
+#define DOUBLE_ALIGN ALIGN_DOUBLE
+
+#define DLALIGN(ptr,offset,align) {\
+ while( (((unsigned long)(ptr + offset)) % align) != 0 ) offset++;\
+}
+
+typedef void (*freefunc_t)(void *);
+#define DLFREEFUNC(func) ((freefunc_t)(func))
+
+typedef union {
+ void* p;
+ char c;
+ short h;
+ int i;
+ long l;
+ float f;
+ double d;
+ char *s;
+} ANY_TYPE;
+
+struct dl_handle {
+ void *ptr;
+ int open;
+ int enable_close;
+};
+
+struct sym_data {
+ void *func;
+ char *name;
+ char *type;
+ int len;
+};
+
+enum DLPTR_CTYPE {
+ DLPTR_CTYPE_UNKNOWN,
+ DLPTR_CTYPE_STRUCT,
+ DLPTR_CTYPE_UNION
+};
+
+struct ptr_data {
+ void *ptr; /* a pointer to the data */
+ freefunc_t free; /* free() */
+ char *stype; /* array of type specifiers */
+ int *ssize; /* size[i] = sizeof(type[i]) > 0 */
+ int slen; /* the number of type specifiers */
+ ID *ids;
+ int ids_num;
+ int ctype; /* DLPTR_CTYPE_UNKNOWN, DLPTR_CTYPE_STRUCT, DLPTR_CTYPE_UNION */
+ long size;
+};
+
+#define RDLPTR(obj) ((struct ptr_data *)(DATA_PTR(obj)))
+#define RDLSYM(obj) ((struct sym_data *)(DATA_PTR(obj)))
+
+void dlfree(void*);
+void *dlmalloc(size_t);
+void *dlrealloc(void*,size_t);
+char *dlstrdup(const char *);
+size_t dlsizeof(const char *);
+
+void *rb_ary2cary(char t, VALUE ary, long *size);
+
+/*
+void rb_dlmem_delete(void *ptr);
+void rb_dlmem_aset(void *ptr, VALUE obj);
+VALUE rb_dlmem_aref(void *ptr);
+*/
+
+void dlptr_free(struct ptr_data *data);
+void dlptr_init(VALUE val);
+
+VALUE rb_dlptr_new(void *ptr, long size, freefunc_t func);
+VALUE rb_dlptr_alloc(long size, freefunc_t func);
+void *rb_dlptr2cptr(VALUE val);
+
+VALUE rb_dlsym_new(void (*func)(), const char *name, const char *type);
+freefunc_t rb_dlsym2csym(VALUE val);
+
+
+#endif /* RUBY_DL_H */
diff --git a/ext/dl/extconf.rb b/ext/dl/extconf.rb
new file mode 100644
index 0000000000..372f1b1384
--- /dev/null
+++ b/ext/dl/extconf.rb
@@ -0,0 +1,197 @@
+require 'mkmf'
+$:.unshift File.dirname(__FILE__)
+require 'type'
+
+if( ARGV.include?("--help") )
+ print <<EOF
+ --help print this messages
+ --with-type-char strictly use type 'char'
+ --with-type-short strictly use type 'short'
+ --with-type-float strictly use type 'float'
+ --with-asm use the embedded assembler for passing arguments.
+ (this option is available for i386 machine now.)
+ --with-args=<max_arg>,<max_cbarg>,<max_cbent>
+ <max_arg>: maximum number of arguments of the function
+ <max_cbarg>: maximum number of arguments of the callback
+ <max_cbent>: maximum number of callback entries
+EOF
+ exit(0)
+end
+
+($CPPFLAGS || $CFLAGS) << " -I."
+
+case RUBY_PLATFORM # from Win32API
+when /cygwin/,/mingw/
+ $CFLAGS << " -fno-defer-pop -fno-omit-frame-pointer"
+end
+
+if (Config::CONFIG['CC'] =~ /gcc/) && (Config::CONFIG['arch'] =~ /i.86/)
+ $with_asm = true
+else
+ $with_asm = false
+end
+
+$with_type_int = try_run(<<EOF)
+int main(){ return sizeof(int) == sizeof(long); }
+EOF
+
+$with_type_float = try_run(<<EOF)
+int main(){ return sizeof(float) == sizeof(double); }
+EOF
+
+$with_type_voidp = try_run(<<EOF)
+int main(){
+ return (sizeof(void *) == sizeof(long))
+ || (sizeof(void *) == sizeof(int));
+}
+EOF
+
+$with_type_char = DLTYPE[CHAR][:sym]
+$with_type_short = DLTYPE[SHORT][:sym]
+$with_type_long = DLTYPE[LONG][:sym]
+$with_type_double= DLTYPE[DOUBLE][:sym]
+$with_type_int &= DLTYPE[INT][:sym]
+$with_type_float &= DLTYPE[FLOAT][:sym]
+$with_type_voidp &= DLTYPE[VOIDP][:sym]
+
+$with_cbtype_voidp = DLTYPE[VOIDP][:cb]
+
+$with_type_char = with_config("type-char") ? true : false
+$with_type_short = with_config("type-short") ? true : false
+$with_type_float = with_config("type-float") ? true : false
+
+$with_asm = with_config("asm") ? true : $with_asm
+
+args = with_config("args")
+max_arg = max_cbarg = max_cbent = nil
+if( $with_asm )
+ $with_type_char = true
+ $with_type_short = true
+ $with_type_float = true
+ max_arg = 0
+end
+if( args )
+ max_arg,max_cbarg,max_cbent = args.split(",").collect{|c| c.to_i}
+ if( !(max_arg && max_cbarg && max_cbent) )
+ print("--with-args=<max_arg>,<max_cbarg>,<max_cbent>\n")
+ exit(1)
+ end
+end
+max_arg ||= 6
+max_cbarg ||= 3
+max_cbent ||= 3
+
+max_callback_type = types2num(DLTYPE.keys.sort[-1,1] * (max_cbarg + 1)) + 1
+max_callback = max_cbent
+
+#m = [1].pack("i")
+#c,cs = m.unpack("c")
+#bigendian = (c == 0)
+#print("bigendian ... #{bigendian ? 'true' : 'false'}\n")
+
+
+$dlconfig_h = <<EOF
+#define MAX_ARG #{max_arg}
+#define MAX_CBARG #{max_cbarg}
+#define MAX_CBENT #{max_cbent}
+#define MAX_CALLBACK_TYPE #{max_callback_type}
+#define MAX_CALLBACK #{max_callback}
+EOF
+
+def dlc_define(const)
+ $dlconfig_h << "#if !defined(#{const})\n" +
+ "# define #{const}\n" +
+ "#endif\n"
+end
+
+if( $with_asm )
+ $dlconfig_h << "#define USE_INLINE_ASM\n"
+end
+if( $with_type_char )
+ $dlconfig_h << "#define WITH_TYPE_CHAR\n"
+end
+if( $with_type_short )
+ $dlconfig_h << "#define WITH_TYPE_SHORT\n"
+end
+if( $with_type_long )
+ $dlconfig_h << "#define WITH_TYPE_LONG\n"
+end
+if( $with_type_double )
+ $dlconfig_h << "#define WITH_TYPE_DOUBLE\n"
+end
+if( $with_type_float )
+ $dlconfig_h << "#define WITH_TYPE_FLOAT\n"
+end
+if( $with_type_int )
+ $dlconfig_h << "#define WITH_TYPE_INT\n"
+end
+if( $with_type_voidp )
+ $dlconfig_h << "#define WITH_TYPE_VOIDP\n"
+end
+if( $with_cbtype_voidp )
+ $dlconfig_h << "#define WITH_CBTYPE_VOIDP\n"
+end
+#if( bigendian )
+# $dlconfig_h << "#define BIGENDIAN"
+#else
+# $dlconfig_h << "#define LITTLEENDIAN"
+#end
+
+
+if( have_header("dlfcn.h") )
+ dlc_define("HAVE_DLFCN_H")
+ have_library("dl")
+ have_func("dlopen")
+ have_func("dlclose")
+ have_func("dlsym")
+ if( have_func("dlerror") )
+ dlc_define("HAVE_DLERROR")
+ end
+elsif( have_header("windows.h") )
+ dlc_define("HAVE_WINDOWS_H")
+ have_func("LoadLibrary")
+ have_func("FreeLibrary")
+ have_func("GetProcAddress")
+else
+ exit(0)
+end
+
+method(:have_func).arity == 1 or have_func("rb_str_cat2", "ruby.h")
+if method(:have_func).arity == 1 or !have_func("rb_block_given_p", "ruby.h")
+ $dlconfig_h << "#define rb_block_given_p rb_iterator_p\n"
+end
+
+def File.update(file, str)
+ begin
+ open(file){|f|f.read} == str
+ rescue Errno::ENOENT
+ false
+ end or open(file, "w"){|f|f.print(str)}
+end
+
+File.update("dlconfig.h", <<EOF)
+#ifndef DLCONFIG_H
+#define DLCONFIG_H
+#{$dlconfig_h}
+#endif /* DLCONFIG_H */
+EOF
+
+File.update("dlconfig.rb", <<EOF)
+MAX_ARG = #{max_arg}
+MAX_CBARG = #{max_cbarg}
+MAX_CBENT = #{max_cbent}
+DLTYPE[CHAR][:sym] = #{$with_type_char}
+DLTYPE[SHORT][:sym] = #{$with_type_short}
+DLTYPE[INT][:sym] = #{$with_type_int}
+DLTYPE[LONG][:sym] = #{$with_type_long}
+DLTYPE[FLOAT][:sym] = #{$with_type_float}
+DLTYPE[DOUBLE][:sym]= #{$with_type_double}
+DLTYPE[VOIDP][:sym] = #{$with_type_voidp}
+EOF
+
+$INSTALLFILES = [
+ ["./dlconfig.h", "$(archdir)$(target_prefix)", "."],
+ ["dl.h", "$(archdir)$(target_prefix)", ""],
+]
+
+create_makefile('dl')
diff --git a/ext/dl/h2rb b/ext/dl/h2rb
new file mode 100644
index 0000000000..00fbd60c82
--- /dev/null
+++ b/ext/dl/h2rb
@@ -0,0 +1,500 @@
+#!/usr/bin/env ruby
+# -*- ruby -*-
+# $Id$
+
+require 'mkmf'
+require 'ftools'
+
+$recursive = false
+$force = false
+$conly = true
+$inc_path = []
+$infilename= nil
+$insert_require = true
+
+def valid_ruby_code?(code)
+ begin
+ eval("BEGIN {return true}; #{code}")
+ rescue SyntaxError
+ return false
+ end
+ return false
+end
+
+def print_usage
+ print <<EOF
+h2rb [-r] [-I <path>] [-d] [<filename>]
+EOF
+end
+
+while( ARGV[0] )
+ case( ARGV[0] )
+ when "-r"
+ ARGV.shift
+ $recursive = true
+ when "-R"
+ ARGV.shift
+ $recursive = false
+ when "-l"
+ ARGV.shift
+ $insert_require = true
+ when "-L"
+ ARGV.shift
+ $insert_require = false
+ when "-c"
+ ARGV.shift
+ $conly = true
+ when "-C"
+ ARGV.shift
+ $conly = false
+ when "-f"
+ ARGV.shift
+ $force = true
+ when "-F"
+ ARGV.shift
+ $force = false
+ when "-I"
+ ARGV.shift
+ $inc_path << ARGV.shift
+ when "-d"
+ ARGV.shift
+ $DEBUG = true
+ when "-h","--help"
+ print_usage()
+ exit 0
+ when /-.*/
+ $stderr.print("unknown option '#{ARGV[0]}'.\n")
+ print_usage()
+ exit 0
+ else
+ $infilename = ARGV.shift
+ end
+end
+
+$inc_dir = File.join(CONFIG["prefix"], "lib", "ruby",
+ CONFIG["MAJOR"] + "." + CONFIG["MINOR"],
+ "dl")
+
+class H2RBError < StandardError; end
+
+
+class H2RB
+ def initialize(inc_dir = nil, inc_path = nil, insert_require = nil)
+ @inc_path = inc_path || []
+ @inc_dir = inc_dir || '.'
+ @indent = 0
+ @parsed_files = []
+ @insert_require = insert_require || false
+ end
+
+ def find_path(file)
+ if( ! file )
+ return nil
+ end
+ if( File.exist?(file) )
+ if( file[0] == ?/ )
+ return file
+ else
+ return file
+ end
+ end
+ @inc_path.each{|path|
+ full = File.join(path, file)
+ if( File.exist?(full) )
+ return full
+ end
+ }
+ return nil
+ end
+
+ def strip_comment(line)
+ if( @commented )
+ if( e = line.index("*/") )
+ line[0..(e+1)] = ""
+ @commented = false
+ else
+ line = ""
+ end
+ else
+ if( s = line.index("/*") )
+ if( e = line.index("*/") )
+ line[s..(e+1)] = ""
+ else
+ line[s..-1] = ""
+ @commented = true
+ end
+ elsif( s = line.index("//") )
+ line[s..(-1)] = ""
+ end
+ end
+
+ line.gsub!(/\s+$/,"")
+ return line
+ end
+
+ def up_indent
+ @indent += 1
+ end
+
+ def down_indent
+ @indent -= 1
+ if( @indent < 0 )
+ raise
+ end
+ end
+
+ def indent
+ " " * @indent
+ end
+
+ def rescue_begin
+ line = "#{indent}begin"
+ up_indent
+ return line
+ end
+
+ def rescue_nameerror
+ down_indent
+ line = [
+ "#{indent}rescue NameError => e",
+ "#{indent} raise e if( $DEBUG )",
+ "#{indent}end"].join($/)
+ return line
+ end
+
+ def parse_enum(line)
+ if( line =~ /enum\s+(\S+\s+)?\{(.+)\}/ )
+ enum_name = $1
+ enum_block = $2
+ if( enum_name )
+ line = "#{indent}# -- enum #{enum_name}\n"
+ else
+ line = "#{indent}# -- enum\n"
+ end
+ enums = enum_block.split(/,/).collect{|e| e.strip}
+ i = 0
+ enums.each{|elem|
+ var,val = elem.split(/=/).collect{|e| e.strip}
+ if( val )
+ i = val.to_i
+ end
+ line += "#{indent}#{var} = #{i.to_s}\n"
+ i += 1
+ }
+ line += "#{indent}# -- end of enum"
+ return line
+ else
+ return nil
+ end
+ end
+
+ def parse_define(line)
+ case line
+ when /^#\s*define\s+(\S+)\(\)/
+ line = nil
+ when /^#\s*define\s+(\S+)\((.+)\)\s+(.+)$/
+ if( @conly )
+ line = nil
+ else
+ defname = $1
+ defargs = $2
+ defval = $3
+ if( !valid_ruby_code?(defval) )
+ defval = "nil # #{defval}"
+ end
+ if( defname[0,1] =~ /^[A-Z]$/ )
+ line = "#{indent}#{defname} = proc{|#{defargs}| #{defval}}"
+ else
+ line = [
+ "#{indent}def #{defname}(#{defargs})",
+ "#{indent} #{defval}",
+ "#{indent}end"
+ ].join("\n")
+ end
+ end
+ when /^#\s*define\s+(\S+)\((.+)\)$/
+ if( @conly )
+ line = nil
+ else
+ defname = $1
+ defargs = $2
+ defval = nil
+ if( !valid_ruby_code?(defval) )
+ defval = "nil # #{defval}"
+ end
+ if( defname[0,1] =~ /^[A-Z]$/ )
+ line = "#{indent}#{defname} = proc{|#{defargs}| #{defval}}"
+ else
+ line = [
+ "#{indent}def #{defname}(#{defargs})",
+ "#{indent} #{defval}",
+ "#{indent}end"
+ ].join("\n")
+ end
+ end
+ when /^#\s*define\s+(\S+)\s+(.+)$/
+ defname = $1
+ defval = $2
+ if( !valid_ruby_code?(defval) )
+ defval = "nil # #{defval}"
+ end
+ line = [rescue_begin, "#{indent}#{defname} = #{defval}", rescue_nameerror].join($/)
+ when /^#\s*define\s+(\S+)$/
+ defname = $1
+ line = "#{indent}#{defname} = nil"
+ else
+ line = nil
+ end
+ return line
+ end
+
+ def parse_undef(line)
+ case line
+ when /^#\s*undef\s+([A-Z]\S+)$/
+ defname = $1
+ line = "#{indent}remove_const(:#{defname})"
+ when /^#\s*undef\s+(\S+)$/
+ defname = $1
+ line = "#{indent}#{defname} = nil"
+ else
+ line = nil
+ end
+ return line
+ end
+
+ def parse_ifdef(line)
+ case line
+ when /^#\s*ifdef\s+(\S+)$/
+ defname = $1
+ line = [
+ rescue_begin,
+ "#{indent}if( defined?(#{defname}) && ! #{defname}.nil? )"].join($/)
+ else
+ line = nil
+ end
+ return line
+ end
+
+ def parse_ifndef(line)
+ case line
+ when /^#\s*ifndef\s+(\S+)$/
+ defname = $1
+ line = [
+ rescue_begin,
+ "#{indent}if( ! defined?(#{defname}) || #{defname}.nil? )"].join($/)
+ else
+ line = nil
+ end
+ return line
+ end
+
+ def parse_if(line)
+ case line
+ when /^#\s*if\s+(.+)$/
+ cond = $1
+ cond.gsub!(/defined(.+)/){ "defined?(#{$1}) && ! #{$1}.nil?" }
+ if( valid_ruby_code?(cond) )
+ line = "#{indent}if( #{cond} )"
+ else
+ line = "#{indent}if( false ) # #{cond}"
+ end
+ line = [rescue_begin, line].join($/)
+ else
+ line = nil
+ end
+ return line
+ end
+
+ def parse_elif(line)
+ case line
+ when /^#\s*elif\s+(.+)$/
+ cond = $1
+ cond.gsub!("defined","defined?")
+ line = "#{indent}elsif( #{cond} )"
+ else
+ line = nil
+ end
+ return line
+ end
+
+ def parse_else(line)
+ case line
+ when /^#\s*else\s*/
+ line = "#{indent}else"
+ else
+ line = nil
+ end
+ return line
+ end
+
+ def parse_endif(line)
+ case line
+ when /^#\s*endif\s*$/
+ line = ["#{indent}end", rescue_nameerror].join($/)
+ else
+ line = nil
+ end
+ return line
+ end
+
+ def parse_include(line)
+ if( ! @insert_require )
+ return nil
+ end
+
+ file = nil
+ case line
+ when /^#\s*include "(.+)"$/
+ file = $1
+ line = "#{indent}require '#{file}'"
+ when /^#\s*include \<(.+)\>$/
+ file = $1
+ line = "#{indent}require '#{file}'"
+ else
+ line = nil
+ end
+ if( @recursive && file && (!@parsed_files.include?(file)) )
+ parse(file, @recursive, @force, @conly)
+ end
+ return line
+ end
+
+
+ def open_files(infilename)
+ if( ! infilename )
+ return [$stdin, $stdout]
+ end
+
+ old_infilename = infilename
+ infilename = find_path(infilename)
+ if( ! infilename )
+ $stderr.print("'#{old_infilename}' was not found.\n")
+ return [nil,nil]
+ end
+
+ if( infilename )
+ if( infilename[0,1] == '/' )
+ outfilename = File.join(@inc_dir, infilename[1..-1] + ".rb")
+ else
+ outfilename = infilename + ".rb"
+ end
+ File.mkpath(File.dirname(outfilename))
+ else
+ outfilename = nil
+ end
+
+ if( infilename )
+ fin = File.open(infilename,"r")
+ else
+ fin = $stdin
+ end
+ if( outfilename )
+ if( File.exist?(outfilename) && (!@force) )
+ $stderr.print("'#{outfilename}' have already existed.\n")
+ return [fin, nil]
+ end
+ fout = File.open(outfilename,"w")
+ else
+ fout = $stdout
+ end
+
+ $stderr.print("#{infilename} -> #{outfilename}\n")
+ if( fout )
+ dir = File.dirname(outfilename)
+ if( dir[0,1] != "." && dir != "" )
+ fout.print("if( ! $LOAD_PATH.include?('#{dir}') )\n",
+ " $LOAD_PATH.push('#{dir}')\n",
+ "end\n")
+ end
+ end
+ return [fin,fout]
+ end
+
+ def parse(infilename = nil, recursive = false, force = false, conly = false)
+ @commented = false
+ @recursive = recursive
+ @force = force
+ @conly = conly
+ @parsed_files << infilename
+
+ fin,fout = open_files(infilename)
+ if( !fin )
+ return
+ end
+
+ begin
+ line_number = 0
+ pre_line = nil
+ fin.each_line{|line|
+ line_number += 1
+ line.chop!
+ if( $DEBUG )
+ $stderr.print("#{line_number}:(#{@indent}):", line, "\n")
+ end
+
+ if( pre_line )
+ line = pre_line + line
+ pre_line = nil
+ end
+
+ if( line[-1,1] == "\\" )
+ pre_line = line[0..-2]
+ next
+ end
+
+ if( eidx = line.index("enum ") )
+ pre_line = line[eidx .. -1]
+ if( i = line.index("{") && j = line.index("}") )
+ line = line[0..j]
+ pre_line = nil
+ else
+ next
+ end
+ end
+
+ line = strip_comment(line)
+ case line
+ when /^enum\s/
+ line = parse_enum(line)
+ when /^#\s*define\s/
+ line = parse_define(line)
+ when /^#\s*undef\s/
+ line = parse_undef(line)
+ when /^#\s*ifdef\s/
+ line = parse_ifdef(line)
+ up_indent
+ when /^#\s*ifndef\s/
+ line = parse_ifndef(line)
+ up_indent
+ when /^#\s*if\s/
+ line = parse_if(line)
+ up_indent
+ when /^#\s*elif\s/
+ down_indent
+ line = parse_elif(line)
+ up_indent
+ when /^#\s*else/
+ down_indent
+ line = parse_else(line)
+ up_indent
+ when /^#\s*endif/
+ down_indent
+ line = parse_endif(line)
+ when /^#\s*include\s/
+ line = parse_include(line)
+ else
+ line = nil
+ end
+ if( line && fout )
+ fout.print(line, " # #{line_number}",$/)
+ end
+ }
+ ensure
+ fin.close if fin
+ fout.close if fout
+ end
+ end
+end
+
+h2rb = H2RB.new($inc_dir, $inc_path, $insert_require)
+h2rb.parse($infilename, $recursive, $force, $conly)
diff --git a/ext/dl/handle.c b/ext/dl/handle.c
new file mode 100644
index 0000000000..d75cb11a8c
--- /dev/null
+++ b/ext/dl/handle.c
@@ -0,0 +1,207 @@
+/* -*- C -*-
+ * $Id$
+ */
+
+#include <ruby.h>
+#include "dl.h"
+
+VALUE rb_cDLHandle;
+
+void
+dlhandle_free(struct dl_handle *dlhandle)
+{
+ if( dlhandle->ptr && dlhandle->open && dlhandle->enable_close ){
+ dlclose(dlhandle->ptr);
+ };
+};
+
+VALUE
+rb_dlhandle_close(VALUE self)
+{
+ struct dl_handle *dlhandle;
+
+ Data_Get_Struct(self, struct dl_handle, dlhandle);
+ dlhandle->open = 0;
+ return INT2NUM(dlclose(dlhandle->ptr));
+};
+
+VALUE
+rb_dlhandle_s_new(int argc, VALUE argv[], VALUE self)
+{
+ void *ptr;
+ VALUE val;
+ struct dl_handle *dlhandle;
+ VALUE lib, flag;
+ char *clib;
+ int cflag;
+ const char *err;
+
+ switch( rb_scan_args(argc, argv, "11", &lib, &flag) ){
+ case 1:
+ clib = STR2CSTR(lib);
+ cflag = RTLD_LAZY | RTLD_GLOBAL;
+ break;
+ case 2:
+ clib = STR2CSTR(lib);
+ cflag = NUM2INT(flag);
+ break;
+ default:
+ rb_bug("rb_dlhandle_new");
+ };
+
+ ptr = dlopen(clib, cflag);
+#if defined(HAVE_DLERROR)
+ if( (err = dlerror()) ){
+ rb_raise(rb_eRuntimeError, err);
+ };
+#else
+ if( !ptr ){
+ err = dlerror();
+ rb_raise(rb_eRuntimeError, err);
+ };
+#endif
+ val = Data_Make_Struct(rb_cDLHandle, struct dl_handle, 0,
+ dlhandle_free, dlhandle);
+ dlhandle->ptr = ptr;
+ dlhandle->open = 1;
+ dlhandle->enable_close = 0;
+
+ rb_obj_call_init(val, argc, argv);
+
+ if( rb_block_given_p() ){
+ rb_ensure(rb_yield, val, rb_dlhandle_close, val);
+ };
+
+ return val;
+};
+
+VALUE
+rb_dlhandle_init(int argc, VALUE argv[], VALUE self)
+{
+ return Qnil;
+};
+
+VALUE
+rb_dlhandle_enable_close(VALUE self)
+{
+ struct dl_handle *dlhandle;
+
+ Data_Get_Struct(self, struct dl_handle, dlhandle);
+ dlhandle->enable_close = 1;
+ return Qnil;
+};
+
+VALUE
+rb_dlhandle_disable_close(VALUE self)
+{
+ struct dl_handle *dlhandle;
+
+ Data_Get_Struct(self, struct dl_handle, dlhandle);
+ dlhandle->enable_close = 0;
+ return Qnil;
+};
+
+VALUE
+rb_dlhandle_to_i(VALUE self)
+{
+ struct dl_handle *dlhandle;
+
+ Data_Get_Struct(self, struct dl_handle, dlhandle);
+ return DLLONG2NUM(dlhandle);
+};
+
+VALUE
+rb_dlhandle_to_ptr(VALUE self)
+{
+ struct dl_handle *dlhandle;
+
+ Data_Get_Struct(self, struct dl_handle, dlhandle);
+ return rb_dlptr_new(dlhandle, sizeof(dlhandle), 0);
+};
+
+VALUE
+rb_dlhandle_sym(int argc, VALUE argv[], VALUE self)
+{
+ VALUE sym, type;
+ void (*func)();
+ VALUE val;
+ struct sym_data *data;
+ int *ctypes;
+ int i, ctypes_len;
+ struct dl_handle *dlhandle;
+ void *handle;
+ const char *name, *stype;
+ const char *err;
+
+ if( rb_scan_args(argc, argv, "11", &sym, &type) == 2 ){
+ Check_Type(type, T_STRING);
+ stype = STR2CSTR(type);
+ }
+ else{
+ stype = NULL;
+ };
+
+ if( sym == Qnil ){
+#if defined(RTLD_NEXT)
+ name = RTLD_NEXT;
+#else
+ name = NULL;
+#endif
+ }
+ else{
+ Check_Type(sym, T_STRING);
+ name = STR2CSTR(sym);
+ };
+
+
+ Data_Get_Struct(self, struct dl_handle, dlhandle);
+ handle = dlhandle->ptr;
+
+ func = dlsym(handle, name);
+#if defined(HAVE_DLERROR)
+ if( (err = dlerror()) && (!func) )
+#else
+ if( !func )
+#endif
+ {
+#if defined(__CYGWIN__) || defined(WIN32) || defined(__MINGW32__)
+ {
+ int len = strlen(name);
+ char *name_a = (char*)dlmalloc(len+2);
+ strcpy(name_a, name);
+ name_a[len] = 'A';
+ name_a[len+1] = '\0';
+ func = dlsym(handle, name_a);
+ dlfree(name_a);
+#if defined(HAVE_DLERROR)
+ if( (err = dlerror()) && (!func) )
+#else
+ if( !func )
+#endif
+ {
+ rb_raise(rb_eRuntimeError, "Unknown symbol \"%sA\".", name);
+ };
+ }
+#else
+ rb_raise(rb_eRuntimeError, "Unknown symbol \"%s\".", name);
+#endif
+ };
+ val = rb_dlsym_new(func, name, stype);
+
+ return val;
+};
+
+void
+Init_dlhandle()
+{
+ rb_cDLHandle = rb_define_class_under(rb_mDL, "Handle", rb_cData);
+ rb_define_singleton_method(rb_cDLHandle, "new", rb_dlhandle_s_new, -1);
+ rb_define_method(rb_cDLHandle, "initialize", rb_dlhandle_init, -1);
+ rb_define_method(rb_cDLHandle, "to_i", rb_dlhandle_to_i, 0);
+ rb_define_method(rb_cDLHandle, "to_ptr", rb_dlhandle_to_ptr, 0);
+ rb_define_method(rb_cDLHandle, "close", rb_dlhandle_close, 0);
+ rb_define_method(rb_cDLHandle, "sym", rb_dlhandle_sym, -1);
+ rb_define_method(rb_cDLHandle, "[]", rb_dlhandle_sym, -1);
+ rb_define_method(rb_cDLHandle, "disable_close", rb_dlhandle_disable_close, 0);
+ rb_define_method(rb_cDLHandle, "enable_close", rb_dlhandle_enable_close, 0);
+};
diff --git a/ext/dl/install.rb b/ext/dl/install.rb
new file mode 100644
index 0000000000..69b1834301
--- /dev/null
+++ b/ext/dl/install.rb
@@ -0,0 +1,49 @@
+require 'mkmf'
+require 'ftools'
+
+SO_LIBS = ["dl.so"]
+
+$ruby_version = CONFIG['MAJOR'] + "." + CONFIG['MINOR']
+$prefix = CONFIG['prefix']
+$libdir = File.join($prefix,'lib')
+$rubylibdir = File.join($libdir, 'ruby', $ruby_version)
+$arch = CONFIG['arch']
+$archdir = File.join($rubylibdir, $arch)
+
+def find(dir, match = /./)
+ Dir.chdir(dir)
+ files = []
+ Dir.new(".").each{|file|
+ if( file != "." && file != ".." )
+ case File.ftype(file)
+ when "file"
+ if( file =~ match )
+ files.push(File.join(dir,file))
+ end
+ when "directory"
+ files += find(file, match).collect{|f| File.join(dir,f)}
+ end
+ end
+ }
+ Dir.chdir("..")
+ return files
+end
+
+def install()
+ rb_files = find(File.join(".","lib"), /.rb$/)
+
+ SO_LIBS.each{|f|
+ File.makedirs($rubylibdir, "#{$archdir}")
+ File.install(f, File.join($archdir,f), 0555, true)
+ }
+
+ rb_files.each{|f|
+ origfile = f
+ instfile = File.join($rubylibdir, origfile.sub("./lib/",""))
+ instdir = File.dirname(instfile)
+ File.makedirs(instdir)
+ File.install(origfile, instfile, 0644, true)
+ }
+end
+
+install()
diff --git a/ext/dl/lib/dl/import.rb b/ext/dl/lib/dl/import.rb
new file mode 100644
index 0000000000..78f6a2b785
--- /dev/null
+++ b/ext/dl/lib/dl/import.rb
@@ -0,0 +1,228 @@
+# -*- ruby -*-
+
+require 'dl'
+
+module DL
+ module Importable
+ LIB_MAP = {}
+
+ module Internal
+ def dlload(*libnames)
+ if( !defined?(@LIBS) )
+ @LIBS = []
+ end
+ libnames.each{|libname|
+ if( !LIB_MAP[libname] )
+ LIB_MAP[libname] = DL.dlopen(libname)
+ end
+ @LIBS.push(LIB_MAP[libname])
+ }
+ end
+ alias dllink :dlload
+
+ # example:
+ # extern "int strlen(char*)"
+ #
+ def extern(proto)
+ proto = proto.gsub(/\s+/, " ").strip
+ case proto
+ when /^([\d\w\*_\s]+)\(([\d\w\*_\s\,\[\]]*)\)$/
+ ret = $1
+ args = $2
+ ret = ret.split(/\s+/)
+ args = args.split(/\s*,\s*/)
+ func = ret.pop
+ ret = ret.join(" ")
+ return import(func, ret, args)
+ else
+ raise(RuntimeError,"can't parse the function prototype: #{proto}")
+ end
+ end
+
+ # example:
+ # import("get_length", "int", ["void*", "int"])
+ #
+ def import(name, rettype, argtypes = nil)
+ if( !defined?(@SYM) )
+ @SYM = {}
+ end
+ @LIBS.each{|lib|
+ rty,_,rdec = encode_type(rettype)
+ ty,enc,dec = encode_types(argtypes)
+ symty = rty + ty
+
+ begin
+ sym = lib[name, symty]
+ rescue
+ next
+ end
+
+ mname = name.dup
+ if( ?A <= mname[0] && mname[0] <= ?Z )
+ mname[0,1] = mname[0,1].downcase
+ end
+ @SYM[mname] = [sym,rdec,enc,dec]
+
+ module_eval [
+ "def #{mname}(*args)",
+ " sym,rdec,enc,dec = @SYM['#{mname}']",
+ " args = enc.call(args)",
+ if( $DEBUG )
+ " p \"[DL] call #{mname} with \#{args.inspect}\""
+ else
+ ""
+ end,
+ " r,rs = sym.call(*args)",
+ if( $DEBUG )
+ " p \"[DL] retval=\#{r.inspect} args=\#{rs.inspect}\""
+ else
+ ""
+ end,
+ " @retval = rdec.call(r)",
+ " @args = dec.call(rs)",
+ " return @retval",
+ "end",
+ "module_function :#{mname}",
+ ].join("\n")
+
+ return @SYM[mname]
+ }
+ raise(RuntimeError, "can't find #{name}.")
+ end
+
+ def _args_
+ return @args
+ end
+
+ def _retval_
+ return @retval
+ end
+
+ def typealias(ty1, ty2, enc=nil, dec=nil)
+ check_type
+ @TYDEFS.unshift([ty1,ty2, enc,dec])
+ end
+
+ def encode_type(ty)
+ check_type
+ orig_ty = ty
+ enc = proc{|v| v}
+ dec = proc{|v| v}
+ @TYDEFS.each{|t1,t2,c1,c2|
+ if( t1.is_a?(String) )
+ t1 = Regexp.new("^" + t1 + "$")
+ end
+ if( ty =~ t1 )
+ ty = ty.gsub(t1,t2)
+ if( c1 )
+ conv1 = enc
+ enc = proc{|v| c1.call(conv1.call(v))}
+ end
+ if( c2 )
+ conv2 = dec
+ dec = proc{|v| c2.call(conv2.call(v))}
+ end
+ end
+ }
+ ty = ty.strip
+ if( ty.length != 1 )
+ raise(TypeError, "unknown type: #{orig_ty}.")
+ end
+ return [ty,enc,dec]
+ end
+
+ def encode_types(tys)
+ encty = []
+ enc = proc{|v| v}
+ dec = proc{|v| v}
+ tys.each_with_index{|ty,idx|
+ ty,c1,c2 = encode_type(ty)
+ encty.push(ty)
+ conv1 = enc
+ enc = proc{|v| v = conv1.call(v); v[idx] = c1.call(v[idx]); v}
+ conv2 = dec
+ dec = proc{|v| v = conv2.call(v); v[idx] = c2.call(v[idx]); v}
+ }
+ return [encty.join, enc, dec]
+ end
+
+ def check_type
+ if( !defined?(@TYDEFS) )
+ init_type
+ end
+ end
+
+ def init_type
+ @TYDEFS = [
+ # for Windows
+ ["DWORD", "unsigned long", nil, nil],
+ ["PDWORD", "unsigned long *", nil, nil],
+ ["WORD", "unsigned int", nil, nil],
+ ["PWORD", "unsigned int *", nil, nil],
+ ["BOOL", "ibool", nil, nil],
+ ["ATOM", "int", nil, nil],
+ ["BYTE", "unsigned char", nil, nil],
+ ["PBYTE", "unsigned char *", nil, nil],
+ ["UINT", "unsigned int", nil, nil],
+ ["ULONG", "unsigned long", nil, nil],
+ ["UCHAR", "unsigned char", nil, nil],
+ ["HANDLE", "unsigned long", nil, nil],
+ ["PHANDLE","void*", nil, nil],
+ ["PVOID", "void*", nil, nil],
+ ["LPCSTR", "char*", nil, nil],
+
+ # Others
+ ["uint", "unsigned int", nil, nil],
+ ["u_int", "unsigned int", nil, nil],
+ ["ulong", "unsigned long", nil, nil],
+ ["u_long", "unsigned long", nil, nil],
+
+ # DL::Importable primitive types
+ ["ibool", "I",
+ proc{|v| v ? 1 : 0},
+ proc{|v| (v != 0) ? true : false}],
+ ["cbool", "C",
+ proc{|v| v ? 1 : 0},
+ proc{|v| (v != 0) ? true : false}],
+ ["lbool", "L",
+ proc{|v| v ? 1 : 0},
+ proc{|v| (v != 0) ? true : false}],
+ ["unsigned char", "I",
+ proc{|v| [v].pack("C").unpack("c")[0]},
+ proc{|v| [v].pack("c").unpack("C")[0]}],
+ ["unsigned int", "I",
+ proc{|v| [v].pack("I").unpack("i")[0]},
+ proc{|v| [v].pack("i").unpack("I")[0]}],
+ ["unsigned long", "L",
+ proc{|v| [v].pack("L").unpack("l")[0]},
+ proc{|v| [v].pack("l").unpack("L")[0]}],
+ ["unsigned char ref", "i",
+ proc{|v| [v].pack("C").unpack("c")[0]},
+ proc{|v| [v].pack("c").unpack("C")[0]}],
+ ["unsigned int ref", "i",
+ proc{|v| [v].pack("I").unpack("i")[0]},
+ proc{|v| [v].pack("i").unpack("I")[0]}],
+ ["unsigned long ref", "l",
+ proc{|v| [v].pack("L").unpack("l")[0]},
+ proc{|v| [v].pack("l").unpack("L")[0]}],
+ ["char ref", "c", nil, nil],
+ ["short ref", "h", nil, nil],
+ ["int ref", "i", nil, nil],
+ ["long ref", "l", nil, nil],
+ ["float ref", "f", nil, nil],
+ ["double ref","d", nil, nil],
+ ["char", "C", nil, nil],
+ ["short", "H", nil, nil],
+ ["int", "I", nil, nil],
+ ["long", "L", nil, nil],
+ ["float", "F", nil, nil],
+ ["double", "D", nil, nil],
+ [/.+\*/, "P", nil, nil],
+ [/.+\[\]/, "a", nil, nil],
+ ["void", "0", nil, nil],
+ ]
+ end
+ end # end of Internal
+ include Internal
+ end # end of Importable
+end
diff --git a/ext/dl/lib/dl/win32.rb b/ext/dl/lib/dl/win32.rb
new file mode 100644
index 0000000000..b507be5fde
--- /dev/null
+++ b/ext/dl/lib/dl/win32.rb
@@ -0,0 +1,26 @@
+# -*- ruby -*-
+
+require 'dl'
+
+class Win32API
+ LIBRARY = {}
+
+ attr_reader :val, :args
+
+ def initialize(lib, func, args, ret)
+ LIBRARY[lib] ||= DL.dlopen(lib)
+ ty = (ret + args).tr('V','0')
+ @sym = LIBRARY[lib].sym(func, ty)
+ @__dll__ = LIBRARY[lib].to_i
+ @__dllname__ = lib
+ @__proc__ = @sym.to_i
+ @val = nil
+ @args = []
+ end
+
+ def call(*args)
+ @val,@args = @sym.call(*args)
+ return @val
+ end
+ alias Call call
+end
diff --git a/ext/dl/mkcall.rb b/ext/dl/mkcall.rb
new file mode 100644
index 0000000000..dad101744b
--- /dev/null
+++ b/ext/dl/mkcall.rb
@@ -0,0 +1,68 @@
+# -*- ruby -*-
+
+require 'mkmf'
+$:.unshift File.dirname(__FILE__)
+require 'type'
+require 'dlconfig'
+
+$int_eq_long = try_run(<<EOF)
+int main() {
+ return sizeof(int) == sizeof(long) ? 0 : 1;
+}
+EOF
+
+def output_arg(x,i)
+ "args[#{i}].#{DLTYPE[x][:stmem]}"
+end
+
+def output_args(types)
+ t = []
+ types[1..-1].each_with_index{|x,i| t.push(output_arg(x,i))}
+ t.join(",")
+end
+
+def output_callfunc(types)
+ t = types[0]
+ stmem = DLTYPE[t][:stmem]
+ ctypes = types2ctypes(types)
+ if( t == VOID )
+ callstm = "(*f)(#{output_args(types)})"
+ else
+ callstm = "ret.#{stmem} = (*f)(#{output_args(types)})"
+ end
+ [ "{",
+ "#{ctypes[0]} (*f)(#{ctypes[1..-1].join(',')}) = func;",
+ "#{callstm};",
+ "}"].join(" ")
+end
+
+def output_case(types)
+ num = types2num(types)
+ callfunc_stm = output_callfunc(types)
+<<EOF
+ case #{num}:
+#ifdef DEBUG
+ printf("#{callfunc_stm}\\n");
+#endif
+ #{callfunc_stm};
+ break;
+EOF
+end
+
+def rec_output(types = [VOID])
+ print output_case(types)
+ if( types.length <= MAX_ARG )
+ DLTYPE.keys.sort.each{|t|
+ if( t != VOID && DLTYPE[t][:sym] )
+ rec_output(types + [t])
+ end
+ }
+ end
+end
+
+DLTYPE.keys.sort.each{|t|
+ if( DLTYPE[t][:sym] )
+ $stderr.printf(" #{DLTYPE[t][:ctype]}\n")
+ rec_output([t])
+ end
+}
diff --git a/ext/dl/mkcallback.rb b/ext/dl/mkcallback.rb
new file mode 100644
index 0000000000..32c7b451d7
--- /dev/null
+++ b/ext/dl/mkcallback.rb
@@ -0,0 +1,83 @@
+# -*- ruby -*-
+
+require 'mkmf'
+$:.unshift File.dirname(__FILE__)
+require 'type'
+require 'dlconfig'
+
+$int_eq_long = try_run(<<EOF)
+int main() {
+ return sizeof(int) == sizeof(long) ? 0 : 1;
+}
+EOF
+
+def func_arg(x,i)
+ ctype = DLTYPE[x][:ctype]
+ "#{ctype} arg#{i}"
+end
+
+def func_args(types)
+ t = []
+ types[1..-1].each_with_index{|x,i| t.push(func_arg(x,i))}
+ t.join(", ")
+end
+
+def funcall_args(types)
+ num = types.length - 1
+ if( num > 0 )
+ t = []
+ types[1..-1].each_with_index{|x,i| t.push(DLTYPE[x][:c2rb].call("arg#{i}"))}
+ return num.to_s + ", " + t.join(", ")
+ else
+ return num.to_s
+ end
+end
+
+def output_func(types, n = 0)
+ func_name = "rb_dl_func#{types2num(types)}_#{n}"
+ code =
+ "#{func_name}(#{func_args(types)}) /* #{types2ctypes(types).inspect} */\n" +
+ "{\n" +
+ " VALUE val, obj;\n" +
+ "#ifdef DEBUG\n" +
+ " printf(\"#{func_name}()\\n\");\n" +
+ "#endif\n" +
+ " obj = rb_hash_aref(DLFuncTable, INT2NUM(#{types2num(types)}));\n" +
+ " obj = rb_hash_aref(obj,INT2NUM(#{n}));\n" +
+ " val = rb_funcall(obj, id_call,\n" +
+ " #{funcall_args(types)});\n"
+
+ rtype = DLTYPE[types[0]][:ctype]
+ rcode = DLTYPE[types[0]][:rb2c]
+ if( rcode )
+ code += " return #{rcode.call('val')};\n"
+ end
+
+ code =
+ rtype + "\n" +
+ code +
+ "}\n\n"
+ if( n < MAX_CBENT - 1)
+ return code + output_func(types, n+1)
+ else
+ return code
+ end
+end
+
+
+def rec_output(types = [VOID])
+ print output_func(types)
+ if( types.length <= MAX_CBARG )
+ DLTYPE.keys.sort.each{|t|
+ if( t != VOID && DLTYPE[t][:cb] )
+ rec_output(types + [t])
+ end
+ }
+ end
+end
+
+DLTYPE.keys.sort.each{|t|
+ if( DLTYPE[t][:cb] )
+ rec_output([t])
+ end
+}
diff --git a/ext/dl/mkcbtable.rb b/ext/dl/mkcbtable.rb
new file mode 100644
index 0000000000..f25f012e4c
--- /dev/null
+++ b/ext/dl/mkcbtable.rb
@@ -0,0 +1,42 @@
+# -*- ruby -*-
+
+require 'mkmf'
+$:.unshift File.dirname(__FILE__)
+require 'type'
+require 'dlconfig'
+
+$int_eq_long = try_run(<<EOF)
+int main() {
+ return sizeof(int) == sizeof(long) ? 0 : 1;
+}
+EOF
+
+def output_func(types, n = 0)
+ code =
+ "/* #{types2ctypes(types).inspect} */\n" +
+ "rb_dl_func_table[#{types2num(types)}][#{n}] " +
+ "= rb_dl_func#{types2num(types)}_#{n};\n"
+ if( n < MAX_CBENT - 1)
+ return code + output_func(types, n+1)
+ else
+ return code
+ end
+end
+
+
+def rec_output(types = [VOID])
+ print output_func(types)
+ if( types.length <= MAX_CBARG )
+ DLTYPE.keys.sort.each{|t|
+ if( t != VOID && DLTYPE[t][:cb] )
+ rec_output(types + [t])
+ end
+ }
+ end
+end
+
+DLTYPE.keys.sort.each{|t|
+ if( DLTYPE[t][:cb] )
+ rec_output([t])
+ end
+}
diff --git a/ext/dl/ptr.c b/ext/dl/ptr.c
new file mode 100644
index 0000000000..6ac214bc46
--- /dev/null
+++ b/ext/dl/ptr.c
@@ -0,0 +1,1075 @@
+/* -*- C -*-
+ * $Id$
+ */
+
+#include <ruby.h>
+#include <version.h> /* for ruby version code */
+#include "dl.h"
+
+VALUE rb_cDLPtrData;
+VALUE rb_mDLMemorySpace;
+static VALUE DLMemoryTable;
+
+#ifndef T_SYMBOL
+# define T_SYMBOL T_FIXNUM
+#endif
+
+#if RUBY_VERSION_CODE < 171
+static VALUE
+rb_hash_delete(VALUE hash, VALUE key)
+{
+ return rb_funcall(hash, rb_intern("delete"), 1, key);
+}
+#endif
+
+static void
+rb_dlmem_delete(void *ptr)
+{
+ rb_hash_delete(DLMemoryTable, DLLONG2NUM(ptr));
+};
+
+static void
+rb_dlmem_aset(void *ptr, VALUE obj)
+{
+ if( obj == Qnil ){
+ rb_dlmem_delete(ptr);
+ }
+ else{
+ rb_hash_aset(DLMemoryTable, DLLONG2NUM(ptr), DLLONG2NUM(obj));
+ };
+};
+
+static VALUE
+rb_dlmem_aref(void *ptr)
+{
+ VALUE val;
+
+ val = rb_hash_aref(DLMemoryTable, DLLONG2NUM(ptr));
+ return val == Qnil ? Qnil : (VALUE)DLNUM2LONG(val);
+};
+
+void
+dlptr_free(struct ptr_data *data)
+{
+ if( data->ptr ){
+ DEBUG_CODE({
+ printf("dlptr_free(): removing the pointer `0x%x' from the MemorySpace\n",
+ data->ptr);
+ });
+ rb_dlmem_delete(data->ptr);
+ if( data->free ){
+ DEBUG_CODE({
+ printf("dlptr_free(): 0x%x(data->ptr:0x%x)\n",data->free,data->ptr);
+ });
+ (*(data->free))(data->ptr);
+ };
+ };
+ if( data->stype ) dlfree(data->stype);
+ if( data->ssize ) dlfree(data->ssize);
+ if( data->ids ) dlfree(data->ids);
+};
+
+void
+dlptr_init(VALUE val)
+{
+ struct ptr_data *data;
+
+ Data_Get_Struct(val, struct ptr_data, data);
+ DEBUG_CODE({
+ printf("dlptr_init(): add the pointer `0x%x' to the MemorySpace\n",
+ data->ptr);
+ });
+ rb_dlmem_aset(data->ptr, val);
+};
+
+VALUE
+rb_dlptr_new(void *ptr, long size, freefunc_t func)
+{
+ struct ptr_data *data;
+ VALUE val;
+
+ if( ptr ){
+ val = rb_dlmem_aref(ptr);
+ if( val == Qnil ){
+ val = Data_Make_Struct(rb_cDLPtrData, struct ptr_data,
+ 0, dlptr_free, data);
+ data->ptr = ptr;
+ data->free = func;
+ data->ctype = DLPTR_CTYPE_UNKNOWN;
+ data->stype = NULL;
+ data->ssize = NULL;
+ data->slen = 0;
+ data->size = size;
+ data->ids = NULL;
+ data->ids_num = 0;
+ dlptr_init(val);
+ }
+ else{
+ if( func ){
+ Data_Get_Struct(val, struct ptr_data, data);
+ data->free = func;
+ };
+ };
+ }
+ else{
+ val = Qnil;
+ };
+
+ return val;
+};
+
+VALUE
+rb_dlptr_alloc(long size, freefunc_t func)
+{
+ return rb_dlptr_new(dlmalloc((size_t)size), size, func);
+};
+
+void *
+rb_dlptr2cptr(VALUE val)
+{
+ struct ptr_data *data;
+ void *ptr;
+
+ if( rb_obj_is_kind_of(val, rb_cDLPtrData) ){
+ Data_Get_Struct(val, struct ptr_data, data);
+ ptr = data->ptr;
+ }
+ else if( val == Qnil ){
+ ptr = NULL;
+ }
+ else{
+ rb_raise(rb_eTypeError, "DL::PtrData was expected");
+ };
+
+ return ptr;
+};
+
+static VALUE
+rb_dlptr_s_new(int argc, VALUE argv[], VALUE klass)
+{
+ VALUE ptr, sym, obj, size;
+ void *p = NULL;
+ void (*f)() = NULL;
+ long s = 0;
+
+ switch( rb_scan_args(argc, argv, "12", &ptr, &size, &sym) ){
+ case 1:
+ p = (void*)(DLNUM2LONG(rb_Integer(ptr)));
+ break;
+ case 2:
+ p = (void*)(DLNUM2LONG(rb_Integer(ptr)));
+ s = DLNUM2LONG(size);
+ break;
+ case 3:
+ p = (void*)(DLNUM2LONG(rb_Integer(ptr)));
+ s = DLNUM2LONG(size);
+ f = rb_dlsym2csym(sym);
+ break;
+ default:
+ rb_bug("rb_dlptr_s_new");
+ };
+
+ obj = rb_dlptr_new(p,s,f);
+
+ rb_obj_call_init(obj, argc, argv);
+
+ return obj;
+};
+
+static VALUE
+rb_dlptr_s_alloc(int argc, VALUE argv[], VALUE klass)
+{
+ VALUE size, sym, obj;
+ int s;
+ void (*f)() = NULL;
+
+ switch( rb_scan_args(argc, argv, "11", &size, &sym) ){
+ case 1:
+ s = NUM2INT(size);
+ break;
+ case 2:
+ s = NUM2INT(size);
+ f = rb_dlsym2csym(sym);
+ break;
+ default:
+ rb_bug("rb_dlptr_s_new");
+ };
+
+ obj = rb_dlptr_alloc(s,f);
+
+ rb_obj_call_init(obj, argc, argv);
+
+ return obj;
+};
+
+static VALUE
+rb_dlptr_init(int argc, VALUE argv[], VALUE self)
+{
+ return Qnil;
+};
+
+static VALUE
+rb_dlptr_cast(int argc, VALUE argv[], VALUE self)
+{
+ VALUE klass, rest, val, *pass_argv;
+ int pass_argc, i;
+ struct ptr_data *data;
+
+ Data_Get_Struct(self, struct ptr_data, data);
+ rb_scan_args(argc, argv, "1*", &klass, &rest);
+
+ /* prepare the arguments of `new' method */
+ pass_argc = argc + 1;
+ pass_argv = ALLOCA_N(VALUE, pass_argc);
+ pass_argv[0] = DLLONG2NUM(data->ptr);
+ pass_argv[1] = rb_dlsym_new(data->free, NULL, NULL);
+ for( i=2; i < pass_argc; i++ ){
+ pass_argv[i] = rb_ary_entry(rest,i-2);
+ };
+
+ /* remove the data */
+ ((struct ptr_data *)(RDATA(self)->data))->ptr = 0;
+ (RDATA(self)->dfree)(RDATA(self)->data);
+
+ /* call the `new' method of klass with prepared arguments */
+ val = rb_funcall2(klass, rb_intern("new"), pass_argc, pass_argv);
+
+ RDATA(self)->basic.klass = RDATA(val)->basic.klass;
+ RDATA(self)->basic.flags = RDATA(val)->basic.flags;
+ RDATA(self)->dmark = RDATA(val)->dmark;
+ RDATA(self)->dfree = RDATA(val)->dfree;
+ RDATA(self)->data = RDATA(val)->data;
+
+ RDATA(val)->dmark = 0;
+ RDATA(val)->dfree = 0;
+
+ return Qnil;
+};
+
+VALUE
+rb_dlptr_to_i(VALUE self)
+{
+ struct ptr_data *data;
+
+ Data_Get_Struct(self, struct ptr_data, data);
+ return DLLONG2NUM(data->ptr);
+};
+
+VALUE
+rb_dlptr_ptr(VALUE self)
+{
+ struct ptr_data *data;
+
+ Data_Get_Struct(self, struct ptr_data, data);
+ return rb_dlptr_new(*((void**)(data->ptr)),0,0);
+};
+
+VALUE
+rb_dlptr_ref(VALUE self)
+{
+ struct ptr_data *data;
+
+ Data_Get_Struct(self, struct ptr_data, data);
+ return rb_dlptr_new(&(data->ptr),0,0);
+};
+
+VALUE
+rb_dlptr_null_p(VALUE self)
+{
+ struct ptr_data *data;
+
+ Data_Get_Struct(self, struct ptr_data, data);
+ return data->ptr ? Qfalse : Qtrue;
+};
+
+VALUE
+rb_dlptr_free_set(VALUE self, VALUE val)
+{
+ struct ptr_data *data;
+ int i;
+
+ Data_Get_Struct(self, struct ptr_data, data);
+
+ data->free = DLFREEFUNC(rb_dlsym2csym(val));
+
+ return Qnil;
+};
+
+VALUE
+rb_dlptr_free_get(VALUE self)
+{
+ struct ptr_data *pdata;
+
+ Data_Get_Struct(self, struct ptr_data, pdata);
+
+ return rb_dlsym_new(pdata->free,"(free)","0P");
+};
+
+VALUE
+rb_dlptr_to_array(int argc, VALUE argv[], VALUE self)
+{
+ struct ptr_data *data;
+ int n;
+ int i;
+ int t;
+ VALUE ary;
+ VALUE type, size;
+
+ Data_Get_Struct(self, struct ptr_data, data);
+
+ switch( rb_scan_args(argc, argv, "11", &type, &size) ){
+ case 2:
+ t = STR2CSTR(type)[0];
+ n = NUM2INT(size);
+ break;
+ case 1:
+ t = STR2CSTR(type)[0];
+ switch( t ){
+ case 'C':
+ n = data->size;
+ break;
+ case 'H':
+ n = data->size / sizeof(short);
+ break;
+ case 'I':
+ n = data->size / sizeof(int);
+ break;
+ case 'L':
+ n = data->size / sizeof(long);
+ break;
+ case 'F':
+ n = data->size / sizeof(float);
+ break;
+ case 'D':
+ n = data->size / sizeof(double);
+ break;
+ case 'S': case 'P':
+ n = data->size / sizeof(void*);
+ break;
+ default:
+ if( t == 'p' || t == 's' ){
+ int i;
+ for( i=0; ((void**)(data->ptr))[i]; i++ ){};
+ n = i;
+ }
+ else{
+ n = 0;
+ };
+ };
+ break;
+ default:
+ rb_bug("rb_dlptr_to_array");
+ };
+
+ ary = rb_ary_new();
+
+ for( i=0; i < n; i++ ){
+ switch( t ){
+ case 'C':
+ rb_ary_push(ary, INT2NUM(((char*)(data->ptr))[i]));
+ break;
+ case 'H':
+ rb_ary_push(ary, INT2NUM(((short*)(data->ptr))[i]));
+ break;
+ case 'I':
+ rb_ary_push(ary, INT2NUM(((int*)(data->ptr))[i]));
+ break;
+ case 'L':
+ rb_ary_push(ary, DLLONG2NUM(((long*)(data->ptr))[i]));
+ break;
+ case 'D':
+ rb_ary_push(ary, rb_float_new(((double*)(data->ptr))[i]));
+ case 'F':
+ rb_ary_push(ary, rb_float_new(((float*)(data->ptr))[i]));
+ break;
+ case 'S':
+ {
+ char *str = ((char**)(data->ptr))[i];
+ if( str ){
+ rb_ary_push(ary, rb_tainted_str_new2(str));
+ }
+ else{
+ rb_ary_push(ary, Qnil);
+ };
+ };
+ break;
+ case 's':
+ {
+ char *str = ((char**)(data->ptr))[i];
+ if( str ){
+ rb_ary_push(ary, rb_tainted_str_new2(str));
+ xfree(str);
+ }
+ else{
+ rb_ary_push(ary, Qnil);
+ };
+ };
+ break;
+ case 'P':
+ rb_ary_push(ary, rb_dlptr_new(((void**)(data->ptr))[i],0,0));
+ break;
+ case 'p':
+ rb_ary_push(ary,
+ rb_dlptr_new(((void**)(data->ptr))[i],0,dlfree));
+ break;
+ };
+ };
+
+ return ary;
+};
+
+
+VALUE
+rb_dlptr_to_s(int argc, VALUE argv[], VALUE self)
+{
+ struct ptr_data *data;
+ VALUE arg1, val;
+ int len;
+
+ Data_Get_Struct(self, struct ptr_data, data);
+ switch( rb_scan_args(argc, argv, "01", &arg1) ){
+ case 0:
+ val = rb_tainted_str_new2((char*)(data->ptr));
+ break;
+ case 1:
+ len = NUM2INT(arg1);
+ val = rb_tainted_str_new((char*)(data->ptr), len);
+ break;
+ default:
+ rb_bug("rb_dlptr_to_s");
+ };
+
+ return val;
+};
+
+VALUE
+rb_dlptr_to_str(int argc, VALUE argv[], VALUE self)
+{
+ struct ptr_data *data;
+ VALUE arg1, val;
+ int len;
+
+ Data_Get_Struct(self, struct ptr_data, data);
+ switch( rb_scan_args(argc, argv, "01", &arg1) ){
+ case 0:
+ val = rb_tainted_str_new((char*)(data->ptr),data->size);
+ break;
+ case 1:
+ len = NUM2INT(arg1);
+ val = rb_tainted_str_new((char*)(data->ptr), len);
+ break;
+ default:
+ rb_bug("rb_dlptr_to_str");
+ };
+
+ return val;
+};
+
+VALUE
+rb_dlptr_inspect(VALUE self)
+{
+ struct ptr_data *data;
+ char str[1024];
+ VALUE name;
+
+ Data_Get_Struct(self, struct ptr_data, data);
+ snprintf(str, 1023, "#<%s:0x%x ptr=0x%x size=%ld free=0x%x>",
+ rb_class2name(CLASS_OF(self)), data, data->ptr, data->size, data->free);
+ return rb_str_new2(str);
+};
+
+VALUE
+rb_dlptr_eql(VALUE self, VALUE other)
+{
+ void *ptr1, *ptr2;
+ ptr1 = rb_dlptr2cptr(self);
+ ptr2 = rb_dlptr2cptr(other);
+
+ return ptr1 == ptr2 ? Qtrue : Qfalse;
+};
+
+VALUE
+rb_dlptr_cmp(VALUE self, VALUE other)
+{
+ void *ptr1, *ptr2;
+ ptr1 = rb_dlptr2cptr(self);
+ ptr2 = rb_dlptr2cptr(other);
+ return DLLONG2NUM((long)ptr1 - (long)ptr2);
+};
+
+VALUE
+rb_dlptr_plus(VALUE self, VALUE other)
+{
+ void *ptr;
+ long num, size;
+
+ ptr = rb_dlptr2cptr(self);
+ size = RDLPTR(self)->size;
+ num = DLNUM2LONG(other);
+ return rb_dlptr_new((void*)(ptr + num), size - num, 0);
+};
+
+VALUE
+rb_dlptr_minus(VALUE self, VALUE other)
+{
+ void *ptr;
+ long num, size;
+
+ ptr = rb_dlptr2cptr(self);
+ size = RDLPTR(self)->size;
+ num = DLNUM2LONG(other);
+ return rb_dlptr_new((void*)(ptr - num), size + num, 0);
+};
+
+VALUE
+rb_dlptr_define_data_type(int argc, VALUE argv[], VALUE self)
+{
+ VALUE data_type, type, rest, vid;
+ struct ptr_data *data;
+ int i, t, len, num;
+ char *ctype;
+ long size;
+
+ rb_scan_args(argc, argv, "11*", &data_type, &type, &rest);
+ Data_Get_Struct(self, struct ptr_data, data);
+
+ if( argc == 1 || (argc == 2 && type == Qnil) ){
+ if( NUM2INT(data_type) == DLPTR_CTYPE_UNKNOWN ){
+ data->ctype = DLPTR_CTYPE_UNKNOWN;
+ data->slen = 0;
+ data->ids_num = 0;
+ if( data->stype ){
+ dlfree(data->stype);
+ data->stype = NULL;
+ };
+ if( data->ids ){
+ dlfree(data->ids);
+ data->ids = NULL;
+ };
+ return Qnil;
+ }
+ else{
+ raise(rb_eArgError, "wrong arguments");
+ };
+ };
+
+ Check_Type(data_type, T_FIXNUM);
+ Check_Type(type, T_STRING);
+ Check_Type(rest, T_ARRAY);
+ t = FIX2INT(data_type);
+ num = RARRAY(rest)->len;
+ for( i=0; i<num; i++ ){
+ vid = rb_ary_entry(rest,i);
+ if( !(TYPE(vid)==T_STRING || TYPE(vid)==T_SYMBOL) ){
+ rb_raise(rb_eTypeError, "#%d must be a string or symbol", i + 2);
+ };
+ };
+
+ data->ctype = t;
+ data->slen = num;
+ data->ids_num = num;
+ if( data->stype ) dlfree(data->stype);
+ data->stype = (char*)dlmalloc(sizeof(char) * num);
+ if( data->ssize ) dlfree(data->ssize);
+ data->ssize = (int*)dlmalloc(sizeof(int) * num);
+ if( data->ids ) dlfree(data->ids);
+ data->ids = (ID*)dlmalloc(sizeof(ID) * data->ids_num);
+
+ ctype = STR2CSTR(type);
+ for( i=0; i<num; i++ ){
+ vid = rb_ary_entry(rest,i);
+ data->ids[i] = rb_to_id(vid);
+ data->stype[i] = *ctype;
+ ctype ++;
+ if( isdigit(*ctype) ){
+ char *p, *d;
+ for( p=ctype; isdigit(*p); p++ ) ;
+ d = ALLOCA_N(char, p - ctype + 1);
+ strncpy(d, ctype, p - ctype);
+ d[p - ctype] = '\0';
+ data->ssize[i] = atoi(d);
+ ctype = p;
+ }
+ else{
+ data->ssize[i] = 1;
+ };
+ };
+
+ if( *ctype ){
+ rb_raise(rb_eArgError, "too few/many arguments");
+ };
+
+ if( !data->size )
+ data->size = dlsizeof(STR2CSTR(type));
+
+ return Qnil;
+};
+
+VALUE
+rb_dlptr_define_struct(int argc, VALUE argv[], VALUE self)
+{
+ VALUE *pass_argv;
+ int pass_argc, i;
+
+ pass_argc = argc + 1;
+ pass_argv = ALLOCA_N(VALUE, pass_argc);
+ pass_argv[0] = INT2FIX(DLPTR_CTYPE_STRUCT);
+ for( i=1; i<pass_argc; i++ ){
+ pass_argv[i] = argv[i-1];
+ };
+ return rb_dlptr_define_data_type(pass_argc, pass_argv, self);
+};
+
+VALUE
+rb_dlptr_define_union(int argc, VALUE argv[], VALUE self)
+{
+ VALUE *pass_argv;
+ int pass_argc, i;
+
+ pass_argc = argc + 1;
+ pass_argv = ALLOCA_N(VALUE, pass_argc);
+ pass_argv[0] = INT2FIX(DLPTR_CTYPE_UNION);
+ for( i=1; i<pass_argc; i++ ){
+ pass_argv[i] = argv[i-1];
+ };
+ return rb_dlptr_define_data_type(pass_argc, pass_argv, self);
+};
+
+VALUE
+rb_dlptr_get_data_type(VALUE self)
+{
+ struct ptr_data *data;
+
+ Data_Get_Struct(self, struct ptr_data, data);
+ if( data->stype )
+ return rb_assoc_new(INT2FIX(data->ctype),
+ rb_tainted_str_new(data->stype, data->slen));
+ else
+ return rb_assoc_new(INT2FIX(data->ctype), Qnil);
+};
+
+static VALUE
+cary2ary(void *ptr, char t, int len)
+{
+ VALUE ary;
+ VALUE elem;
+ int i;
+
+ if( len < 1 )
+ return Qnil;
+
+ if( len == 1 ){
+ switch( t ){
+ case 'I': case 'i':
+ elem = INT2NUM(*((int*)ptr));
+ ptr += sizeof(int);
+ break;
+ case 'L': case 'l':
+ elem = DLLONG2NUM(*((long*)ptr));
+ ptr += sizeof(long);
+ break;
+ case 'P': case 'p':
+ elem = rb_dlptr_new(*((void**)ptr),0, 0);
+ ptr += sizeof(void*);
+ break;
+ case 'F': case 'f':
+ elem = rb_float_new(*((float*)ptr));
+ ptr += sizeof(float);
+ break;
+ case 'D': case 'd':
+ elem = rb_float_new(*((float*)ptr));
+ ptr += sizeof(double);
+ break;
+ case 'C': case 'c':
+ elem = INT2NUM(*((char*)ptr));
+ ptr += sizeof(char);
+ break;
+ case 'H': case 'h':
+ elem = INT2NUM(*((short*)ptr));
+ ptr += sizeof(short);
+ default:
+ raise(rb_eDLTypeError, "unsupported type '%c'", t);
+ };
+ return elem;
+ };
+
+ ary = rb_ary_new();
+ for( i=0; i < len; i++ ){
+ switch( t ){
+ case 'I': case 'i':
+ elem = INT2NUM(*((int*)ptr));
+ ptr += sizeof(int);
+ break;
+ case 'L': case 'l':
+ elem = DLLONG2NUM(*((long*)ptr));
+ ptr += sizeof(long);
+ break;
+ case 'P': case 'p':
+ elem = rb_dlptr_new(*((void**)ptr), 0, 0);
+ ptr += sizeof(void*);
+ break;
+ case 'F': case 'f':
+ elem = rb_float_new(*((float*)ptr));
+ ptr += sizeof(float);
+ break;
+ case 'D': case 'd':
+ elem = rb_float_new(*((float*)ptr));
+ ptr += sizeof(double);
+ break;
+ case 'C': case 'c':
+ elem = INT2NUM(*((char*)ptr));
+ ptr += sizeof(char);
+ break;
+ case 'H': case 'h':
+ elem = INT2NUM(*((short*)ptr));
+ ptr += sizeof(short);
+ default:
+ raise(rb_eDLTypeError, "unsupported type '%c'", t);
+ };
+ rb_ary_push(ary, elem);
+ };
+
+ return ary;
+};
+
+VALUE
+rb_dlptr_aref(int argc, VALUE argv[], VALUE self)
+{
+ VALUE val, key = Qnil, num = Qnil;
+ ID id;
+ int idx;
+ struct ptr_data *data;
+ int i;
+ int offset;
+
+ if( rb_scan_args(argc, argv, "11", &key, &num) == 1 ){
+ num = INT2NUM(0);
+ };
+
+ if( TYPE(key) == T_FIXNUM || TYPE(key) == T_BIGNUM ){
+ VALUE pass[] = {num};
+ return rb_dlptr_to_str(1, pass, rb_dlptr_plus(self, key));
+ };
+
+ if( ! (TYPE(key) == T_STRING || TYPE(key) == T_SYMBOL ) ){
+ rb_raise(rb_eTypeError, "the key must be a string or symbol");
+ };
+
+ id = rb_to_id(key);
+ Data_Get_Struct(self, struct ptr_data, data);
+ switch( data->ctype ){
+ case DLPTR_CTYPE_STRUCT:
+ offset = 0;
+ for( i=0; i < data->ids_num; i++ ){
+ if( data->ids[i] == id ){
+ switch( data->stype[i] ){
+ case 'I':
+ DLALIGN(data->ptr,offset,INT_ALIGN);
+ break;
+ case 'L':
+ DLALIGN(data->ptr,offset,LONG_ALIGN);
+ break;
+ case 'P':
+ DLALIGN(data->ptr,offset,VOIDP_ALIGN);
+ case 'F':
+ DLALIGN(data->ptr,offset,FLOAT_ALIGN);
+ break;
+ case 'D':
+ DLALIGN(data->ptr,offset,DOUBLE_ALIGN);
+ break;
+ case 'C':
+ break;
+ case 'H':
+ DLALIGN(data->ptr,offset,SHORT_ALIGN);
+ break;
+ default:
+ raise(rb_eDLTypeError, "unsupported type '%c'", data->stype[i]);
+ };
+ return cary2ary(data->ptr + offset, data->stype[i], data->ssize[i]);
+ };
+ switch( data->stype[i] ){
+ case 'I':
+ case 'i':
+ offset += sizeof(int) * data->ssize[i];
+ break;
+ case 'L':
+ case 'l':
+ offset += sizeof(long) * data->ssize[i];
+ break;
+ case 'P':
+ case 'p':
+ offset += sizeof(void*) * data->ssize[i];
+ break;
+ case 'F':
+ case 'f':
+ offset += sizeof(float) * data->ssize[i];
+ break;
+ case 'D':
+ case 'd':
+ offset += sizeof(double) * data->ssize[i];
+ break;
+ case 'C':
+ case 'c':
+ offset += sizeof(char) * data->ssize[i];
+ break;
+ case 'H':
+ case 'h':
+ offset += sizeof(short) * data->ssize[i];
+ break;
+ default:
+ raise(rb_eDLTypeError, "unsupported type '%c'", data->stype[i]);
+ };
+ };
+ break;
+ case DLPTR_CTYPE_UNION:
+ for( i=0; i < data->ids_num; i++ ){
+ if( data->ids[i] == id ){
+ return cary2ary(data->ptr + offset, data->stype[i], data->ssize[i]);
+ };
+ };
+ break;
+ }; /* end of switch */
+
+ rb_raise(rb_eNameError, "undefined key `%s' for %s",
+ rb_id2name(id), rb_class2name(CLASS_OF(self)));
+
+ return Qnil;
+};
+
+static void *
+ary2cary(char t, VALUE val, long *size)
+{
+ void *ptr;
+
+ if( TYPE(val) == T_ARRAY ){
+ ptr = rb_ary2cary(t, val, size);
+ }
+ else{
+ ptr = rb_ary2cary(t, rb_ary_new3(1, val), size);
+ };
+ return ptr;
+};
+
+VALUE
+rb_dlptr_aset(int argc, VALUE argv[], VALUE self)
+{
+ VALUE key = Qnil, num = Qnil, val = Qnil;
+ ID id;
+ struct ptr_data *data;
+ int i;
+ int offset;
+ long memsize;
+ void *memimg;
+
+ switch( rb_scan_args(argc, argv, "21", &key, &num, &val) ){
+ case 2:
+ val = num;
+ num = Qnil;
+ break;
+ };
+
+ if( TYPE(key) == T_FIXNUM || TYPE(key) == T_BIGNUM ){
+ void *dst, *src;
+ int len;
+
+ Check_Type(val, T_STRING);
+ Data_Get_Struct(self, struct ptr_data, data);
+ dst = (void*)((long)(data->ptr) + DLNUM2LONG(key));
+ src = STR2CSTR(val);
+ if( num == Qnil ){
+ len = RSTRING(val)->len;
+ }
+ else{
+ len = NUM2INT(num);
+ };
+ memcpy(dst, src, len);
+ return val;
+ };
+
+ if( ! (TYPE(key) == T_STRING || TYPE(key) == T_SYMBOL ) ){
+ rb_raise(rb_eTypeError, "the key must be a string or symbol");
+ };
+
+ id = rb_to_id(key);
+ Data_Get_Struct(self, struct ptr_data, data);
+ switch( data->ctype ){
+ case DLPTR_CTYPE_STRUCT:
+ offset = 0;
+ for( i=0; i < data->ids_num; i++ ){
+ if( data->ids[i] == id ){
+ switch( data->stype[i] ){
+ case 'I':
+ DLALIGN(data->ptr,offset,INT_ALIGN);
+ break;
+ case 'L':
+ DLALIGN(data->ptr,offset,LONG_ALIGN);
+ break;
+ case 'P':
+ DLALIGN(data->ptr,offset,VOIDP_ALIGN);
+ break;
+ case 'D':
+ DLALIGN(data->ptr,offset,DOUBLE_ALIGN);
+ break;
+ case 'F':
+ DLALIGN(data->ptr,offset,FLOAT_ALIGN);
+ break;
+ case 'C':
+ break;
+ case 'H':
+ DLALIGN(data->ptr,offset,SHORT_ALIGN);
+ break;
+ default:
+ raise(rb_eDLTypeError, "unsupported type '%c'", data->stype[i]);
+ };
+ memimg = ary2cary(data->stype[i], val, &memsize);
+ memcpy(data->ptr + offset, memimg, memsize);
+ return val;
+ };
+ switch( data->stype[i] ){
+ case 'I':
+ case 'i':
+ offset += sizeof(int) * data->ssize[i];
+ break;
+ case 'L':
+ case 'l':
+ offset += sizeof(long) * data->ssize[i];
+ break;
+ case 'P':
+ case 'p':
+ offset += sizeof(void*) * data->ssize[i];
+ break;
+ case 'D':
+ case 'd':
+ offset += sizeof(double) * data->ssize[i];
+ break;
+ case 'F':
+ case 'f':
+ offset += sizeof(float) * data->ssize[i];
+ break;
+ case 'C':
+ case 'c':
+ offset += sizeof(char) * data->ssize[i];
+ break;
+ case 'H':
+ case 'h':
+ offset += sizeof(short) * data->ssize[i];
+ break;
+ default:
+ raise(rb_eDLTypeError, "unsupported type '%c'", data->stype[i]);
+ };
+ };
+ return val;
+ /* break; */
+ case DLPTR_CTYPE_UNION:
+ for( i=0; i < data->ids_num; i++ ){
+ if( data->ids[i] == id ){
+ switch( data->stype[i] ){
+ case 'I': case 'i':
+ memsize = sizeof(int) * data->ssize[i];
+ break;
+ case 'L': case 'l':
+ memsize = sizeof(long) * data->ssize[i];
+ break;
+ case 'P': case 'p':
+ memsize = sizeof(void*) * data->ssize[i];
+ break;
+ case 'F': case 'f':
+ memsize = sizeof(float) * data->ssize[i];
+ break;
+ case 'D': case 'd':
+ memsize = sizeof(double) * data->ssize[i];
+ break;
+ case 'C': case 'c':
+ memsize = sizeof(char) * data->ssize[i];
+ break;
+ case 'H': case 'h':
+ memsize = sizeof(short) * data->ssize[i];
+ break;
+ default:
+ raise(rb_eDLTypeError, "unsupported type '%c'", data->stype[i]);
+ };
+ memimg = ary2cary(data->stype[i], val, NULL);
+ memcpy(data->ptr, memimg, memsize);
+ };
+ };
+ return val;
+ /* break; */
+ };
+
+ rb_raise(rb_eNameError, "undefined key `%s' for %s",
+ rb_id2name(id), rb_class2name(CLASS_OF(self)));
+
+ return Qnil;
+};
+
+VALUE
+rb_dlptr_size(int argc, VALUE argv[], VALUE self)
+{
+ VALUE size;
+
+ switch( rb_scan_args(argc, argv, "01", &size) ){
+ case 0:
+ return DLLONG2NUM(RDLPTR(self)->size);
+ case 1:
+ RDLPTR(self)->size = DLNUM2LONG(size);
+ return size;
+ };
+};
+
+static VALUE
+dlmem_each_i(VALUE assoc, void *data)
+{
+ VALUE key, val;
+ key = rb_ary_entry(assoc, 0);
+ val = rb_ary_entry(assoc, 1);
+ rb_yield(rb_assoc_new(key,(VALUE)DLNUM2LONG(val)));
+ return Qnil;
+};
+
+VALUE
+rb_dlmem_each(VALUE self)
+{
+ rb_iterate(rb_each, DLMemoryTable, dlmem_each_i, 0);
+ return Qnil;
+};
+
+void
+Init_dlptr()
+{
+ rb_cDLPtrData = rb_define_class_under(rb_mDL, "PtrData", rb_cData);
+ rb_define_singleton_method(rb_cDLPtrData, "new", rb_dlptr_s_new, -1);
+ rb_define_singleton_method(rb_cDLPtrData, "alloc", rb_dlptr_s_alloc, -1);
+ rb_define_method(rb_cDLPtrData, "initialize", rb_dlptr_init, -1);
+ rb_define_method(rb_cDLPtrData, "free=", rb_dlptr_free_set, 1);
+ rb_define_method(rb_cDLPtrData, "free", rb_dlptr_free_get, 0);
+ rb_define_method(rb_cDLPtrData, "to_i", rb_dlptr_to_i, 0);
+ rb_define_method(rb_cDLPtrData, "ptr", rb_dlptr_ptr, 0);
+ rb_define_method(rb_cDLPtrData, "+@", rb_dlptr_ptr, 0);
+ rb_define_method(rb_cDLPtrData, "ref", rb_dlptr_ref, 0);
+ rb_define_method(rb_cDLPtrData, "-@", rb_dlptr_ref, 0);
+ rb_define_method(rb_cDLPtrData, "null?", rb_dlptr_null_p, 0);
+ rb_define_method(rb_cDLPtrData, "to_a", rb_dlptr_to_array, -1);
+ rb_define_method(rb_cDLPtrData, "to_s", rb_dlptr_to_s, -1);
+ rb_define_method(rb_cDLPtrData, "to_str", rb_dlptr_to_str, -1);
+ rb_define_method(rb_cDLPtrData, "inspect", rb_dlptr_inspect, 0);
+ rb_define_method(rb_cDLPtrData, "<=>", rb_dlptr_cmp, 1);
+ rb_define_method(rb_cDLPtrData, "==", rb_dlptr_eql, 1);
+ rb_define_method(rb_cDLPtrData, "eql?", rb_dlptr_eql, 1);
+ rb_define_method(rb_cDLPtrData, "+", rb_dlptr_plus, 1);
+ rb_define_method(rb_cDLPtrData, "-", rb_dlptr_minus, 1);
+ rb_define_method(rb_cDLPtrData, "cast!", rb_dlptr_cast, -1);
+ rb_define_method(rb_cDLPtrData, "define_data_type",
+ rb_dlptr_define_data_type, -1);
+ rb_define_method(rb_cDLPtrData, "struct!", rb_dlptr_define_struct, -1);
+ rb_define_method(rb_cDLPtrData, "union!", rb_dlptr_define_union, -1);
+ rb_define_method(rb_cDLPtrData, "data_type", rb_dlptr_get_data_type, 0);
+ rb_define_method(rb_cDLPtrData, "[]", rb_dlptr_aref, -1);
+ rb_define_method(rb_cDLPtrData, "[]=", rb_dlptr_aset, -1);
+ rb_define_method(rb_cDLPtrData, "size", rb_dlptr_size, -1);
+ rb_define_method(rb_cDLPtrData, "size=", rb_dlptr_size, -1);
+
+ rb_mDLMemorySpace = rb_define_module_under(rb_mDL, "MemorySpace");
+ DLMemoryTable = rb_hash_new();
+ rb_define_const(rb_mDLMemorySpace, "MemoryTable", DLMemoryTable);
+ rb_define_module_function(rb_mDLMemorySpace, "each", rb_dlmem_each, 0);
+};
diff --git a/ext/dl/sample/drives.rb b/ext/dl/sample/drives.rb
new file mode 100644
index 0000000000..8a590404b1
--- /dev/null
+++ b/ext/dl/sample/drives.rb
@@ -0,0 +1,70 @@
+# -*- ruby -*-
+# drives.rb -- find existing drives and show the drive type.
+
+require 'dl'
+require 'dl/import'
+
+module Kernel32
+ extend DL::Importable
+
+ dlload "kernel32"
+
+ extern "long GetLogicalDrives()"
+ extern "int GetDriveType(char*)"
+ extern "long GetDiskFreeSpace(char*, long ref, long ref, long ref, long ref)"
+end
+
+include Kernel32
+
+buff = Kernel32.getLogicalDrives()
+
+i = 0
+ds = []
+while( i < 26 )
+ mask = (1 << i)
+ if( buff & mask > 0 )
+ ds.push((65+i).chr)
+ end
+ i += 1
+end
+
+=begin
+From the cygwin's /usr/include/w32api/winbase.h:
+#define DRIVE_UNKNOWN 0
+#define DRIVE_NO_ROOT_DIR 1
+#define DRIVE_REMOVABLE 2
+#define DRIVE_FIXED 3
+#define DRIVE_REMOTE 4
+#define DRIVE_CDROM 5
+#define DRIVE_RAMDISK 6
+=end
+
+types = [
+ "unknown",
+ "no root dir",
+ "Removable",
+ "Fixed",
+ "Remote",
+ "CDROM",
+ "RAM",
+]
+print("Drive : Type (Free Space/Available Space)\n")
+ds.each{|d|
+ t = Kernel32.getDriveType(d + ":\\")
+ Kernel32.getDiskFreeSpace(d + ":\\", 0, 0, 0, 0)
+ _,sec_per_clus,byte_per_sec,free_clus,total_clus = Kernel32._args_
+ fbytes = sec_per_clus * byte_per_sec * free_clus
+ tbytes = sec_per_clus * byte_per_sec * total_clus
+ unit = "B"
+ if( fbytes > 1024 && tbytes > 1024 )
+ fbytes = fbytes / 1024
+ tbytes = tbytes / 1024
+ unit = "K"
+ end
+ if( fbytes > 1024 && tbytes > 1024 )
+ fbytes = fbytes / 1024
+ tbytes = tbytes / 1024
+ unit = "M"
+ end
+ print("#{d} : #{types[t]} (#{fbytes} #{unit}/#{tbytes} #{unit})\n")
+}
diff --git a/ext/dl/sample/getch.rb b/ext/dl/sample/getch.rb
new file mode 100644
index 0000000000..3f7261c979
--- /dev/null
+++ b/ext/dl/sample/getch.rb
@@ -0,0 +1,5 @@
+require 'dl'
+
+crtdll = DL::dlopen("crtdll")
+getch = crtdll['_getch', 'L']
+print(getch.call, "\n")
diff --git a/ext/dl/sample/libc.rb b/ext/dl/sample/libc.rb
new file mode 100644
index 0000000000..c88fc687f2
--- /dev/null
+++ b/ext/dl/sample/libc.rb
@@ -0,0 +1,84 @@
+require 'dl'
+
+module LIBC
+ begin
+ LIB = DL.dlopen('libc.so.6')
+ rescue RuntimeError
+ LIB = DL.dlopen('libc.so.5')
+ end
+
+ SYM = {
+ :atoi => LIB['atoi', 'IS'],
+ :isdigit => LIB['isdigit', 'II'],
+ }
+
+ def atoi(str)
+ r,rs = SYM[:atoi].call(str)
+ return r
+ end
+
+ def isdigit(c)
+ r,rs = SYM[:isdigit].call(c)
+ return (r != 0)
+ end
+end
+
+module LIBC
+ SYM[:strcat] = LIB['strcat', 'SsS']
+ def strcat(str1,str2)
+ r,rs = SYM[:strcat].call(str1 + "\0#{str2}",str2)
+ return rs[0]
+ end
+end
+
+module LIBC
+ SYM[:fopen] = LIB['fopen', 'PSS']
+ SYM[:fclose] = LIB['fclose', '0P']
+ SYM[:fgetc] = LIB['fgetc', 'IP']
+
+ def fopen(filename, mode)
+ r,rs = SYM[:fopen].call(filename, mode)
+ return r
+ end
+
+ def fclose(ptr)
+ SYM[:fclose].call(ptr)
+ return nil
+ end
+
+ def fgetc(ptr)
+ r,rs = SYM[:fgetc].call(ptr)
+ return r
+ end
+end
+
+module LIBC
+ SYM[:strlen] = LIB['strlen', 'IP']
+ def strlen(str)
+ r,rs = SYM[:strlen].call(str)
+ return r
+ end
+end
+
+$cb1 = DL.set_callback('IPP', 0){|ptr1, ptr2|
+ str1 = ptr1.ptr.to_s
+ str2 = ptr2.ptr.to_s
+ str1 <=> str2
+}
+
+module LIBC
+ SYM[:qsort] = LIB['qsort', '0aIIP']
+ def qsort(ary, comp)
+ len = ary.length
+ r,rs = SYM[:qsort].call(ary, len, DL.sizeof('P'), comp)
+ return rs[0].to_a('S', len)
+ end
+end
+
+include LIBC
+
+p atoi("10")
+p isdigit(?1)
+p isdigit(?a)
+p strcat("a", "b")
+p qsort(["a","c","b"],$cb1)
diff --git a/ext/dl/sample/msgbox.rb b/ext/dl/sample/msgbox.rb
new file mode 100644
index 0000000000..091e646091
--- /dev/null
+++ b/ext/dl/sample/msgbox.rb
@@ -0,0 +1,19 @@
+# This script works on Windows.
+
+require 'dl'
+
+User32 = DL.dlopen("user32")
+Kernel32 = DL.dlopen("kernel32")
+
+MB_OK = 0
+MB_OKCANCEL = 1
+
+message_box = User32['MessageBoxA', 'ILSSI']
+r,rs = message_box.call(0, 'ok?', 'error', MB_OKCANCEL)
+
+case r
+when 1
+ print("OK!\n")
+when 2
+ print("Cancel!\n")
+end
diff --git a/ext/dl/sample/msgbox2.rb b/ext/dl/sample/msgbox2.rb
new file mode 100644
index 0000000000..e49846cc5e
--- /dev/null
+++ b/ext/dl/sample/msgbox2.rb
@@ -0,0 +1,18 @@
+# This script works on Windows.
+
+require 'dl/win32'
+
+MB_OK = 0
+MB_OKCANCEL = 1
+
+message_box = Win32API.new("user32",'MessageBoxA', 'ISSI', 'I')
+r = message_box.call(0, 'ok?', 'error', MB_OKCANCEL)
+
+case r
+when 1
+ print("OK!\n")
+when 2
+ print("Cancel!\n")
+else
+ p r
+end
diff --git a/ext/dl/sample/stream.rb b/ext/dl/sample/stream.rb
new file mode 100644
index 0000000000..179836999d
--- /dev/null
+++ b/ext/dl/sample/stream.rb
@@ -0,0 +1,87 @@
+# -*- ruby -*-
+# Display a file name and stream names of a file with those size.
+
+require 'dl'
+require 'dl/import'
+
+module NTFS
+ extend DL::Importable
+
+ dlload "kernel32.dll"
+
+ OPEN_EXISTING = 3
+ GENERIC_READ = 0x80000000
+ BACKUP_DATA = 0x00000001
+ BACKUP_ALTERNATE_DATA = 0x00000004
+ FILE_SHARE_READ = 0x00000001
+ FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
+
+ typealias "LPSECURITY_ATTRIBUTES", "void*"
+
+ extern "BOOL BackupRead(HANDLE, PBYTE, DWORD, PDWORD, BOOL, BOOL, PVOID)"
+ extern "BOOL BackupSeek(HANDLE, DWORD, DWORD, PDWORD, PDWORD, PVOID)"
+ extern "BOOL CloseHandle(HANDLE)"
+ extern "HANDLE CreateFile(LPCSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES,
+ DWORD, DWORD, HANDLE)"
+
+ module_function
+
+ def streams(filename)
+ status = []
+ h = createFile(filename,GENERIC_READ,FILE_SHARE_READ,nil,
+ OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,0)
+ if( h != 0 )
+ begin
+ # allocate the memory for backup data used in backupRead().
+ data = DL.malloc(DL.sizeof("L5"))
+ data.struct!("LLLLL", :id, :attrs, :size_low, :size_high, :name_size)
+
+ # allocate memories for references to long values used in backupRead().
+ context = DL.malloc(DL.sizeof("L"))
+ lval = DL.malloc(DL.sizeof("L"))
+
+ while( backupRead(h, data, data.size, lval, false, false, context) )
+ size = data[:size_low] + (data[:size_high] << (DL.sizeof("I") * 8))
+ case data[:id]
+ when BACKUP_ALTERNATE_DATA
+ stream_name = DL.malloc(data[:name_size])
+ backupRead(h, stream_name, stream_name.size,
+ lval, false, false, context)
+ name = stream_name[0, stream_name.size]
+ name.tr!("\000","")
+ if( name =~ /^:(.*?):.*$/ )
+ status.push([$1,size])
+ end
+ when BACKUP_DATA
+ status.push([nil,size])
+ else
+ raise(RuntimeError, "unknown data type #{data[:id]}.")
+ end
+ l1 = DL.malloc(DL.sizeof("L"))
+ l2 = DL.malloc(DL.sizeof("L"))
+ if( !backupSeek(h, data[:size_low], data[:size_high], l1, l2, context) )
+ break
+ end
+ end
+ ensure
+ backupRead(h, nil, 0, lval, true, false, context)
+ closeHandle(h)
+ end
+ return status
+ else
+ raise(RuntimeError, "can't open #{filename}.\n")
+ end
+ end
+end
+
+ARGV.each{|filename|
+ if( File.exist?(filename) )
+ NTFS.streams(filename).each{|name,size|
+ if( name )
+ print("#{filename}:#{name}\t#{size}bytes\n")
+ else
+ print("#{filename}\t#{size}bytes\n")
+ end
+ }
+ end
+}
diff --git a/ext/dl/sym.c b/ext/dl/sym.c
new file mode 100644
index 0000000000..683092ae7b
--- /dev/null
+++ b/ext/dl/sym.c
@@ -0,0 +1,771 @@
+/* -*- C -*-
+ * $Id$
+ */
+
+#include <ruby.h>
+#include "dl.h"
+
+VALUE rb_cDLSymbol;
+
+#ifndef HAVE_RB_STR_CAT2
+static VALUE
+rb_str_cat2(VALUE str, const char *s)
+{
+ return rb_str_cat(str, s, strlen(s));
+}
+#endif
+
+static const char *
+char2type(int ch)
+{
+ switch (ch) {
+ case '0':
+ return "void";
+ case 'P':
+ return "void *";
+ case 'p':
+ return "void *";
+ case 'C':
+ return "char";
+ case 'c':
+ return "char *";
+ case 'H':
+ return "short";
+ case 'h':
+ return "short *";
+ case 'I':
+ return "int";
+ case 'i':
+ return "int *";
+ case 'L':
+ return "long";
+ case 'l':
+ return "long *";
+ case 'F':
+ return "double";
+ case 'f':
+ return "double *";
+ case 'D':
+ return "double";
+ case 'd':
+ return "double *";
+ case 'S':
+ return "const char *";
+ case 's':
+ return "char *";
+ case 'A':
+ return "[]";
+ case 'a':
+ return "[]"; /* ?? */
+ };
+ return NULL;
+};
+
+void
+dlsym_free(struct sym_data *data)
+{
+ if( data->name ){
+ DEBUG_CODE({
+ printf("dlsym_free(): free(data->name:%s)\n",data->name);
+ });
+ free(data->name);
+ };
+ if( data->type ){
+ DEBUG_CODE({
+ printf("dlsym_free(): free(data->type:%s)\n",data->type);
+ });
+ free(data->type);
+ };
+};
+
+VALUE
+rb_dlsym_new(void (*func)(), const char *name, const char *type)
+{
+ VALUE val;
+ struct sym_data *data;
+ const char *ptype;
+
+ if( !type || !type[0] ){
+ return rb_dlptr_new((void*)func, 0, 0);
+ };
+
+ for( ptype = type; *ptype; ptype ++ ){
+ if( ! char2type(*ptype) ){
+ rb_raise(rb_eDLTypeError, "unknown type specifier '%c'", *ptype);
+ };
+ };
+
+ if( func ){
+ val = Data_Make_Struct(rb_cDLSymbol, struct sym_data, 0, dlsym_free, data);
+ data->func = func;
+ data->name = name ? strdup(name) : NULL;
+ data->type = type ? strdup(type) : NULL;
+ data->len = type ? strlen(type) : 0;
+#ifndef USE_INLINE_ASM
+ if( data->len - 1 > MAX_ARG ){
+ rb_raise(rb_eDLError, "maximum number of arguments is %d.", MAX_ARG);
+ };
+#endif
+ }
+ else{
+ val = Qnil;
+ };
+
+ return val;
+};
+
+freefunc_t
+rb_dlsym2csym(VALUE val)
+{
+ struct sym_data *data;
+ void (*func)();
+
+ if( rb_obj_is_kind_of(val, rb_cDLSymbol) ){
+ Data_Get_Struct(val, struct sym_data, data);
+ func = data->func;
+ }
+ else if( val == Qnil ){
+ func = NULL;
+ }
+ else{
+ rb_raise(rb_eTypeError, "DL::Symbol was expected");
+ };
+
+ return func;
+};
+
+VALUE
+rb_dlsym_s_new(int argc, VALUE argv[], VALUE self)
+{
+ VALUE addr, name, type;
+ VALUE val;
+ void *saddr;
+ const char *sname, *stype;
+
+ switch( rb_scan_args(argc, argv, "12", &addr, &name, &type) ){
+ case 3:
+ break;
+ case 2:
+ type = Qnil;
+ break;
+ case 1:
+ name = Qnil;
+ type = Qnil;
+ };
+
+ saddr = (void*)(DLNUM2LONG(rb_Integer(addr)));
+ sname = (name == Qnil) ? NULL : STR2CSTR(name);
+ stype = (type == Qnil) ? NULL : STR2CSTR(type);
+
+ val = rb_dlsym_new(saddr, sname, stype);
+
+ if( val != Qnil ){
+ rb_obj_call_init(val, argc, argv);
+ };
+
+ return val;
+};
+
+VALUE
+rb_dlsym_initialize(int argc, VALUE argv[], VALUE self)
+{
+ return Qnil;
+};
+
+VALUE
+rb_s_dlsym_char2type(VALUE self, VALUE ch)
+{
+ const char *type;
+
+ type = char2type(STR2CSTR(ch)[0]);
+
+ if (type == NULL)
+ return Qnil;
+ else
+ return rb_str_new2(type);
+};
+
+VALUE
+rb_dlsym_name(VALUE self)
+{
+ struct sym_data *sym;
+
+ Data_Get_Struct(self, struct sym_data, sym);
+ return sym->name ? rb_tainted_str_new2(sym->name) : Qnil;
+};
+
+VALUE
+rb_dlsym_proto(VALUE self)
+{
+ struct sym_data *sym;
+
+ Data_Get_Struct(self, struct sym_data, sym);
+ return sym->type ? rb_tainted_str_new2(sym->type) : Qnil;
+};
+
+VALUE
+rb_dlsym_cproto(VALUE self)
+{
+ struct sym_data *sym;
+ const char *ptype, *typestr;
+ size_t len;
+ VALUE val;
+
+ Data_Get_Struct(self, struct sym_data, sym);
+
+ ptype = sym->type;
+
+ if( ptype ){
+ typestr = char2type(*ptype++);
+ len = strlen(typestr);
+
+ val = rb_tainted_str_new(typestr, len);
+ if (typestr[len - 1] != '*')
+ rb_str_cat(val, " ", 1);
+
+ if( sym->name ){
+ rb_str_cat2(val, sym->name);
+ }
+ else{
+ rb_str_cat2(val, "(null)");
+ };
+ rb_str_cat(val, "(", 1);
+
+ while (*ptype) {
+ const char *ty = char2type(*ptype++);
+ rb_str_cat2(val, ty);
+ if (*ptype)
+ rb_str_cat(val, ", ", 2);
+ }
+
+ rb_str_cat(val, ");", 2);
+ }
+ else{
+ val = rb_tainted_str_new2("void (");
+ if( sym->name ){
+ rb_str_cat2(val, sym->name);
+ }
+ else{
+ rb_str_cat2(val, "(null)");
+ };
+ rb_str_cat2(val, ")()");
+ };
+
+ return val;
+};
+
+VALUE
+rb_dlsym_inspect(VALUE self)
+{
+ VALUE proto;
+ VALUE val;
+ char *str;
+ int str_size;
+ struct sym_data *sym;
+
+ Data_Get_Struct(self, struct sym_data, sym);
+ proto = rb_dlsym_cproto(self);
+
+ str_size = RSTRING(proto)->len + 100;
+ str = dlmalloc(str_size);
+ snprintf(str, str_size - 1,
+ "#<DL::Symbol:0x%x func=0x%x '%s'>",
+ sym, sym->func, STR2CSTR(proto));
+ val = rb_tainted_str_new2(str);
+ dlfree(str);
+
+ return val;
+};
+
+
+VALUE
+rb_dlsym_call(int argc, VALUE argv[], VALUE self)
+{
+ struct sym_data *sym;
+ ANY_TYPE *args;
+ ANY_TYPE *dargs;
+ ANY_TYPE ret;
+ int *dtypes;
+ VALUE val;
+ VALUE dvals;
+ int i;
+ long ftype;
+ void *func;
+
+ Data_Get_Struct(self, struct sym_data, sym);
+ DEBUG_CODE({
+ printf("rb_dlsym_call(): type = '%s', func = 0x%x\n", sym->type, sym->func);
+ });
+ if( (sym->len - 1) != argc ){
+ rb_raise(rb_eArgError, "%d arguments are needed", sym->len - 1);
+ };
+
+ ftype = 0;
+ dvals = Qnil;
+
+ args = ALLOC_N(ANY_TYPE, sym->len - 1);
+ dargs = ALLOC_N(ANY_TYPE, sym->len - 1);
+ dtypes = ALLOC_N(int, sym->len - 1);
+#define FREE_ARGS {xfree(args); xfree(dargs); xfree(dtypes);}
+
+ for( i = sym->len - 2; i >= 0; i-- ){
+ dtypes[i] = 0;
+
+ switch( sym->type[i+1] ){
+ case 'p':
+ dtypes[i] = 'p';
+ case 'P':
+ {
+ struct ptr_data *data;
+ VALUE pval;
+
+ if( argv[i] == Qnil ){
+ ANY2P(args[i]) = DLVOIDP(0);
+ }
+ else{
+ if( rb_obj_is_kind_of(argv[i], rb_cDLPtrData) ){
+ pval = argv[i];
+ }
+ else{
+ pval = rb_funcall(argv[i], rb_intern("to_ptr"), 0);
+ if( !rb_obj_is_kind_of(pval, rb_cDLPtrData) ){
+ rb_raise(rb_eDLTypeError, "unexpected type of argument #%d", i);
+ };
+ };
+ Data_Get_Struct(pval, struct ptr_data, data);
+ ANY2P(args[i]) = DLVOIDP(data->ptr);
+ };
+ };
+ PUSH_P(ftype);
+ break;
+ case 'a':
+ dtypes[i] = 'a';
+ case 'A':
+ if( argv[i] == Qnil ){
+ ANY2P(args[i]) = DLVOIDP(0);
+ }
+ else{
+ ANY2P(args[i]) = DLVOIDP(rb_ary2cary(0, argv[i], NULL));
+ };
+ PUSH_P(ftype);
+ break;
+ case 'C':
+ ANY2C(args[i]) = DLCHAR(NUM2CHR(argv[i]));
+ PUSH_C(ftype);
+ break;
+ case 'c':
+ ANY2C(dargs[i]) = DLCHAR(NUM2CHR(argv[i]));
+ ANY2P(args[i]) = DLVOIDP(&(ANY2C(dargs[i])));
+ dtypes[i] = 'c';
+ PUSH_P(ftype);
+ break;
+ case 'H':
+ ANY2H(args[i]) = DLSHORT(NUM2CHR(argv[i]));
+ PUSH_C(ftype);
+ break;
+ case 'h':
+ ANY2H(dargs[i]) = DLSHORT(NUM2CHR(argv[i]));
+ ANY2P(args[i]) = DLVOIDP(&(ANY2H(dargs[i])));
+ dtypes[i] = 'h';
+ PUSH_P(ftype);
+ break;
+ case 'I':
+ ANY2I(args[i]) = DLINT(NUM2INT(argv[i]));
+ PUSH_I(ftype);
+ break;
+ case 'i':
+ ANY2I(dargs[i]) = DLINT(NUM2INT(argv[i]));
+ ANY2P(args[i]) = DLVOIDP(&(ANY2I(dargs[i])));
+ dtypes[i] = 'i';
+ PUSH_P(ftype);
+ break;
+ case 'L':
+ ANY2L(args[i]) = DLNUM2LONG(argv[i]);
+ PUSH_L(ftype);
+ break;
+ case 'l':
+ ANY2L(dargs[i]) = DLNUM2LONG(argv[i]);
+ ANY2P(args[i]) = DLVOIDP(&(ANY2L(dargs[i])));
+ dtypes[i] = 'l';
+ PUSH_P(ftype);
+ break;
+ case 'F':
+ Check_Type(argv[i], T_FLOAT);
+ ANY2F(args[i]) = DLFLOAT(RFLOAT(argv[i])->value);
+ PUSH_F(ftype);
+ break;
+ case 'f':
+ Check_Type(argv[i], T_FLOAT);
+ ANY2F(dargs[i]) = DLFLOAT(RFLOAT(argv[i])->value);
+ ANY2P(args[i]) = DLVOIDP(&(ANY2F(dargs[i])));
+ dtypes[i] = 'f';
+ PUSH_P(ftype);
+ break;
+ case 'D':
+ Check_Type(argv[i], T_FLOAT);
+ ANY2D(args[i]) = RFLOAT(argv[i])->value;
+ PUSH_D(ftype);
+ break;
+ case 'd':
+ Check_Type(argv[i], T_FLOAT);
+ ANY2D(dargs[i]) = RFLOAT(argv[i])->value;
+ ANY2P(args[i]) = DLVOIDP(&(ANY2D(dargs[i])));
+ dtypes[i] = 'd';
+ PUSH_P(ftype);
+ break;
+ case 'S':
+ if( argv[i] == Qnil ){
+ ANY2S(args[i]) = DLSTR(0);
+ }
+ else{
+ ANY2S(args[i]) = DLSTR(STR2CSTR(argv[i]));
+ };
+ PUSH_P(ftype);
+ break;
+ case 's':
+ if( argv[i] == Qnil ){
+ raise(rb_eDLError, "#%d must be a string",i);
+ };
+ ANY2S(args[i]) = DLSTR(dlmalloc(RSTRING(argv[i])->len + 1));
+ memcpy((char*)(ANY2S(args[i])), STR2CSTR(argv[i]), RSTRING(argv[i])->len + 1);
+ dtypes[i] = 's';
+ PUSH_P(ftype);
+ break;
+ default:
+ FREE_ARGS;
+ rb_raise(rb_eDLTypeError,
+ "unknown type '%c' of the return value.",
+ sym->type[i+1]);
+ };
+ };
+
+ switch( sym->type[0] ){
+ case '0':
+ PUSH_0(ftype);
+ break;
+ case 'P':
+ case 'p':
+ case 'S':
+ case 's':
+ case 'A':
+ case 'a':
+ PUSH_P(ftype);
+ break;
+ case 'C':
+ case 'c':
+ PUSH_C(ftype);
+ break;
+ case 'H':
+ case 'h':
+ PUSH_H(ftype);
+ break;
+ case 'I':
+ case 'i':
+ PUSH_I(ftype);
+ break;
+ case 'L':
+ case 'l':
+ PUSH_L(ftype);
+ break;
+ case 'F':
+ case 'f':
+ PUSH_F(ftype);
+ break;
+ case 'D':
+ case 'd':
+ PUSH_D(ftype);
+ break;
+ default:
+ FREE_ARGS;
+ rb_raise(rb_eDLTypeError,
+ "unknown type `%c' of the return value.",
+ sym->type[0]);
+ };
+
+ func = sym->func;
+
+#ifdef USE_INLINE_ASM
+ ASM_START(sym->type);
+ for( i = sym->len - 2; i >= 0; i-- ){
+ switch( sym->type[i+1] ){
+ case 'p':
+ case 'P':
+ ASM_PUSH_P(ANY2P(args[i]));
+ break;
+ case 'a':
+ case 'A':
+ ASM_PUSH_P(ANY2P(args[i]));
+ break;
+ case 'C':
+ ASM_PUSH_C(ANY2C(args[i]));
+ break;
+ case 'c':
+ ASM_PUSH_P(ANY2P(args[i]));
+ break;
+ case 'H':
+ ASM_PUSH_H(ANY2H(args[i]));
+ break;
+ case 'h':
+ ASM_PUSH_P(ANY2P(args[i]));
+ break;
+ case 'I':
+ ASM_PUSH_I(ANY2I(args[i]));
+ break;
+ case 'i':
+ ASM_PUSH_P(ANY2P(args[i]));
+ break;
+ case 'L':
+ ASM_PUSH_L(ANY2L(args[i]));
+ break;
+ case 'l':
+ ASM_PUSH_P(ANY2P(args[i]));
+ break;
+ case 'F':
+ ASM_PUSH_F(ANY2F(args[i]));
+ break;
+ case 'f':
+ ASM_PUSH_P(ANY2P(args[i]));
+ break;
+ case 'D':
+ ASM_PUSH_D(ANY2D(args[i]));
+ break;
+ case 'd':
+ ASM_PUSH_P(ANY2P(args[i]));
+ break;
+ case 'S':
+ case 's':
+ ASM_PUSH_P(ANY2S(args[i]));
+ break;
+ };
+ }
+ ASM_END(sym->type);
+
+ {
+ switch( sym->type[0] ){
+ case '0':
+ {
+ void (*f)() = func;
+ f();
+ };
+ break;
+ case 'P':
+ case 'p':
+ {
+ void * (*f)() = func;
+ ret.p = f();
+ };
+ break;
+ case 'C':
+ case 'c':
+ {
+ char (*f)() = func;
+ ret.c = f();
+ };
+ break;
+ case 'H':
+ case 'h':
+ {
+ short (*f)() = func;
+ ret.h = f();
+ };
+ break;
+ case 'I':
+ case 'i':
+ {
+ int (*f)() = func;
+ ret.i = f();
+ };
+ break;
+ case 'L':
+ case 'l':
+ {
+ long (*f)() = func;
+ ret.l = f();
+ };
+ break;
+ case 'F':
+ case 'f':
+ {
+ float (*f)() = func;
+ ret.f = f();
+ };
+ break;
+ case 'D':
+ case 'd':
+ {
+ double (*f)() = func;
+ ret.d = f();
+ };
+ break;
+ case 'S':
+ case 's':
+ {
+ char * (*f)() = func;
+ ret.s = f();
+ };
+ break;
+ default:
+ FREE_ARGS;
+ rb_raise(rb_eDLTypeError, "unknown type `%c'", sym->type[0]);
+ };
+ };
+#else
+ switch(ftype){
+#include "call.func"
+ default:
+ FREE_ARGS;
+ rb_raise(rb_eDLTypeError, "unsupported function type `%s'", sym->type);
+ };
+#endif
+
+ switch( sym->type[0] ){
+ case '0':
+ val = Qnil;
+ break;
+ case 'P':
+ val = rb_dlptr_new((void*)(ANY2P(ret)), 0, 0);
+ break;
+ case 'p':
+ val = rb_dlptr_new((void*)(ANY2P(ret)), 0, dlfree);
+ break;
+ case 'C':
+ case 'c':
+ val = CHR2FIX((char)(ANY2C(ret)));
+ break;
+ case 'H':
+ case 'h':
+ val = INT2NUM((short)(ANY2H(ret)));
+ break;
+ case 'I':
+ case 'i':
+ val = INT2NUM((int)(ANY2I(ret)));
+ break;
+ case 'L':
+ case 'l':
+ val = DLLONG2NUM((long)(ANY2L(ret)));
+ break;
+ case 'F':
+ case 'f':
+ val = rb_float_new((double)(ANY2F(ret)));
+ break;
+ case 'D':
+ case 'd':
+ val = rb_float_new((double)(ANY2D(ret)));
+ break;
+ case 'S':
+ if( ANY2S(ret) ){
+ val = rb_tainted_str_new2((char*)(ANY2S(ret)));
+ }
+ else{
+ val = Qnil;
+ };
+ break;
+ case 's':
+ if( ANY2S(ret) ){
+ val = rb_tainted_str_new2((char*)(ANY2S(ret)));
+ DEBUG_CODE({
+ printf("dlfree(%s)\n",(char*)(ANY2S(ret)));
+ });
+ dlfree((void*)(ANY2S(ret)));
+ }
+ else{
+ val = Qnil;
+ };
+ break;
+ default:
+ FREE_ARGS;
+ rb_raise(rb_eDLTypeError, "unknown type `%c'", sym->type[0]);
+ };
+
+ dvals = rb_ary_new();
+ for( i = 0; i <= sym->len - 2; i++ ){
+ if( dtypes[i] ){
+ switch( dtypes[i] ){
+ case 'c':
+ rb_ary_push(dvals, CHR2FIX(*((char*)(ANY2P(args[i])))));
+ break;
+ case 'h':
+ rb_ary_push(dvals, INT2NUM(*((short*)(ANY2P(args[i])))));
+ break;
+ case 'i':
+ rb_ary_push(dvals, INT2NUM(*((int*)(ANY2P(args[i])))));
+ break;
+ case 'l':
+ rb_ary_push(dvals, DLLONG2NUM(*((long*)(ANY2P(args[i])))));
+ break;
+ case 'f':
+ rb_ary_push(dvals, rb_float_new(*((float*)(ANY2P(args[i])))));
+ break;
+ case 'd':
+ rb_ary_push(dvals, rb_float_new(*((double*)(ANY2P(args[i])))));
+ break;
+ case 'p':
+ rb_ary_push(dvals, rb_dlptr_new((void*)(ANY2P(args[i])), 0, 0));
+ break;
+ case 'a':
+ rb_ary_push(dvals, rb_dlptr_new((void*)ANY2P(args[i]), 0, 0));
+ break;
+ case 's':
+ rb_ary_push(dvals, rb_tainted_str_new2((char*)ANY2S(args[i])));
+ DEBUG_CODE({
+ printf("dlfree(%s)\n",(char*)ANY2S(args[i]));
+ });
+ dlfree((void*)ANY2S(args[i]));
+ break;
+ default:
+ {
+ char c = dtypes[i];
+ FREE_ARGS;
+ rb_raise(rb_eRuntimeError, "unknown argument type '%c'", i, c);
+ };
+ };
+ }
+ else{
+ switch( sym->type[i+1] ){
+ case 'A':
+ dlfree((void*)ANY2P(args[i]));
+ break;
+ };
+ rb_ary_push(dvals, argv[i]);
+ };
+ };
+
+#undef FREE_ARGS
+ return rb_assoc_new(val,dvals);
+};
+
+VALUE
+rb_dlsym_to_i(VALUE self)
+{
+ struct sym_data *sym;
+
+ Data_Get_Struct(self, struct sym_data, sym);
+ return DLLONG2NUM(sym);
+};
+
+VALUE
+rb_dlsym_to_ptr(VALUE self)
+{
+ struct sym_data *sym;
+
+ Data_Get_Struct(self, struct sym_data, sym);
+ return rb_dlptr_new(sym->func, sizeof(freefunc_t), 0);
+};
+
+void
+Init_dlsym()
+{
+ rb_cDLSymbol = rb_define_class_under(rb_mDL, "Symbol", rb_cData);
+ rb_define_singleton_method(rb_cDLSymbol, "new", rb_dlsym_s_new, -1);
+ rb_define_singleton_method(rb_cDLSymbol, "char2type", rb_s_dlsym_char2type, 1);
+ rb_define_method(rb_cDLSymbol, "initialize", rb_dlsym_initialize, -1);
+ rb_define_method(rb_cDLSymbol, "call", rb_dlsym_call, -1);
+ rb_define_method(rb_cDLSymbol, "[]", rb_dlsym_call, -1);
+ rb_define_method(rb_cDLSymbol, "name", rb_dlsym_name, 0);
+ rb_define_method(rb_cDLSymbol, "proto", rb_dlsym_proto, 0);
+ rb_define_method(rb_cDLSymbol, "cproto", rb_dlsym_cproto, 0);
+ rb_define_method(rb_cDLSymbol, "inspect", rb_dlsym_inspect, 0);
+ rb_define_method(rb_cDLSymbol, "to_s", rb_dlsym_cproto, 0);
+ rb_define_method(rb_cDLSymbol, "to_ptr", rb_dlsym_to_ptr, 0);
+ rb_define_method(rb_cDLSymbol, "to_i", rb_dlsym_to_i, 0);
+};
diff --git a/ext/dl/test/libtest.def b/ext/dl/test/libtest.def
new file mode 100644
index 0000000000..d2585286e9
--- /dev/null
+++ b/ext/dl/test/libtest.def
@@ -0,0 +1,29 @@
+EXPORTS
+test_alloc_test_struct
+test_append
+test_arylen
+test_c2i
+test_call_func1
+test_callback1
+test_close
+test_d2f
+test_f2d
+test_fill_test_struct
+test_fill_test_union
+test_gets
+test_i2c
+test_init
+test_isucc
+test_lcc
+test_lsucc
+test_open
+test_strcat
+test_strlen
+test_succ
+test_data_init
+test_data_add
+test_data_print
+test_data_aref
+test_set_long_value
+test_get_long_value
+internal_long_value
diff --git a/ext/dl/test/test.c b/ext/dl/test/test.c
new file mode 100644
index 0000000000..c5ead79813
--- /dev/null
+++ b/ext/dl/test/test.c
@@ -0,0 +1,251 @@
+#include <stdio.h>
+#include <string.h>
+
+static char internal_string[] = "internal_string";
+long internal_long_value = 100;
+
+struct test_struct {
+ char c;
+ long l;
+};
+
+union test_union {
+ char c;
+ int i;
+ long l;
+ void *p;
+};
+
+struct test_data {
+ char name[1024];
+ struct test_data *next;
+};
+
+long
+test_get_long_value()
+{
+ return internal_long_value;
+};
+
+void
+test_set_long_value(long l)
+{
+ internal_long_value = l;
+};
+
+void
+test_fill_test_struct(struct test_struct *ptr, char c, long l)
+{
+ ptr->c = c;
+ ptr->l = l;
+};
+
+void
+test_fill_test_union(union test_union *ptr, long l)
+{
+ ptr->l = l;
+};
+
+struct test_struct *
+test_alloc_test_struct(char c, long l)
+{
+ struct test_struct *data;
+
+ data = (struct test_struct *)malloc(sizeof(struct test_struct));
+ data->c = c;
+ data->l = l;
+
+ return data;
+};
+
+int
+test_c2i(char c)
+{
+ return (int)c;
+};
+
+char
+test_i2c(int i)
+{
+ return (char)i;
+};
+
+long
+test_lcc(char c1, char c2)
+{
+ return (long)(c1 + c2);
+};
+
+double
+test_f2d(float f)
+{
+ double d;
+ d = f;
+ return d;
+};
+
+float
+test_d2f(double d)
+{
+ float f;
+ f = d;
+ return f;
+};
+
+int
+test_strlen(const char *str)
+{
+ return strlen(str);
+};
+
+int
+test_isucc(int i)
+{
+ return (i+1);
+};
+
+long
+test_lsucc(long l)
+{
+ return (l+1);
+};
+
+void
+test_succ(long *l)
+{
+ (*l)++;
+};
+
+char *
+test_strcat(char *str1, const char *str2)
+{
+ return strcat(str1, str2);
+};
+
+int
+test_arylen(char *ary[])
+{
+ int i;
+ for( i=0; ary[i]; i++ ){};
+ return i;
+};
+
+void
+test_append(char *ary[], int len, char *astr)
+{
+ int i;
+ int size1,size2;
+ char *str;
+
+ size2 = strlen(astr);
+
+ for( i=0; i <= len - 1; i++ ){
+ size1 = strlen(ary[i]);
+ str = (char*)malloc(size1 + size2 + 1);
+ strcpy(str, ary[i]);
+ strcat(str, astr);
+ ary[i] = str;
+ };
+};
+
+void
+test_init(int *argc, char **argv[])
+{
+ int i;
+ printf("test_init(0x%x,0x%x)\n",argc,argv);
+ printf("\t*(0x%x) => %d\n",argc,*argc);
+ for( i=0; i < (*argc); i++ ){
+ printf("\t(*(0x%x)[%d]) => %s\n", argv, i, (*argv)[i]);
+ };
+};
+
+FILE *
+test_open(const char *filename, const char *mode)
+{
+ FILE *file;
+ file = fopen(filename,mode);
+ printf("test_open(%s,%s):0x%x\n",filename,mode,file);
+ return file;
+};
+
+void
+test_close(FILE *file)
+{
+ printf("test_close(0x%x)\n",file);
+ fclose(file);
+};
+
+char *
+test_gets(char *s, int size, FILE *f)
+{
+ return fgets(s,size,f);
+};
+
+typedef int callback1_t(int, char *);
+
+int
+test_callback1(int err, const char *msg)
+{
+ printf("internal callback function (err = %d, msg = '%s')\n",
+ err, msg ? msg : "(null)");
+ return 1;
+};
+
+int
+test_call_func1(callback1_t *func)
+{
+ if( func ){
+ return (*func)(0, "this is test_call_func1.");
+ }
+ else{
+ printf("test_call_func1(func = 0)\n");
+ return -1;
+ }
+};
+
+struct test_data *
+test_data_init()
+{
+ struct test_data *data;
+
+ data = (struct test_data *)malloc(sizeof(struct test_data));
+ data->next = NULL;
+ memset(data->name, 0, 1024);
+
+ return data;
+};
+
+void
+test_data_add(struct test_data *list, const char *name)
+{
+ struct test_data *data;
+
+ data = (struct test_data *)malloc(sizeof(struct test_data));
+ strcpy(data->name, name);
+ data->next = list->next;
+ list->next = data;
+};
+
+void
+test_data_print(struct test_data *list)
+{
+ struct test_data *data;
+
+ for( data = list->next; data; data = data->next ){
+ printf("name = %s\n", data->name);
+ };
+};
+
+struct data *
+test_data_aref(struct test_data *list, int i)
+{
+ struct test_data *data;
+ int j;
+
+ for( data = list->next, j=0; data; data = data->next, j++ ){
+ if( i == j ){
+ return data;
+ };
+ };
+ return NULL;
+};
diff --git a/ext/dl/test/test.rb b/ext/dl/test/test.rb
new file mode 100644
index 0000000000..de9a9cc533
--- /dev/null
+++ b/ext/dl/test/test.rb
@@ -0,0 +1,272 @@
+# -*- ruby -*-
+
+require 'dl'
+require 'dl/import'
+
+def assert(label, ty, *conds)
+ cond = !conds.include?(false)
+ if( cond )
+ printf("succeed in `#{label}'\n")
+ else
+ case ty
+ when :may
+ printf("fail in `#{label}' ... expected\n")
+ when :must
+ printf("fail in `#{label}' ... unexpected\n")
+ when :raise
+ raise(RuntimeError, "fail in `#{label}'")
+ end
+ end
+end
+
+def debug(*xs)
+ if( $DEBUG )
+ xs.each{|x|
+ p x
+ }
+ end
+end
+
+print("VERSION = #{DL::VERSION}\n")
+print("MAJOR_VERSION = #{DL::MAJOR_VERSION}\n")
+print("MINOR_VERSION = #{DL::MINOR_VERSION}\n")
+print("\n")
+print("MAX_ARG = #{DL::MAX_ARG}\n")
+print("MAX_CBARG = #{DL::MAX_CBARG}\n")
+print("MAX_CBENT = #{DL::MAX_CBENT}\n")
+print("\n")
+print("DL::FREE = #{DL::FREE.inspect}\n")
+print("\n")
+
+$LIB = nil
+if( !$LIB && File.exist?("libtest.so") )
+ $LIB = "./libtest.so"
+end
+if( !$LIB && File.exist?("test/libtest.so") )
+ $LIB = "./test/libtest.so"
+end
+
+module LIBTest
+ extend DL::Importable
+
+ dlload($LIB)
+ extern "int test_c2i(char)"
+ extern "char test_i2c(int)"
+ extern "long test_lcc(char, char)"
+ extern "double test_f2d(float)"
+ extern "float test_d2f(double)"
+ extern "int test_strlen(char*)"
+ extern "int test_isucc(int)"
+ extern "long test_lsucc(long)"
+ extern "void test_succ(long *)"
+ extern "int test_arylen(int [])"
+ extern "void test_append(char*[], int, char *)"
+end
+
+DL.dlopen($LIB){|h|
+ c2i = h["test_c2i","IC"]
+ debug c2i
+ r,rs = c2i[?a]
+ debug r,rs
+ assert("c2i", :may, r == ?a)
+ assert("extern c2i", :must, r == LIBTest.test_c2i(?a))
+
+ i2c = h["test_i2c","CI"]
+ debug i2c
+ r,rs = i2c[?a]
+ debug r,rs
+ assert("i2c", :may, r == ?a)
+ assert("exern i2c", :must, r == LIBTest.test_i2c(?a))
+
+ lcc = h["test_lcc","LCC"]
+ debug lcc
+ r,rs = lcc[1,2]
+ assert("lcc", :may, r == 3)
+ assert("extern lcc", :must, r == LIBTest.test_lcc(1,2))
+
+ f2d = h["test_f2d","DF"]
+ debug f2d
+ r,rs = f2d[20.001]
+ debug r,rs
+ assert("f2d", :may, r.to_i == 20)
+ assert("extern f2d", :must, r = LIBTest.test_f2d(20.001))
+
+ d2f = h["test_d2f","FD"]
+ debug d2f
+ r,rs = d2f[20.001]
+ debug r,rs
+ assert("d2f", :may, r.to_i == 20)
+ assert("extern d2f", :must, r == LIBTest.test_d2f(20.001))
+
+ strlen = h["test_strlen","IS"]
+ debug strlen
+ r,rs = strlen["0123456789"]
+ debug r,rs
+ assert("strlen", :must, r == 10)
+ assert("extern strlen", :must, r == LIBTest.test_strlen("0123456789"))
+
+ isucc = h["test_isucc","II"]
+ debug isucc
+ r,rs = isucc[2]
+ debug r,rs
+ assert("isucc", :must, r == 3)
+ assert("extern isucc", :must, r == LIBTest.test_isucc(2))
+
+ lsucc = h["test_lsucc","LL"]
+ debug lsucc
+ r,rs = lsucc[10000000]
+ debug r,rs
+ assert("lsucc", :must, r == 10000001)
+ assert("extern lsucc", :must, r == LIBTest.test_lsucc(10000000))
+
+ succ = h["test_succ","0l"]
+ debug succ
+ r,rs = succ[0]
+ debug r,rs
+ assert("succ", :must, rs[0] == 1)
+ l = DL.malloc(DL.sizeof("L"))
+ l.struct!("L",:lval)
+ LIBTest.test_succ(l)
+ assert("extern succ", :must, rs[0] == l[:lval])
+
+ arylen = h["test_arylen","IA"]
+ debug arylen
+ r,rs = arylen[["a","b","c","d",nil]]
+ debug r,rs
+ assert("arylen", :must, r == 4)
+
+ arylen = h["test_arylen","IP"]
+ debug arylen
+ r,rs = arylen[["a","b","c","d",nil]]
+ debug r,rs
+ assert("arylen", :must, r == 4)
+ assert("extern arylen", :must, r == LIBTest.test_arylen(["a","b","c","d",nil]))
+
+ append = h["test_append","0aIS"]
+ debug append
+ r,rs = append[["a","b","c"],3,"x"]
+ debug r,rs
+ assert("append", :must, rs[0].to_a('S',3) == ["ax","bx","cx"])
+
+ LIBTest.test_append(["a","b","c"],3,"x")
+ assert("extern append", :must, rs[0].to_a('S',3) == LIBTest._args_[0].to_a('S',3))
+
+ strcat = h["test_strcat","SsS"]
+ debug strcat
+ r,rs = strcat["abc\0","x"]
+ debug r,rs
+ assert("strcat", :must, rs[0].to_s == "abcx")
+
+ init = h["test_init","0iP"]
+ debug init
+ argc = 3
+ argv = ["arg0","arg1","arg2"].to_ptr
+ r,rs = init[argc, argv.ref]
+ debug r,rs
+}
+
+
+h = DL.dlopen($LIB)
+
+sym_open = h["test_open", "PSS"]
+sym_gets = h["test_gets", "SsIP"]
+sym_close = h["test_close", "0P"]
+debug sym_open,sym_gets,sym_close
+
+line = "Hello world!\n"
+File.open("tmp.txt", "w"){|f|
+ f.print(line)
+}
+
+fp,rs = sym_open["tmp.txt", "r"]
+if( fp )
+ fp.free = sym_close
+ r,rs = sym_gets[" " * 256, 256, fp]
+ debug r,rs
+ assert("open,gets", :must, rs[0] == line)
+else
+ assert("open,gets", :must, line == nil)
+end
+File.unlink("tmp.txt")
+
+
+callback1 = h["test_callback1"]
+debug callback1
+r,rs = h["test_call_func1", "IP"][callback1]
+debug r,rs
+assert("callback1", :must, r == 1)
+
+
+callback2 = DL.set_callback("LLP", 0){|arg1,arg2|
+ ptr = arg2 # DL::PtrData.new(arg2)
+ msg = ptr.to_s
+ print("user defined callback function",
+ "(err = #{arg1}, msg = '#{msg}')\n")
+ 2
+}
+debug callback2
+r,rs = h["test_call_func1", "IP"][callback2]
+debug r,rs
+assert("callback2", :must, r == 2)
+
+
+ptr = DL.malloc(DL.sizeof('CL'))
+ptr.struct!("CL", :c, :l)
+ptr["c"] = 0
+ptr["l"] = 0
+r,rs = h["test_fill_test_struct","0PIL"][ptr,100,1000]
+debug r,rs
+assert("fill_test_struct", :must, ptr["c"] == 100, ptr["l"] == 1000)
+assert("fill_test_struct", :must, ptr[:c] == 100, ptr[:l] == 1000) unless (Fixnum === :-)
+
+
+r,rs = h["test_alloc_test_struct", "PIL"][100,200]
+r.free = DL::FREE
+r.struct!("CL", :c, :l)
+assert("alloc_test_struct", :must, r["c"] == 100, r["l"] == 200)
+assert("alloc_test_struct", :must, r[:c] == 100, r[:l] == 200) unless (Fixnum === :-)
+
+ptr = h["test_strlen"]
+sym1 = DL::Symbol.new(ptr,"foo","0")
+sym2 = h["test_strlen","LS"]
+assert("Symbol.new", :must, ptr == sym1.to_ptr, sym1.to_ptr == sym2.to_ptr)
+
+set_val = h["test_set_long_value","0"]
+get_val = h["test_get_long_value","L"]
+lval = get_val[][0]
+ptr = h["internal_long_value"]
+ptr.struct!("l", :l)
+assert("get value", :must, ptr["l"] == lval)
+assert("get value", :must, ptr[:l] == lval) unless (Fixnum === :-)
+ptr["l"] = 200
+lval = get_val[][0]
+assert("set value", :must, ptr["l"] == lval)
+assert("set value", :must, ptr[:l] == lval) unless (Fixnum === :-)
+
+
+data_init = h["test_data_init", "P"]
+data_add = h["test_data_add", "0PS"]
+data_print = h["test_data_print", "0P"]
+data_aref = h["test_data_aref", "PPI"]
+r,rs = data_init[]
+ptr = r
+data_add[ptr, "name1"]
+data_add[ptr, "name2"]
+data_add[ptr, "name3"]
+data_print[ptr]
+
+r,rs = data_aref[ptr, 1]
+ptr = r
+ptr.struct!("C1024P", :name, :next)
+assert("data_aref", :must,
+ ptr["name"].collect{|c| c.chr}.join.split("\0")[0] == "name2")
+assert("data_aref", :must,
+ ptr["name"].collect{|c| c.chr}.join.split("\0")[0] == "name2") unless (Fixnum === :-)
+ptr = ptr["next"]
+ptr.struct!("C1024P", :name, :next)
+assert("data_aref", :must,
+ ptr["name"].collect{|c| c.chr}.join.split("\0")[0] == "name1")
+assert("data_aref", :must,
+ ptr["name"].collect{|c| c.chr}.join.split("\0")[0] == "name1") unless (Fixnum === :-)
+
+GC.start
diff --git a/ext/dl/type.rb b/ext/dl/type.rb
new file mode 100644
index 0000000000..804420c395
--- /dev/null
+++ b/ext/dl/type.rb
@@ -0,0 +1,115 @@
+# example:
+# DLTYPE[INT][:rb2c]["arg0"] => "NUM2INT(arg0)"
+# DLTYPE[DOUBLE][:c2rb]["r"] => "rb_float_new(r)"
+
+DLTYPE = {
+ VOID = 0x00 => {
+ :name => 'VOID',
+ :rb2c => nil,
+ :c2rb => nil,
+ :ctype => "void",
+ :stmem => "v",
+ :sym => true,
+ :cb => true,
+ },
+ CHAR = 0x01 => {
+ :name => 'CHAR',
+ :rb2c => proc{|x| "NUM2CHR(#{x})"},
+ :c2rb => proc{|x| "CHR2FIX(#{x})"},
+ :ctype => "char",
+ :stmem => "c",
+ :sym => false,
+ :cb => false,
+ },
+ SHORT = 0x02 => {
+ :name => 'SHORT',
+ :rb2c => proc{|x| "FIX2INT(#{x})"},
+ :c2rb => proc{|x| "INT2FIX(#{x})"},
+ :ctype => "short",
+ :stmem => "h",
+ :sym => false,
+ :cb => false,
+ },
+ INT = 0x03 => {
+ :name => 'INT',
+ :rb2c => proc{|x| "NUM2INT(#{x})"},
+ :c2rb => proc{|x| "INT2NUM(#{x})"},
+ :ctype => "int",
+ :stmem => "i",
+ :sym => true,
+ :cb => false,
+ },
+ LONG = 0x04 => {
+ :name => 'LONG',
+ :rb2c => proc{|x| "NUM2INT(#{x})"},
+ :c2rb => proc{|x| "INT2NUM(#{x})"},
+ :ctype => "long",
+ :stmem => "l",
+ :sym => true,
+ :cb => true,
+ },
+ FLOAT = 0x05 => {
+ :name => 'FLOAT',
+ :rb2c => proc{|x| "(float)(RFLOAT(#{x})->value)"},
+ :c2rb => proc{|x| "rb_float_new((double)#{x})"},
+ :ctype => "float",
+ :stmem => "f",
+ :sym => false,
+ :cb => false,
+ },
+ DOUBLE = 0x06 => {
+ :name => 'DOUBLE',
+ :rb2c => proc{|x| "RFLOAT(#{x})->value"},
+ :c2rb => proc{|x| "rb_float_new(#{x})"},
+ :ctype => "double",
+ :stmem => "d",
+ :sym => true,
+ :cb => true,
+ },
+ VOIDP = 0x07 => {
+ :name => 'VOIDP',
+ :rb2c => proc{|x| "rb_dlptr2cptr(#{x})"},
+ :c2rb => proc{|x| "rb_dlptr_new(#{x},sizeof(void*),0)"},
+ :ctype => "void *",
+ :stmem => "p",
+ :sym => true,
+ :cb => true,
+ },
+}
+
+def tpush(t, x)
+ (t << 3)|x
+end
+
+def tget(t, i)
+ (t & (0x07 << (i * 3))) >> (i * 3)
+end
+
+def types2num(types)
+ res = 0x00
+ r = types.reverse
+ r.each{|t|
+ res = tpush(res,t)
+ }
+ res
+end
+
+def num2types(num)
+ ts = []
+ i = 0
+ t = tget(num,i)
+ while( (t != VOID && i > 0) || (i == 0) )
+ ts.push(DLTYPE[t][:ctype])
+ i += 1
+ t = tget(num,i)
+ end
+ ts
+end
+
+def types2ctypes(types)
+ res = []
+ types.each{|t|
+ res.push(DLTYPE[t][:ctype])
+ }
+ res
+end