diff options
-rw-r--r-- | README.os2 | 436 | ||||
-rw-r--r-- | hints/os2.sh | 12 | ||||
-rwxr-xr-x | installperl | 2 | ||||
-rw-r--r-- | lib/ExtUtils/MM_Unix.pm | 2 | ||||
-rw-r--r-- | makedef.pl | 8 | ||||
-rw-r--r-- | os2/Changes | 40 | ||||
-rw-r--r-- | os2/OS2/REXX/DLL/Changes | 2 | ||||
-rw-r--r-- | os2/OS2/REXX/DLL/DLL.pm | 57 | ||||
-rw-r--r-- | os2/os2.c | 641 | ||||
-rw-r--r-- | os2/os2ish.h | 222 | ||||
-rw-r--r-- | os2/perl2cmd.pl | 18 |
11 files changed, 1356 insertions, 84 deletions
diff --git a/README.os2 b/README.os2 index bb1adb1248..05c088ad0e 100644 --- a/README.os2 +++ b/README.os2 @@ -869,7 +869,10 @@ compatibility with XFree86-OS/2). Get a corrected one from If you have I<exactly the same version of Perl> installed already, make sure that no copies or perl are currently running. Later steps of the build may fail since an older version of F<perl.dll> loaded into -memory may be found. +memory may be found. Running C<make test> becomes meaningless, since +the test are checking a previous build of perl (this situation is detected +and reported by F<lib/os2_base.t> test). Do not forget to unset +C<PERL_EMXLOAD_SEC> in environment. Also make sure that you have F</tmp> directory on the current drive, and F<.> directory in your C<LIBPATH>. One may try to correct the @@ -1089,6 +1092,433 @@ say, by doing first. +=head1 Building a binary distribution + +[This section provides a short overview only...] + +Building should proceed differently depending on whether the version of perl +you install is already present and used on your system, or is a new version +not yet used. The description below assumes that the version is new, so +installing its DLLs and F<.pm> files will not disrupt the operation of your +system even if some intermediate steps are not yet fully working. + +The other cases require a little bit more convoluted procedures. Below I +suppose that the current version of Perl is C<5.8.2>, so the executables are +named accordingly. + +=over + +=item 1. + +Fully build and test the Perl distribution. Make sure that no tests are +failing with C<test> and C<aout_test> targets; fix the bugs in Perl and +the Perl test suite detected by these tests. Make sure that C<all_test> +make target runs as clean as possible. Check that C<os2/perlrexx.cmd> +runs fine. + +=item 2. + +Fully install Perl, including C<installcmd> target. Copy the generated DLLs +to C<LIBPATH>; copy the numbered Perl executables (as in F<perl5.8.2.exe>) +to C<PATH>; copy C<perl_.exe> to C<PATH> as C<perl_5.8.2.exe>. Think whether +you need backward-compatibility DLLs. In most cases you do not need to install +them yet; but sometime this may simplify the following steps. + +=item 3. + +Make sure that C<CPAN.pm> can download files from CPAN. If not, you may need +to manually install C<Net::FTP>. + +=item 4. + +Install the bundle C<Bundle::OS2_default> + + perl5.8.2 -MCPAN -e "install Bundle::OS2_default" < nul |& tee 00cpan_i_1 + +This may take a couple of hours on 1GHz processor (when run the first time). +And this should not be necessarily a smooth procedure. Some modules may not +specify required dependencies, so one may need to repeat this procedure several +times until the results stabilize. + + perl5.8.2 -MCPAN -e "install Bundle::OS2_default" < nul |& tee 00cpan_i_2 + perl5.8.2 -MCPAN -e "install Bundle::OS2_default" < nul |& tee 00cpan_i_3 + +Even after they stabilize, some tests may fail. + +Fix as many discovered bugs as possible. Document all the bugs which are not +fixed, and all the failures with unknown reasons. Inspect the produced logs +F<00cpan_i_1> to find suspiciously skipped tests, and other fishy events. + +Keep in mind that I<installation> of some modules may fail too: for example, +the DLLs to update may be already loaded by F<CPAN.pm>. Inspect the C<install> +logs (in the example above F<00cpan_i_1> etc) for errors, and install things +manually, as in + + cd $CPANHOME/.cpan/build/Digest-MD5-2.31 + make install + +Some distributions may fail some tests, but you may want to install them +anyway (as above, or via C<force install> command of C<CPAN.pm> shell-mode). + +Since this procedure may take quite a long time to complete, it makes sense +to "freeze" your CPAN configuration by disabling periodic updates of the +local copy of CPAN index: set C<index_expire> to some big value (I use 365), +then save the settings + + CPAN> o conf index_expire 365 + CPAN> o conf commit + +Reset back to the default value C<1> when you are finished. + +=item 5. + +When satisfied with the results, rerun the C<installcmd> target. Now you +can copy C<perl5.8.2.exe> to C<perl.exe>, and install the other OMF-build +executables: C<perl__.exe> etc. They are ready to be used. + +=item 6. + +Change to the C<./pod> directory of the build tree, download the Perl logo +F<CamelGrayBig.BMP>, and run + + ( perl2ipf > perl.ipf ) |& tee 00ipf + ipfc /INF perl.ipf |& tee 00inf + +This produces the Perl docs online book C<perl.INF>. Install in on +C<BOOKSHELF> path. + +=item 7. + +Now is the time to build statically linked executable F<perl_.exe> which +includes newly-installed via C<Bundle::OS2_default> modules. Doing testing +via C<CPAN.pm> is going to be painfully slow, since it statically links +a new executable per XS extension. + +Here is a possible workaround: create a toplevel F<Makefile.PL> in +F<$CPANHOME/.cpan/build/> with contents being (compare with L<Making +executables with a custom collection of statically loaded extensions>) + + use ExtUtils::MakeMaker; + WriteMakefile NAME => 'dummy'; + +execute this as + + perl_5.8.2.exe Makefile.PL <nul |& tee 00aout_c1 + make -k all test <nul |& 00aout_t1 + +Again, this procedure should not be absolutely smooth. Some C<Makefile.PL>'s +in subdirectories may be buggy, and would not run as "child" scripts. The +interdependency of modules can strike you; however, since non-XS modules +are already installed, the prerequisites of most modules have a very good +chance to be present. + +If you discover some glitches, move directories of problematic modules to a +different location; if these modules are non-XS modules, you may just ignore +them - they are already installed; the remaining, XS, modules you need to +install manually one by one. + +After each such removal you need to rerun the C<Makefile.PL>/C<make> process; +usually this procedure converges soon. (But be sure to convert all the +necessary external C libraries from F<.lib> format to F<.a> format: run one of + + emxaout foo.lib + emximp -o foo.a foo.lib + +whichever is appropriate.) Also, make sure that the DLLs for external +libraries are usable with with executables compiled without C<-Zmtd> options. + +When you are sure that only a few subdirectories +lead to failures, you may want to add C<-j4> option to C<make> to speed up +skipping subdirectories with already finished build. + +When you are satisfied with the results of tests, install the build C libraries +for extensions: + + make install |& tee 00aout_i + +Now you can rename the file F<./perl.exe> generated during the last phase +to F<perl_5.8.2.exe>; place it on C<PATH>; if there is an inter-dependency +between some XS modules, you may need to repeat the C<test>/C<install> loop +with this new executable and some excluded modules - until the procedure +converges. + +Now you have all the necessary F<.a> libraries for these Perl modules in the +places where Perl builder can find it. Use the perl builder: change to an +empty directory, create a "dummy" F<Makefile.PL> again, and run + + perl_5.8.2.exe Makefile.PL |& tee 00c + make perl |& tee 00p + +This should create an executable F<./perl.exe> with all the statically loaded +extensions built in. Compare the generated F<perlmain.c> files to make sure +that during the iterations the number of loaded extensions only increases. +Rename F<./perl.exe> to F<perl_5.8.2.exe> on C<PATH>. + +When it converges, you got a functional variant of F<perl_5.8.2.exe>; copy it +to C<perl_.exe>. You are done with generation of the local Perl installation. + +=item 8. + +Make sure that the installed modules are actually installed in the location +of the new Perl, and are not inherited from entries of @INC given for +inheritance from the older versions of Perl: set C<PERLLIB_582_PREFIX> to +redirect the new version of Perl to a new location, and copy the installed +files to this new location. Redo the tests to make sure that the versions of +modules inherited from older versions of Perl are not needed. + +Actually, the log output of L<pod2ipf> during the step 6 gives a very detailed +info about which modules are loaded from which place; so you may use it as +an additional verification tool. + +Check that some temporary files did not make into the perl install tree. +Run something like this + + pfind . -f "!(/\.(pm|pl|ix|al|h|a|lib|txt|pod|imp|bs|dll|ld|bs|inc|xbm|yml|cgi|uu|e2x|skip|packlist|eg|cfg|html|pub|enc|all|ini|po|pot)$/i or /^\w+$/") | less + +in the install tree (both top one and F<sitelib> one). + +Compress all the DLLs with F<lxlite>. The tiny F<.exe> can be compressed with +C</c:max> (the bug only appears when there is a fixup in the last 6 bytes of a +page (?); since the tiny executables are much smaller than a page, the bug +will not hit). Do not compress C<perl_.exe> - it would not work under DOS. + +=item 9. + +Now you can generate the binary distribution. This is done by running the +test of the CPAN distribution C<OS2::SoftInstaller>. Tune up the file +F<test.pl> to suit the layout of current version of Perl first. Do not +forget to pack the necessary external DLLs accordingly. Include the +description of the bugs and test suite failures you could not fix. Include +the small-stack versions of Perl executables from Perl build directory. + +Include F<perl5.def> so that people can relink the perl DLL preserving +the binary compatibility, or can create compatibility DLLs. Include the diff +files (C<diff -pu old new>) of fixes you did so that people can rebuild your +version. Include F<perl5.map> so that one can use remote debugging. + +=item 10. + +Share what you did with the other people. Relax. Enjoy fruits of your work. + +=item 11. + +Brace yourself for thanks, bug reports, hate mail and spam coming as result +of the previous step. No good deed should remain unpunished! + +=back + +=head1 Building custom F<.EXE> files + +The Perl executables can be easily rebuilt at any moment. Moreover, one can +use the I<embedding> interface (see L<perlembed>) to make very customized +executables. + +=head2 Making executables with a custom collection of statically loaded extensions + +It is a little bit easier to do so while I<decreasing> the list of statically +loaded extensions. We discuss this case only here. + +=over + +=item 1. + +Change to an empty directory, and create a placeholder <Makefile.PL>: + + use ExtUtils::MakeMaker; + WriteMakefile NAME => 'dummy'; + +=item 2. + +Run it with the flavor of Perl (F<perl.exe> or F<perl_.exe>) you want to +rebuild. + + perl_ Makefile.PL + +=item 3. + +Ask it to create new Perl executable: + + make perl + +(you may need to manually add C<PERLTYPE=-DPERL_CORE> to this commandline on +some versions of Perl; the symptom is that the command-line globbing does not +work from OS/2 shells with the newly-compiled executable; check with + + .\perl.exe -wle "print for @ARGV" * + +). + +=item 4. + +The previous step created F<perlmain.c> which contains a list of newXS() calls +near the end. Removing unnecessary calls, and rerunning + + make perl + +will produce a customized executable. + +=back + +=head2 Making executables with a custom search-paths + +The default perl executable is flexible enough to support most usages. +However, one may want something yet more flexible; for example, one may want +to find Perl DLL relatively to the location of the EXE file; or one may want +to ignore the environment when setting the Perl-library search patch, etc. + +If you fill comfortable with I<embedding> interface (see L<perlembed>), such +things are easy to do repeating the steps outlined in L<Making +executables with a custom collection of statically loaded extensions>, and +doing more comprehensive edits to main() of F<perlmain.c>. The people with +little desire to understand Perl can just rename main(), and do necessary +modification in a custom main() which calls the renamed function in appropriate +time. + +However, there is a third way: perl DLL exports the main() function and several +callbacks to customize the search path. Below is a complete example of a +"Perl loader" which + +=over + +=item 1. + +Looks for Perl DLL in the directory C<$exedir/../dll>; + +=item 2. + +Prepends the above directory to C<BEGINLIBPATH>; + +=item 3. + +Fails if the Perl DLL found via C<BEGINLIBPATH> is different from what was +loaded on step 1; e.g., another process could have loaded it from C<LIBPATH> +or from a different value of C<BEGINLIBPATH>. In these cases one needs to +modify the setting of the system so that this other process either does not +run, or loads the DLL from C<BEGINLIBPATH> with C<LIBPATHSTRICT=T> (available +with kernels after September 2000). + +=item 4. + +Loads Perl library from C<$exedir/../dll/lib/>. + +=item 5. + +Uses Bourne shell from C<$exedir/../dll/sh/ksh.exe>. + +=back + +For best results compile the C file below with the same options as the Perl +DLL. However, a lot of functionality will work even if the executable is not +an EMX applications, e.g., if compiled with + + gcc -Wall -DDOSISH -DOS2=1 -O2 -s -Zomf -Zsys perl-starter.c -DPERL_DLL_BASENAME=\"perl312F\" -Zstack 8192 -Zlinker /PM:VIO + +Here is the sample C file: + + #define INCL_DOS + #define INCL_NOPM + /* These are needed for compile if os2.h includes os2tk.h, not os2emx.h */ + #define INCL_DOSPROCESS + #include <os2.h> + + #include "EXTERN.h" + #define PERL_IN_MINIPERLMAIN_C + #include "perl.h" + + static char *me; + HMODULE handle; + + static void + die_with(char *msg1, char *msg2, char *msg3, char *msg4) + { + ULONG c; + char *s = " error: "; + + DosWrite(2, me, strlen(me), &c); + DosWrite(2, s, strlen(s), &c); + DosWrite(2, msg1, strlen(msg1), &c); + DosWrite(2, msg2, strlen(msg2), &c); + DosWrite(2, msg3, strlen(msg3), &c); + DosWrite(2, msg4, strlen(msg4), &c); + DosWrite(2, "\r\n", 2, &c); + exit(255); + } + + typedef ULONG (*fill_extLibpath_t)(int type, char *pre, char *post, int replace, char *msg); + typedef int (*main_t)(int type, char *argv[], char *env[]); + typedef int (*handler_t)(void* data, int which); + + #ifndef PERL_DLL_BASENAME + # define PERL_DLL_BASENAME "perl" + #endif + + static HMODULE + load_perl_dll(char *basename) + { + char buf[300], fail[260]; + STRLEN l, dirl; + fill_extLibpath_t f; + ULONG rc_fullname; + HMODULE handle, handle1; + + if (_execname(buf, sizeof(buf) - 13) != 0) + die_with("Can't find full path: ", strerror(errno), "", ""); + /* XXXX Fill `me' with new value */ + l = strlen(buf); + while (l && buf[l-1] != '/' && buf[l-1] != '\\') + l--; + dirl = l - 1; + strcpy(buf + l, basename); + l += strlen(basename); + strcpy(buf + l, ".dll"); + if ( (rc_fullname = DosLoadModule(fail, sizeof fail, buf, &handle)) != 0 + && DosLoadModule(fail, sizeof fail, basename, &handle) != 0 ) + die_with("Can't load DLL ", buf, "", ""); + if (rc_fullname) + return handle; /* was loaded with short name; all is fine */ + if (DosQueryProcAddr(handle, 0, "fill_extLibpath", (PFN*)&f)) + die_with(buf, ": DLL exports no symbol ", "fill_extLibpath", ""); + buf[dirl] = 0; + if (f(0 /*BEGINLIBPATH*/, buf /* prepend */, NULL /* append */, + 0 /* keep old value */, me)) + die_with(me, ": prepending BEGINLIBPATH", "", ""); + if (DosLoadModule(fail, sizeof fail, basename, &handle1) != 0) + die_with(me, ": finding perl DLL again via BEGINLIBPATH", "", ""); + buf[dirl] = '\\'; + if (handle1 != handle) { + if (DosQueryModuleName(handle1, sizeof(fail), fail)) + strcpy(fail, "???"); + die_with(buf, ":\n\tperl DLL via BEGINLIBPATH is different: \n\t", + fail, + "\n\tYou may need to manipulate global BEGINLIBPATH and LIBPATHSTRICT" + "\n\tso that the other copy is loaded via BEGINLIBPATH."); + } + return handle; + } + + int + main(int argc, char **argv, char **env) + { + main_t f; + handler_t h; + + me = argv[0]; + /**/ + handle = load_perl_dll(PERL_DLL_BASENAME); + + if (DosQueryProcAddr(handle, 0, "Perl_OS2_handler_install", (PFN*)&h)) + die_with(PERL_DLL_BASENAME, ": DLL exports no symbol ", "Perl_OS2_handler_install", ""); + if ( !h((void *)"~installprefix", Perlos2_handler_perllib_from) + || !h((void *)"~dll", Perlos2_handler_perllib_to) + || !h((void *)"~dll/sh/ksh.exe", Perlos2_handler_perl_sh) ) + die_with(PERL_DLL_BASENAME, ": Can't install @INC manglers", "", ""); + + if (DosQueryProcAddr(handle, 0, "dll_perlmain", (PFN*)&f)) + die_with(PERL_DLL_BASENAME, ": DLL exports no symbol ", "dll_perlmain", ""); + return f(argc, argv, env); + } + + =head1 Build FAQ =head2 Some C</> became C<\> in pdksh. @@ -2270,8 +2700,8 @@ have a low probability of affecting small programs. =head1 BUGS -This description was not updated since 5.6.1, see F<os2/Changes> for -more info. +This description is not updated often (since 5.6.1?), see F<./os2/Changes> +(L<perlos2delta>) for more info. =cut diff --git a/hints/os2.sh b/hints/os2.sh index a3fc0b6c50..8c8ef21bb2 100644 --- a/hints/os2.sh +++ b/hints/os2.sh @@ -131,19 +131,23 @@ aout_lib_ext='.a' aout_ar='ar' aout_plibext='.a' aout_lddlflags="-Zdll $ld_dll_optimize" + +# -D__ST_MT_ERRNO__ allows a quick relink with -Zmtd to check problems +# which may be due to linking with -Zmtd DLLs + # Cannot have 32000K stack: get SYS0170 ?! if [ $emxcrtrev -ge 50 ]; then - aout_ldflags='-Zexe -Zsmall-conv -Zstack 16000' + aout_ldflags='-Zexe -Zsmall-conv -Zstack 16000 -D__ST_MT_ERRNO__' else - aout_ldflags='-Zexe -Zstack 16000' + aout_ldflags='-Zexe -Zstack 16000 -D__ST_MT_ERRNO__' fi # To get into config.sh: aout_ldflags="$aout_ldflags" aout_d_fork='define' -aout_ccflags="-DDOSISH -DPERL_IS_AOUT -DOS2=2 -DEMBED -I. $_defemxcrtrev" -aout_cppflags="-DDOSISH -DPERL_IS_AOUT -DOS2=2 -DEMBED -I. $_defemxcrtrev" +aout_ccflags="-DDOSISH -DPERL_IS_AOUT -DOS2=2 -DEMBED -I. $_defemxcrtrev -D__ST_MT_ERRNO__" +aout_cppflags="-DDOSISH -DPERL_IS_AOUT -DOS2=2 -DEMBED -I. $_defemxcrtrev -D__ST_MT_ERRNO__" aout_use_clib='c' aout_usedl='undef' aout_archobjs="os2.o dl_os2.o" diff --git a/installperl b/installperl index 5be24b9d45..4a3c07d223 100755 --- a/installperl +++ b/installperl @@ -404,7 +404,7 @@ if ($Is_VMS) { # We did core file selection during build } else { # [als] hard-coded 'libperl' name... not good! - @corefiles = <*.h libperl*.*>; + @corefiles = <*.h libperl*.* perl*$Config{lib_ext}>; # AIX needs perl.exp installed as well. push(@corefiles,'perl.exp') if $^O eq 'aix'; diff --git a/lib/ExtUtils/MM_Unix.pm b/lib/ExtUtils/MM_Unix.pm index 6e48d6c398..a1c21d223a 100644 --- a/lib/ExtUtils/MM_Unix.pm +++ b/lib/ExtUtils/MM_Unix.pm @@ -2777,7 +2777,7 @@ $(MAKE_APERL_FILE) : $(FIRST_MAKEFILE) require File::Find; File::Find::find(sub { return unless m/\Q$self->{LIB_EXT}\E$/; - return if m/^libperl/; + return if m/^libperl/ or m/^perl\Q$self->{LIB_EXT}\E$/; # Skip purified versions of libraries (e.g., DynaLoader_pure_p1_c0_032.a) return if m/_pure_\w+_\w+_\w+\.\w+$/ and -f "$File::Find::dir/.pure"; diff --git a/makedef.pl b/makedef.pl index 3db62ab7eb..033f639af5 100644 --- a/makedef.pl +++ b/makedef.pl @@ -400,6 +400,10 @@ elsif ($PLATFORM eq 'os2') { nthreads_cond os2_cond_wait os2_stat + os2_execname + async_mssleep + msCounter + InfoTable pthread_join pthread_create pthread_detach @@ -1344,6 +1348,10 @@ foreach my $symbol (sort keys %export) { if ($PLATFORM eq 'os2') { print <<EOP; dll_perlmain=main + fill_extLibpath + dir_subst + Perl_OS2_handler_install + ; LAST_ORDINAL=$sym_ord EOP } diff --git a/os2/Changes b/os2/Changes index bcd970d745..3bd33a56c5 100644 --- a/os2/Changes +++ b/os2/Changes @@ -779,3 +779,43 @@ After @21574: is void. New executables perl___<number> generated with decreased stack size (good when virtual memory is low; e.g. floppy boot). + +After 5.8.2 (@21668): + Fixes to installperl scripts to avoid junk output, allow overwrite + of existing files (File::Copy::copy is mapped to DosCopy() + with flags which would not overwrite). + Disable DynaLoading of Perl modules with AOUT build (will core anyway). + For AOUT build: Quick hack to construct directories necessary for + /*/% stuff [maybe better do it from hints/os2.sh?]. + AOUT build: do -D__ST_MT_ERRNO__ to simplify linking with -Zmtd + (e.g., to test GD: gd.dll linked with -Zmtd). + MANIFEST.SKIP was read without a drive part of the filename. + Rename Cwd::extLibpath*() to OS2::... (old names still preserved). + Install perl.lib and perl.a too. + New methods libPath_find(),has_f32(),handle(),fullname() for OS2::DLL. + Enable quad support using long long. + New C exported functions os2_execname(), async_mssleep(), msCounter(), + InfoTable(), dir_subst(), Perl_OS2_handler_install(), + fill_extLibpath(). + async_mssleep() uses some undocumented features which allow usage of + highest possible resolution of sleep() while preserving low + priority (raise of resolution may be not available before + Warp3fp40; resolution is 8ms/CLOCK_SCALE). + usleep() and select(undef,undef,undef,$t) are using this + interface for time up to 0.5sec. + New convenience macros os2win_croak_0OK(rc,msg), os2win_croak(rc,msg), + os2cp_croak(rc,msg). + Supports ~installprefix, ~exe, ~dll in PERLLIB_PREFIX etc (actual + directories are substituted). + New functions OS2::msCounter(), OS2::ms_sleep(), OS2::_InfoTable(). + Checks stack when fixing EMX being under-initialized (-Zomf -Zsys + produces 32K stack???). + New environment variables PERL_BEGINLIBPATH, PERL_PRE_BEGINLIBPATH, + PERL_POST_BEGINLIBPATH, PERL_ENDLIBPATH, + PERL_PRE_ENDLIBPATH PERL_POST_ENDLIBPATH (~-enabled); + PERL_EMXLOAD_SECS. + Better handling of FIRST_MAKEFILE (propagate to subdirs during test, + do not require Makefile.PL present). + perl2cmd converter: do not rewrite if no change. + README.os2 updated with info on building binary distributions and + custom perl executables (but not much else). diff --git a/os2/OS2/REXX/DLL/Changes b/os2/OS2/REXX/DLL/Changes index 874f7fab4a..e2c656dd90 100644 --- a/os2/OS2/REXX/DLL/Changes +++ b/os2/OS2/REXX/DLL/Changes @@ -1,2 +1,4 @@ 0.01: Split out of OS2::REXX +0.02: + New methods libPath_find(), has_f32(), handle() and fullname(). diff --git a/os2/OS2/REXX/DLL/DLL.pm b/os2/OS2/REXX/DLL/DLL.pm index 537a2107fc..5d8a24ea7b 100644 --- a/os2/OS2/REXX/DLL/DLL.pm +++ b/os2/OS2/REXX/DLL/DLL.pm @@ -1,6 +1,6 @@ package OS2::DLL; -our $VERSION = '1.01'; +our $VERSION = '1.02'; use Carp; use XSLoader; @@ -58,6 +58,20 @@ sub load { $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; + -f $_ and return $_ for @path; + return; +} + package OS2::DLL::dll; use Carp; @ISA = 'OS2::DLL'; @@ -102,6 +116,16 @@ sub find 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; @@ -186,6 +210,37 @@ 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 @@ -12,6 +12,7 @@ #include <os2.h> #include "dlfcn.h" #include <emx/syscalls.h> +#include <sys/emxload.h> #include <sys/uflags.h> @@ -32,6 +33,14 @@ #include "EXTERN.h" #include "perl.h" +enum module_name_how { mod_name_handle, mod_name_shortname, mod_name_full, + mod_name_C_function = 0x100, mod_name_HMODULE = 0x200}; + +/* Find module name to which *this* subroutine is compiled */ +#define module_name(how) module_name_at(&module_name_at, how) + +static SV* module_name_at(void *pp, enum module_name_how how); + void croak_with_os2error(char *s) { @@ -118,6 +127,7 @@ static struct perlos2_state_t { int po2__my_pwent; /* = -1; */ int po2_DOS_harderr_state; /* = -1; */ signed char po2_DOS_suppression_state; /* = -1; */ + PFN po2_ExtFCN[ORD_NENTRIES]; /* Labeled by ord ORD_*. */ /* struct PMWIN_entries_t po2_PMWIN_entries; */ @@ -153,7 +163,10 @@ static struct perlos2_state_t { int po2_emx_runtime_init; /* If 1, we need to manually init it */ int po2_emx_exception_init; /* If 1, we need to manually set it */ int po2_emx_runtime_secondary; - + char* (*po2_perllib_mangle_installed)(char *s, unsigned int l); + char* po2_perl_sh_installed; + PGINFOSEG po2_gTable; + PLINFOSEG po2_lTable; } perlos2_state = { -1, /* po2__my_pwent */ -1, /* po2_DOS_harderr_state */ @@ -195,10 +208,13 @@ static struct perlos2_state_t { #define emx_runtime_init (Perl_po2()->po2_emx_runtime_init) #define emx_exception_init (Perl_po2()->po2_emx_exception_init) #define emx_runtime_secondary (Perl_po2()->po2_emx_runtime_secondary) +#define perllib_mangle_installed (Perl_po2()->po2_perllib_mangle_installed) +#define perl_sh_installed (Perl_po2()->po2_perl_sh_installed) +#define gTable (Perl_po2()->po2_gTable) +#define lTable (Perl_po2()->po2_lTable) const Perl_PFN * const pExtFCN = (Perl_po2()->po2_ExtFCN); - #if defined(USE_5005THREADS) || defined(USE_ITHREADS) typedef void (*emx_startroutine)(void *); @@ -966,7 +982,7 @@ do_spawn_ve(pTHX_ SV *really, U32 flag, U32 execf, char *inicmd, U32 addflag) { int trueflag = flag; int rc, pass = 1; - char *real_name; + char *real_name = NULL; /* Shut down the warning */ char const * args[4]; static const char * const fargs[4] = { "/bin/sh", "-c", "\"$@\"", "spawn-via-shell", }; @@ -2100,34 +2116,50 @@ void CroakWinError(int die, char *name) { FillWinError; - if (die && Perl_rc) { - dTHX; + if (die && Perl_rc) + croak_with_os2error(name ? name : "Win* API call"); +} - Perl_croak(aTHX_ "%s: %s", (name ? name : "Win* API call"), os2error(Perl_rc)); - } +static char * +dllname2buffer(pTHX_ char *buf, STRLEN l) +{ + char *o; + STRLEN ll; + SV *dll = Nullsv; + + dll = module_name(mod_name_full); + o = SvPV(dll, ll); + if (ll < l) + memcpy(buf,o,ll); + SvREFCNT_dec(dll); + return (ll >= l ? "???" : buf); } -char * -os2_execname(pTHX) +static char * +execname2buffer(char *buf, STRLEN l, char *oname) { - char buf[300], *p, *o = PL_origargv[0], ok = 1; + char *p, *orig = oname, ok = oname != NULL; - if (_execname(buf, sizeof buf) != 0) - return o; + if (_execname(buf, l) != 0) { + if (!oname || strlen(oname) >= l) + return oname; + strcpy(buf, oname); + ok = 0; + } p = buf; while (*p) { if (*p == '\\') *p = '/'; if (*p == '/') { - if (ok && *o != '/' && *o != '\\') + if (ok && *oname != '/' && *oname != '\\') ok = 0; - } else if (ok && tolower(*o) != tolower(*p)) + } else if (ok && tolower(*oname) != tolower(*p)) ok = 0; p++; - o++; + oname++; } - if (ok) { /* PL_origargv[0] matches the real name. Use PL_origargv[0]: */ - strcpy(buf, PL_origargv[0]); /* _execname() is always uppercased */ + if (ok) { /* orig matches the real name. Use orig: */ + strcpy(buf, orig); /* _execname() is always uppercased */ p = buf; while (*p) { if (*p == '\\') @@ -2135,61 +2167,238 @@ os2_execname(pTHX) p++; } } - p = savepv(buf); + return buf; +} + +char * +os2_execname(pTHX) +{ + char buf[300], *p = execname2buffer(buf, sizeof buf, PL_origargv[0]); + + p = savepv(p); SAVEFREEPV(p); return p; } +int +Perl_OS2_handler_install(void *handler, enum Perlos2_handler how) +{ + char *s, b[300]; + + switch (how) { + case Perlos2_handler_mangle: + perllib_mangle_installed = (char *(*)(char *s, unsigned int l))handler; + return 1; + case Perlos2_handler_perl_sh: + s = (char *)handler; + s = dir_subst(s, strlen(s), b, sizeof b, 0, "handler_perl_sh"); + perl_sh_installed = savepv(s); + return 1; + case Perlos2_handler_perllib_from: + s = (char *)handler; + s = dir_subst(s, strlen(s), b, sizeof b, 0, "handler_perllib_from"); + oldl = strlen(s); + oldp = savepv(s); + return 1; + case Perlos2_handler_perllib_to: + s = (char *)handler; + s = dir_subst(s, strlen(s), b, sizeof b, 0, "handler_perllib_to"); + newl = strlen(s); + newp = savepv(s); + strcpy(mangle_ret, newp); + s = mangle_ret - 1; + while (*++s) + if (*s == '\\') + *s = '/'; + return 1; + default: + return 0; + } +} + +/* Returns a malloc()ed copy */ +char * +dir_subst(char *s, unsigned int l, char *b, unsigned int bl, enum dir_subst_e flags, char *msg) +{ + char *from, *to = b, *e = b; /* `to' assignment: shut down the warning */ + STRLEN froml = 0, tol = 0, rest = 0; /* froml: likewise */ + + if (l >= 2 && s[0] == '~') { + switch (s[1]) { + case 'i': case 'I': + from = "installprefix"; break; + case 'd': case 'D': + from = "dll"; break; + case 'e': case 'E': + from = "exe"; break; + default: + from = NULL; + froml = l + 1; /* Will not match */ + break; + } + if (from) + froml = strlen(from) + 1; + if (l >= froml && strnicmp(s + 2, from + 1, froml - 2) == 0) { + int strip = 1; + + switch (s[1]) { + case 'i': case 'I': + strip = 0; + tol = strlen(INSTALL_PREFIX); + if (tol >= bl) { + if (flags & dir_subst_fatal) + Perl_croak_nocontext("INSTALL_PREFIX too long: `%s'", INSTALL_PREFIX); + else + return NULL; + } + memcpy(b, INSTALL_PREFIX, tol + 1); + to = b; + e = b + tol; + break; + case 'd': case 'D': + if (flags & dir_subst_fatal) { + dTHX; + + to = dllname2buffer(aTHX_ b, bl); + } else { /* No Perl present yet */ + HMODULE self = find_myself(); + APIRET rc = DosQueryModuleName(self, bl, b); + + if (rc) + return 0; + to = b - 1; + while (*++to) + if (*to == '\\') + *to = '/'; + to = b; + } + break; + case 'e': case 'E': + if (flags & dir_subst_fatal) { + dTHX; + + to = execname2buffer(b, bl, PL_origargv[0]); + } else + to = execname2buffer(b, bl, NULL); + break; + } + if (!to) + return NULL; + if (strip) { + e = strrchr(to, '/'); + if (!e && (flags & dir_subst_fatal)) + Perl_croak_nocontext("%s: Can't parse EXE/DLL name: '%s'", msg, to); + else if (!e) + return NULL; + *e = 0; + } + s += froml; l -= froml; + if (!l) + return to; + if (!tol) + tol = strlen(to); + + while (l >= 3 && (s[0] == '/' || s[0] == '\\') + && s[1] == '.' && s[2] == '.' + && (l == 3 || s[3] == '/' || s[3] == '\\' || s[3] == ';')) { + e = strrchr(b, '/'); + if (!e && (flags & dir_subst_fatal)) + Perl_croak_nocontext("%s: Error stripping dirs from EXE/DLL/INSTALLDIR name", msg); + else if (!e) + return NULL; + *e = 0; + l -= 3; s += 3; + } + if (l && s[0] != '/' && s[0] != '\\' && s[0] != ';') + *e++ = '/'; + } + } /* Else: copy as is */ + if (l && (flags & dir_subst_pathlike)) { + STRLEN i = 0; + + while ( i < l - 2 && s[i] != ';') /* May have ~char after `;' */ + i++; + if (i < l - 2) { /* Found */ + rest = l - i - 1; + l = i + 1; + } + } + if (e + l >= b + bl) { + if (flags & dir_subst_fatal) + Perl_croak_nocontext("%s: name `%s%s' too long", msg, b, s); + else + return NULL; + } + memcpy(e, s, l); + if (rest) { + e = dir_subst(s + l, rest, e + l, bl - (e + l - b), flags, msg); + return e ? b : e; + } + e[l] = 0; + return b; +} + +char * +perllib_mangle_with(char *s, unsigned int l, char *from, unsigned int froml, char *to, unsigned int tol) +{ + if (!to) + return s; + if (l == 0) + l = strlen(s); + if (l < froml || strnicmp(from, s, froml) != 0) + return s; + if (l + tol - froml > STATIC_FILE_LENGTH || tol > STATIC_FILE_LENGTH) + Perl_croak_nocontext("Malformed PERLLIB_PREFIX"); + if (to && to != mangle_ret) + memcpy(mangle_ret, to, tol); + strcpy(mangle_ret + tol, s + froml); + return mangle_ret; +} + char * perllib_mangle(char *s, unsigned int l) { + char *name; + + if (perllib_mangle_installed && (name = perllib_mangle_installed(s,l))) + return name; if (!newp && !notfound) { - newp = getenv("PERLLIB_" STRINGIFY(PERL_REVISION) + newp = getenv(name = "PERLLIB_" STRINGIFY(PERL_REVISION) STRINGIFY(PERL_VERSION) STRINGIFY(PERL_SUBVERSION) "_PREFIX"); if (!newp) - newp = getenv("PERLLIB_" STRINGIFY(PERL_REVISION) + newp = getenv(name = "PERLLIB_" STRINGIFY(PERL_REVISION) STRINGIFY(PERL_VERSION) "_PREFIX"); if (!newp) - newp = getenv("PERLLIB_" STRINGIFY(PERL_REVISION) "_PREFIX"); + newp = getenv(name = "PERLLIB_" STRINGIFY(PERL_REVISION) "_PREFIX"); if (!newp) - newp = getenv("PERLLIB_PREFIX"); + newp = getenv(name = "PERLLIB_PREFIX"); if (newp) { - char *s; + char *s, b[300]; oldp = newp; - while (*newp && !isSPACE(*newp) && *newp != ';') { - newp++; oldl++; /* Skip digits. */ - } - while (*newp && (isSPACE(*newp) || *newp == ';')) { + while (*newp && !isSPACE(*newp) && *newp != ';') + newp++; /* Skip old name. */ + oldl = newp - oldp; + s = dir_subst(oldp, oldl, b, sizeof b, dir_subst_fatal, name); + oldp = savepv(s); + oldl = strlen(s); + while (*newp && (isSPACE(*newp) || *newp == ';')) newp++; /* Skip whitespace. */ - } - newl = strlen(newp); - if (newl == 0 || oldl == 0) { - Perl_croak_nocontext("Malformed PERLLIB_PREFIX"); - } - strcpy(mangle_ret, newp); - s = mangle_ret; - while (*s) { - if (*s == '\\') *s = '/'; - s++; - } - } else { + Perl_OS2_handler_install((void *)newp, Perlos2_handler_perllib_to); + if (newl == 0 || oldl == 0) + Perl_croak_nocontext("Malformed %s", name); + } else notfound = 1; - } } - if (!newp) { + if (!newp) return s; - } - if (l == 0) { + if (l == 0) l = strlen(s); - } - if (l < oldl || strnicmp(oldp, s, oldl) != 0) { + if (l < oldl || strnicmp(oldp, s, oldl) != 0) return s; - } - if (l + newl - oldl > STATIC_FILE_LENGTH || newl > STATIC_FILE_LENGTH) { + if (l + newl - oldl > STATIC_FILE_LENGTH || newl > STATIC_FILE_LENGTH) Perl_croak_nocontext("Malformed PERLLIB_PREFIX"); - } strcpy(mangle_ret + newl, s + oldl); return mangle_ret; } @@ -2394,6 +2603,105 @@ XS(XS_OS2_Errors2Drive) XSRETURN(1); } +int +async_mssleep(ULONG ms, int switch_priority) { + /* This is similar to DosSleep(), but has 8ms granularity in time-critical + threads even on Warp3. */ + HEV hevEvent1 = 0; /* Event semaphore handle */ + HTIMER htimerEvent1 = 0; /* Timer handle */ + APIRET rc = NO_ERROR; /* Return code */ + int ret = 1; + ULONG priority = 0, nesting; /* Shut down the warnings */ + PPIB pib; + PTIB tib; + char *e = NULL; + APIRET badrc; + + if (!(_emx_env & 0x200)) /* DOS */ + return !_sleep2(ms); + + os2cp_croak(DosCreateEventSem(NULL, /* Unnamed */ + &hevEvent1, /* Handle of semaphore returned */ + DC_SEM_SHARED, /* Shared needed for DosAsyncTimer */ + FALSE), /* Semaphore is in RESET state */ + "DosCreateEventSem"); + + if (ms >= switch_priority) + switch_priority = 0; + if (switch_priority) { + if (CheckOSError(DosGetInfoBlocks(&tib, &pib))) + switch_priority = 0; + else { + /* In Warp3, to switch scheduling to 8ms step, one needs to do + DosAsyncTimer() in time-critical thread. On laters versions, + more and more cases of wait-for-something are covered. + + It turns out that on Warp3fp42 it is the priority at the time + of DosAsyncTimer() which matters. Let's hope that this works + with later versions too... XXXX + */ + priority = (tib->tib_ptib2->tib2_ulpri); + if ((priority & 0xFF00) == 0x0300) /* already time-critical */ + switch_priority = 0; + /* Make us time-critical. Just modifying TIB is not enough... */ + /* tib->tib_ptib2->tib2_ulpri = 0x0300;*/ + /* We do not want to run at high priority if a signal causes us + to longjmp() out of this section... */ + if (DosEnterMustComplete(&nesting)) + switch_priority = 0; + else + DosSetPriority(PRTYS_THREAD, PRTYC_TIMECRITICAL, 0, 0); + } + } + + if ((badrc = DosAsyncTimer(ms, + (HSEM) hevEvent1, /* Semaphore to post */ + &htimerEvent1))) /* Timer handler (returned) */ + e = "DosAsyncTimer"; + + if (switch_priority && tib->tib_ptib2->tib2_ulpri == 0x0300) { + /* Nobody switched priority while we slept... Ignore errors... */ + /* tib->tib_ptib2->tib2_ulpri = priority; */ /* Get back... */ + if (!(rc = DosSetPriority(PRTYS_THREAD, (priority>>8) & 0xFF, 0, 0))) + rc = DosSetPriority(PRTYS_THREAD, 0, priority & 0xFF, 0); + } + if (switch_priority) + rc = DosExitMustComplete(&nesting); /* Ignore errors */ + + /* The actual blocking call is made with "normal" priority. This way we + should not bother with DosSleep(0) etc. to compensate for us interrupting + higher-priority threads. The goal is to prohibit the system spending too + much time halt()ing, not to run us "no matter what". */ + if (!e) /* Wait for AsyncTimer event */ + badrc = DosWaitEventSem(hevEvent1, SEM_INDEFINITE_WAIT); + + if (e) ; /* Do nothing */ + else if (badrc == ERROR_INTERRUPT) + ret = 0; + else if (badrc) + e = "DosWaitEventSem"; + if ((rc = DosCloseEventSem(hevEvent1)) && !e) { /* Get rid of semaphore */ + e = "DosCloseEventSem"; + badrc = rc; + } + if (e) + os2cp_croak(badrc, e); + return ret; +} + +XS(XS_OS2_ms_sleep) /* for testing only... */ +{ + dXSARGS; + ULONG ms, lim; + + if (items > 2 || items < 1) + Perl_croak_nocontext("Usage: OS2::ms_sleep(wait_ms [, high_priority_limit])"); + ms = SvUV(ST(0)); + lim = items > 1 ? SvUV(ST(1)) : ms + 1; + async_mssleep(ms, lim); + XSRETURN_EMPTY; +} + ULONG (*pDosTmrQueryFreq) (PULONG); ULONG (*pDosTmrQueryTime) (unsigned long long *); @@ -2425,6 +2733,37 @@ XS(XS_OS2_Timer) XSRETURN(1); } +XS(XS_OS2_msCounter) +{ + dXSARGS; + + if (items != 0) + Perl_croak_nocontext("Usage: OS2::msCounter()"); + { + dXSTARG; + + XSprePUSH; PUSHu(msCounter()); + } + XSRETURN(1); +} + +XS(XS_OS2__InfoTable) +{ + dXSARGS; + int is_local = 0; + + if (items > 1) + Perl_croak_nocontext("Usage: OS2::_infoTable([isLocal])"); + if (items == 1) + is_local = (int)SvIV(ST(0)); + { + dXSTARG; + + XSprePUSH; PUSHu(InfoTable(is_local)); + } + XSRETURN(1); +} + static const char * const dc_fields[] = { "FAMILY", "IO_CAPS", @@ -3219,11 +3558,13 @@ typedef APIRET (*PELP)(PSZ path, ULONG type); #endif APIRET -ExtLIBPATH(ULONG ord, PSZ path, IV type) +ExtLIBPATH(ULONG ord, PSZ path, IV type, int fatal) { ULONG what; - PFN f = loadByOrdinal(ord, 1); /* Guarantied to load or die! */ + PFN f = loadByOrdinal(ord, fatal); /* if fatal: load or die! */ + if (!f) /* Impossible with fatal */ + return Perl_rc; if (type > 0) what = END_LIBPATH; else if (type == 0) @@ -3233,23 +3574,35 @@ ExtLIBPATH(ULONG ord, PSZ path, IV type) return (*(PELP)f)(path, what); } -#define extLibpath(to,type) \ - (CheckOSError(ExtLIBPATH(ORD_DosQueryExtLibpath, (to), (type))) ? NULL : (to) ) +#define extLibpath(to,type, fatal) \ + (CheckOSError(ExtLIBPATH(ORD_DosQueryExtLibpath, (to), (type), fatal)) ? NULL : (to) ) + +#define extLibpath_set(p,type, fatal) \ + (!CheckOSError(ExtLIBPATH(ORD_DosSetExtLibpath, (p), (type), fatal))) -#define extLibpath_set(p,type) \ - (!CheckOSError(ExtLIBPATH(ORD_DosSetExtLibpath, (p), (type)))) +static void +early_error(char *msg1, char *msg2, char *msg3) +{ /* Buffer overflow detected; there is very little we can do... */ + ULONG rc; + + DosWrite(2, msg1, strlen(msg1), &rc); + DosWrite(2, msg2, strlen(msg2), &rc); + DosWrite(2, msg3, strlen(msg3), &rc); + DosExit(EXIT_PROCESS, 2); +} XS(XS_Cwd_extLibpath) { dXSARGS; if (items < 0 || items > 1) - Perl_croak_nocontext("Usage: Cwd::extLibpath(type = 0)"); + Perl_croak_nocontext("Usage: OS2::extLibpath(type = 0)"); { IV type; char to[1024]; U32 rc; char * RETVAL; dXSTARG; + STRLEN l; if (items < 1) type = 0; @@ -3258,9 +3611,13 @@ XS(XS_Cwd_extLibpath) } to[0] = 1; to[1] = 0; /* Sometimes no error reported */ - RETVAL = extLibpath(to, type); + RETVAL = extLibpath(to, type, 1); /* Make errors fatal */ if (RETVAL && RETVAL[0] == 1 && RETVAL[1] == 0) - Perl_croak_nocontext("panic Cwd::extLibpath parameter"); + Perl_croak_nocontext("panic OS2::extLibpath parameter"); + l = strlen(to); + if (l >= sizeof(to)) + early_error("Buffer overflow while getting BEGIN/ENDLIBPATH: `", + to, "'\r\n"); /* Will not return */ sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG; } @@ -3271,7 +3628,7 @@ XS(XS_Cwd_extLibpath_set) { dXSARGS; if (items < 1 || items > 2) - Perl_croak_nocontext("Usage: Cwd::extLibpath_set(s, type = 0)"); + Perl_croak_nocontext("Usage: OS2::extLibpath_set(s, type = 0)"); { STRLEN n_a; char * s = (char *)SvPV(ST(0),n_a); @@ -3285,13 +3642,74 @@ XS(XS_Cwd_extLibpath_set) type = SvIV(ST(1)); } - RETVAL = extLibpath_set(s, type); + RETVAL = extLibpath_set(s, type, 1); /* Make errors fatal */ ST(0) = boolSV(RETVAL); if (SvREFCNT(ST(0))) sv_2mortal(ST(0)); } XSRETURN(1); } +ULONG +fill_extLibpath(int type, char *pre, char *post, int replace, char *msg) +{ + char buf[2048], *to = buf, buf1[300], *s; + STRLEN l; + ULONG rc; + + if (!pre && !post) + return 0; + if (pre) { + pre = dir_subst(pre, strlen(pre), buf1, sizeof buf1, dir_subst_pathlike, msg); + if (!pre) + return ERROR_INVALID_PARAMETER; + l = strlen(pre); + if (l >= sizeof(buf)/2) + return ERROR_BUFFER_OVERFLOW; + s = pre - 1; + while (*++s) + if (*s == '/') + *s = '\\'; /* Be extra causious */ + memcpy(to, pre, l); + if (!l || to[l-1] != ';') + to[l++] = ';'; + to += l; + } + + if (!replace) { + to[0] = 1; to[1] = 0; /* Sometimes no error reported */ + rc = ExtLIBPATH(ORD_DosQueryExtLibpath, to, type, 0); /* Do not croak */ + if (rc) + return rc; + if (to[0] == 1 && to[1] == 0) + return ERROR_INVALID_PARAMETER; + to += strlen(to); + if (buf + sizeof(buf) - 1 <= to) /* Buffer overflow */ + early_error("Buffer overflow while getting BEGIN/ENDLIBPATH: `", + buf, "'\r\n"); /* Will not return */ + if (to > buf && to[-1] != ';') + *to++ = ';'; + } + if (post) { + post = dir_subst(post, strlen(post), buf1, sizeof buf1, dir_subst_pathlike, msg); + if (!post) + return ERROR_INVALID_PARAMETER; + l = strlen(post); + if (l + to - buf >= sizeof(buf) - 1) + return ERROR_BUFFER_OVERFLOW; + s = post - 1; + while (*++s) + if (*s == '/') + *s = '\\'; /* Be extra causious */ + memcpy(to, post, l); + if (!l || to[l-1] != ';') + to[l++] = ';'; + to += l; + } + *to = 0; + rc = ExtLIBPATH(ORD_DosSetExtLibpath, buf, type, 0); /* Do not croak */ + return rc; +} + /* Input: Address, BufLen APIRET APIENTRY DosQueryModFromEIP (HMODULE * hmod, ULONG * obj, ULONG BufLen, PCHAR Buf, @@ -3303,9 +3721,6 @@ DeclOSFuncByORD(APIRET, _DosQueryModFromEIP,ORD_DosQueryModFromEIP, ULONG * Offset, ULONG Address), (hmod, obj, BufLen, Buf, Offset, Address)) -enum module_name_how { mod_name_handle, mod_name_shortname, mod_name_full, - mod_name_C_function = 0x100, mod_name_HMODULE = 0x200}; - static SV* module_name_at(void *pp, enum module_name_how how) { @@ -3351,9 +3766,6 @@ module_name_of_cv(SV *cv, enum module_name_how how) return module_name_at(CvXSUB(SvRV(cv)), how); } -/* Find module name to which *this* subroutine is compiled */ -#define module_name(how) module_name_at(&module_name_at, how) - XS(XS_OS2_DLLname) { dXSARGS; @@ -3589,6 +4001,8 @@ Xs_OS2_init(pTHX) newXS("File::Copy::syscopy", XS_File__Copy_syscopy, file); newXS("Cwd::extLibpath", XS_Cwd_extLibpath, file); newXS("Cwd::extLibpath_set", XS_Cwd_extLibpath_set, file); + newXS("OS2::extLibpath", XS_Cwd_extLibpath, file); + newXS("OS2::extLibpath_set", XS_Cwd_extLibpath_set, file); } newXS("OS2::Error", XS_OS2_Error, file); newXS("OS2::Errors2Drive", XS_OS2_Errors2Drive, file); @@ -3620,6 +4034,9 @@ Xs_OS2_init(pTHX) newXSproto("OS2::_headerInfo", XS_OS2__headerInfo, file, "$$;$$"); newXSproto("OS2::libPath", XS_OS2_libPath, file, ""); newXSproto("OS2::Timer", XS_OS2_Timer, file, ""); + newXSproto("OS2::msCounter", XS_OS2_msCounter, file, ""); + newXSproto("OS2::ms_sleep", XS_OS2_ms_sleep, file, "$;$"); + newXSproto("OS2::_InfoTable", XS_OS2__InfoTable, file, ";$"); newXSproto("OS2::incrMaxFHandles", XS_OS2_incrMaxFHandles, file, ";$"); newXSproto("OS2::SysValues", XS_OS2_SysValues, file, ";$$"); newXSproto("OS2::SysValues_set", XS_OS2_SysValues_set, file, "$$;$"); @@ -3741,6 +4158,12 @@ force_init_emx_runtime(EXCEPTIONREGISTRATIONRECORD *preg, ULONG flags) oldstack = tib->tib_pstack; oldstackend = tib->tib_pstacklimit; + if ( (char*)&s < (char*)oldstack + 4*1024 + || (char *)oldstackend < (char*)oldstack + 52*1024 ) + early_error("It is a lunacy to try to run EMX Perl ", + "with less than 64K of stack;\r\n", + " at least with non-EMX starter...\r\n"); + /* Minimize the damage to the stack via reducing the size of argv. */ if (flags & FORCE_EMX_INIT_CONTRACT_ARGV) { pib->pib_pchcmd = "\0\0"; /* Need 3 concatenated strings */ @@ -3863,7 +4286,7 @@ extern ULONG __os_version(); /* See system.doc */ void check_emx_runtime(char **env, EXCEPTIONREGISTRATIONRECORD *preg) { - ULONG v_crt, v_emx, count = 0, rc, rc1, maybe_inited = 0; + ULONG v_crt, v_emx, count = 0, rc = NO_ERROR, rc1, maybe_inited = 0; static HMTX hmtx_emx_init = NULLHANDLE; static int emx_init_done = 0; @@ -4000,7 +4423,8 @@ Perl_OS2_init(char **env) void Perl_OS2_init3(char **env, void **preg, int flags) { - char *shell; + char *shell, *s; + ULONG rc; _uflags (_UF_SBRK_MODEL, _UF_SBRK_ARBITRARY); MALLOC_INIT; @@ -4009,15 +4433,20 @@ Perl_OS2_init3(char **env, void **preg, int flags) settmppath(); OS2_Perl_data.xs_init = &Xs_OS2_init; - if ( (shell = getenv("PERL_SH_DRIVE")) ) { + if (perl_sh_installed) { + int l = strlen(perl_sh_installed); + + New(1304, PL_sh_path, l + 1, char); + memcpy(PL_sh_path, perl_sh_installed, l + 1); + } else if ( (shell = getenv("PERL_SH_DRIVE")) ) { New(1304, PL_sh_path, strlen(SH_PATH) + 1, char); strcpy(PL_sh_path, SH_PATH); PL_sh_path[0] = shell[0]; } else if ( (shell = getenv("PERL_SH_DIR")) ) { int l = strlen(shell), i; - if (shell[l-1] == '/' || shell[l-1] == '\\') { + + while (l && (shell[l-1] == '/' || shell[l-1] == '\\')) l--; - } New(1304, PL_sh_path, l + 8, char); strncpy(PL_sh_path, shell, l); strcpy(PL_sh_path + l, "/sh.exe"); @@ -4032,6 +4461,29 @@ Perl_OS2_init3(char **env, void **preg, int flags) os2_mytype = my_type(); /* Do it before morphing. Needed? */ os2_mytype_ini = os2_mytype; Perl_os2_initial_mode = -1; /* Uninit */ + + s = getenv("PERL_BEGINLIBPATH"); + if (s) + rc = fill_extLibpath(0, s, NULL, 1, "PERL_BEGINLIBPATH"); + else + rc = fill_extLibpath(0, getenv("PERL_PRE_BEGINLIBPATH"), getenv("PERL_POST_BEGINLIBPATH"), 0, "PERL_(PRE/POST)_BEGINLIBPATH"); + if (!rc) { + s = getenv("PERL_ENDLIBPATH"); + if (s) + rc = fill_extLibpath(1, s, NULL, 1, "PERL_ENDLIBPATH"); + else + rc = fill_extLibpath(1, getenv("PERL_PRE_ENDLIBPATH"), getenv("PERL_POST_ENDLIBPATH"), 0, "PERL_(PRE/POST)_ENDLIBPATH"); + } + if (rc) { + char buf[1024]; + + snprintf(buf, sizeof buf, "Error setting BEGIN/ENDLIBPATH: %s\n", + os2error(rc)); + DosWrite(2, buf, strlen(buf), &rc); + exit(2); + } + + _emxload_env("PERL_EMXLOAD_SECS"); /* Some DLLs reset FP flags on load. We may have been linked with them */ _control87(MCW_EM, MCW_EM); } @@ -4460,3 +4912,52 @@ int fork_with_resources() return rc; } +/* APIRET APIENTRY DosGetInfoSeg(PSEL pselGlobal, PSEL pselLocal); */ + +ULONG _THUNK_FUNCTION(Dos16GetInfoSeg)(USHORT *pGlobal, USHORT *pLocal); + +APIRET APIENTRY +myDosGetInfoSeg(PGINFOSEG *pGlobal, PLINFOSEG *pLocal) +{ + APIRET rc; + USHORT gSel, lSel; /* Will not cross 64K boundary */ + + rc = ((USHORT) + (_THUNK_PROLOG (4+4); + _THUNK_FLAT (&gSel); + _THUNK_FLAT (&lSel); + _THUNK_CALL (Dos16GetInfoSeg))); + if (rc) + return rc; + *pGlobal = MAKEPGINFOSEG(gSel); + *pLocal = MAKEPLINFOSEG(lSel); + return rc; +} + +static void +GetInfoTables(void) +{ + ULONG rc = 0; + + MUTEX_LOCK(&perlos2_state_mutex); + if (!gTable) + rc = myDosGetInfoSeg(&gTable, &lTable); + MUTEX_UNLOCK(&perlos2_state_mutex); + os2cp_croak(rc, "Dos16GetInfoSeg"); +} + +ULONG +msCounter(void) +{ /* XXXX Is not lTable thread-specific? */ + if (!gTable) + GetInfoTables(); + return gTable->SIS_MsCount; +} + +ULONG +InfoTable(int local) +{ + if (!gTable) + GetInfoTables(); + return local ? (ULONG)lTable : (ULONG)gTable; +} diff --git a/os2/os2ish.h b/os2/os2ish.h index accba2a0c9..b3b3ed0c14 100644 --- a/os2/os2ish.h +++ b/os2/os2ish.h @@ -318,6 +318,11 @@ void my_setpwent (void); void my_endpwent (void); char *gcvt_os2(double value, int digits, char *buffer); +extern int async_mssleep(unsigned long ms, int switch_priority); +extern unsigned long msCounter(void); +extern unsigned long InfoTable(int local); +extern unsigned long find_myself(void); + #define MAX_SLEEP (((1<30) / (1000/4))-1) /* 1<32 msec */ static __inline__ unsigned @@ -358,7 +363,7 @@ struct passwd *my_getpwnam (__const__ char *); #define strtoll _strtoll #define strtoull _strtoull -#define usleep(usec) ((void)_sleep2(((usec)+500)/1000)) +#define usleep(usec) ((void)async_mssleep(((usec)+500)/1000, 500)) /* @@ -749,6 +754,21 @@ enum entries_ordinals { void ResetWinError(void); void CroakWinError(int die, char *name); +enum Perlos2_handler { + Perlos2_handler_mangle = 1, + Perlos2_handler_perl_sh, + Perlos2_handler_perllib_from, + Perlos2_handler_perllib_to, +}; +enum dir_subst_e { + dir_subst_fatal = 1, + dir_subst_pathlike = 2 +}; + +extern int Perl_OS2_handler_install(void *handler, enum Perlos2_handler how); +extern char *dir_subst(char *s, unsigned int l, char *b, unsigned int bl, enum dir_subst_e flags, char *msg); +extern unsigned long fill_extLibpath(int type, char *pre, char *post, int replace, char *msg); + #define PERLLIB_MANGLE(s, n) perllib_mangle((s), (n)) char *perllib_mangle(char *, unsigned int); @@ -759,7 +779,7 @@ static __inline__ int my_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) { if (nfds == 0 && timeout && (_emx_env & 0x200)) { - if (DosSleep(1000 * timeout->tv_sec + (timeout->tv_usec + 500)/1000) == 0) + if (async_mssleep(1000 * timeout->tv_sec + (timeout->tv_usec + 500)/1000, 500)) return 0; errno = EINTR; return -1; @@ -782,6 +802,18 @@ int getpriority(int which /* ignored */, int pid); void croak_with_os2error(char *s) __attribute__((noreturn)); +/* void return value */ +#define os2cp_croak(rc,msg) (CheckOSError(rc) && (croak_with_os2error(msg),0)) + +/* propagates rc */ +#define os2win_croak(rc,msg) \ + SaveCroakWinError((expr), 1 /* die */, /* no prefix */, (msg)) + +/* propagates rc; use with functions which may return 0 on success */ +#define os2win_croak_0OK(rc,msg) \ + SaveCroakWinError((ResetWinError, (expr)), \ + 1 /* die */, /* no prefix */, (msg)) + #ifdef PERL_CORE int os2_do_spawn(pTHX_ char *cmd); int os2_do_aspawn(pTHX_ SV *really, SV **vmark, SV **vsp); @@ -851,6 +883,192 @@ int os2_do_aspawn(pTHX_ SV *really, SV **vmark, SV **vsp); #endif +/* ************************************************* */ +#ifndef MAKEPLINFOSEG + +/* From $DDK\base32\rel\os2c\include\base\os2\16bit\infoseg.h + typedefs */ + +/* + * The structure below defines the content and organization of the system + * information segment (InfoSeg). The actual table is statically defined in + * SDATA.ASM. Ring 0, read/write access is obtained by the clock device + * driver using the DevHlp GetDOSVar function. (GetDOSVar returns a ring 0, + * read-only selector to all other requestors.) + * + * In order to prevent an errant process from destroying the infoseg, two + * identical global infosegs are maintained. One is in the tiled shared + * arena and is accessible in user mode (and therefore can potentially be + * overwritten from ring 2), and the other is in the system arena and is + * accessible only in kernel mode. All kernel code (except the clock driver) + * is responsible for updating BOTH copies of the infoseg. The copy kept + * in the system arena is addressable as DOSGROUP:SISData, and the copy + * in the shared arena is addressable via a system arena alias. 16:16 and + * 0:32 pointers to the alias are stored in _Sis2. + */ + +typedef struct InfoSegGDT { + +/* Time (offset 0x00) */ + +unsigned long SIS_BigTime; /* Time from 1-1-1970 in seconds */ +unsigned long SIS_MsCount; /* Freerunning milliseconds counter */ +unsigned char SIS_HrsTime; /* Hours */ +unsigned char SIS_MinTime; /* Minutes */ +unsigned char SIS_SecTime; /* Seconds */ +unsigned char SIS_HunTime; /* Hundredths of seconds */ +unsigned short SIS_TimeZone; /* Timezone in min from GMT (Set to EST) */ +unsigned short SIS_ClkIntrvl; /* Timer interval (units=0.0001 secs) */ + +/* Date (offset 0x10) */ + +unsigned char SIS_DayDate; /* Day-of-month (1-31) */ +unsigned char SIS_MonDate; /* Month (1-12) */ +unsigned short SIS_YrsDate; /* Year (>= 1980) */ +unsigned char SIS_DOWDate; /* Day-of-week (1-1-80 = Tues = 3) */ + +/* Version (offset 0x15) */ + +unsigned char SIS_VerMajor; /* Major version number */ +unsigned char SIS_VerMinor; /* Minor version number */ +unsigned char SIS_RevLettr; /* Revision letter */ + +/* System Status (offset 0x18) */ + +unsigned char SIS_CurScrnGrp; /* Fgnd screen group # */ +unsigned char SIS_MaxScrnGrp; /* Maximum number of screen groups */ +unsigned char SIS_HugeShfCnt; /* Shift count for huge segments */ +unsigned char SIS_ProtMdOnly; /* Protect-mode-only indicator */ +unsigned short SIS_FgndPID; /* Foreground process ID */ + +/* Scheduler Parms (offset 0x1E) */ + +unsigned char SIS_Dynamic; /* Dynamic variation flag (1=enabled) */ +unsigned char SIS_MaxWait; /* Maxwait (seconds) */ +unsigned short SIS_MinSlice; /* Minimum timeslice (milliseconds) */ +unsigned short SIS_MaxSlice; /* Maximum timeslice (milliseconds) */ + +/* Boot Drive (offset 0x24) */ + +unsigned short SIS_BootDrv; /* Drive from which system was booted */ + +/* RAS Major Event Code Table (offset 0x26) */ + +unsigned char SIS_mec_table[32]; /* Table of RAS Major Event Codes (MECs) */ + +/* Additional Session Data (offset 0x46) */ + +unsigned char SIS_MaxVioWinSG; /* Max. no. of VIO windowable SG's */ +unsigned char SIS_MaxPresMgrSG; /* Max. no. of Presentation Manager SG's */ + +/* Error logging Information (offset 0x48) */ + +unsigned short SIS_SysLog; /* Error Logging Status */ + +/* Additional RAS Information (offset 0x4A) */ + +unsigned short SIS_MMIOBase; /* Memory mapped I/O selector */ +unsigned long SIS_MMIOAddr; /* Memory mapped I/O address */ + +/* Additional 2.0 Data (offset 0x50) */ + +unsigned char SIS_MaxVDMs; /* Max. no. of Virtual DOS machines */ +unsigned char SIS_Reserved; + +unsigned char SIS_perf_mec_table[32]; /* varga 6/5/97 Table of Perfomance Major Event Codes (MECS) varga*/ +} GINFOSEG, *PGINFOSEG; + +#define SIS_LEN sizeof(struct InfoSegGDT) + +/* + * InfoSeg LDT Data Segment Structure + * + * The structure below defines the content and organization of the system + * information in a special per-process segment to be accessible by the + * process through the LDT (read-only). + * + * As in the global infoseg, two copies of the current processes local + * infoseg exist, one accessible in both user and kernel mode, the other + * only in kernel mode. Kernel code is responsible for updating BOTH copies. + * Pointers to the local infoseg copy are stored in _Lis2. + * + * Note that only the currently running process has an extra copy of the + * local infoseg. The copy is done at context switch time. + */ + +typedef struct InfoSegLDT { +unsigned short LIS_CurProcID; /* Current process ID */ +unsigned short LIS_ParProcID; /* Process ID of parent */ +unsigned short LIS_CurThrdPri; /* Current thread priority */ +unsigned short LIS_CurThrdID; /* Current thread ID */ +unsigned short LIS_CurScrnGrp; /* Screengroup */ +unsigned char LIS_ProcStatus; /* Process status bits */ +unsigned char LIS_fillbyte1; /* filler byte */ +unsigned short LIS_Fgnd; /* Current process is in foreground */ +unsigned char LIS_ProcType; /* Current process type */ +unsigned char LIS_fillbyte2; /* filler byte */ + +unsigned short LIS_AX; /* @@V1 Environment selector */ +unsigned short LIS_BX; /* @@V1 Offset of command line start */ +unsigned short LIS_CX; /* @@V1 Length of Data Segment */ +unsigned short LIS_DX; /* @@V1 STACKSIZE from the .EXE file */ +unsigned short LIS_SI; /* @@V1 HEAPSIZE from the .EXE file */ +unsigned short LIS_DI; /* @@V1 Module handle of the application */ +unsigned short LIS_DS; /* @@V1 Data Segment Handle of application */ + +unsigned short LIS_PackSel; /* First tiled selector in this EXE */ +unsigned short LIS_PackShrSel; /* First selector above shared arena */ +unsigned short LIS_PackPckSel; /* First selector above packed arena */ +/* #ifdef SMP */ +unsigned long LIS_pTIB; /* Pointer to TIB */ +unsigned long LIS_pPIB; /* Pointer to PIB */ +/* #endif */ +} LINFOSEG, *PLINFOSEG; + +#define LIS_LEN sizeof(struct InfoSegLDT) + + +/* + * Process Type codes + * + * These are the definitons for the codes stored + * in the LIS_ProcType field in the local infoseg. + */ + +#define LIS_PT_FULLSCRN 0 /* Full screen app. */ +#define LIS_PT_REALMODE 1 /* Real mode process */ +#define LIS_PT_VIOWIN 2 /* VIO windowable app. */ +#define LIS_PT_PRESMGR 3 /* Presentation Manager app. */ +#define LIS_PT_DETACHED 4 /* Detached app. */ + + +/* + * + * Process Status Bit Definitions + * + */ + +#define LIS_PS_EXITLIST 0x01 /* In exitlist handler */ + + +/* + * Flags equates for the Global Info Segment + * SIS_SysLog WORD in Global Info Segment + * + * xxxx xxxx xxxx xxx0 Error Logging Disabled + * xxxx xxxx xxxx xxx1 Error Logging Enabled + * + * xxxx xxxx xxxx xx0x Error Logging not available + * xxxx xxxx xxxx xx1x Error Logging available + */ + +#define LF_LOGENABLE 0x0001 /* Logging enabled */ +#define LF_LOGAVAILABLE 0x0002 /* Logging available */ + +#define MAKEPGINFOSEG(sel) ((PGINFOSEG)MAKEP(sel, 0)) +#define MAKEPLINFOSEG(sel) ((PLINFOSEG)MAKEP(sel, 0)) + +#endif /* ndef(MAKEPLINFOSEG) */ + /* ************************************************************ */ #define Dos32QuerySysState DosQuerySysState #define QuerySysState(flags, pid, buf, bufsz) \ diff --git a/os2/perl2cmd.pl b/os2/perl2cmd.pl index 4db40a0a31..07529ad8e8 100644 --- a/os2/perl2cmd.pl +++ b/os2/perl2cmd.pl @@ -2,6 +2,7 @@ # Note that we cannot put hashbang to be extproc to make Configure work. use Config; +use File::Compare; $dir = shift; $dir =~ s|/|\\|g ; @@ -26,9 +27,11 @@ foreach $file (<$idir/*>) { $base =~ s|\.pl$||; #$file =~ s|/|\\|g ; warn "Clashing output name for $file, skipping" if $seen{$base}++; - print "Processing $file => $dir\\$base.cmd\n"; + my $new = (-f "$dir/$base.cmd" ? '' : ' (new file)'); + print "Processing $file => $dir/$base.cmd$new\n"; + my $ext = ($new ? '.cmd' : '.tcm'); open IN, '<', $file or warn, next; - open OUT, '>', "$dir/$base.cmd" or warn, next; + open OUT, '>', "$dir/$base$ext" or warn, next; my $firstline = <IN>; my $flags = ''; $flags = $2 if $firstline =~ /^#!\s*(\S+)\s+-([^#]+?)\s*(#|$)/; @@ -36,5 +39,16 @@ foreach $file (<$idir/*>) { print OUT $_ while <IN>; close IN or warn, next; close OUT or warn, next; + chmod 0444, "$dir/$base$ext"; + next if $new; + if (compare "$dir/$base$ext", "$dir/$base.cmd") { # different + chmod 0666, "$dir/$base.cmd"; + unlink "$dir/$base.cmd"; + rename "$dir/$base$ext", "$dir/$base.cmd"; + } else { + chmod 0666, "$dir/$base$ext"; + unlink "$dir/$base$ext"; + print "...unchanged...\n"; + } } |