This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
pp_rv2av and pp_rv2hv have a lot of common code, so it's certainly a
[perl5.git] / os2 / os2.c
index d5c3e2e..ff1c7bb 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 *);
@@ -344,7 +360,7 @@ pthread_startit(void *arg1)
            Renew(thread_join_data, thread_join_count, thread_join_t);
            Zero(thread_join_data + oc, thread_join_count - oc, thread_join_t);
        } else {
-           Newz(1323, thread_join_data, thread_join_count, thread_join_t);
+           Newxz(thread_join_data, thread_join_count, thread_join_t);
        }
     }
     if (thread_join_data[tid].state != pthreads_st_none) {
@@ -474,7 +490,7 @@ os2_cond_wait(perl_cond *c, perl_mutex *m)
     int rc;
     STRLEN n_a;
     if ((rc = DosResetEventSem(*c,&n_a)) && (rc != ERROR_ALREADY_RESET))
-       Perl_rc = rc, croak_with_os2error("panic: COND_WAIT-reset");
+       Perl_rc = CheckOSError(rc), croak_with_os2error("panic: COND_WAIT-reset");
     if (m) MUTEX_UNLOCK(m);                                    
     if (CheckOSError(DosWaitEventSem(*c,SEM_INDEFINITE_WAIT))
        && (rc != ERROR_INTERRUPT))
@@ -620,6 +636,8 @@ static const struct {
   {&pmwin_handle, NULL, 780},          /* WinLoadPointer */
   {&pmwin_handle, NULL, 828},          /* WinQuerySysPointer */
   {&doscalls_handle, NULL, 417},       /* DosReplaceModule */
+  {&doscalls_handle, NULL, 976},       /* DosPerfSysCall */
+  {&rexxapi_handle, "RexxRegisterSubcomExe", 0},
 };
 
 HMODULE
@@ -759,17 +777,19 @@ get_sysinfo(ULONG pid, ULONG flags)
     ULONG rc, buf_len = QSS_INI_BUFFER;
     PQTOPLEVEL psi;
 
-    if (!pidtid_lookup) {
-       pidtid_lookup = 1;
-       *(PFN*)&pDosVerifyPidTid = loadByOrdinal(ORD_DosVerifyPidTid, 0);
-    }
-    if (pDosVerifyPidTid) {    /* Warp3 or later */
-       /* Up to some fixpak QuerySysState() kills the system if a non-existent
-          pid is used. */
-       if (CheckOSError(pDosVerifyPidTid(pid, 1)))
-           return 0;
+    if (pid) {
+       if (!pidtid_lookup) {
+           pidtid_lookup = 1;
+           *(PFN*)&pDosVerifyPidTid = loadByOrdinal(ORD_DosVerifyPidTid, 0);
+       }
+       if (pDosVerifyPidTid) { /* Warp3 or later */
+           /* Up to some fixpak QuerySysState() kills the system if a non-existent
+              pid is used. */
+           if (CheckOSError(pDosVerifyPidTid(pid, 1)))
+               return 0;
+        }
     }
-    New(1322, pbuffer, buf_len, char);
+    Newx(pbuffer, buf_len, char);
     /* QSS_PROCESS | QSS_MODULE | QSS_SEMAPHORES | QSS_SHARED */
     rc = QuerySysState(flags, pid, pbuffer, buf_len);
     while (rc == ERROR_BUFFER_OVERFLOW) {
@@ -782,7 +802,7 @@ get_sysinfo(ULONG pid, ULONG flags)
        return 0;
     }
     psi = (PQTOPLEVEL)pbuffer;
-    if (psi && pid && pid != psi->procdata->pid) {
+    if (psi && pid && psi->procdata && pid != psi->procdata->pid) {
       Safefree(psi);
       Perl_croak_nocontext("panic: wrong pid in sysinfo");
     }
@@ -962,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", };
@@ -1127,7 +1147,7 @@ do_spawn_ve(pTHX_ SV *really, U32 flag, U32 execf, char *inicmd, U32 addflag)
                           does not append ".exe", so we could have
                           reached this place). */
                        sv_catpv(scrsv, ".exe");
-                       scr = SvPV(scrsv, n_a); /* Reload */
+                       PL_Argv[0] = scr = SvPV(scrsv, n_a);    /* Reload */
                        if (PerlLIO_stat(scr,&PL_statbuf) >= 0
                            && !S_ISDIR(PL_statbuf.st_mode)) {  /* Found */
                                real_name = scr;
@@ -1354,7 +1374,7 @@ do_spawn3(pTHX_ char *cmd, int execf, int flag)
     if (strnEQ(cmd,"/bin/sh",7) && isSPACE(cmd[7])) {
        STRLEN l = strlen(PL_sh_path);
        
-       New(1302, news, strlen(cmd) - 7 + l + 1, char);
+       Newx(news, strlen(cmd) - 7 + l + 1, char);
        strcpy(news, PL_sh_path);
        strcpy(news + l, cmd + 7);
        cmd = news;
@@ -1427,7 +1447,7 @@ do_spawn3(pTHX_ char *cmd, int execf, int flag)
     }
 
     /* cmd="a" may lead to "sh", "-c", "\"$@\"", "a", "a.cmd", NULL */
-    New(1303,PL_Argv, (s - cmd + 11) / 2, char*);
+    Newx(PL_Argv, (s - cmd + 11) / 2, char*);
     PL_Cmd = savepvn(cmd, s-cmd);
     a = PL_Argv;
     for (s = PL_Cmd; *s;) {
@@ -1449,42 +1469,47 @@ do_spawn3(pTHX_ char *cmd, int execf, int flag)
     return rc;
 }
 
+#define ASPAWN_WAIT    0
+#define ASPAWN_EXEC    1
+#define ASPAWN_NOWAIT  2
+
 /* Array spawn/exec.  */
 int
-os2_aspawn4(pTHX_ SV *really, register SV **vmark, register SV **vsp, int execing)
+os2_aspawn_4(pTHX_ SV *really, register SV **args, I32 cnt, int execing)
 {
-    register SV **mark = (SV **)vmark;
-    register SV **sp = (SV **)vsp;
+    register SV **argp = (SV **)args;
+    register SV **last = argp + cnt;
     register char **a;
     int rc;
     int flag = P_WAIT, flag_set = 0;
     STRLEN n_a;
 
-    if (sp > mark) {
-       New(1301,PL_Argv, sp - mark + 3, char*);
+    if (cnt) {
+       Newx(PL_Argv, cnt + 3, char*); /* 3 extra to expand #! */
        a = PL_Argv;
 
-       if (mark < sp && SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
-               ++mark;
-               flag = SvIVx(*mark);
-               flag_set = 1;
-
-       }
+       if (cnt > 1 && SvNIOKp(*argp) && !SvPOKp(*argp)) {
+           flag = SvIVx(*argp);
+           flag_set = 1;
+       } else
+           --argp;
 
-       while (++mark <= sp) {
-           if (*mark)
-               *a++ = SvPVx(*mark, n_a);
+       while (++argp < last) {
+           if (*argp)
+               *a++ = SvPVx(*argp, n_a);
            else
                *a++ = "";
        }
        *a = Nullch;
 
        if ( flag_set && (a == PL_Argv + 1)
-            && !really && !execing ) {                 /* One arg? */
+            && !really && execing == ASPAWN_WAIT ) {           /* One arg? */
            rc = do_spawn3(aTHX_ a[-1], EXECF_SPAWN_BYFLAG, flag);
-       } else
-           rc = do_spawn_ve(aTHX_ really, flag,
-                            (execing ? EXECF_EXEC : EXECF_SPAWN), NULL, 0);
+       } else {
+           const int execf[3] = {EXECF_SPAWN, EXECF_EXEC, EXECF_SPAWN_NOWAIT};
+           
+           rc = do_spawn_ve(aTHX_ really, flag, execf[execing], NULL, 0);
+       }
     } else
        rc = -1;
     do_execfree();
@@ -1495,14 +1520,14 @@ os2_aspawn4(pTHX_ SV *really, register SV **vmark, register SV **vsp, int execin
 int
 os2_do_aspawn(pTHX_ SV *really, register SV **vmark, register SV **vsp)
 {
-    return os2_aspawn4(aTHX_ really, vmark, vsp, 0);
+    return os2_aspawn_4(aTHX_ really, vmark + 1, vsp - vmark, ASPAWN_WAIT);
 }
 
 /* Array exec.  */
 bool
 Perl_do_aexec(pTHX_ SV* really, SV** vmark, SV** vsp)
 {
-    return os2_aspawn4(aTHX_ really, vmark, vsp, 1);
+    return os2_aspawn_4(aTHX_ really, vmark + 1, vsp - vmark, ASPAWN_EXEC);
 }
 
 int
@@ -1518,7 +1543,7 @@ do_spawn_nowait(pTHX_ char *cmd)
 }
 
 bool
-Perl_do_exec(pTHX_ char *cmd)
+Perl_do_exec(pTHX_ const char *cmd)
 {
     do_spawn3(aTHX_ cmd, EXECF_EXEC, 0);
     return FALSE;
@@ -1531,7 +1556,7 @@ os2exec(pTHX_ char *cmd)
 }
 
 PerlIO *
-my_syspopen(pTHX_ char *cmd, char *mode)
+my_syspopen4(pTHX_ char *cmd, char *mode, I32 cnt, SV** args)
 {
 #ifndef USE_POPEN
     int p[2];
@@ -1579,7 +1604,10 @@ my_syspopen(pTHX_ char *cmd, char *mode)
     fcntl(p[this], F_SETFD, FD_CLOEXEC);
     if (newfd != -1)
        fcntl(newfd, F_SETFD, FD_CLOEXEC);
-    pid = do_spawn_nowait(aTHX_ cmd);
+    if (cnt) { /* Args: "Real cmd", before first arg, the last, execing */
+       pid = os2_aspawn_4(aTHX_ Nullsv, args, cnt, ASPAWN_NOWAIT);
+    } else
+       pid = do_spawn_nowait(aTHX_ cmd);
     if (newfd == -1)
        close(*mode == 'r');            /* It was closed initially */
     else if (newfd != (*mode == 'r')) {        /* Probably this check is not needed */
@@ -1610,6 +1638,9 @@ my_syspopen(pTHX_ char *cmd, char *mode)
     PerlIO *res;
     SV *sv;
 
+    if (cnt)
+       Perl_croak(aTHX_ "List form of piped open not implemented");
+
 #  ifdef TRYSHELL
     res = popen(cmd, mode);
 #  else
@@ -1628,6 +1659,12 @@ my_syspopen(pTHX_ char *cmd, char *mode)
 
 }
 
+PerlIO *
+my_syspopen(pTHX_ char *cmd, char *mode)
+{
+    return my_syspopen4(aTHX_ cmd, mode, 0, NULL);
+}
+
 /******************************************************************/
 
 #ifndef HAS_FORK
@@ -1848,9 +1885,114 @@ XS(XS_OS2_replaceModule)
        if (!replaceModule(target, source, backup))
            croak_with_os2error("replaceModule() error");
     }
-    XSRETURN_EMPTY;
+    XSRETURN_YES;
 }
 
+/* APIRET APIENTRY DosPerfSysCall(ULONG ulCommand, ULONG ulParm1,
+                                  ULONG ulParm2, ULONG ulParm3); */
+
+DeclOSFuncByORD(ULONG,perfSysCall,ORD_DosPerfSysCall,
+               (ULONG ulCommand, ULONG ulParm1, ULONG ulParm2, ULONG ulParm3),
+               (ulCommand, ulParm1, ulParm2, ulParm3))
+
+#ifndef CMD_KI_RDCNT
+#  define CMD_KI_RDCNT 0x63
+#endif
+#ifndef CMD_KI_GETQTY
+#  define CMD_KI_GETQTY 0x41
+#endif
+#ifndef QSV_NUMPROCESSORS
+#  define QSV_NUMPROCESSORS         26
+#endif
+
+typedef unsigned long long myCPUUTIL[4];       /* time/idle/busy/intr */
+
+/*
+NO_OUTPUT ULONG
+perfSysCall(ULONG ulCommand, ULONG ulParm1, ULONG ulParm2, ULONG ulParm3)
+    PREINIT:
+       ULONG rc;
+    POSTCALL:
+       if (!RETVAL)
+           croak_with_os2error("perfSysCall() error");
+ */
+
+static int
+numprocessors(void)
+{
+    ULONG res;
+
+    if (DosQuerySysInfo(QSV_NUMPROCESSORS, QSV_NUMPROCESSORS, (PVOID)&res, sizeof(res)))
+       return 1;                       /* Old system? */
+    return res;
+}
+
+XS(XS_OS2_perfSysCall); /* prototype to pass -Wmissing-prototypes */
+XS(XS_OS2_perfSysCall)
+{
+    dXSARGS;
+    if (items < 0 || items > 4)
+       Perl_croak(aTHX_ "Usage: OS2::perfSysCall(ulCommand = CMD_KI_RDCNT, ulParm1= 0, ulParm2= 0, ulParm3= 0)");
+    SP -= items;
+    {
+       dXSTARG;
+       ULONG RETVAL, ulCommand, ulParm1, ulParm2, ulParm3, res;
+       myCPUUTIL u[64];
+       int total = 0, tot2 = 0;
+
+       if (items < 1)
+           ulCommand = CMD_KI_RDCNT;
+       else {
+           ulCommand = (ULONG)SvUV(ST(0));
+       }
+
+       if (items < 2) {
+           total = (ulCommand == CMD_KI_RDCNT ? numprocessors() : 0);
+           ulParm1 = (total ? (ULONG)u : 0);
+
+           if (total > C_ARRAY_LENGTH(u))
+               croak("Unexpected number of processors: %d", total);
+       } else {
+           ulParm1 = (ULONG)SvUV(ST(1));
+       }
+
+       if (items < 3) {
+           tot2 = (ulCommand == CMD_KI_GETQTY);
+           ulParm2 = (tot2 ? (ULONG)&res : 0);
+       } else {
+           ulParm2 = (ULONG)SvUV(ST(2));
+       }
+
+       if (items < 4)
+           ulParm3 = 0;
+       else {
+           ulParm3 = (ULONG)SvUV(ST(3));
+       }
+
+       RETVAL = perfSysCall(ulCommand, ulParm1, ulParm2, ulParm3);
+       if (!RETVAL)
+           croak_with_os2error("perfSysCall() error");
+       XSprePUSH;
+       if (total) {
+           int i,j;
+
+           if (GIMME_V != G_ARRAY) {
+               PUSHn(u[0][0]);         /* Total ticks on the first processor */
+               XSRETURN(1);
+           }
+           EXTEND(SP, 4*total);
+           for (i=0; i < total; i++)
+               for (j=0; j < 4; j++)
+                   PUSHs(sv_2mortal(newSVnv(u[i][j])));
+           XSRETURN(4*total);
+       }
+       if (tot2) {
+           PUSHu(res);
+           XSRETURN(1);
+       }
+    }
+    XSRETURN_EMPTY;
+}
 
 #define PERL_PATCHLEVEL_H_IMPLICIT     /* Do not init local_patches. */
 #include "patchlevel.h"
@@ -1964,6 +2106,21 @@ os2error(int rc)
            case PMERR_NOT_IN_A_PM_SESSION:
                name = "PMERR_NOT_IN_A_PM_SESSION";
                break;
+           case PMERR_INVALID_ATOM:
+               name = "PMERR_INVALID_ATOM";
+               break;
+           case PMERR_INVALID_HATOMTBL:
+               name = "PMERR_INVALID_HATOMTMB";
+               break;
+           case PMERR_INVALID_INTEGER_ATOM:
+               name = "PMERR_INVALID_INTEGER_ATOM";
+               break;
+           case PMERR_INVALID_ATOM_NAME:
+               name = "PMERR_INVALID_ATOM_NAME";
+               break;
+           case PMERR_ATOM_NAME_NOT_FOUND:
+               name = "PMERR_ATOM_NAME_NOT_FOUND";
+               break;
            }
            sprintf(s, "%s%s[No description found in OSO001.MSG]", 
                    name, (*name ? "=" : ""));
@@ -1993,34 +2150,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 == '\\')
@@ -2028,61 +2201,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;
 }
@@ -2287,6 +2637,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_YES;
+}
+
 ULONG (*pDosTmrQueryFreq) (PULONG);
 ULONG (*pDosTmrQueryTime) (unsigned long long *);
 
@@ -2318,6 +2767,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",
@@ -2420,20 +2900,35 @@ XS(XS_OS2_DevCap)
                                          - CAPS_FAMILY + 1,
                                        si)))
            rc1 = Perl_rc;
+       else {
+           EXTEND(SP,2*(CAPS_DEVICE_POLYSET_POINTS - CAPS_FAMILY + 1));
+           while (i < CAPS_DEVICE_POLYSET_POINTS - CAPS_FAMILY + 1) {
+               ST(j) = sv_newmortal();
+               sv_setpv(ST(j++), dc_fields[i]);
+               ST(j) = sv_newmortal();
+               sv_setiv(ST(j++), si[i]);
+               i++;
+           }
+           i = CAPS_DEVICE_POLYSET_POINTS + 1;
+           while (i < CAPS_DEVICE_POLYSET_POINTS + 11) { /* Just in case... */
+               LONG l;
+
+               if (CheckWinError(pDevQueryCaps(hScreenDC, i, 1, &l)))
+                   break;
+               EXTEND(SP, j + 2);
+               ST(j) = sv_newmortal();
+               sv_setiv(ST(j++), i);
+               ST(j) = sv_newmortal();
+               sv_setiv(ST(j++), l);
+               i++;
+           }       
+       }
        if (!items && CheckWinError(pDevCloseDC(hScreenDC)))
            Perl_warn_nocontext("DevCloseDC() failed: %s", os2error(Perl_rc));
        if (rc1)
            Perl_rc = rc1, croak_with_os2error("DevQueryCaps() failed");
-       EXTEND(SP,2*(CAPS_DEVICE_POLYSET_POINTS - CAPS_FAMILY + 1));
-       while (i < CAPS_DEVICE_POLYSET_POINTS - CAPS_FAMILY + 1) {
-           ST(j) = sv_newmortal();
-           sv_setpv(ST(j++), dc_fields[i]);
-           ST(j) = sv_newmortal();
-           sv_setiv(ST(j++), si[i]);
-           i++;
-       }
+       XSRETURN(j);
     }
-    XSRETURN(2 * (CAPS_DEVICE_POLYSET_POINTS - CAPS_FAMILY + 1));
 }
 
 LONG (*pWinQuerySysValue) (HWND hwndDesktop, LONG iSysValue);
@@ -2631,7 +3126,7 @@ XS(XS_OS2_SysValues_set)
        if (CheckWinError(pWinSetSysValue(hwnd, which, val)))
            croak_with_os2error("SysValues_set()");
     }
-    XSRETURN_EMPTY;
+    XSRETURN_YES;
 }
 
 #define QSV_MAX_WARP3                          QSV_MAX_COMP_LENGTH
@@ -2686,7 +3181,7 @@ XS(XS_OS2_SysInfo)
                                         (PVOID)si,
                                         sizeof(si))))
            croak_with_os2error("DosQuerySysInfo() failed");
-       while (last++ <= C_ARRAY_LENGTH(si)) {
+       while (++last <= C_ARRAY_LENGTH(si)) {
            if (CheckOSError(DosQuerySysInfo(last, last, /* One entry only */
                                             (PVOID)(si+last-1),
                                             sizeof(*si)))) {
@@ -2695,13 +3190,16 @@ XS(XS_OS2_SysInfo)
                break;
            }
        }
-       last--;
+       last--;                 /* Count of successfully processed offsets */
        EXTEND(SP,2*last);
        while (i < last) {
            ST(j) = sv_newmortal();
-           sv_setpv(ST(j++), si_fields[i]);
+           if (i < C_ARRAY_LENGTH(si_fields))
+               sv_setpv(ST(j++),  si_fields[i]);
+           else
+               sv_setiv(ST(j++),  i + 1);
            ST(j) = sv_newmortal();
-           sv_setiv(ST(j++), si[i]);
+           sv_setuv(ST(j++), si[i]);
            i++;
        }
        XSRETURN(2 * last);
@@ -2773,7 +3271,7 @@ XS(XS_OS2_Beep)
        if (CheckOSError(DosBeep(freq, ms)))
            croak_with_os2error("SysValues_set()");
     }
-    XSRETURN_EMPTY;
+    XSRETURN_YES;
 }
 
 
@@ -3112,11 +3610,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)
@@ -3126,23 +3626,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;
@@ -3151,9 +3663,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;
     }
@@ -3164,7 +3680,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);
@@ -3178,13 +3694,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,
@@ -3196,9 +3773,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)
 {
@@ -3244,9 +3818,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;
@@ -3400,7 +3971,7 @@ XS(XS_OS2_mytype_set)
     else
        Perl_croak(aTHX_ "Usage: OS2::mytype_set(type)");
     my_type_set(type);
-    XSRETURN_EMPTY;
+    XSRETURN_YES;
 }
 
 
@@ -3471,6 +4042,459 @@ XS(XS_OS2_incrMaxFHandles)              /* DosSetRelMaxFH */
     XSRETURN(1);
 }
 
+/* wait>0: force wait, wait<0: force nowait;
+   if restore, save/restore flags; otherwise flags are in oflags.
+
+   Returns 1 if connected, 0 if not (due to nowait); croaks on error. */
+static ULONG
+connectNPipe(ULONG hpipe, int wait, ULONG restore, ULONG oflags)
+{
+    ULONG ret = ERROR_INTERRUPT, rc, flags;
+
+    if (restore && wait)
+       os2cp_croak(DosQueryNPHState(hpipe, &oflags), "DosQueryNPHState()");
+    /* DosSetNPHState fails if more bits than NP_NOWAIT|NP_READMODE_MESSAGE */
+    oflags &= (NP_NOWAIT | NP_READMODE_MESSAGE);
+    flags = (oflags & ~NP_NOWAIT) | (wait > 0 ? NP_WAIT : NP_NOWAIT);
+    /* We know (o)flags unless wait == 0 && restore */
+    if (wait && (flags != oflags))
+       os2cp_croak(DosSetNPHState(hpipe, flags), "DosSetNPHState()");
+    while (ret == ERROR_INTERRUPT)
+       ret = DosConnectNPipe(hpipe);
+    (void)CheckOSError(ret);
+    if (restore && wait && (flags != oflags))
+       os2cp_croak(DosSetNPHState(hpipe, oflags), "DosSetNPHState() back");
+    /* We know flags unless wait == 0 && restore */
+    if ( ((wait || restore) ? (flags & NP_NOWAIT) : 1)
+        && (ret == ERROR_PIPE_NOT_CONNECTED) )
+       return 0;                       /* normal return value */
+    if (ret == NO_ERROR)
+       return 1;
+    croak_with_os2error("DosConnectNPipe()");
+}
+
+/* With a lot of manual editing:
+NO_OUTPUT ULONG
+DosCreateNPipe(PCSZ pszName, OUTLIST HPIPE hpipe, ULONG ulOpenMode, int connect = 1, int count = 1, ULONG ulInbufLength = 8192, ULONG ulOutbufLength = ulInbufLength, ULONG ulPipeMode = count | NP_NOWAIT | NP_TYPE_BYTE | NP_READMODE_BYTE, ULONG ulTimeout = 0)
+   PREINIT:
+       ULONG rc;
+   C_ARGS:
+       pszName, &hpipe, ulOpenMode, ulPipeMode, ulInbufLength, ulOutbufLength, ulTimeout
+   POSTCALL:
+       if (CheckOSError(RETVAL))
+           croak_with_os2error("OS2::mkpipe() error");
+*/
+XS(XS_OS2_pipe); /* prototype to pass -Wmissing-prototypes */
+XS(XS_OS2_pipe)
+{
+    dXSARGS;
+    if (items < 2 || items > 8)
+       Perl_croak(aTHX_ "Usage: OS2::pipe(pszName, ulOpenMode, connect= 1, count= 1, ulInbufLength= 8192, ulOutbufLength= ulInbufLength, ulPipeMode= count | NP_NOWAIT | NP_TYPE_BYTE | NP_READMODE_BYTE, ulTimeout= 0)");
+    {
+       ULONG   RETVAL;
+       PCSZ    pszName = ( SvOK(ST(0)) ? (PCSZ)SvPV(ST(0),PL_na) : NULL );
+       HPIPE   hpipe;
+       SV      *OpenMode = ST(1);
+       ULONG   ulOpenMode;
+       int     connect = 0, count, message_r = 0, message = 0, b = 0;
+       ULONG   ulInbufLength,  ulOutbufLength, ulPipeMode, ulTimeout, rc;
+       STRLEN  len;
+       char    *s, buf[10], *s1, *perltype = Nullch;
+       PerlIO  *perlio;
+       double  timeout;
+
+       if (!pszName || !*pszName)
+           Perl_croak(aTHX_ "OS2::pipe(): empty pipe name");
+       s = SvPV(OpenMode, len);
+       if (len == 4 && strEQ(s, "wait")) {     /* DosWaitNPipe() */
+           ULONG ms = 0xFFFFFFFF, ret = ERROR_INTERRUPT; /* Indefinite */
+
+           if (items == 3) {
+               timeout = (double)SvNV(ST(2));
+               ms = timeout * 1000;
+               if (timeout < 0)
+                   ms = 0xFFFFFFFF; /* Indefinite */
+               else if (timeout && !ms)
+                   ms = 1;
+           } else if (items > 3)
+               Perl_croak(aTHX_ "OS2::pipe(): too many arguments for wait-for-connect: %ld", (long)items);
+
+           while (ret == ERROR_INTERRUPT)
+               ret = DosWaitNPipe(pszName, ms);        /* XXXX Update ms? */
+           os2cp_croak(ret, "DosWaitNPipe()");
+           XSRETURN_YES;
+       }
+       if (len == 4 && strEQ(s, "call")) {     /* DosCallNPipe() */
+           ULONG ms = 0xFFFFFFFF, got; /* Indefinite */
+           STRLEN l;
+           char *s;
+           char buf[8192];
+           STRLEN ll = sizeof(buf);
+           char *b = buf;
+
+           if (items < 3 || items > 5)
+               Perl_croak(aTHX_ "usage: OS2::pipe(pszName, 'call', write [, timeout= 0xFFFFFFFF, buffsize = 8192])");
+           s = SvPV(ST(2), l);
+           if (items >= 4) {
+               timeout = (double)SvNV(ST(3));
+               ms = timeout * 1000;
+               if (timeout < 0)
+                   ms = 0xFFFFFFFF; /* Indefinite */
+               else if (timeout && !ms)
+                   ms = 1;
+           }
+           if (items >= 5) {
+               STRLEN lll = SvUV(ST(4));
+               SV *sv = NEWSV(914, lll);
+
+               sv_2mortal(sv);
+               ll = lll;
+               b = SvPVX(sv);
+           }       
+
+           os2cp_croak(DosCallNPipe(pszName, s, l, b, ll, &got, ms),
+                       "DosCallNPipe()");
+           XSRETURN_PVN(b, got);
+       }
+       s1 = buf;
+       if (len && len <= 3 && !(*s >= '0' && *s <= '9')) {
+           int r, w, R, W;
+
+           r = strchr(s, 'r') != 0;
+           w = strchr(s, 'w') != 0;
+           R = strchr(s, 'R') != 0;
+           W = strchr(s, 'W') != 0;
+           b = strchr(s, 'b') != 0;
+           if (r + w + R + W + b != len || (r && R) || (w && W))
+               Perl_croak(aTHX_ "OS2::pipe(): unknown OpenMode argument: `%s'", s);
+           if ((r || R) && (w || W))
+               ulOpenMode = NP_INHERIT | NP_NOWRITEBEHIND | NP_ACCESS_DUPLEX;
+           else if (r || R)
+               ulOpenMode = NP_INHERIT | NP_NOWRITEBEHIND | NP_ACCESS_INBOUND;
+           else
+               ulOpenMode = NP_INHERIT | NP_NOWRITEBEHIND | NP_ACCESS_OUTBOUND;
+           if (R)
+               message = message_r = 1;
+           if (W)
+               message = 1;
+           else if (w && R)
+               Perl_croak(aTHX_ "OS2::pipe(): can't have message read mode for non-message pipes");
+       } else
+           ulOpenMode = (ULONG)SvUV(OpenMode); /* ST(1) */
+
+       if ( (ulOpenMode & 0x3) == NP_ACCESS_DUPLEX
+            || (ulOpenMode & 0x3) == NP_ACCESS_INBOUND )
+           *s1++ = 'r';
+       if ( (ulOpenMode & 0x3) == NP_ACCESS_DUPLEX )
+           *s1++ = '+';
+       if ( (ulOpenMode & 0x3) == NP_ACCESS_OUTBOUND )
+           *s1++ = 'w';
+       if (b)
+           *s1++ = 'b';
+       *s1 = 0;
+       if ( (ulOpenMode & 0x3) == NP_ACCESS_DUPLEX )
+           perltype = "+<&";
+       else if ( (ulOpenMode & 0x3) == NP_ACCESS_OUTBOUND )
+           perltype = ">&";
+       else
+           perltype = "<&";
+
+       if (items < 3)
+           connect = -1;                       /* no wait */
+       else if (SvTRUE(ST(2))) {
+           s = SvPV(ST(2), len);
+           if (len == 6 && strEQ(s, "nowait"))
+               connect = -1;                   /* no wait */
+           else if (len == 4 && strEQ(s, "wait"))
+               connect = 1;                    /* wait */
+           else
+               Perl_croak(aTHX_ "OS2::pipe(): unknown connect argument: `%s'", s);
+       }
+
+       if (items < 4)
+           count = 1;
+       else
+           count = (int)SvIV(ST(3));
+
+       if (items < 5)
+           ulInbufLength = 8192;
+       else
+           ulInbufLength = (ULONG)SvUV(ST(4));
+
+       if (items < 6)
+           ulOutbufLength = ulInbufLength;
+       else
+           ulOutbufLength = (ULONG)SvUV(ST(5));
+
+       if (count < -1 || count == 0 || count >= 255)
+           Perl_croak(aTHX_ "OS2::pipe(): count should be -1 or between 1 and 254: %ld", (long)count);
+       if (count < 0 )
+           count = 255;                /* Unlimited */
+
+       ulPipeMode = count;
+       if (items < 7)
+           ulPipeMode |= (NP_WAIT 
+                          | (message ? NP_TYPE_MESSAGE : NP_TYPE_BYTE)
+                          | (message_r ? NP_READMODE_MESSAGE : NP_READMODE_BYTE));
+       else
+           ulPipeMode |= (ULONG)SvUV(ST(6));
+
+       if (items < 8)
+           timeout = 0;
+       else
+           timeout = (double)SvNV(ST(7));
+       ulTimeout = timeout * 1000;
+       if (timeout < 0)
+           ulTimeout = 0xFFFFFFFF; /* Indefinite */
+       else if (timeout && !ulTimeout)
+           ulTimeout = 1;
+
+       RETVAL = DosCreateNPipe(pszName, &hpipe, ulOpenMode, ulPipeMode, ulInbufLength, ulOutbufLength, ulTimeout);
+       if (CheckOSError(RETVAL))
+           croak_with_os2error("OS2::pipe(): DosCreateNPipe() error");
+
+       if (connect)
+           connectNPipe(hpipe, connect, 1, 0); /* XXXX wait, retval */
+       hpipe = __imphandle(hpipe);
+
+       perlio = PerlIO_fdopen(hpipe, buf);
+       ST(0) = sv_newmortal();
+       {
+           GV *gv = newGVgen("OS2::pipe");
+           if ( do_open(gv, perltype, strlen(perltype), FALSE, 0, 0, perlio) )
+               sv_setsv(ST(0), sv_bless(newRV((SV*)gv), gv_stashpv("IO::Handle",1)));
+           else
+               ST(0) = &PL_sv_undef;
+       }
+    }
+    XSRETURN(1);
+}
+
+XS(XS_OS2_pipeCntl); /* prototype to pass -Wmissing-prototypes */
+XS(XS_OS2_pipeCntl)
+{
+    dXSARGS;
+    if (items < 2 || items > 3)
+       Perl_croak(aTHX_ "Usage: OS2::pipeCntl(pipe, op [, wait])");
+    {
+       ULONG   rc;
+       PerlIO *perlio = IoIFP(sv_2io(ST(0)));
+       IV      fn = PerlIO_fileno(perlio);
+       HPIPE   hpipe = (HPIPE)fn;
+       STRLEN  len;
+       char    *s = SvPV(ST(1), len);
+       int     wait = 0, disconnect = 0, connect = 0, message = -1, query = 0;
+       int     peek = 0, state = 0, info = 0;
+
+       if (fn < 0)
+           Perl_croak(aTHX_ "OS2::pipeCntl(): not a pipe");    
+       if (items == 3)
+           wait = (SvTRUE(ST(2)) ? 1 : -1);
+
+       switch (len) {
+       case 4:
+           if (strEQ(s, "byte"))
+               message = 0;
+           else if (strEQ(s, "peek"))
+               peek = 1;
+           else if (strEQ(s, "info"))
+               info = 1;
+           else
+               goto unknown;
+           break;
+       case 5:
+           if (strEQ(s, "reset"))
+               disconnect = connect = 1;
+           else if (strEQ(s, "state"))
+               query = 1;
+           else
+               goto unknown;
+           break;
+       case 7:
+           if (strEQ(s, "connect"))
+               connect = 1;
+           else if (strEQ(s, "message"))
+               message = 1;
+           else
+               goto unknown;
+           break;
+       case 9:
+           if (!strEQ(s, "readstate"))
+               goto unknown;
+           state = 1;
+           break;
+       case 10:
+           if (!strEQ(s, "disconnect"))
+               goto unknown;
+           disconnect = 1;
+           break;
+       default:
+         unknown:
+           Perl_croak(aTHX_ "OS2::pipeCntl(): unknown argument: `%s'", s);
+           break;
+       }
+
+       if (items == 3 && !connect)
+           Perl_croak(aTHX_ "OS2::pipeCntl(): no wait argument for `%s'", s);
+
+       XSprePUSH;              /* Do not need arguments any more */
+       if (disconnect) {
+           os2cp_croak(DosDisConnectNPipe(hpipe), "OS2::pipeCntl(): DosDisConnectNPipe()");
+           PerlIO_clearerr(perlio);
+       }
+       if (connect) {
+           if (!connectNPipe(hpipe, wait , 1, 0))
+               XSRETURN_IV(-1);
+       }
+       if (query) {
+           ULONG flags;
+
+           os2cp_croak(DosQueryNPHState(hpipe, &flags), "DosQueryNPHState()");
+           XSRETURN_UV(flags);
+       }
+       if (peek || state || info) {
+           ULONG BytesRead, PipeState;
+           AVAILDATA BytesAvail;
+
+           os2cp_croak( DosPeekNPipe(hpipe, NULL, 0, &BytesRead, &BytesAvail,
+                                     &PipeState), "DosPeekNPipe() for state");
+           if (state) {
+               EXTEND(SP, 3);
+               PUSHs(newSVuv(PipeState));
+               /*   Bytes (available/in-message) */
+               PUSHs(newSViv(BytesAvail.cbpipe));
+               PUSHs(newSViv(BytesAvail.cbmessage));
+               XSRETURN(3);
+           } else if (info) {
+               /* L S S C C C/Z*
+                  ID of the (remote) computer
+                  buffers (out/in)
+                  instances (max/actual)
+                */
+               struct pipe_info_t {
+                   ULONG id;                   /* char id[4]; */
+                   PIPEINFO pInfo;
+                   char buf[512];
+               } b;
+               int size;
+
+               os2cp_croak( DosQueryNPipeInfo(hpipe, 1, &b.pInfo, sizeof(b) - STRUCT_OFFSET(struct pipe_info_t, pInfo)),
+                            "DosQueryNPipeInfo(1)");
+               os2cp_croak( DosQueryNPipeInfo(hpipe, 2, &b.id, sizeof(b.id)),
+                            "DosQueryNPipeInfo(2)");
+               size = b.pInfo.cbName;
+               /* Trailing 0 is included in cbName - undocumented; so
+                  one should always extract with Z* */
+               if (size)               /* name length 254 or less */
+                   size--;
+               else
+                   size = strlen(b.pInfo.szName);
+               EXTEND(SP, 6);
+               PUSHs(newSVpvn(b.pInfo.szName, size));
+               PUSHs(newSVuv(b.id));
+               PUSHs(newSViv(b.pInfo.cbOut));
+               PUSHs(newSViv(b.pInfo.cbIn));
+               PUSHs(newSViv(b.pInfo.cbMaxInst));
+               PUSHs(newSViv(b.pInfo.cbCurInst));
+               XSRETURN(6);
+           } else if (BytesAvail.cbpipe == 0) {
+               XSRETURN_NO;
+           } else {
+               SV *tmp = NEWSV(914, BytesAvail.cbpipe);
+               char *s = SvPVX(tmp);
+
+               sv_2mortal(tmp);
+               os2cp_croak( DosPeekNPipe(hpipe, s, BytesAvail.cbpipe, &BytesRead,
+                                         &BytesAvail, &PipeState), "DosPeekNPipe()");
+               SvCUR_set(tmp, BytesRead);
+               *SvEND(tmp) = 0;
+               SvPOK_on(tmp);
+               XSprePUSH; PUSHs(tmp);
+               XSRETURN(1);
+           }
+       }
+       if (message > -1) {
+           ULONG oflags, flags;
+
+           os2cp_croak(DosQueryNPHState(hpipe, &oflags), "DosQueryNPHState()");
+           /* DosSetNPHState fails if more bits than NP_NOWAIT|NP_READMODE_MESSAGE */
+           oflags &= (NP_NOWAIT | NP_READMODE_MESSAGE);
+           flags = (oflags & NP_NOWAIT)
+               | (message ? NP_READMODE_MESSAGE : NP_READMODE_BYTE);
+           if (flags != oflags)
+               os2cp_croak(DosSetNPHState(hpipe, flags), "DosSetNPHState()");
+       }
+    }
+    XSRETURN_YES;
+}
+
+/*
+NO_OUTPUT ULONG
+DosOpen(PCSZ pszFileName, OUTLIST HFILE hFile, OUTLIST ULONG ulAction, ULONG ulOpenFlags, ULONG ulOpenMode = OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW, ULONG ulAttribute = FILE_NORMAL, ULONG ulFileSize = 0, PEAOP2 pEABuf = NULL);
+   PREINIT:
+       ULONG rc;
+   C_ARGS:
+       pszFileName, &hFile, &ulAction, ulFileSize, ulAttribute, ulOpenFlags, ulOpenMode, pEABuf
+   POSTCALL:
+       if (CheckOSError(RETVAL))
+           croak_with_os2error("OS2::open() error");
+*/
+XS(XS_OS2_open); /* prototype to pass -Wmissing-prototypes */
+XS(XS_OS2_open)
+{
+    dXSARGS;
+    if (items < 2 || items > 6)
+       Perl_croak(aTHX_ "Usage: OS2::open(pszFileName, ulOpenMode, ulOpenFlags= OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW, ulAttribute= FILE_NORMAL, ulFileSize= 0, pEABuf= NULL)");
+    {
+#line 39 "pipe.xs"
+       ULONG rc;
+#line 113 "pipe.c"
+       ULONG   RETVAL;
+       PCSZ    pszFileName = ( SvOK(ST(0)) ? (PCSZ)SvPV(ST(0),PL_na) : NULL );
+       HFILE   hFile;
+       ULONG   ulAction;
+       ULONG   ulOpenMode = (ULONG)SvUV(ST(1));
+       ULONG   ulOpenFlags;
+       ULONG   ulAttribute;
+       ULONG   ulFileSize;
+       PEAOP2  pEABuf;
+
+       if (items < 3)
+           ulOpenFlags = OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW;
+       else {
+           ulOpenFlags = (ULONG)SvUV(ST(2));
+       }
+
+       if (items < 4)
+           ulAttribute = FILE_NORMAL;
+       else {
+           ulAttribute = (ULONG)SvUV(ST(3));
+       }
+
+       if (items < 5)
+           ulFileSize = 0;
+       else {
+           ulFileSize = (ULONG)SvUV(ST(4));
+       }
+
+       if (items < 6)
+           pEABuf = NULL;
+       else {
+           pEABuf = (PEAOP2)SvUV(ST(5));
+       }
+
+       RETVAL = DosOpen(pszFileName, &hFile, &ulAction, ulFileSize, ulAttribute, ulOpenFlags, ulOpenMode, pEABuf);
+       if (CheckOSError(RETVAL))
+           croak_with_os2error("OS2::open() error");
+       XSprePUSH;      EXTEND(SP,2);
+       PUSHs(sv_newmortal());
+       sv_setuv(ST(0), (UV)hFile);
+       PUSHs(sv_newmortal());
+       sv_setuv(ST(1), (UV)ulAction);
+    }
+    XSRETURN(2);
+}
+
 int
 Xs_OS2_init(pTHX)
 {
@@ -3482,6 +4506,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);
@@ -3503,6 +4529,7 @@ Xs_OS2_init(pTHX)
         newXS("Cwd::sys_cwd", XS_Cwd_sys_cwd, file);
         newXS("Cwd::sys_abspath", XS_Cwd_sys_abspath, file);
         newXS("OS2::replaceModule", XS_OS2_replaceModule, file);
+        newXS("OS2::perfSysCall", XS_OS2_perfSysCall, file);
         newXSproto("OS2::_control87", XS_OS2__control87, file, "$$");
         newXSproto("OS2::get_control87", XS_OS2_get_control87, file, "");
         newXSproto("OS2::set_control87", XS_OS2_set_control87, file, ";$$");
@@ -3512,15 +4539,26 @@ 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, "$$;$");
         newXSproto("OS2::Beep", XS_OS2_Beep, file, ";$$");
+        newXSproto("OS2::pipe", XS_OS2_pipe, file, "$$;$$$$$$");
+        newXSproto("OS2::pipeCntl", XS_OS2_pipeCntl, file, "$$;$");
+        newXSproto("OS2::open", XS_OS2_open, file, "$$;$$$$");
        gv = gv_fetchpv("OS2::is_aout", TRUE, SVt_PV);
        GvMULTI_on(gv);
 #ifdef PERL_IS_AOUT
        sv_setiv(GvSV(gv), 1);
 #endif
+       gv = gv_fetchpv("OS2::is_static", TRUE, SVt_PV);
+       GvMULTI_on(gv);
+#ifdef PERL_IS_AOUT
+       sv_setiv(GvSV(gv), 1);
+#endif
        gv = gv_fetchpv("OS2::can_fork", TRUE, SVt_PV);
        GvMULTI_on(gv);
        sv_setiv(GvSV(gv), exe_is_aout());
@@ -3628,6 +4666,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 */
@@ -3750,7 +4794,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;
 
@@ -3841,7 +4885,7 @@ check_emx_runtime(char **env, EXCEPTIONREGISTRATIONRECORD *preg)
            c++;
            e = e + strlen(e) + 1;
        }
-       New(1307, env, c + 1, char*);
+       Newx(env, c + 1, char*);
        ep = env;
        e = pib->pib_pchenv;
        while (c--) {
@@ -3887,7 +4931,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;
@@ -3896,16 +4941,21 @@ Perl_OS2_init3(char **env, void **preg, int flags)
 
     settmppath();
     OS2_Perl_data.xs_init = &Xs_OS2_init;
-    if ( (shell = getenv("PERL_SH_DRIVE")) ) {
-       New(1304, PL_sh_path, strlen(SH_PATH) + 1, char);
+    if (perl_sh_installed) {
+       int l = strlen(perl_sh_installed);
+
+       Newx(PL_sh_path, l + 1, char);
+       memcpy(PL_sh_path, perl_sh_installed, l + 1);
+    } else if ( (shell = getenv("PERL_SH_DRIVE")) ) {
+       Newx(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);
+       Newx(PL_sh_path, l + 8, char);
        strncpy(PL_sh_path, shell, l);
        strcpy(PL_sh_path + l, "/sh.exe");
        for (i = 0; i < l; i++) {
@@ -3919,10 +4969,67 @@ 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);
 }
 
+int
+fd_ok(int fd)
+{
+    static ULONG max_fh = 0;
+
+    if (!(_emx_env & 0x200)) return 1;         /* not OS/2. */
+    if (fd >= max_fh) {                                /* Renew */
+       LONG delta = 0;
+
+       if (DosSetRelMaxFH(&delta, &max_fh))    /* Assume it OK??? */
+           return 1;
+    }
+    return fd < max_fh;
+}
+
+/* Kernels up to Oct 2003 trap on (invalid) dup(max_fh); [off-by-one + double fault].  */
+int
+dup2(int from, int to)
+{
+    if (fd_ok(from < to ? to : from))
+       return _dup2(from, to);
+    errno = EBADF;
+    return -1;
+}
+
+int
+dup(int from)
+{
+    if (fd_ok(from))
+       return _dup(from);
+    errno = EBADF;
+    return -1;
+}
+
 #undef tmpnam
 #undef tmpfile
 
@@ -3968,7 +5075,7 @@ my_rmdir (__const__ char *s)
 
     if (s[l-1] == '/' || s[l-1] == '\\') {     /* EMX mkdir fails... */
        if (l >= sizeof b)
-           New(1305, buf, l + 1, char);
+           Newx(buf, l + 1, char);
        strcpy(buf,s);
        while (l > 1 && (s[l-1] == '/' || s[l-1] == '\\'))
            l--;
@@ -3993,7 +5100,7 @@ my_mkdir (__const__ char *s, long perm)
 
     if (s[l-1] == '/' || s[l-1] == '\\') {     /* EMX mkdir fails... */
        if (l >= sizeof b)
-           New(1305, buf, l + 1, char);
+           Newx(buf, l + 1, char);
        strcpy(buf,s);
        while (l > 1 && (s[l-1] == '/' || s[l-1] == '\\'))
            l--;
@@ -4313,3 +5420,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;
+}