xref: /openbsd-src/gnu/usr.bin/perl/os2/OS2/OS2-REXX/DLL/DLL.pm (revision 9f11ffb7133c203312a01e4b986886bc88c7d74b)
1b39c5158Smillertpackage OS2::DLL;
2b39c5158Smillert
3*9f11ffb7Safresh1our $VERSION = '1.07';
4b39c5158Smillert
5b39c5158Smillertuse Carp;
6b39c5158Smillertuse XSLoader;
7b39c5158Smillert
8b39c5158Smillert@libs = split(/;/, $ENV{'PERL5REXX'} || $ENV{'PERLREXX'} || $ENV{'LIBPATH'} || $ENV{'PATH'});
9b39c5158Smillert%dlls = ();
10b39c5158Smillert
11b39c5158Smillert# Preloaded methods go here.  Autoload methods go after __END__, and are
12b39c5158Smillert# processed by the autosplit program.
13b39c5158Smillert
14b39c5158Smillert# Cannot be autoload, the autoloader is used for the REXX functions.
15b39c5158Smillert
16b39c5158Smillertmy $load_with_dirs = sub {
17b39c5158Smillert	my ($class, $file, @where) = (@_);
18b39c5158Smillert	return $dlls{$file} if $dlls{$file};
19b39c5158Smillert	my $handle;
20b39c5158Smillert	foreach (@where) {
21b39c5158Smillert		$handle = DynaLoader::dl_load_file("$_/$file.dll");
22b39c5158Smillert		last if $handle;
23b39c5158Smillert	}
24b39c5158Smillert	$handle = DynaLoader::dl_load_file($file) unless $handle;
25b39c5158Smillert	return undef unless $handle;
26b39c5158Smillert	my @packs = $INC{'OS2/REXX.pm'} ? qw(OS2::DLL::dll OS2::REXX) : 'OS2::DLL::dll';
27b39c5158Smillert	my $p = "OS2::DLL::dll::$file";
28b39c5158Smillert	@{"$p\::ISA"} = @packs;
29b39c5158Smillert	*{"$p\::AUTOLOAD"} = \&OS2::DLL::dll::AUTOLOAD;
30b39c5158Smillert	return $dlls{$file} =
31b39c5158Smillert	  bless {Handle => $handle, File => $file, Queue => 'SESSION' }, $p;
32b39c5158Smillert};
33b39c5158Smillert
34b39c5158Smillertmy $new_dll = sub {
35b39c5158Smillert  my ($dirs, $class, $file) = (shift, shift, shift);
36b39c5158Smillert  my $handle;
37b39c5158Smillert  push @_, @libs if $dirs;
38b39c5158Smillert  $handle = $load_with_dirs->($class, $file, @_)
39b39c5158Smillert    and return $handle;
40b39c5158Smillert  my $path = @_ ? " from '@_'" : '';
41b39c5158Smillert  my $err = DynaLoader::dl_error();
42b39c5158Smillert  $err =~ s/\s+at\s+\S+\s+line\s+\S+\s*\z//;
43b39c5158Smillert  croak "Can't load '$file'$path: $err";
44b39c5158Smillert};
45b39c5158Smillert
46b39c5158Smillertsub new {
47b39c5158Smillert  confess 'Usage: OS2::DLL->new( <file> [<dirs>] )' unless @_ >= 2;
48b39c5158Smillert  $new_dll->(1, @_);
49b39c5158Smillert}
50b39c5158Smillert
51b39c5158Smillertsub module {
52b39c5158Smillert  confess 'Usage: OS2::DLL->module( <file> [<dirs>] )' unless @_ >= 2;
53b39c5158Smillert  $new_dll->(0, @_);
54b39c5158Smillert}
55b39c5158Smillert
56b39c5158Smillertsub load {
57b39c5158Smillert  confess 'Usage: load OS2::DLL <file> [<dirs>]' unless $#_ >= 1;
58b39c5158Smillert  $load_with_dirs->(@_, @libs);
59b39c5158Smillert}
60b39c5158Smillert
61b39c5158Smillertsub libPath_find {
62b39c5158Smillert  my ($name, $flags, @path) = (shift, shift);
63b39c5158Smillert  $flags = 0x7 unless defined $flags;
64b39c5158Smillert  push @path, split /;/, OS2::extLibpath	if $flags & 0x1;	# BEGIN
65b39c5158Smillert  push @path, split /;/, OS2::libPath		if $flags & 0x2;
66b39c5158Smillert  push @path, split /;/, OS2::extLibpath(1)	if $flags & 0x4;	# END
67b39c5158Smillert  s,(?![/\\])$,/,  for @path;
68b39c5158Smillert  s,\\,/,g	   for @path;
69b39c5158Smillert  $name .= ".dll" unless $name =~ /\.[^\\\/]*$/;
70b39c5158Smillert  $_ .= $name for @path;
71b39c5158Smillert  return grep -f $_, @path if $flags & 0x8;
72b39c5158Smillert  -f $_ and return $_ for @path;
73b39c5158Smillert  return;
74b39c5158Smillert}
75b39c5158Smillert
76b39c5158Smillertpackage OS2::DLL::dll;
77b39c5158Smillertuse Carp;
78b39c5158Smillert@ISA = 'OS2::DLL';
79b39c5158Smillert
80b39c5158Smillertsub AUTOLOAD {
81b39c5158Smillert    $AUTOLOAD =~ /^OS2::DLL::dll::.+::(.+)$/
82b39c5158Smillert      or confess("Undefined subroutine &$AUTOLOAD called");
83b39c5158Smillert    return undef if $1 eq "DESTROY";
84b39c5158Smillert    die "AUTOLOAD loop" if $1 eq "AUTOLOAD";
85b39c5158Smillert    $_[0]->find($1) or confess($@);
86b39c5158Smillert    goto &$AUTOLOAD;
87b39c5158Smillert}
88b39c5158Smillert
89b39c5158Smillertsub wrapper_REXX {
90b39c5158Smillert	confess 'Usage: $dllhandle->wrapper_REXX($func_name)' unless @_ == 2;
91b39c5158Smillert	my $self   = shift;
92b39c5158Smillert	my $file   = $self->{File};
93b39c5158Smillert	my $handle = $self->{Handle};
94b39c5158Smillert	my $prefix = exists($self->{Prefix}) ? $self->{Prefix} : "";
95b39c5158Smillert	my $queue  = $self->{Queue};
96b39c5158Smillert	my $name = shift;
97b39c5158Smillert	$prefix = '' if $name =~ /^#\d+/;	# loading by ordinal
98b39c5158Smillert	my $addr = (DynaLoader::dl_find_symbol($handle, uc $prefix.$name)
99b39c5158Smillert		    || DynaLoader::dl_find_symbol($handle, $prefix.$name));
100b39c5158Smillert	return sub {
101b39c5158Smillert	  OS2::DLL::_call($name, $addr, $queue, @_);
102b39c5158Smillert	} if $addr;
103b39c5158Smillert	my $err = DynaLoader::dl_error();
104b39c5158Smillert	$err =~ s/\s+at\s+\S+\s+line\s+\S+\s*\z//;
105b39c5158Smillert	croak "Can't find symbol `$name' in DLL `$file': $err";
106b39c5158Smillert}
107b39c5158Smillert
108b39c5158Smillertsub find
109b39c5158Smillert{
110b39c5158Smillert	my $self   = shift;
111b39c5158Smillert	my $file   = $self->{File};
112b39c5158Smillert	my $p	   = ref $self;
113b39c5158Smillert	foreach (@_) {
114b39c5158Smillert		my $f = eval {$self->wrapper_REXX($_)} or return 0;
115b39c5158Smillert		${"${p}::"}{$_} = sub { shift; $f->(@_) };
116b39c5158Smillert	}
117b39c5158Smillert	return 1;
118b39c5158Smillert}
119b39c5158Smillert
120b39c5158Smillertsub handle	{ shift->{Handle} }
121b39c5158Smillertsub fullname	{ OS2::DLLname(0x202, shift->handle) }
122b39c5158Smillert#sub modname	{ OS2::DLLname(0x201, shift->handle) }
123b39c5158Smillert
124b39c5158Smillertsub has_f32 {
125b39c5158Smillert   my $handle = shift->handle;
126b39c5158Smillert   my $name = shift;
127b39c5158Smillert   DynaLoader::dl_find_symbol($handle, $name);
128b39c5158Smillert}
129b39c5158Smillert
130b39c5158SmillertXSLoader::load 'OS2::DLL';
131b39c5158Smillert
132b39c5158Smillert1;
133b39c5158Smillert__END__
134b39c5158Smillert
135b39c5158Smillert=head1 NAME
136b39c5158Smillert
137b39c5158SmillertOS2::DLL - access to DLLs with REXX calling convention.
138b39c5158Smillert
139b39c5158Smillert=head2 NOTE
140b39c5158Smillert
141b39c5158SmillertWhen you use this module, the REXX variable pool is not available.
142b39c5158Smillert
143b39c5158SmillertSee documentation of L<OS2::REXX> module if you need the variable pool.
144b39c5158Smillert
145b39c5158Smillert=head1 SYNOPSIS
146b39c5158Smillert
147b39c5158Smillert use OS2::DLL;
148b39c5158Smillert $emx_dll = OS2::DLL->module('emx');
149b39c5158Smillert $emx_version = $emx_dll->emx_revision();
150b39c5158Smillert $func_emx_version = $emx_dll->wrapper_REXX('#128'); # emx_revision
151b39c5158Smillert $emx_version = $func_emx_version->();
152b39c5158Smillert
153b39c5158Smillert=head1 DESCRIPTION
154b39c5158Smillert
15591f110e0Safresh1=head2 Create a DLL handle
156b39c5158Smillert
157b39c5158Smillert	$dll = OS2::DLL->module( NAME [, WHERE] );
158b39c5158Smillert
159b39c5158SmillertLoads an OS/2 module NAME, looking in directories WHERE (adding the
160b39c5158Smillertextension F<.dll>), if the DLL is not found there, loads in the usual OS/2 way
161b39c5158Smillert(via LIBPATH and other settings).  Croaks with a verbose report on failure.
162b39c5158Smillert
163b39c5158SmillertThe DLL is not unloaded when the return value is destroyed.
164b39c5158Smillert
165b39c5158Smillert=head2 Create a DLL handle (looking in some strange locations)
166b39c5158Smillert
167b39c5158Smillert	$dll = OS2::DLL->new( NAME [, WHERE] );
168b39c5158Smillert
169b39c5158SmillertSame as C<module>|L<Create a DLL handle>, but in addition to WHERE, looks
170b39c5158Smillertin environment paths PERL5REXX, PERLREXX, PATH (provided for backward
171b39c5158Smillertcompatibility).
172b39c5158Smillert
173b39c5158Smillert=head2 Loads DLL by name
174b39c5158Smillert
175b39c5158Smillert	$dll = load OS2::DLL NAME [, WHERE];
176b39c5158Smillert
177b39c5158SmillertSame as C<new>|L<Create a DLL handle (looking in some strange locations)>,
178b39c5158Smillertbut returns DLL object reference, or undef on failure (in this case one can
179b39c5158Smillertget the reason via C<DynaLoader::dl_error()>) (provided for backward
180b39c5158Smillertcompatibility).
181b39c5158Smillert
182b39c5158Smillert=head2 Check for functions (optional):
183b39c5158Smillert
184b39c5158Smillert	BOOL = $dll->find(NAME [, NAME [, ...]]);
185b39c5158Smillert
186b39c5158SmillertReturns true if all functions are available.  As a side effect, creates
187b39c5158Smillerta REXX wrapper with the specified name in the package constructed by the name
188b39c5158Smillertof the DLL so that the next call to C<< $dll->NAME() >> will pick up the cached
189b39c5158Smillertmethod.
190b39c5158Smillert
191b39c5158Smillert=head2 Create a Perl wrapper (optional):
192b39c5158Smillert
193b39c5158Smillert	$func = $dll->wrapper_REXX(NAME);
194b39c5158Smillert
195b39c5158SmillertReturns a reference to a Perl function wrapper for the entry point NAME
196b39c5158Smillertin the DLL.  Similar to the OS/2 API, the NAME may be C<"#123"> - in this case
197b39c5158Smillertthe ordinal is loaded.   Croaks with a meaningful error message if NAME does
198b39c5158Smillertnot exists (although the message for the case when the name is an ordinal may
199b39c5158Smillertbe confusing).
200b39c5158Smillert
201b39c5158Smillert=head2 Call external function with REXX calling convention:
202b39c5158Smillert
203b39c5158Smillert	$ret_string = $dll->function_name(arguments);
204b39c5158Smillert
205b39c5158SmillertReturns the return string if the REXX return code is 0, else undef.
206b39c5158SmillertDies with error message if the function is not available.  On the first call
207b39c5158Smillertresolves the name in the DLL and caches the Perl wrapper; future calls go
208b39c5158Smillertthrough the wrapper.
209b39c5158Smillert
210b39c5158SmillertUnless used inside REXX environment (see L<OS2::REXX>), the REXX runtime
211b39c5158Smillertenvironment (variable pool, queue etc.) is not available to the called
212b39c5158Smillertfunction.
213b39c5158Smillert
214b39c5158Smillert=head1 Inspecting the module
215b39c5158Smillert
216b39c5158Smillert=over
217b39c5158Smillert
218b39c5158Smillert=item $module->handle
219b39c5158Smillert
220b39c5158Smillert=item $module->fullname
221b39c5158Smillert
222b39c5158SmillertReturn the (integer) handle and full path name of a loaded DLL.
223b39c5158Smillert
224b39c5158SmillertTODO: the module name (whatever is specified in the C<LIBRARY> statement
225b39c5158Smillertof F<.def> file when linking) via OS2::Proc.
226b39c5158Smillert
227b39c5158Smillert=item $module->has_f32($name)
228b39c5158Smillert
229b39c5158SmillertReturns the address of a 32-bit entry point with name $name, or 0 if none
230b39c5158Smillertfound.  (Keep in mind that some entry points may be 16-bit, and some may have
231b39c5158Smillertcapitalized names comparing to callable-from-C counterparts.)  Name of the
232b39c5158Smillertform C<#197> will find entry point with ordinal 197.
233b39c5158Smillert
234b39c5158Smillert=item libPath_find($name [, $flags])
235b39c5158Smillert
236b39c5158SmillertLooks for the DLL $name on C<BEGINLIBPATH>, C<LIBPATH>, C<ENDLIBPATH> if
237b39c5158Smillertbits 0x1, 0x2, 0x4 of $flags are set correspondingly.  If called with no
238b39c5158Smillertarguments, looks on all 3 locations.  Returns the full name of the found
239b39c5158Smillertfile.  B<DLL is not loaded.>
240b39c5158Smillert
241b39c5158Smillert$name has F<.dll> appended unless it already has an extension.
242b39c5158Smillert
243b39c5158Smillert=back
244b39c5158Smillert
245b39c5158Smillert=head1 Low-level API
246b39c5158Smillert
247b39c5158Smillert=over
248b39c5158Smillert
249b39c5158Smillert=item Call a _System linkage function via a pointer
250b39c5158Smillert
251b39c5158SmillertIf a function takes up to 20 ULONGs and returns ULONG:
252b39c5158Smillert
253b39c5158Smillert $res = call20( $pointer, $arg0, $arg1, ...);
254b39c5158Smillert
255b39c5158Smillert=item Same for packed arguments:
256b39c5158Smillert
257b39c5158Smillert $res = call20_p( $pointer, pack 'L20', $arg0, $arg1, ...);
258b39c5158Smillert
259b39c5158Smillert=item Same for C<regparm(3)> function:
260b39c5158Smillert
261b39c5158Smillert $res = call20_rp3( $pointer, $arg0, $arg1, ...);
262b39c5158Smillert
263b39c5158Smillert=item Same for packed arguments and C<regparm(3)> function
264b39c5158Smillert
265b39c5158Smillert $res = call20_rp3_p( $pointer, pack 'L20', $arg0, $arg1, ...);
266b39c5158Smillert
267b39c5158Smillert=item Same for a function which returns non-0 and sets system-error on error
268b39c5158Smillert
269b8851fccSafresh1 call20_Dos( $msg, $pointer, $arg0, $arg1, ...); # die("$msg: $^E")
270b8851fccSafresh1                                                            if error
271b39c5158Smillert
272b39c5158Smillert[Good for C<Dos*> API - and rare C<Win*> calls.]
273b39c5158Smillert
274b39c5158Smillert=item Same for a function which returns 0 and sets WinLastError() on error
275b39c5158Smillert
276b39c5158Smillert $res = call20_Win( $msg, $pointer, $arg0, $arg1, ...);
277b39c5158Smillert # would die("$msg: $^E") if error
278b39c5158Smillert
279b39c5158Smillert[Good for most of C<Win*> API.]
280b39c5158Smillert
281b39c5158Smillert=item Same for a function which returns 0 and sets WinLastError() on error but
282b39c5158Smillert0 is also a valid return
283b39c5158Smillert
284b39c5158Smillert $res = call20_Win_0OK( $msg, $pointer, $arg0, $arg1, ...);
285b39c5158Smillert # would die("$msg: $^E") if error
286b39c5158Smillert
287b39c5158Smillert[Good for some of C<Win*> API.]
288b39c5158Smillert
289b39c5158Smillert=item As previous, but without die()
290b39c5158Smillert
291b39c5158Smillert $res = call20_Win_0OK_survive( $pointer, $arg0, $arg1, ...);
292b39c5158Smillert if ($res == 0 and $^E) {	# Do error processing here
293b39c5158Smillert }
294b39c5158Smillert
295b39c5158Smillert[Good for some of C<Win*> API.]
296b39c5158Smillert
297b39c5158Smillert=back
298b39c5158Smillert
299b39c5158Smillert=head1 ENVIRONMENT
300b39c5158Smillert
301b39c5158SmillertIf C<PERL_REXX_DEBUG> is set, emits debugging output.  Looks for DLLs
302b39c5158Smillertin C<PERL5REXX>, C<PERLREXX>, C<PATH>.
303b39c5158Smillert
304b39c5158Smillert=head1 AUTHOR
305b39c5158Smillert
306b39c5158SmillertExtracted by Ilya Zakharevich perl-module-OS2-DLL@ilyaz.org from L<OS2::REXX>
307b39c5158Smillertwritten by Andreas Kaiser ak@ananke.s.bawue.de.
308b39c5158Smillert
309b39c5158Smillert=cut
310