Recalculate dstr in Perl_sv_setsv_flags, as dstr may have been upgraded.
[p5sagit/p5-mst-13.2.git] / os2 / os2.c
index 88b5f5d..38e47dd 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) {
@@ -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) {
@@ -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;) {
@@ -1461,7 +1481,7 @@ os2_aspawn4(pTHX_ SV *really, register SV **vmark, register SV **vsp, int execin
     STRLEN n_a;
 
     if (sp > mark) {
-       New(1301,PL_Argv, sp - mark + 3, char*);
+       Newx(PL_Argv, sp - mark + 3, char*);
        a = PL_Argv;
 
        if (mark < sp && SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
@@ -1518,7 +1538,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;
@@ -1851,6 +1871,109 @@ XS(XS_OS2_replaceModule)
     XSRETURN_EMPTY;
 }
 
+/* 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");
+       if (total) {
+           int i,j;
+
+           if (GIMME_V != G_ARRAY) {
+               PUSHn(u[0][0]);         /* Total ticks on the first processor */
+               XSRETURN(1);
+           }
+           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"
@@ -1993,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 == '\\')
@@ -2028,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;
 }
@@ -2287,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 *);
 
@@ -2318,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",
@@ -3112,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)
@@ -3126,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)))
+
+static void
+early_error(char *msg1, char *msg2, char *msg3)
+{      /* Buffer overflow detected; there is very little we can do... */
+    ULONG rc;
 
-#define extLibpath_set(p,type)                                         \
-    (!CheckOSError(ExtLIBPATH(ORD_DosSetExtLibpath, (p), (type))))
+    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 +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;
     }
@@ -3164,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);
@@ -3178,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,
@@ -3196,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)
 {
@@ -3244,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;
@@ -3482,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);
@@ -3503,6 +4024,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,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, "$$;$");
@@ -3521,6 +4046,11 @@ Xs_OS2_init(pTHX)
 #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 +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 */
@@ -3750,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;
 
@@ -3841,7 +4377,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 +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;
@@ -3896,16 +4433,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 +4461,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 +4567,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 +4592,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 +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;
+}