OS/2 update
Ilya Zakharevich [Thu, 18 Dec 2003 14:10:29 +0000 (06:10 -0800)]
Message-ID: <20031218221029.GA7898@math.berkeley.edu>

p4raw-id: //depot/perl@22032

README.os2
hints/os2.sh
installperl
lib/ExtUtils/MM_Unix.pm
makedef.pl
os2/Changes
os2/OS2/REXX/DLL/Changes
os2/OS2/REXX/DLL/DLL.pm
os2/os2.c
os2/os2ish.h
os2/perl2cmd.pl

index bb1adb1..05c088a 100644 (file)
@@ -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
 
index a3fc0b6..8c8ef21 100644 (file)
@@ -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"
index 5be24b9..4a3c07d 100755 (executable)
@@ -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';
index 6e48d6c..a1c21d2 100644 (file)
@@ -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";
 
index 3db62ab..033f639 100644 (file)
@@ -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
 }
index bcd970d..3bd33a5 100644 (file)
@@ -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).
index 874f7fa..e2c656d 100644 (file)
@@ -1,2 +1,4 @@
 0.01:
        Split out of OS2::REXX
+0.02:
+       New methods libPath_find(), has_f32(), handle() and fullname().
index 537a210..5d8a24e 100644 (file)
@@ -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
index e8e10d9..776031d 100644 (file)
--- a/os2/os2.c
+++ b/os2/os2.c
@@ -12,6 +12,7 @@
 #include <os2.h>
 #include "dlfcn.h"
 #include <emx/syscalls.h>
+#include <sys/emxload.h>
 
 #include <sys/uflags.h>
 
 #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;
+}
index accba2a..b3b3ed0 100644 (file)
@@ -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) \
index 4db40a0..07529ad 100644 (file)
@@ -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";
+  }
 }