summaryrefslogtreecommitdiff
path: root/os2/OS2/OS2-REXX/DLL/DLL.pm
diff options
context:
space:
mode:
Diffstat (limited to 'os2/OS2/OS2-REXX/DLL/DLL.pm')
-rw-r--r--os2/OS2/OS2-REXX/DLL/DLL.pm308
1 files changed, 308 insertions, 0 deletions
diff --git a/os2/OS2/OS2-REXX/DLL/DLL.pm b/os2/OS2/OS2-REXX/DLL/DLL.pm
new file mode 100644
index 0000000000..2a2486e863
--- /dev/null
+++ b/os2/OS2/OS2-REXX/DLL/DLL.pm
@@ -0,0 +1,308 @@
+package OS2::DLL;
+
+our $VERSION = '1.03';
+
+use Carp;
+use XSLoader;
+
+@libs = split(/;/, $ENV{'PERL5REXX'} || $ENV{'PERLREXX'} || $ENV{'LIBPATH'} || $ENV{'PATH'});
+%dlls = ();
+
+# Preloaded methods go here. Autoload methods go after __END__, and are
+# processed by the autosplit program.
+
+# Cannot be autoload, the autoloader is used for the REXX functions.
+
+my $load_with_dirs = sub {
+ my ($class, $file, @where) = (@_);
+ return $dlls{$file} if $dlls{$file};
+ my $handle;
+ foreach (@where) {
+ $handle = DynaLoader::dl_load_file("$_/$file.dll");
+ last if $handle;
+ }
+ $handle = DynaLoader::dl_load_file($file) unless $handle;
+ return undef unless $handle;
+ my @packs = $INC{'OS2/REXX.pm'} ? qw(OS2::DLL::dll OS2::REXX) : 'OS2::DLL::dll';
+ my $p = "OS2::DLL::dll::$file";
+ @{"$p\::ISA"} = @packs;
+ *{"$p\::AUTOLOAD"} = \&OS2::DLL::dll::AUTOLOAD;
+ return $dlls{$file} =
+ bless {Handle => $handle, File => $file, Queue => 'SESSION' }, $p;
+};
+
+my $new_dll = sub {
+ my ($dirs, $class, $file) = (shift, shift, shift);
+ my $handle;
+ push @_, @libs if $dirs;
+ $handle = $load_with_dirs->($class, $file, @_)
+ and return $handle;
+ my $path = @_ ? " from '@_'" : '';
+ my $err = DynaLoader::dl_error();
+ $err =~ s/\s+at\s+\S+\s+line\s+\S+\s*\z//;
+ croak "Can't load '$file'$path: $err";
+};
+
+sub new {
+ confess 'Usage: OS2::DLL->new( <file> [<dirs>] )' unless @_ >= 2;
+ $new_dll->(1, @_);
+}
+
+sub module {
+ confess 'Usage: OS2::DLL->module( <file> [<dirs>] )' unless @_ >= 2;
+ $new_dll->(0, @_);
+}
+
+sub load {
+ confess 'Usage: load OS2::DLL <file> [<dirs>]' unless $#_ >= 1;
+ $load_with_dirs->(@_, @libs);
+}
+
+sub libPath_find {
+ my ($name, $flags, @path) = (shift, shift);
+ $flags = 0x7 unless defined $flags;
+ push @path, split /;/, OS2::extLibpath if $flags & 0x1; # BEGIN
+ push @path, split /;/, OS2::libPath if $flags & 0x2;
+ push @path, split /;/, OS2::extLibpath(1) if $flags & 0x4; # END
+ s,(?![/\\])$,/, for @path;
+ s,\\,/,g for @path;
+ $name .= ".dll" unless $name =~ /\.[^\\\/]*$/;
+ $_ .= $name for @path;
+ return grep -f $_, @path if $flags & 0x8;
+ -f $_ and return $_ for @path;
+ return;
+}
+
+package OS2::DLL::dll;
+use Carp;
+@ISA = 'OS2::DLL';
+
+sub AUTOLOAD {
+ $AUTOLOAD =~ /^OS2::DLL::dll::.+::(.+)$/
+ or confess("Undefined subroutine &$AUTOLOAD called");
+ return undef if $1 eq "DESTROY";
+ die "AUTOLOAD loop" if $1 eq "AUTOLOAD";
+ $_[0]->find($1) or confess($@);
+ goto &$AUTOLOAD;
+}
+
+sub wrapper_REXX {
+ confess 'Usage: $dllhandle->wrapper_REXX($func_name)' unless @_ == 2;
+ my $self = shift;
+ my $file = $self->{File};
+ my $handle = $self->{Handle};
+ my $prefix = exists($self->{Prefix}) ? $self->{Prefix} : "";
+ my $queue = $self->{Queue};
+ my $name = shift;
+ $prefix = '' if $name =~ /^#\d+/; # loading by ordinal
+ my $addr = (DynaLoader::dl_find_symbol($handle, uc $prefix.$name)
+ || DynaLoader::dl_find_symbol($handle, $prefix.$name));
+ return sub {
+ OS2::DLL::_call($name, $addr, $queue, @_);
+ } if $addr;
+ my $err = DynaLoader::dl_error();
+ $err =~ s/\s+at\s+\S+\s+line\s+\S+\s*\z//;
+ croak "Can't find symbol `$name' in DLL `$file': $err";
+}
+
+sub find
+{
+ my $self = shift;
+ my $file = $self->{File};
+ my $p = ref $self;
+ foreach (@_) {
+ my $f = eval {$self->wrapper_REXX($_)} or return 0;
+ ${"${p}::"}{$_} = sub { shift; $f->(@_) };
+ }
+ return 1;
+}
+
+sub handle { shift->{Handle} }
+sub fullname { OS2::DLLname(0x202, shift->handle) }
+#sub modname { OS2::DLLname(0x201, shift->handle) }
+
+sub has_f32 {
+ my $handle = shift->handle;
+ my $name = shift;
+ DynaLoader::dl_find_symbol($handle, $name);
+}
+
+XSLoader::load 'OS2::DLL';
+
+1;
+__END__
+
+=head1 NAME
+
+OS2::DLL - access to DLLs with REXX calling convention.
+
+=head2 NOTE
+
+When you use this module, the REXX variable pool is not available.
+
+See documentation of L<OS2::REXX> module if you need the variable pool.
+
+=head1 SYNOPSIS
+
+ use OS2::DLL;
+ $emx_dll = OS2::DLL->module('emx');
+ $emx_version = $emx_dll->emx_revision();
+ $func_emx_version = $emx_dll->wrapper_REXX('#128'); # emx_revision
+ $emx_version = $func_emx_version->();
+
+=head1 DESCRIPTION
+
+=head2 Create a DLL handle
+
+ $dll = OS2::DLL->module( NAME [, WHERE] );
+
+Loads an OS/2 module NAME, looking in directories WHERE (adding the
+extension F<.dll>), if the DLL is not found there, loads in the usual OS/2 way
+(via LIBPATH and other settings). Croaks with a verbose report on failure.
+
+The DLL is not unloaded when the return value is destroyed.
+
+=head2 Create a DLL handle (looking in some strange locations)
+
+ $dll = OS2::DLL->new( NAME [, WHERE] );
+
+Same as L<C<module>|Create a DLL handle>, but in addition to WHERE, looks
+in environment paths PERL5REXX, PERLREXX, PATH (provided for backward
+compatibility).
+
+=head2 Loads DLL by name
+
+ $dll = load OS2::DLL NAME [, WHERE];
+
+Same as L<C<new>|Create a DLL handle (looking in some strange locations)>,
+but returns DLL object reference, or undef on failure (in this case one can
+get the reason via C<DynaLoader::dl_error()>) (provided for backward
+compatibility).
+
+=head2 Check for functions (optional):
+
+ BOOL = $dll->find(NAME [, NAME [, ...]]);
+
+Returns true if all functions are available. As a side effect, creates
+a REXX wrapper with the specified name in the package constructed by the name
+of the DLL so that the next call to C<$dll->NAME()> will pick up the cached
+method.
+
+=head2 Create a Perl wrapper (optional):
+
+ $func = $dll->wrapper_REXX(NAME);
+
+Returns a reference to a Perl function wrapper for the entry point NAME
+in the DLL. Similar to the OS/2 API, the NAME may be C<"#123"> - in this case
+the ordinal is loaded. Croaks with a meaningful error message if NAME does
+not exists (although the message for the case when the name is an ordinal may
+be confusing).
+
+=head2 Call external function with REXX calling convention:
+
+ $ret_string = $dll->function_name(arguments);
+
+Returns the return string if the REXX return code is 0, else undef.
+Dies with error message if the function is not available. On the first call
+resolves the name in the DLL and caches the Perl wrapper; future calls go
+through the wrapper.
+
+Unless used inside REXX environment (see L<OS2::REXX>), the REXX runtime
+environment (variable pool, queue etc.) is not available to the called
+function.
+
+=head1 Inspecting the module
+
+=over
+
+=item $module->handle
+
+=item $module->fullname
+
+Return the (integer) handle and full path name of a loaded DLL.
+
+TODO: the module name (whatever is specified in the C<LIBRARY> statement
+of F<.def> file when linking) via OS2::Proc.
+
+=item $module->has_f32($name)
+
+Returns the address of a 32-bit entry point with name $name, or 0 if none
+found. (Keep in mind that some entry points may be 16-bit, and some may have
+capitalized names comparing to callable-from-C counterparts.) Name of the
+form C<#197> will find entry point with ordinal 197.
+
+=item libPath_find($name [, $flags])
+
+Looks for the DLL $name on C<BEGINLIBPATH>, C<LIBPATH>, C<ENDLIBPATH> if
+bits 0x1, 0x2, 0x4 of $flags are set correspondingly. If called with no
+arguments, looks on all 3 locations. Returns the full name of the found
+file. B<DLL is not loaded.>
+
+$name has F<.dll> appended unless it already has an extension.
+
+=back
+
+=head1 Low-level API
+
+=over
+
+=item Call a _System linkage function via a pointer
+
+If a function takes up to 20 ULONGs and returns ULONG:
+
+ $res = call20( $pointer, $arg0, $arg1, ...);
+
+=item Same for packed arguments:
+
+ $res = call20_p( $pointer, pack 'L20', $arg0, $arg1, ...);
+
+=item Same for C<regparm(3)> function:
+
+ $res = call20_rp3( $pointer, $arg0, $arg1, ...);
+
+=item Same for packed arguments and C<regparm(3)> function
+
+ $res = call20_rp3_p( $pointer, pack 'L20', $arg0, $arg1, ...);
+
+=item Same for a function which returns non-0 and sets system-error on error
+
+ call20_Dos( $msg, $pointer, $arg0, $arg1, ...); # die("$msg: $^E") if error
+
+[Good for C<Dos*> API - and rare C<Win*> calls.]
+
+=item Same for a function which returns 0 and sets WinLastError() on error
+
+ $res = call20_Win( $msg, $pointer, $arg0, $arg1, ...);
+ # would die("$msg: $^E") if error
+
+[Good for most of C<Win*> API.]
+
+=item Same for a function which returns 0 and sets WinLastError() on error but
+0 is also a valid return
+
+ $res = call20_Win_0OK( $msg, $pointer, $arg0, $arg1, ...);
+ # would die("$msg: $^E") if error
+
+[Good for some of C<Win*> API.]
+
+=item As previous, but without die()
+
+ $res = call20_Win_0OK_survive( $pointer, $arg0, $arg1, ...);
+ if ($res == 0 and $^E) { # Do error processing here
+ }
+
+[Good for some of C<Win*> API.]
+
+=back
+
+=head1 ENVIRONMENT
+
+If C<PERL_REXX_DEBUG> is set, emits debugging output. Looks for DLLs
+in C<PERL5REXX>, C<PERLREXX>, C<PATH>.
+
+=head1 AUTHOR
+
+Extracted by Ilya Zakharevich perl-module-OS2-DLL@ilyaz.org from L<OS2::REXX>
+written by Andreas Kaiser ak@ananke.s.bawue.de.
+
+=cut