#define INCL_DOS
#define INCL_NOPM
#define INCL_DOSFILEMGR
#define INCL_DOSMEMMGR
#define INCL_DOSERRORS
#define INCL_WINERRORS
#define INCL_WINSYS
/* These 3 are needed for compile if os2.h includes os2tk.h, not os2emx.h */
#define INCL_DOSPROCESS
#define SPU_DISABLESUPPRESSION 0
#define SPU_ENABLESUPPRESSION 1
#include <os2.h>
#include "dlfcn.h"
#include <emx/syscalls.h>
#include <sys/emxload.h>
#include <sys/uflags.h>
/*
* Various Unix compatibility functions for OS/2
*/
#include <stdio.h>
#include <errno.h>
#include <limits.h>
#include <process.h>
#include <fcntl.h>
#include <pwd.h>
#include <grp.h>
#define PERLIO_NOT_STDIO 0
#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)
{
Perl_croak_nocontext("%s: %s", s, os2error(Perl_rc));
}
struct PMWIN_entries_t PMWIN_entries;
/*****************************************************************************/
/* 2.1 would not resolve symbols on demand, and has no ExtLIBPATH. */
struct dll_handle_t {
const char *modname;
HMODULE handle;
int requires_pm;
};
static struct dll_handle_t dll_handles[] = {
{"doscalls", 0, 0},
{"tcp32dll", 0, 0},
{"pmwin", 0, 1},
{"rexx", 0, 0},
{"rexxapi", 0, 0},
{"sesmgr", 0, 0},
{"pmshapi", 0, 1},
{"pmwp", 0, 1},
{"pmgpi", 0, 1},
{NULL, 0},
};
enum dll_handle_e {
dll_handle_doscalls,
dll_handle_tcp32dll,
dll_handle_pmwin,
dll_handle_rexx,
dll_handle_rexxapi,
dll_handle_sesmgr,
dll_handle_pmshapi,
dll_handle_pmwp,
dll_handle_pmgpi,
dll_handle_LAST,
};
#define doscalls_handle (dll_handles[dll_handle_doscalls])
#define tcp_handle (dll_handles[dll_handle_tcp32dll])
#define pmwin_handle (dll_handles[dll_handle_pmwin])
#define rexx_handle (dll_handles[dll_handle_rexx])
#define rexxapi_handle (dll_handles[dll_handle_rexxapi])
#define sesmgr_handle (dll_handles[dll_handle_sesmgr])
#define pmshapi_handle (dll_handles[dll_handle_pmshapi])
#define pmwp_handle (dll_handles[dll_handle_pmwp])
#define pmgpi_handle (dll_handles[dll_handle_pmgpi])
/* The following local-scope data is not yet included:
fargs.140 // const => OK
ino.165 // locked - and the access is almost cosmetic
layout_table.260 // startup only, locked
osv_res.257 // startup only, locked
old_esp.254 // startup only, locked
priors // const ==> OK
use_my_flock.283 // locked
emx_init_done.268 // locked
dll_handles // locked
hmtx_emx_init.267 // THIS is the lock for startup
perlos2_state_mutex // THIS is the lock for all the rest
BAD:
perlos2_state // see below
*/
/* The following global-scope data is not yet included:
OS2_Perl_data
pthreads_states // const now?
start_thread_mutex
thread_join_count // protected
thread_join_data // protected
tmppath
pDosVerifyPidTid
Perl_OS2_init3() - should it be protected?
*/
OS2_Perl_data_t OS2_Perl_data;
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; */
int po2_emx_wasnt_initialized;
char po2_fname[9];
int po2_rmq_cnt;
int po2_grent_cnt;
char *po2_newp;
char *po2_oldp;
int po2_newl;
int po2_oldl;
int po2_notfound;
char po2_mangle_ret[STATIC_FILE_LENGTH+1];
ULONG po2_os2_dll_fake;
ULONG po2_os2_mytype;
ULONG po2_os2_mytype_ini;
int po2_pidtid_lookup;
struct passwd po2_pw;
int po2_pwent_cnt;
char po2_pthreads_state_buf[80];
char po2_os2error_buf[300];
/* There is no big sense to make it thread-specific, since signals
are delivered to thread 1 only. XXXX Maybe make it into an array? */
int po2_spawn_pid;
int po2_spawn_killed;
jmp_buf po2_at_exit_buf;
int po2_longjmp_at_exit;
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 */
-1, /* po2_DOS_suppression_state */
};
#define Perl_po2() (&perlos2_state)
#define ExtFCN (Perl_po2()->po2_ExtFCN)
/* #define PMWIN_entries (Perl_po2()->po2_PMWIN_entries) */
#define emx_wasnt_initialized (Perl_po2()->po2_emx_wasnt_initialized)
#define fname (Perl_po2()->po2_fname)
#define rmq_cnt (Perl_po2()->po2_rmq_cnt)
#define grent_cnt (Perl_po2()->po2_grent_cnt)
#define newp (Perl_po2()->po2_newp)
#define oldp (Perl_po2()->po2_oldp)
#define newl (Perl_po2()->po2_newl)
#define oldl (Perl_po2()->po2_oldl)
#define notfound (Perl_po2()->po2_notfound)
#define mangle_ret (Perl_po2()->po2_mangle_ret)
#define os2_dll_fake (Perl_po2()->po2_os2_dll_fake)
#define os2_mytype (Perl_po2()->po2_os2_mytype)
#define os2_mytype_ini (Perl_po2()->po2_os2_mytype_ini)
#define pidtid_lookup (Perl_po2()->po2_pidtid_lookup)
#define pw (Perl_po2()->po2_pw)
#define pwent_cnt (Perl_po2()->po2_pwent_cnt)
#define _my_pwent (Perl_po2()->po2__my_pwent)
#define pthreads_state_buf (Perl_po2()->po2_pthreads_state_buf)
#define os2error_buf (Perl_po2()->po2_os2error_buf)
/* There is no big sense to make it thread-specific, since signals
are delivered to thread 1 only. XXXX Maybe make it into an array? */
#define spawn_pid (Perl_po2()->po2_spawn_pid)
#define spawn_killed (Perl_po2()->po2_spawn_killed)
#define DOS_harderr_state (Perl_po2()->po2_DOS_harderr_state)
#define DOS_suppression_state (Perl_po2()->po2_DOS_suppression_state)
#define at_exit_buf (Perl_po2()->po2_at_exit_buf)
#define longjmp_at_exit (Perl_po2()->po2_longjmp_at_exit)
#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 *);
typedef void* (*pthreads_startroutine)(void *);
enum pthreads_state {
pthreads_st_none = 0,
pthreads_st_run,
pthreads_st_exited,
pthreads_st_detached,
pthreads_st_waited,
pthreads_st_norun,
pthreads_st_exited_waited,
};
const char * const pthreads_states[] = {
"uninit",
"running",
"exited",
"detached",
"waited for",
"could not start",
"exited, then waited on",
};
enum pthread_exists { pthread_not_existant = -0xff };
static const char*
pthreads_state_string(enum pthreads_state state)
{
if (state < 0 || state >= sizeof(pthreads_states)/sizeof(*pthreads_states)) {
snprintf(pthreads_state_buf, sizeof(pthreads_state_buf),
"unknown thread state %d", (int)state);
return pthreads_state_buf;
}
return pthreads_states[state];
}
typedef struct {
void *status;
perl_cond cond;
enum pthreads_state state;
} thread_join_t;
thread_join_t *thread_join_data;
int thread_join_count;
perl_mutex start_thread_mutex;
static perl_mutex perlos2_state_mutex;
int
pthread_join(perl_os_thread tid, void **status)
{
MUTEX_LOCK(&start_thread_mutex);
if (tid < 1 || tid >= thread_join_count) {
MUTEX_UNLOCK(&start_thread_mutex);
if (tid != pthread_not_existant)
Perl_croak_nocontext("panic: join with a thread with strange ordinal %d", (int)tid);
Perl_warn_nocontext("panic: join with a thread which could not start");
*status = 0;
return 0;
}
switch (thread_join_data[tid].state) {
case pthreads_st_exited:
thread_join_data[tid].state = pthreads_st_exited_waited;
*status = thread_join_data[tid].status;
MUTEX_UNLOCK(&start_thread_mutex);
COND_SIGNAL(&thread_join_data[tid].cond);
break;
case pthreads_st_waited:
MUTEX_UNLOCK(&start_thread_mutex);
Perl_croak_nocontext("join with a thread with a waiter");
break;
case pthreads_st_norun:
{
int state = (int)thread_join_data[tid].status;
thread_join_data[tid].state = pthreads_st_none;
MUTEX_UNLOCK(&start_thread_mutex);
Perl_croak_nocontext("panic: join with a thread which could not run"
" due to attempt of tid reuse (state='%s')",
pthreads_state_string(state));
break;
}
case pthreads_st_run:
{
perl_cond cond;
thread_join_data[tid].state = pthreads_st_waited;
thread_join_data[tid].status = (void *)status;
COND_INIT(&thread_join_data[tid].cond);
cond = thread_join_data[tid].cond;
COND_WAIT(&thread_join_data[tid].cond, &start_thread_mutex);
COND_DESTROY(&cond);
MUTEX_UNLOCK(&start_thread_mutex);
break;
}
default:
MUTEX_UNLOCK(&start_thread_mutex);
Perl_croak_nocontext("panic: join with thread in unknown thread state: '%s'",
pthreads_state_string(thread_join_data[tid].state));
break;
}
return 0;
}
typedef struct {
pthreads_startroutine sub;
void *arg;
void *ctx;
} pthr_startit;
/* The lock is used:
a) Since we temporarily usurp the caller interp, so malloc() may
use it to decide on debugging the call;
b) Since *args is on the caller's stack.
*/
void
pthread_startit(void *arg1)
{
/* Thread is already started, we need to transfer control only */
pthr_startit args = *(pthr_startit *)arg1;
int tid = pthread_self();
void *rc;
int state;
if (tid <= 1) {
/* Can't croak, the setjmp() is not in scope... */
char buf[80];
snprintf(buf, sizeof(buf),
"panic: thread with strange ordinal %d created\n\r", tid);
write(2,buf,strlen(buf));
MUTEX_UNLOCK(&start_thread_mutex);
return;
}
/* Until args.sub resets it, makes debugging Perl_malloc() work: */
PERL_SET_CONTEXT(0);
if (tid >= thread_join_count) {
int oc = thread_join_count;
thread_join_count = tid + 5 + tid/5;
if (thread_join_data) {
Renew(thread_join_data, thread_join_count, thread_join_t);
Zero(thread_join_data + oc, thread_join_count - oc, thread_join_t);
} else {
Newxz(thread_join_data, thread_join_count, thread_join_t);
}
}
if (thread_join_data[tid].state != pthreads_st_none) {
/* Can't croak, the setjmp() is not in scope... */
char buf[80];
snprintf(buf, sizeof(buf),
"panic: attempt to reuse thread id %d (state='%s')\n\r",
tid, pthreads_state_string(thread_join_data[tid].state));
write(2,buf,strlen(buf));
thread_join_data[tid].status = (void*)thread_join_data[tid].state;
thread_join_data[tid].state = pthreads_st_norun;
MUTEX_UNLOCK(&start_thread_mutex);
return;
}
thread_join_data[tid].state = pthreads_st_run;
/* Now that we copied/updated the guys, we may release the caller... */
MUTEX_UNLOCK(&start_thread_mutex);
rc = (*args.sub)(args.arg);
MUTEX_LOCK(&start_thread_mutex);
switch (thread_join_data[tid].state) {
case pthreads_st_waited:
COND_SIGNAL(&thread_join_data[tid].cond);
thread_join_data[tid].state = pthreads_st_none;
*((void**)thread_join_data[tid].status) = rc;
break;
case pthreads_st_detached:
thread_join_data[tid].state = pthreads_st_none;
break;
case pthreads_st_run:
/* Somebody can wait on us; cannot exit, since OS can reuse the tid
and our waiter will get somebody else's status. */
thread_join_data[tid].state = pthreads_st_exited;
thread_join_data[tid].status = rc;
COND_INIT(&thread_join_data[tid].cond);
COND_WAIT(&thread_join_data[tid].cond, &start_thread_mutex);
COND_DESTROY(&thread_join_data[tid].cond);
thread_join_data[tid].state = pthreads_st_none; /* Ready to reuse */
break;
default:
state = thread_join_data[tid].state;
MUTEX_UNLOCK(&start_thread_mutex);
Perl_croak_nocontext("panic: unexpected thread state on exit: '%s'",
pthreads_state_string(state));
}
MUTEX_UNLOCK(&start_thread_mutex);
}
int
pthread_create(perl_os_thread *tidp, const pthread_attr_t *attr,
void *(*start_routine)(void*), void *arg)
{
dTHX;
pthr_startit args;
args.sub = (void*)start_routine;
args.arg = arg;
args.ctx = PERL_GET_CONTEXT;
MUTEX_LOCK(&start_thread_mutex);
/* Test suite creates 31 extra threads;
on machine without shared-memory-hogs this stack sizeis OK with 31: */
*tidp = _beginthread(pthread_startit, /*stack*/ NULL,
/*stacksize*/ 4*1024*1024, (void*)&args);
if (*tidp == -1) {
*tidp = pthread_not_existant;
MUTEX_UNLOCK(&start_thread_mutex);
return EINVAL;
}
MUTEX_LOCK(&start_thread_mutex); /* Wait for init to proceed */
MUTEX_UNLOCK(&start_thread_mutex);
return 0;
}
int
pthread_detach(perl_os_thread tid)
{
MUTEX_LOCK(&start_thread_mutex);
if (tid < 1 || tid >= thread_join_count) {
MUTEX_UNLOCK(&start_thread_mutex);
if (tid != pthread_not_existant)
Perl_croak_nocontext("panic: detach of a thread with strange ordinal %d", (int)tid);
Perl_warn_nocontext("detach of a thread which could not start");
return 0;
}
switch (thread_join_data[tid].state) {
case pthreads_st_waited:
MUTEX_UNLOCK(&start_thread_mutex);
Perl_croak_nocontext("detach on a thread with a waiter");
break;
case pthreads_st_run:
thread_join_data[tid].state = pthreads_st_detached;
MUTEX_UNLOCK(&start_thread_mutex);
break;
case pthreads_st_exited:
MUTEX_UNLOCK(&start_thread_mutex);
COND_SIGNAL(&thread_join_data[tid].cond);
break;
case pthreads_st_detached:
MUTEX_UNLOCK(&start_thread_mutex);
Perl_warn_nocontext("detach on an already detached thread");
break;
case pthreads_st_norun:
{
int state = (int)thread_join_data[tid].status;
thread_join_data[tid].state = pthreads_st_none;
MUTEX_UNLOCK(&start_thread_mutex);
Perl_croak_nocontext("panic: detaching thread which could not run"
" due to attempt of tid reuse (state='%s')",
pthreads_state_string(state));
break;
}
default:
MUTEX_UNLOCK(&start_thread_mutex);
Perl_croak_nocontext("panic: detach of a thread with unknown thread state: '%s'",
pthreads_state_string(thread_join_data[tid].state));
break;
}
return 0;
}
/* This is a very bastardized version; may be OK due to edge trigger of Wait */
int
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 = CheckOSError(rc), croak_with_os2error("panic: COND_WAIT-reset");
if (m) MUTEX_UNLOCK(m);
if (CheckOSError(DosWaitEventSem(*c,SEM_INDEFINITE_WAIT))
&& (rc != ERROR_INTERRUPT))
croak_with_os2error("panic: COND_WAIT");
if (rc == ERROR_INTERRUPT)
errno = EINTR;
if (m) MUTEX_LOCK(m);
return 0;
}
#endif
static int exe_is_aout(void);
/* This should match enum entries_ordinals defined in os2ish.h. */
static const struct {
struct dll_handle_t *dll;
const char *entryname;
int entrypoint;
} loadOrdinals[] = {
{&doscalls_handle, NULL, 874}, /* DosQueryExtLibpath */
{&doscalls_handle, NULL, 873}, /* DosSetExtLibpath */
{&doscalls_handle, NULL, 460}, /* DosVerifyPidTid */
{&tcp_handle, "SETHOSTENT", 0},
{&tcp_handle, "SETNETENT" , 0},
{&tcp_handle, "SETPROTOENT", 0},
{&tcp_handle, "SETSERVENT", 0},
{&tcp_handle, "GETHOSTENT", 0},
{&tcp_handle, "GETNETENT" , 0},
{&tcp_handle, "GETPROTOENT", 0},
{&tcp_handle, "GETSERVENT", 0},
{&tcp_handle, "ENDHOSTENT", 0},
{&tcp_handle, "ENDNETENT", 0},
{&tcp_handle, "ENDPROTOENT", 0},
{&tcp_handle, "ENDSERVENT", 0},
{&pmwin_handle, NULL, 763}, /* WinInitialize */
{&pmwin_handle, NULL, 716}, /* WinCreateMsgQueue */
{&pmwin_handle, NULL, 726}, /* WinDestroyMsgQueue */
{&pmwin_handle, NULL, 918}, /* WinPeekMsg */
{&pmwin_handle, NULL, 915}, /* WinGetMsg */
{&pmwin_handle, NULL, 912}, /* WinDispatchMsg */
{&pmwin_handle, NULL, 753}, /* WinGetLastError */
{&pmwin_handle, NULL, 705}, /* WinCancelShutdown */
/* These are needed in extensions.
How to protect PMSHAPI: it comes through EMX functions? */
{&rexx_handle, "RexxStart", 0},
{&rexx_handle, "RexxVariablePool", 0},
{&rexxapi_handle, "RexxRegisterFunctionExe", 0},
{&rexxapi_handle, "RexxDeregisterFunction", 0},
{&sesmgr_handle, "DOSSMSETTITLE", 0}, /* Would not work runtime-loaded */
{&pmshapi_handle, "PRF32QUERYPROFILESIZE", 0},
{&pmshapi_handle, "PRF32OPENPROFILE", 0},
{&pmshapi_handle, "PRF32CLOSEPROFILE", 0},
{&pmshapi_handle, "PRF32QUERYPROFILE", 0},
{&pmshapi_handle, "PRF32RESET", 0},
{&pmshapi_handle, "PRF32QUERYPROFILEDATA", 0},
{&pmshapi_handle, "PRF32WRITEPROFILEDATA", 0},
/* At least some of these do not work by name, since they need
WIN32 instead of WIN... */
#if 0
These were generated with
nm I:\emx\lib\os2.a | fgrep -f API-list | grep = > API-list-entries
perl -wnle "next unless /^0+\s+E\s+_(\w+)=(\w+).(\d+)/; print qq( ORD_$1,)" API-list-entries > API-list-ORD_
perl -wnle "next unless /^0+\s+E\s+_(\w+)=(\w+).(\d+)/; print qq( {${2}_handle, NULL, $3},\t\t/* $1 */)" WinSwitch-API-list-entries >API-list-entry
#endif
{&pmshapi_handle, NULL, 123}, /* WinChangeSwitchEntry */
{&pmshapi_handle, NULL, 124}, /* WinQuerySwitchEntry */
{&pmshapi_handle, NULL, 125}, /* WinQuerySwitchHandle */
{&pmshapi_handle, NULL, 126}, /* WinQuerySwitchList */
{&pmshapi_handle, NULL, 131}, /* WinSwitchToProgram */
{&pmwin_handle, NULL, 702}, /* WinBeginEnumWindows */
{&pmwin_handle, NULL, 737}, /* WinEndEnumWindows */
{&pmwin_handle, NULL, 740}, /* WinEnumDlgItem */
{&pmwin_handle, NULL, 756}, /* WinGetNextWindow */
{&pmwin_handle, NULL, 768}, /* WinIsChild */
{&pmwin_handle, NULL, 799}, /* WinQueryActiveWindow */
{&pmwin_handle, NULL, 805}, /* WinQueryClassName */
{&pmwin_handle, NULL, 817}, /* WinQueryFocus */
{&pmwin_handle, NULL, 834}, /* WinQueryWindow */
{&pmwin_handle, NULL, 837}, /* WinQueryWindowPos */
{&pmwin_handle, NULL, 838}, /* WinQueryWindowProcess */
{&pmwin_handle, NULL, 841}, /* WinQueryWindowText */
{&pmwin_handle, NULL, 842}, /* WinQueryWindowTextLength */
{&pmwin_handle, NULL, 860}, /* WinSetFocus */
{&pmwin_handle, NULL, 875}, /* WinSetWindowPos */
{&pmwin_handle, NULL, 877}, /* WinSetWindowText */
{&pmwin_handle, NULL, 883}, /* WinShowWindow */
{&pmwin_handle, NULL, 772}, /* WinIsWindow */
{&pmwin_handle, NULL, 899}, /* WinWindowFromId */
{&pmwin_handle, NULL, 900}, /* WinWindowFromPoint */
{&pmwin_handle, NULL, 919}, /* WinPostMsg */
{&pmwin_handle, NULL, 735}, /* WinEnableWindow */
{&pmwin_handle, NULL, 736}, /* WinEnableWindowUpdate */
{&pmwin_handle, NULL, 773}, /* WinIsWindowEnabled */
{&pmwin_handle, NULL, 774}, /* WinIsWindowShowing */
{&pmwin_handle, NULL, 775}, /* WinIsWindowVisible */
{&pmwin_handle, NULL, 839}, /* WinQueryWindowPtr */
{&pmwin_handle, NULL, 843}, /* WinQueryWindowULong */
{&pmwin_handle, NULL, 844}, /* WinQueryWindowUShort */
{&pmwin_handle, NULL, 874}, /* WinSetWindowBits */
{&pmwin_handle, NULL, 876}, /* WinSetWindowPtr */
{&pmwin_handle, NULL, 878}, /* WinSetWindowULong */
{&pmwin_handle, NULL, 879}, /* WinSetWindowUShort */
{&pmwin_handle, NULL, 813}, /* WinQueryDesktopWindow */
{&pmwin_handle, NULL, 851}, /* WinSetActiveWindow */
{&doscalls_handle, NULL, 360}, /* DosQueryModFromEIP */
{&doscalls_handle, NULL, 582}, /* Dos32QueryHeaderInfo */
{&doscalls_handle, NULL, 362}, /* DosTmrQueryFreq */
{&doscalls_handle, NULL, 363}, /* DosTmrQueryTime */
{&pmwp_handle, NULL, 262}, /* WinQueryActiveDesktopPathname */
{&pmwin_handle, NULL, 765}, /* WinInvalidateRect */
{&pmwin_handle, NULL, 906}, /* WinCreateFrameControl */
{&pmwin_handle, NULL, 807}, /* WinQueryClipbrdFmtInfo */
{&pmwin_handle, NULL, 808}, /* WinQueryClipbrdOwner */
{&pmwin_handle, NULL, 809}, /* WinQueryClipbrdViewer */
{&pmwin_handle, NULL, 806}, /* WinQueryClipbrdData */
{&pmwin_handle, NULL, 793}, /* WinOpenClipbrd */
{&pmwin_handle, NULL, 707}, /* WinCloseClipbrd */
{&pmwin_handle, NULL, 854}, /* WinSetClipbrdData */
{&pmwin_handle, NULL, 855}, /* WinSetClipbrdOwner */
{&pmwin_handle, NULL, 856}, /* WinSetClipbrdViewer */
{&pmwin_handle, NULL, 739}, /* WinEnumClipbrdFmts */
{&pmwin_handle, NULL, 733}, /* WinEmptyClipbrd */
{&pmwin_handle, NULL, 700}, /* WinAddAtom */
{&pmwin_handle, NULL, 744}, /* WinFindAtom */
{&pmwin_handle, NULL, 721}, /* WinDeleteAtom */
{&pmwin_handle, NULL, 803}, /* WinQueryAtomUsage */
{&pmwin_handle, NULL, 802}, /* WinQueryAtomName */
{&pmwin_handle, NULL, 801}, /* WinQueryAtomLength */
{&pmwin_handle, NULL, 830}, /* WinQuerySystemAtomTable */
{&pmwin_handle, NULL, 714}, /* WinCreateAtomTable */
{&pmwin_handle, NULL, 724}, /* WinDestroyAtomTable */
{&pmwin_handle, NULL, 794}, /* WinOpenWindowDC */
{&pmgpi_handle, NULL, 610}, /* DevOpenDC */
{&pmgpi_handle, NULL, 606}, /* DevQueryCaps */
{&pmgpi_handle, NULL, 604}, /* DevCloseDC */
{&pmwin_handle, NULL, 789}, /* WinMessageBox */
{&pmwin_handle, NULL, 1015}, /* WinMessageBox2 */
{&pmwin_handle, NULL, 829}, /* WinQuerySysValue */
{&pmwin_handle, NULL, 873}, /* WinSetSysValue */
{&pmwin_handle, NULL, 701}, /* WinAlarm */
{&pmwin_handle, NULL, 745}, /* WinFlashWindow */
{&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
loadModule(const char *modname, int fail)
{
HMODULE h = (HMODULE)dlopen(modname, 0);
if (!h && fail)
Perl_croak_nocontext("Error loading module '%s': %s",
modname, dlerror());
return h;
}
/* const char* const ptypes[] = { "FS", "DOS", "VIO", "PM", "DETACH" }; */
static int
my_type()
{
int rc;
TIB *tib;
PIB *pib;
if (!(_emx_env & 0x200)) return 1; /* not OS/2. */
if (CheckOSError(DosGetInfoBlocks(&tib, &pib)))
return -1;
return (pib->pib_ultype);
}
static void
my_type_set(int type)
{
int rc;
TIB *tib;
PIB *pib;
if (!(_emx_env & 0x200))
Perl_croak_nocontext("Can't set type on DOS"); /* not OS/2. */
if (CheckOSError(DosGetInfoBlocks(&tib, &pib)))
croak_with_os2error("Error getting info blocks");
pib->pib_ultype = type;
}
PFN
loadByOrdinal(enum entries_ordinals ord, int fail)
{
if (sizeof(loadOrdinals)/sizeof(loadOrdinals[0]) != ORD_NENTRIES)
Perl_croak_nocontext(
"Wrong size of loadOrdinals array: expected %d, actual %d",
sizeof(loadOrdinals)/sizeof(loadOrdinals[0]), ORD_NENTRIES);
if (ExtFCN[ord] == NULL) {
PFN fcn = (PFN)-1;
APIRET rc;
if (!loadOrdinals[ord].dll->handle) {
if (loadOrdinals[ord].dll->requires_pm && my_type() < 2) { /* FS */
char *s = getenv("PERL_ASIF_PM");
if (!s || !atoi(s)) {
/* The module will not function well without PM.
The usual way to detect PM is the existence of the mutex
\SEM32\PMDRAG.SEM. */
HMTX hMtx = 0;
if (CheckOSError(DosOpenMutexSem("\\SEM32\\PMDRAG.SEM",
&hMtx)))
Perl_croak_nocontext("Looks like we have no PM; will not load DLL %s without $ENV{PERL_ASIF_PM}",
loadOrdinals[ord].dll->modname);
DosCloseMutexSem(hMtx);
}
}
MUTEX_LOCK(&perlos2_state_mutex);
loadOrdinals[ord].dll->handle
= loadModule(loadOrdinals[ord].dll->modname, fail);
MUTEX_UNLOCK(&perlos2_state_mutex);
}
if (!loadOrdinals[ord].dll->handle)
return 0; /* Possible with FAIL==0 only */
if (CheckOSError(DosQueryProcAddr(loadOrdinals[ord].dll->handle,
loadOrdinals[ord].entrypoint,
loadOrdinals[ord].entryname,&fcn))) {
char buf[20], *s = (char*)loadOrdinals[ord].entryname;
if (!fail)
return 0;
if (!s)
sprintf(s = buf, "%d", loadOrdinals[ord].entrypoint);
Perl_croak_nocontext(
"This version of OS/2 does not support %s.%s",
loadOrdinals[ord].dll->modname, s);
}
ExtFCN[ord] = fcn;
}
if ((long)ExtFCN[ord] == -1)
Perl_croak_nocontext("panic queryaddr");
return ExtFCN[ord];
}
void
init_PMWIN_entries(void)
{
int i;
for (i = ORD_WinInitialize; i <= ORD_WinCancelShutdown; i++)
((PFN*)&PMWIN_entries)[i - ORD_WinInitialize] = loadByOrdinal(i, 1);
}
/*****************************************************/
/* socket forwarders without linking with tcpip DLLs */
DeclFuncByORD(struct hostent *, gethostent, ORD_GETHOSTENT, (void), ())
DeclFuncByORD(struct netent *, getnetent, ORD_GETNETENT, (void), ())
DeclFuncByORD(struct protoent *, getprotoent, ORD_GETPROTOENT, (void), ())
DeclFuncByORD(struct servent *, getservent, ORD_GETSERVENT, (void), ())
DeclVoidFuncByORD(sethostent, ORD_SETHOSTENT, (int x), (x))
DeclVoidFuncByORD(setnetent, ORD_SETNETENT, (int x), (x))
DeclVoidFuncByORD(setprotoent, ORD_SETPROTOENT, (int x), (x))
DeclVoidFuncByORD(setservent, ORD_SETSERVENT, (int x), (x))
DeclVoidFuncByORD(endhostent, ORD_ENDHOSTENT, (void), ())
DeclVoidFuncByORD(endnetent, ORD_ENDNETENT, (void), ())
DeclVoidFuncByORD(endprotoent, ORD_ENDPROTOENT, (void), ())
DeclVoidFuncByORD(endservent, ORD_ENDSERVENT, (void), ())
/* priorities */
static const signed char priors[] = {0, 1, 3, 2}; /* Last two interchanged,
self inverse. */
#define QSS_INI_BUFFER 1024
ULONG (*pDosVerifyPidTid) (PID pid, TID tid);
PQTOPLEVEL
get_sysinfo(ULONG pid, ULONG flags)
{
char *pbuffer;
ULONG rc, buf_len = QSS_INI_BUFFER;
PQTOPLEVEL psi;
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;
}
}
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) {
Renew(pbuffer, buf_len *= 2, char);
rc = QuerySysState(flags, pid, pbuffer, buf_len);
}
if (rc) {
FillOSError(rc);
Safefree(pbuffer);
return 0;
}
psi = (PQTOPLEVEL)pbuffer;
if (psi && pid && psi->procdata && pid != psi->procdata->pid) {
Safefree(psi);
Perl_croak_nocontext("panic: wrong pid in sysinfo");
}
return psi;
}
#define PRIO_ERR 0x1111
static ULONG
sys_prio(pid)
{
ULONG prio;
PQTOPLEVEL psi;
if (!pid)
return PRIO_ERR;
psi = get_sysinfo(pid, QSS_PROCESS);
if (!psi)
return PRIO_ERR;
prio = psi->procdata->threads->priority;
Safefree(psi);
return prio;
}
int
setpriority(int which, int pid, int val)
{
ULONG rc, prio = sys_prio(pid);
if (!(_emx_env & 0x200)) return 0; /* Nop if not OS/2. */
if (priors[(32 - val) >> 5] + 1 == (prio >> 8)) {
/* Do not change class. */
return CheckOSError(DosSetPriority((pid < 0)
? PRTYS_PROCESSTREE : PRTYS_PROCESS,
0,
(32 - val) % 32 - (prio & 0xFF),
abs(pid)))
? -1 : 0;
} else /* if ((32 - val) % 32 == (prio & 0xFF)) */ {
/* Documentation claims one can change both class and basevalue,
* but I find it wrong. */
/* Change class, but since delta == 0 denotes absolute 0, correct. */
if (CheckOSError(DosSetPriority((pid < 0)
? PRTYS_PROCESSTREE : PRTYS_PROCESS,
priors[(32 - val) >> 5] + 1,
0,
abs(pid))))
return -1;
if ( ((32 - val) % 32) == 0 ) return 0;
return CheckOSError(DosSetPriority((pid < 0)
? PRTYS_PROCESSTREE : PRTYS_PROCESS,
0,
(32 - val) % 32,
abs(pid)))
? -1 : 0;
}
}
int
getpriority(int which /* ignored */, int pid)
{
ULONG ret;
if (!(_emx_env & 0x200)) return 0; /* Nop if not OS/2. */
ret = sys_prio(pid);
if (ret == PRIO_ERR) {
return -1;
}
return (1 - priors[((ret >> 8) - 1)])*32 - (ret & 0xFF);
}
/*****************************************************************************/
/* spawn */
static Signal_t
spawn_sighandler(int sig)
{
/* Some programs do not arrange for the keyboard signals to be
delivered to them. We need to deliver the signal manually. */
/* We may get a signal only if
a) kid does not receive keyboard signal: deliver it;
b) kid already died, and we get a signal. We may only hope
that the pid number was not reused.
*/
if (spawn_killed)
sig = SIGKILL; /* Try harder. */
kill(spawn_pid, sig);
spawn_killed = 1;
}
static int
result(pTHX_ int flag, int pid)
{
int r, status;
Signal_t (*ihand)(); /* place to save signal during system() */
Signal_t (*qhand)(); /* place to save signal during system() */
#ifndef __EMX__
RESULTCODES res;
int rpid;
#endif
if (pid < 0 || flag != 0)
return pid;
#ifdef __EMX__
spawn_pid = pid;
spawn_killed = 0;
ihand = rsignal(SIGINT, &spawn_sighandler);
qhand = rsignal(SIGQUIT, &spawn_sighandler);
do {
r = wait4pid(pid, &status, 0);
} while (r == -1 && errno == EINTR);
rsignal(SIGINT, ihand);
rsignal(SIGQUIT, qhand);
PL_statusvalue = (U16)status;
if (r < 0)
return -1;
return status & 0xFFFF;
#else
ihand = rsignal(SIGINT, SIG_IGN);
r = DosWaitChild(DCWA_PROCESS, DCWW_WAIT, &res, &rpid, pid);
rsignal(SIGINT, ihand);
PL_statusvalue = res.codeResult << 8 | res.codeTerminate;
if (r)
return -1;
return PL_statusvalue;
#endif
}
enum execf_t {
EXECF_SPAWN,
EXECF_EXEC,
EXECF_TRUEEXEC,
EXECF_SPAWN_NOWAIT,
EXECF_SPAWN_BYFLAG,
EXECF_SYNC
};
static ULONG
file_type(char *path)
{
int rc;
ULONG apptype;
if (!(_emx_env & 0x200))
Perl_croak_nocontext("file_type not implemented on DOS"); /* not OS/2. */
if (CheckOSError(DosQueryAppType(path, &apptype))) {
switch (rc) {
case ERROR_FILE_NOT_FOUND:
case ERROR_PATH_NOT_FOUND:
return -1;
case ERROR_ACCESS_DENIED: /* Directory with this name found? */
return -3;
default: /* Found, but not an
executable, or some other
read error. */
return -2;
}
}
return apptype;
}
/* Spawn/exec a program, revert to shell if needed. */
/* global PL_Argv[] contains arguments. */
extern ULONG _emx_exception ( EXCEPTIONREPORTRECORD *,
EXCEPTIONREGISTRATIONRECORD *,
CONTEXTRECORD *,
void *);
int
do_spawn_ve(pTHX_ SV *really, U32 flag, U32 execf, char *inicmd, U32 addflag)
{
int trueflag = flag;
int rc, pass = 1;
char *real_name = NULL; /* Shut down the warning */
char const * args[4];
static const char * const fargs[4]
= { "/bin/sh", "-c", "\"$@\"", "spawn-via-shell", };
const char * const *argsp = fargs;
int nargs = 4;
int force_shell;
int new_stderr = -1, nostderr = 0;
int fl_stderr = 0;
STRLEN n_a;
char *buf;
PerlIO *file;
if (flag == P_WAIT)
flag = P_NOWAIT;
if (really && !*(real_name = SvPV(really, n_a)))
really = NULL;
retry:
if (strEQ(PL_Argv[0],"/bin/sh"))
PL_Argv[0] = PL_sh_path;
/* We should check PERL_SH* and PERLLIB_* as well? */
if (!really || pass >= 2)
real_name = PL_Argv[0];
if (real_name[0] != '/' && real_name[0] != '\\'
&& !(real_name[0] && real_name[1] == ':'
&& (real_name[2] == '/' || real_name[2] != '\\'))
) /* will spawnvp use PATH? */
TAINT_ENV(); /* testing IFS here is overkill, probably */
reread:
force_shell = 0;
if (_emx_env & 0x200) { /* OS/2. */
int type = file_type(real_name);
type_again:
if (type == -1) { /* Not found */
errno = ENOENT;
rc = -1;
goto do_script;
}
else if (type == -2) { /* Not an EXE */
errno = ENOEXEC;
rc = -1;
goto do_script;
}
else if (type == -3) { /* Is a directory? */
/* Special-case this */
char tbuf[512];
int l = strlen(real_name);
if (l + 5 <= sizeof tbuf) {
strcpy(tbuf, real_name);
strcpy(tbuf + l, ".exe");
type = file_type(tbuf);
if (type >= -3)
goto type_again;
}
errno = ENOEXEC;
rc = -1;
goto do_script;
}
switch (type & 7) {
/* Ignore WINDOWCOMPAT and FAPI, start them the same type we are. */
case FAPPTYP_WINDOWAPI:
{ /* Apparently, kids are started basing on startup type, not the morphed type */
if (os2_mytype != 3) { /* not PM */
if (flag == P_NOWAIT)
flag = P_PM;
else if ((flag & 7) != P_PM && (flag & 7) != P_SESSION && ckWARN(WARN_EXEC))
Perl_warner(aTHX_ packWARN(WARN_EXEC), "Starting PM process with flag=%d, mytype=%d",
flag, os2_mytype);
}
}
break;
case FAPPTYP_NOTWINDOWCOMPAT:
{
if (os2_mytype != 0) { /* not full screen */
if (flag == P_NOWAIT)
flag = P_SESSION;
else if ((flag & 7) != P_SESSION && ckWARN(WARN_EXEC))
Perl_warner(aTHX_ packWARN(WARN_EXEC), "Starting Full Screen process with flag=%d, mytype=%d",
flag, os2_mytype);
}
}
break;
case FAPPTYP_NOTSPEC:
/* Let the shell handle this... */
force_shell = 1;
buf = ""; /* Pacify a warning */
file = 0; /* Pacify a warning */
goto doshell_args;
break;
}
}
if (addflag) {
addflag = 0;
new_stderr = dup(2); /* Preserve stderr */
if (new_stderr == -1) {
if (errno == EBADF)
nostderr = 1;
else {
rc = -1;
goto finish;
}
} else
fl_stderr = fcntl(2, F_GETFD);
rc = dup2(1,2);
if (rc == -1)
goto finish;
fcntl(new_stderr, F_SETFD, FD_CLOEXEC);
}
#if 0
rc = result(aTHX_ trueflag, spawnvp(flag,real_name,PL_Argv));
#else
if (execf == EXECF_TRUEEXEC)
rc = execvp(real_name,PL_Argv);
else if (execf == EXECF_EXEC)
rc = spawnvp(trueflag | P_OVERLAY,real_name,PL_Argv);
else if (execf == EXECF_SPAWN_NOWAIT)
rc = spawnvp(flag,real_name,PL_Argv);
else if (execf == EXECF_SYNC)
rc = spawnvp(trueflag,real_name,PL_Argv);
else /* EXECF_SPAWN, EXECF_SPAWN_BYFLAG */
rc = result(aTHX_ trueflag,
spawnvp(flag,real_name,PL_Argv));
#endif
if (rc < 0 && pass == 1) {
do_script:
if (real_name == PL_Argv[0]) {
int err = errno;
if (err == ENOENT || err == ENOEXEC) {
/* No such file, or is a script. */
/* Try adding script extensions to the file name, and
search on PATH. */
char *scr = find_script(PL_Argv[0], TRUE, NULL, 0);
if (scr) {
char *s = 0, *s1;
SV *scrsv = sv_2mortal(newSVpv(scr, 0));
SV *bufsv = sv_newmortal();
Safefree(scr);
scr = SvPV(scrsv, n_a); /* free()ed later */
file = PerlIO_open(scr, "r");
PL_Argv[0] = scr;
if (!file)
goto panic_file;
buf = sv_gets(bufsv, file, 0 /* No append */);
if (!buf)
buf = ""; /* XXX Needed? */
if (!buf[0]) { /* Empty... */
PerlIO_close(file);
/* Special case: maybe from -Zexe build, so
there is an executable around (contrary to
documentation, DosQueryAppType sometimes (?)
does not append ".exe", so we could have
reached this place). */
sv_catpv(scrsv, ".exe");
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;
pass++;
goto reread;
} else { /* Restore */
SvCUR_set(scrsv, SvCUR(scrsv) - 4);
*SvEND(scrsv) = 0;
}
}
if (PerlIO_close(file) != 0) { /* Failure */
panic_file:
if (ckWARN(WARN_EXEC))
Perl_warner(aTHX_ packWARN(WARN_EXEC), "Error reading \"%s\": %s",
scr, Strerror(errno));
buf = ""; /* Not #! */
goto doshell_args;
}
if (buf[0] == '#') {
if (buf[1] == '!')
s = buf + 2;
} else if (buf[0] == 'e') {
if (strnEQ(buf, "extproc", 7)
&& isSPACE(buf[7]))
s = buf + 8;
} else if (buf[0] == 'E') {
if (strnEQ(buf, "EXTPROC", 7)
&& isSPACE(buf[7]))
s = buf + 8;
}
if (!s) {
buf = ""; /* Not #! */
goto doshell_args;
}
s1 = s;
nargs = 0;
argsp = args;
while (1) {
/* Do better than pdksh: allow a few args,
strip trailing whitespace. */
while (isSPACE(*s))
s++;
if (*s == 0)
break;
if (nargs == 4) {
nargs = -1;
break;
}
args[nargs++] = s;
while (*s && !isSPACE(*s))
s++;
if (*s == 0)
break;
*s++ = 0;
}
if (nargs == -1) {
Perl_warner(aTHX_ packWARN(WARN_EXEC), "Too many args on %.*s line of \"%s\"",
s1 - buf, buf, scr);
nargs = 4;
argsp = fargs;
}
/* Can jump from far, buf/file invalid if force_shell: */
doshell_args:
{
char **a = PL_Argv;
const char *exec_args[2];
if (force_shell
|| (!buf[0] && file)) { /* File without magic */
/* In fact we tried all what pdksh would
try. There is no point in calling
pdksh, we may just emulate its logic. */
char *shell = getenv("EXECSHELL");
char *shell_opt = NULL;
if (!shell) {
char *s;
shell_opt = "/c";
shell = getenv("OS2_SHELL");
if (inicmd) { /* No spaces at start! */
s = inicmd;
while (*s && !isSPACE(*s)) {
if (*s++ == '/') {
inicmd = NULL; /* Cannot use */
break;
}
}
}
if (!inicmd) {
s = PL_Argv[0];
while (*s) {
/* Dosish shells will choke on slashes
in paths, fortunately, this is
important for zeroth arg only. */
if (*s == '/')
*s = '\\';
s++;
}
}
}
/* If EXECSHELL is set, we do not set */
if (!shell)
shell = ((_emx_env & 0x200)
? "c:/os2/cmd.exe"
: "c:/command.com");
nargs = shell_opt ? 2 : 1; /* shell file args */
exec_args[0] = shell;
exec_args[1] = shell_opt;
argsp = exec_args;
if (nargs == 2 && inicmd) {
/* Use the original cmd line */
/* XXXX This is good only until we refuse
quoted arguments... */
PL_Argv[0] = inicmd;
PL_Argv[1] = NULL;
}
} else if (!buf[0] && inicmd) { /* No file */
/* Start with the original cmdline. */
/* XXXX This is good only until we refuse
quoted arguments... */
PL_Argv[0] = inicmd;
PL_Argv[1] = NULL;
nargs = 2; /* shell -c */
}
while (a[1]) /* Get to the end */
a++;
a++; /* Copy finil NULL too */
while (a >= PL_Argv) {
*(a + nargs) = *a; /* PL_Argv was preallocated to be
long enough. */
a--;
}
while (--nargs >= 0) /* XXXX Discard const... */
PL_Argv[nargs] = (char*)argsp[nargs];
/* Enable pathless exec if #! (as pdksh). */
pass = (buf[0] == '#' ? 2 : 3);
goto retry;
}
}
/* Not found: restore errno */
errno = err;
}
} else if (errno == ENOEXEC) { /* Cannot transfer `real_name' via shell. */
if (rc < 0 && ckWARN(WARN_EXEC))
Perl_warner(aTHX_ packWARN(WARN_EXEC), "Can't %s script `%s' with ARGV[0] being `%s'",
((execf != EXECF_EXEC && execf != EXECF_TRUEEXEC)
? "spawn" : "exec"),
real_name, PL_Argv[0]);
goto warned;
} else if (errno == ENOENT) { /* Cannot transfer `real_name' via shell. */
if (rc < 0 && ckWARN(WARN_EXEC))
Perl_warner(aTHX_ packWARN(WARN_EXEC), "Can't %s `%s' with ARGV[0] being `%s' (looking for executables only, not found)",
((execf != EXECF_EXEC && execf != EXECF_TRUEEXEC)
? "spawn" : "exec"),
real_name, PL_Argv[0]);
goto warned;
}
} else if (rc < 0 && pass == 2 && errno == ENOENT) { /* File not found */
char *no_dir = strrchr(PL_Argv[0], '/');
/* Do as pdksh port does: if not found with /, try without
path. */
if (no_dir) {
PL_Argv[0] = no_dir + 1;
pass++;
goto retry;
}
}
if (rc < 0 && ckWARN(WARN_EXEC))
Perl_warner(aTHX_ packWARN(WARN_EXEC), "Can't %s \"%s\": %s\n",
((execf != EXECF_EXEC && execf != EXECF_TRUEEXEC)
? "spawn" : "exec"),
real_name, Strerror(errno));
warned:
if (rc < 0 && (execf != EXECF_SPAWN_NOWAIT)
&& ((trueflag & 0xFF) == P_WAIT))
rc = -1;
finish:
if (new_stderr != -1) { /* How can we use error codes? */
dup2(new_stderr, 2);
close(new_stderr);
fcntl(2, F_SETFD, fl_stderr);
} else if (nostderr)
close(2);
return rc;
}
/* Try converting 1-arg form to (usually shell-less) multi-arg form. */
int
do_spawn3(pTHX_ char *cmd, int execf, int flag)
{
register char **a;
register char *s;
char *shell, *copt, *news = NULL;
int rc, seenspace = 0, mergestderr = 0;
#ifdef TRYSHELL
if ((shell = getenv("EMXSHELL")) != NULL)
copt = "-c";
else if ((shell = getenv("SHELL")) != NULL)
copt = "-c";
else if ((shell = getenv("COMSPEC")) != NULL)
copt = "/C";
else
shell = "cmd.exe";
#else
/* Consensus on perl5-porters is that it is _very_ important to
have a shell which will not change between computers with the
same architecture, to avoid "action on a distance".
And to have simple build, this shell should be sh. */
shell = PL_sh_path;
copt = "-c";
#endif
while (*cmd && isSPACE(*cmd))
cmd++;
if (strnEQ(cmd,"/bin/sh",7) && isSPACE(cmd[7])) {
STRLEN l = strlen(PL_sh_path);
Newx(news, strlen(cmd) - 7 + l + 1, char);
strcpy(news, PL_sh_path);
strcpy(news + l, cmd + 7);
cmd = news;
}
/* save an extra exec if possible */
/* see if there are shell metacharacters in it */
if (*cmd == '.' && isSPACE(cmd[1]))
goto doshell;
if (strnEQ(cmd,"exec",4) && isSPACE(cmd[4]))
goto doshell;
for (s = cmd; *s && isALPHA(*s); s++) ; /* catch VAR=val gizmo */
if (*s == '=')
goto doshell;
for (s = cmd; *s; s++) {
if (*s != ' ' && !isALPHA(*s) && strchr("$&*(){}[]'\";\\|?<>~`\n",*s)) {
if (*s == '\n' && s[1] == '\0') {
*s = '\0';
break;
} else if (*s == '\\' && !seenspace) {
continue; /* Allow backslashes in names */
} else if (*s == '>' && s >= cmd + 3
&& s[-1] == '2' && s[1] == '&' && s[2] == '1'
&& isSPACE(s[-2]) ) {
char *t = s + 3;
while (*t && isSPACE(*t))
t++;
if (!*t) {
s[-2] = '\0';
mergestderr = 1;
break; /* Allow 2>&1 as the last thing */
}
}
/* We do not convert this to do_spawn_ve since shell
should be smart enough to start itself gloriously. */
doshell:
if (execf == EXECF_TRUEEXEC)
rc = execl(shell,shell,copt,cmd,(char*)0);
else if (execf == EXECF_EXEC)
rc = spawnl(P_OVERLAY,shell,shell,copt,cmd,(char*)0);
else if (execf == EXECF_SPAWN_NOWAIT)
rc = spawnl(P_NOWAIT,shell,shell,copt,cmd,(char*)0);
else if (execf == EXECF_SPAWN_BYFLAG)
rc = spawnl(flag,shell,shell,copt,cmd,(char*)0);
else {
/* In the ak code internal P_NOWAIT is P_WAIT ??? */
if (execf == EXECF_SYNC)
rc = spawnl(P_WAIT,shell,shell,copt,cmd,(char*)0);
else
rc = result(aTHX_ P_WAIT,
spawnl(P_NOWAIT,shell,shell,copt,cmd,(char*)0));
if (rc < 0 && ckWARN(WARN_EXEC))
Perl_warner(aTHX_ packWARN(WARN_EXEC), "Can't %s \"%s\": %s",
(execf == EXECF_SPAWN ? "spawn" : "exec"),
shell, Strerror(errno));
if (rc < 0)
rc = -1;
}
if (news)
Safefree(news);
return rc;
} else if (*s == ' ' || *s == '\t') {
seenspace = 1;
}
}
/* cmd="a" may lead to "sh", "-c", "\"$@\"", "a", "a.cmd", NULL */
Newx(PL_Argv, (s - cmd + 11) / 2, char*);
PL_Cmd = savepvn(cmd, s-cmd);
a = PL_Argv;
for (s = PL_Cmd; *s;) {
while (*s && isSPACE(*s)) s++;
if (*s)
*(a++) = s;
while (*s && !isSPACE(*s)) s++;
if (*s)
*s++ = '\0';
}
*a = NULL;
if (PL_Argv[0])
rc = do_spawn_ve(aTHX_ NULL, flag, execf, cmd, mergestderr);
else
rc = -1;
if (news)
Safefree(news);
do_execfree();
return rc;
}
#define ASPAWN_WAIT 0
#define ASPAWN_EXEC 1
#define ASPAWN_NOWAIT 2
/* Array spawn/exec. */
int
os2_aspawn_4(pTHX_ SV *really, register SV **args, I32 cnt, int execing)
{
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 (cnt) {
Newx(PL_Argv, cnt + 3, char*); /* 3 extra to expand #! */
a = PL_Argv;
if (cnt > 1 && SvNIOKp(*argp) && !SvPOKp(*argp)) {
flag = SvIVx(*argp);
flag_set = 1;
} else
--argp;
while (++argp < last) {
if (*argp)
*a++ = SvPVx(*argp, n_a);
else
*a++ = "";
}
*a = NULL;
if ( flag_set && (a == PL_Argv + 1)
&& !really && execing == ASPAWN_WAIT ) { /* One arg? */
rc = do_spawn3(aTHX_ a[-1], EXECF_SPAWN_BYFLAG, flag);
} 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();
return rc;
}
/* Array spawn. */
int
os2_do_aspawn(pTHX_ SV *really, register SV **vmark, register SV **vsp)
{
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_aspawn_4(aTHX_ really, vmark + 1, vsp - vmark, ASPAWN_EXEC);
}
int
os2_do_spawn(pTHX_ char *cmd)
{
return do_spawn3(aTHX_ cmd, EXECF_SPAWN, 0);
}
int
do_spawn_nowait(pTHX_ char *cmd)
{
return do_spawn3(aTHX_ cmd, EXECF_SPAWN_NOWAIT,0);
}
bool
Perl_do_exec(pTHX_ const char *cmd)
{
do_spawn3(aTHX_ cmd, EXECF_EXEC, 0);
return FALSE;
}
bool
os2exec(pTHX_ char *cmd)
{
return do_spawn3(aTHX_ cmd, EXECF_TRUEEXEC, 0);
}
PerlIO *
my_syspopen4(pTHX_ char *cmd, char *mode, I32 cnt, SV** args)
{
#ifndef USE_POPEN
int p[2];
register I32 this, that, newfd;
register I32 pid;
SV *sv;
int fh_fl = 0; /* Pacify the warning */
/* `this' is what we use in the parent, `that' in the child. */
this = (*mode == 'w');
that = !this;
if (PL_tainting) {
taint_env();
taint_proper("Insecure %s%s", "EXEC");
}
if (pipe(p) < 0)
return NULL;
/* Now we need to spawn the child. */
if (p[this] == (*mode == 'r')) { /* if fh 0/1 was initially closed. */
int new = dup(p[this]);
if (new == -1)
goto closepipes;
close(p[this]);
p[this] = new;
}
newfd = dup(*mode == 'r'); /* Preserve std* */
if (newfd == -1) {
/* This cannot happen due to fh being bad after pipe(), since
pipe() should have created fh 0 and 1 even if they were
initially closed. But we closed p[this] before. */
if (errno != EBADF) {
closepipes:
close(p[0]);
close(p[1]);
return NULL;
}
} else
fh_fl = fcntl(*mode == 'r', F_GETFD);
if (p[that] != (*mode == 'r')) { /* if fh 0/1 was initially closed. */
dup2(p[that], *mode == 'r');
close(p[that]);
}
/* Where is `this' and newfd now? */
fcntl(p[this], F_SETFD, FD_CLOEXEC);
if (newfd != -1)
fcntl(newfd, F_SETFD, FD_CLOEXEC);
if (cnt) { /* Args: "Real cmd", before first arg, the last, execing */
pid = os2_aspawn_4(aTHX_ NULL, 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 */
dup2(newfd, *mode == 'r'); /* Return std* back. */
close(newfd);
fcntl(*mode == 'r', F_SETFD, fh_fl);
} else
fcntl(*mode == 'r', F_SETFD, fh_fl);
if (p[that] == (*mode == 'r'))
close(p[that]);
if (pid == -1) {
close(p[this]);
return NULL;
}
if (p[that] < p[this]) { /* Make fh as small as possible */
dup2(p[this], p[that]);
close(p[this]);
p[this] = p[that];
}
sv = *av_fetch(PL_fdpid,p[this],TRUE);
(void)SvUPGRADE(sv,SVt_IV);
SvIVX(sv) = pid;
PL_forkprocess = pid;
return PerlIO_fdopen(p[this], mode);
#else /* USE_POPEN */
PerlIO *res;
SV *sv;
if (cnt)
Perl_croak(aTHX_ "List form of piped open not implemented");
# ifdef TRYSHELL
res = popen(cmd, mode);
# else
char *shell = getenv("EMXSHELL");
my_setenv("EMXSHELL", PL_sh_path);
res = popen(cmd, mode);
my_setenv("EMXSHELL", shell);
# endif
sv = *av_fetch(PL_fdpid, PerlIO_fileno(res), TRUE);
(void)SvUPGRADE(sv,SVt_IV);
SvIVX(sv) = -1; /* A cooky. */
return res;
#endif /* USE_POPEN */
}
PerlIO *
my_syspopen(pTHX_ char *cmd, char *mode)
{
return my_syspopen4(aTHX_ cmd, mode, 0, NULL);
}
/******************************************************************/
#ifndef HAS_FORK
int
fork(void)
{
Perl_croak_nocontext(PL_no_func, "Unsupported function fork");
errno = EINVAL;
return -1;
}
#endif
/*******************************************************************/
/* not implemented in EMX 0.9d */
char * ctermid(char *s) { return 0; }
#ifdef MYTTYNAME /* was not in emx0.9a */
void * ttyname(x) { return 0; }
#endif
/*****************************************************************************/
/* not implemented in C Set++ */
#ifndef __EMX__
int setuid(x) { errno = EINVAL; return -1; }
int setgid(x) { errno = EINVAL; return -1; }
#endif
/*****************************************************************************/
/* stat() hack for char/block device */
#if OS2_STAT_HACK
enum os2_stat_extra { /* EMX 0.9d fix 4 defines up to 0100000 */
os2_stat_archived = 0x1000000, /* 0100000000 */
os2_stat_hidden = 0x2000000, /* 0200000000 */
os2_stat_system = 0x4000000, /* 0400000000 */
os2_stat_force = 0x8000000, /* Do not ignore flags on chmod */
};
#define OS2_STAT_SPECIAL (os2_stat_system | os2_stat_archived | os2_stat_hidden)
static void
massage_os2_attr(struct stat *st)
{
if ( ((st->st_mode & S_IFMT) != S_IFREG
&& (st->st_mode & S_IFMT) != S_IFDIR)
|| !(st->st_attr & (FILE_ARCHIVED | FILE_HIDDEN | FILE_SYSTEM)))
return;
if ( st->st_attr & FILE_ARCHIVED )
st->st_mode |= (os2_stat_archived | os2_stat_force);
if ( st->st_attr & FILE_HIDDEN )
st->st_mode |= (os2_stat_hidden | os2_stat_force);
if ( st->st_attr & FILE_SYSTEM )
st->st_mode |= (os2_stat_system | os2_stat_force);
}
/* First attempt used DosQueryFSAttach which crashed the system when
used with 5.001. Now just look for /dev/. */
int
os2_stat(const char *name, struct stat *st)
{
static int ino = SHRT_MAX;
STRLEN l = strlen(name);
if ( ( l < 8 || l > 9) || strnicmp(name, "/dev/", 5) != 0
|| ( stricmp(name + 5, "con") != 0
&& stricmp(name + 5, "tty") != 0
&& stricmp(name + 5, "nul") != 0
&& stricmp(name + 5, "null") != 0) ) {
int s = stat(name, st);
if (s)
return s;
massage_os2_attr(st);
return 0;
}
memset(st, 0, sizeof *st);
st->st_mode = S_IFCHR|0666;
MUTEX_LOCK(&perlos2_state_mutex);
st->st_ino = (ino-- & 0x7FFF);
MUTEX_UNLOCK(&perlos2_state_mutex);
st->st_nlink = 1;
return 0;
}
int
os2_fstat(int handle, struct stat *st)
{
int s = fstat(handle, st);
if (s)
return s;
massage_os2_attr(st);
return 0;
}
#undef chmod
int
os2_chmod (const char *name, int pmode) /* Modelled after EMX src/lib/io/chmod.c */
{
int attr, rc;
if (!(pmode & os2_stat_force))
return chmod(name, pmode);
attr = __chmod (name, 0, 0); /* Get attributes */
if (attr < 0)
return -1;
if (pmode & S_IWRITE)
attr &= ~FILE_READONLY;
else
attr |= FILE_READONLY;
/* New logic */
attr &= ~(FILE_ARCHIVED | FILE_HIDDEN | FILE_SYSTEM);
if ( pmode & os2_stat_archived )
attr |= FILE_ARCHIVED;
if ( pmode & os2_stat_hidden )
attr |= FILE_HIDDEN;
if ( pmode & os2_stat_system )
attr |= FILE_SYSTEM;
rc = __chmod (name, 1, attr);
if (rc >= 0) rc = 0;
return rc;
}
#endif
#ifdef USE_PERL_SBRK
/* SBRK() emulation, mostly moved to malloc.c. */
void *
sys_alloc(int size) {
void *got;
APIRET rc = DosAllocMem(&got, size, PAG_COMMIT | PAG_WRITE);
if (rc == ERROR_NOT_ENOUGH_MEMORY) {
return (void *) -1;
} else if ( rc )
Perl_croak_nocontext("Got an error from DosAllocMem: %li", (long)rc);
return got;
}
#endif /* USE_PERL_SBRK */
/* tmp path */
const char *tmppath = TMPPATH1;
void
settmppath()
{
char *p = getenv("TMP"), *tpath;
int len;
if (!p) p = getenv("TEMP");
if (!p) p = getenv("TMPDIR");
if (!p) return;
len = strlen(p);
tpath = (char *)malloc(len + strlen(TMPPATH1) + 2);
if (tpath) {
strcpy(tpath, p);
tpath[len] = '/';
strcpy(tpath + len + 1, TMPPATH1);
tmppath = tpath;
}
}
#include "XSUB.h"
XS(XS_File__Copy_syscopy)
{
dXSARGS;
if (items < 2 || items > 3)
Perl_croak_nocontext("Usage: File::Copy::syscopy(src,dst,flag=0)");
{
STRLEN n_a;
char * src = (char *)SvPV(ST(0),n_a);
char * dst = (char *)SvPV(ST(1),n_a);
U32 flag;
int RETVAL, rc;
dXSTARG;
if (items < 3)
flag = 0;
else {
flag = (unsigned long)SvIV(ST(2));
}
RETVAL = !CheckOSError(DosCopy(src, dst, flag));
XSprePUSH; PUSHi((IV)RETVAL);
}
XSRETURN(1);
}
/* APIRET APIENTRY DosReplaceModule (PCSZ pszOld, PCSZ pszNew, PCSZ pszBackup); */
DeclOSFuncByORD(ULONG,replaceModule,ORD_DosReplaceModule,
(char *old, char *new, char *backup), (old, new, backup))
XS(XS_OS2_replaceModule); /* prototype to pass -Wmissing-prototypes */
XS(XS_OS2_replaceModule)
{
dXSARGS;
if (items < 1 || items > 3)
Perl_croak(aTHX_ "Usage: OS2::replaceModule(target [, source [, backup]])");
{
char * target = (char *)SvPV_nolen(ST(0));
char * source = (items < 2) ? NULL : (char *)SvPV_nolen(ST(1));
char * backup = (items < 3) ? NULL : (char *)SvPV_nolen(ST(2));
if (!replaceModule(target, source, backup))
croak_with_os2error("replaceModule() error");
}
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"
#undef PERL_PATCHLEVEL_H_IMPLICIT
char *
mod2fname(pTHX_ SV *sv)
{
int pos = 6, len, avlen;
unsigned int sum = 0;
char *s;
STRLEN n_a;
if (!SvROK(sv)) Perl_croak_nocontext("Not a reference given to mod2fname");
sv = SvRV(sv);
if (SvTYPE(sv) != SVt_PVAV)
Perl_croak_nocontext("Not array reference given to mod2fname");
avlen = av_len((AV*)sv);
if (avlen < 0)
Perl_croak_nocontext("Empty array reference given to mod2fname");
s = SvPV(*av_fetch((AV*)sv, avlen, FALSE), n_a);
strncpy(fname, s, 8);
len = strlen(s);
if (len < 6) pos = len;
while (*s) {
sum = 33 * sum + *(s++); /* Checksumming first chars to
* get the capitalization into c.s. */
}
avlen --;
while (avlen >= 0) {
s = SvPV(*av_fetch((AV*)sv, avlen, FALSE), n_a);
while (*s) {
sum = 33 * sum + *(s++); /* 7 is primitive mod 13. */
}
avlen --;
}
/* We always load modules as *specific* DLLs, and with the full name.
When loading a specific DLL by its full name, one cannot get a
different DLL, even if a DLL with the same basename is loaded already.
Thus there is no need to include the version into the mangling scheme. */
#if 0
sum += PERL_VERSION * 200 + PERL_SUBVERSION * 2; /* Up to 5.6.1 */
#else
# ifndef COMPATIBLE_VERSION_SUM /* Binary compatibility with the 5.00553 binary */
# define COMPATIBLE_VERSION_SUM (5 * 200 + 53 * 2)
# endif
sum += COMPATIBLE_VERSION_SUM;
#endif
fname[pos] = 'A' + (sum % 26);
fname[pos + 1] = 'A' + (sum / 26 % 26);
fname[pos + 2] = '\0';
return (char *)fname;
}
XS(XS_DynaLoader_mod2fname)
{
dXSARGS;
if (items != 1)
Perl_croak_nocontext("Usage: DynaLoader::mod2fname(sv)");
{
SV * sv = ST(0);
char * RETVAL;
dXSTARG;
RETVAL = mod2fname(aTHX_ sv);
sv_setpv(TARG, RETVAL);
XSprePUSH; PUSHTARG;
}
XSRETURN(1);
}
char *
os2error(int rc)
{
dTHX;
ULONG len;
char *s;
int number = SvTRUE(get_sv("OS2::nsyserror", GV_ADD));
if (!(_emx_env & 0x200)) return ""; /* Nop if not OS/2. */
if (rc == 0)
return "";
if (number) {
sprintf(os2error_buf, "SYS%04d=%#x: ", rc, rc);
s = os2error_buf + strlen(os2error_buf);
} else
s = os2error_buf;
if (DosGetMessage(NULL, 0, s, sizeof(os2error_buf) - 1 - (s-os2error_buf),
rc, "OSO001.MSG", &len)) {
char *name = "";
if (!number) {
sprintf(os2error_buf, "SYS%04d=%#x: ", rc, rc);
s = os2error_buf + strlen(os2error_buf);
}
switch (rc) {
case PMERR_INVALID_HWND:
name = "PMERR_INVALID_HWND";
break;
case PMERR_INVALID_HMQ:
name = "PMERR_INVALID_HMQ";
break;
case PMERR_CALL_FROM_WRONG_THREAD:
name = "PMERR_CALL_FROM_WRONG_THREAD";
break;
case PMERR_NO_MSG_QUEUE:
name = "PMERR_NO_MSG_QUEUE";
break;
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 ? "=" : ""));
} else {
s[len] = '\0';
if (len && s[len - 1] == '\n')
s[--len] = 0;
if (len && s[len - 1] == '\r')
s[--len] = 0;
if (len && s[len - 1] == '.')
s[--len] = 0;
if (len >= 10 && number && strnEQ(s, os2error_buf, 7)
&& s[7] == ':' && s[8] == ' ')
/* Some messages start with SYSdddd:, some not */
Move(s + 9, s, (len -= 9) + 1, char);
}
return os2error_buf;
}
void
ResetWinError(void)
{
WinError_2_Perl_rc;
}
void
CroakWinError(int die, char *name)
{
FillWinError;
if (die && Perl_rc)
croak_with_os2error(name ? name : "Win* API call");
}
static char *
dllname2buffer(pTHX_ char *buf, STRLEN l)
{
char *o;
STRLEN ll;
SV *dll = NULL;
dll = module_name(mod_name_full);
o = SvPV(dll, ll);
if (ll < l)
memcpy(buf,o,ll);
SvREFCNT_dec(dll);
return (ll >= l ? "???" : buf);
}
static char *
execname2buffer(char *buf, STRLEN l, char *oname)
{
char *p, *orig = oname, ok = oname != NULL;
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 && *oname != '/' && *oname != '\\')
ok = 0;
} else if (ok && tolower(*oname) != tolower(*p))
ok = 0;
p++;
oname++;
}
if (ok) { /* orig matches the real name. Use orig: */
strcpy(buf, orig); /* _execname() is always uppercased */
p = buf;
while (*p) {
if (*p == '\\')
*p = '/';
p++;
}
}
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(name = "PERLLIB_" STRINGIFY(PERL_REVISION)
STRINGIFY(PERL_VERSION) STRINGIFY(PERL_SUBVERSION)
"_PREFIX");
if (!newp)
newp = getenv(name = "PERLLIB_" STRINGIFY(PERL_REVISION)
STRINGIFY(PERL_VERSION) "_PREFIX");
if (!newp)
newp = getenv(name = "PERLLIB_" STRINGIFY(PERL_REVISION) "_PREFIX");
if (!newp)
newp = getenv(name = "PERLLIB_PREFIX");
if (newp) {
char *s, b[300];
oldp = 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. */
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)
return s;
if (l == 0)
l = strlen(s);
if (l < oldl || strnicmp(oldp, s, oldl) != 0)
return s;
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;
}
unsigned long
Perl_hab_GET() /* Needed if perl.h cannot be included */
{
return perl_hab_GET();
}
static void
Create_HMQ(int serve, char *message) /* Assumes morphing */
{
unsigned fpflag = _control87(0,0);
init_PMWIN_entries();
/* 64 messages if before OS/2 3.0, ignored otherwise */
Perl_hmq = (*PMWIN_entries.CreateMsgQueue)(perl_hab_GET(), 64);
if (!Perl_hmq) {
dTHX;
SAVEINT(rmq_cnt); /* Allow catch()ing. */
if (rmq_cnt++)
_exit(188); /* Panic can try to create a window. */
CroakWinError(1, message ? message : "Cannot create a message queue");
}
if (serve != -1)
(*PMWIN_entries.CancelShutdown)(Perl_hmq, !serve);
/* We may have loaded some modules */
_control87(fpflag, MCW_EM); /* Some modules reset FP flags on (un)load */
}
#define REGISTERMQ_WILL_SERVE 1
#define REGISTERMQ_IMEDIATE_UNMORPH 2
HMQ
Perl_Register_MQ(int serve)
{
if (Perl_hmq_refcnt <= 0) {
PPIB pib;
PTIB tib;
Perl_hmq_refcnt = 0; /* Be extra safe */
DosGetInfoBlocks(&tib, &pib);
if (!Perl_morph_refcnt) {
Perl_os2_initial_mode = pib->pib_ultype;
/* Try morphing into a PM application. */
if (pib->pib_ultype != 3) /* 2 is VIO */
pib->pib_ultype = 3; /* 3 is PM */
}
Create_HMQ(-1, /* We do CancelShutdown ourselves */
"Cannot create a message queue, or morph to a PM application");
if ((serve & REGISTERMQ_IMEDIATE_UNMORPH)) {
if (!Perl_morph_refcnt && Perl_os2_initial_mode != 3)
pib->pib_ultype = Perl_os2_initial_mode;
}
}
if (serve & REGISTERMQ_WILL_SERVE) {
if ( Perl_hmq_servers <= 0 /* Safe to inform us on shutdown, */
&& Perl_hmq_refcnt > 0 ) /* this was switched off before... */
(*PMWIN_entries.CancelShutdown)(Perl_hmq, 0);
Perl_hmq_servers++;
} else if (!Perl_hmq_servers) /* Do not inform us on shutdown */
(*PMWIN_entries.CancelShutdown)(Perl_hmq, 1);
Perl_hmq_refcnt++;
if (!(serve & REGISTERMQ_IMEDIATE_UNMORPH))
Perl_morph_refcnt++;
return Perl_hmq;
}
int
Perl_Serve_Messages(int force)
{
int cnt = 0;
QMSG msg;
if (Perl_hmq_servers > 0 && !force)
return 0;
if (Perl_hmq_refcnt <= 0)
Perl_croak_nocontext("No message queue");
while ((*PMWIN_entries.PeekMsg)(Perl_hab, &msg, NULLHANDLE, 0, 0, PM_REMOVE)) {
cnt++;
if (msg.msg == WM_QUIT)
Perl_croak_nocontext("QUITing...");
(*PMWIN_entries.DispatchMsg)(Perl_hab, &msg);
}
return cnt;
}
int
Perl_Process_Messages(int force, I32 *cntp)
{
QMSG msg;
if (Perl_hmq_servers > 0 && !force)
return 0;
if (Perl_hmq_refcnt <= 0)
Perl_croak_nocontext("No message queue");
while ((*PMWIN_entries.GetMsg)(Perl_hab, &msg, NULLHANDLE, 0, 0)) {
if (cntp)
(*cntp)++;
(*PMWIN_entries.DispatchMsg)(Perl_hab, &msg);
if (msg.msg == WM_DESTROY)
return -1;
if (msg.msg == WM_CREATE)
return +1;
}
Perl_croak_nocontext("QUITing...");
}
void
Perl_Deregister_MQ(int serve)
{
if (serve & REGISTERMQ_WILL_SERVE)
Perl_hmq_servers--;
if (--Perl_hmq_refcnt <= 0) {
unsigned fpflag = _control87(0,0);
init_PMWIN_entries(); /* To be extra safe */
(*PMWIN_entries.DestroyMsgQueue)(Perl_hmq);
Perl_hmq = 0;
/* We may have (un)loaded some modules */
_control87(fpflag, MCW_EM); /* Some modules reset FP flags on (un)load */
} else if ((serve & REGISTERMQ_WILL_SERVE) && Perl_hmq_servers <= 0)
(*PMWIN_entries.CancelShutdown)(Perl_hmq, 1); /* Last server exited */
if (!(serve & REGISTERMQ_IMEDIATE_UNMORPH) && (--Perl_morph_refcnt <= 0)) {
/* Try morphing back from a PM application. */
PPIB pib;
PTIB tib;
DosGetInfoBlocks(&tib, &pib);
if (pib->pib_ultype == 3) /* 3 is PM */
pib->pib_ultype = Perl_os2_initial_mode;
else
Perl_warn_nocontext("Unexpected program mode %d when morphing back from PM",
pib->pib_ultype);
}
}
#define sys_is_absolute(path) ( isALPHA((path)[0]) && (path)[1] == ':' \
&& ((path)[2] == '/' || (path)[2] == '\\'))
#define sys_is_rooted _fnisabs
#define sys_is_relative _fnisrel
#define current_drive _getdrive
#undef chdir /* Was _chdir2. */
#define sys_chdir(p) (chdir(p) == 0)
#define change_drive(d) (_chdrive(d), (current_drive() == toupper(d)))
XS(XS_OS2_Error)
{
dXSARGS;
if (items != 2)
Perl_croak_nocontext("Usage: OS2::Error(harderr, exception)");
{
int arg1 = SvIV(ST(0));
int arg2 = SvIV(ST(1));
int a = ((arg1 ? FERR_ENABLEHARDERR : FERR_DISABLEHARDERR)
| (arg2 ? FERR_ENABLEEXCEPTION : FERR_DISABLEEXCEPTION));
int RETVAL = ((arg1 ? 1 : 0) | (arg2 ? 2 : 0));
unsigned long rc;
if (CheckOSError(DosError(a)))
Perl_croak_nocontext("DosError(%d) failed: %s", a, os2error(Perl_rc));
ST(0) = sv_newmortal();
if (DOS_harderr_state >= 0)
sv_setiv(ST(0), DOS_harderr_state);
DOS_harderr_state = RETVAL;
}
XSRETURN(1);
}
XS(XS_OS2_Errors2Drive)
{
dXSARGS;
if (items != 1)
Perl_croak_nocontext("Usage: OS2::Errors2Drive(drive)");
{
STRLEN n_a;
SV *sv = ST(0);
int suppress = SvOK(sv);
char *s = suppress ? SvPV(sv, n_a) : NULL;
char drive = (s ? *s : 0);
unsigned long rc;
if (suppress && !isALPHA(drive))
Perl_croak_nocontext("Non-char argument '%c' to OS2::Errors2Drive()", drive);
if (CheckOSError(DosSuppressPopUps((suppress
? SPU_ENABLESUPPRESSION
: SPU_DISABLESUPPRESSION),
drive)))
Perl_croak_nocontext("DosSuppressPopUps(%c) failed: %s", drive,
os2error(Perl_rc));
ST(0) = sv_newmortal();
if (DOS_suppression_state > 0)
sv_setpvn(ST(0), &DOS_suppression_state, 1);
else if (DOS_suppression_state == 0)
sv_setpvn(ST(0), "", 0);
DOS_suppression_state = drive;
}
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 *);
XS(XS_OS2_Timer)
{
dXSARGS;
static ULONG freq;
unsigned long long count;
ULONG rc;
if (items != 0)
Perl_croak_nocontext("Usage: OS2::Timer()");
if (!freq) {
*(PFN*)&pDosTmrQueryFreq = loadByOrdinal(ORD_DosTmrQueryFreq, 0);
*(PFN*)&pDosTmrQueryTime = loadByOrdinal(ORD_DosTmrQueryTime, 0);
MUTEX_LOCK(&perlos2_state_mutex);
if (!freq)
if (CheckOSError(pDosTmrQueryFreq(&freq)))
croak_with_os2error("DosTmrQueryFreq");
MUTEX_UNLOCK(&perlos2_state_mutex);
}
if (CheckOSError(pDosTmrQueryTime(&count)))
croak_with_os2error("DosTmrQueryTime");
{
dXSTARG;
XSprePUSH; PUSHn(((NV)count)/freq);
}
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",
"TECHNOLOGY",
"DRIVER_VERSION",
"WIDTH",
"HEIGHT",
"WIDTH_IN_CHARS",
"HEIGHT_IN_CHARS",
"HORIZONTAL_RESOLUTION",
"VERTICAL_RESOLUTION",
"CHAR_WIDTH",
"CHAR_HEIGHT",
"SMALL_CHAR_WIDTH",
"SMALL_CHAR_HEIGHT",
"COLORS",
"COLOR_PLANES",
"COLOR_BITCOUNT",
"COLOR_TABLE_SUPPORT",
"MOUSE_BUTTONS",
"FOREGROUND_MIX_SUPPORT",
"BACKGROUND_MIX_SUPPORT",
"VIO_LOADABLE_FONTS",
"WINDOW_BYTE_ALIGNMENT",
"BITMAP_FORMATS",
"RASTER_CAPS",
"MARKER_HEIGHT",
"MARKER_WIDTH",
"DEVICE_FONTS",
"GRAPHICS_SUBSET",
"GRAPHICS_VERSION",
"GRAPHICS_VECTOR_SUBSET",
"DEVICE_WINDOWING",
"ADDITIONAL_GRAPHICS",
"PHYS_COLORS",
"COLOR_INDEX",
"GRAPHICS_CHAR_WIDTH",
"GRAPHICS_CHAR_HEIGHT",
"HORIZONTAL_FONT_RES",
"VERTICAL_FONT_RES",
"DEVICE_FONT_SIM",
"LINEWIDTH_THICK",
"DEVICE_POLYSET_POINTS",
};
enum {
DevCap_dc, DevCap_hwnd
};
HDC (*pWinOpenWindowDC) (HWND hwnd);
HMF (*pDevCloseDC) (HDC hdc);
HDC (*pDevOpenDC) (HAB hab, LONG lType, PCSZ pszToken, LONG lCount,
PDEVOPENDATA pdopData, HDC hdcComp);
BOOL (*pDevQueryCaps) (HDC hdc, LONG lStart, LONG lCount, PLONG alArray);
XS(XS_OS2_DevCap)
{
dXSARGS;
if (items > 2)
Perl_croak_nocontext("Usage: OS2::DevCap()");
{
/* Device Capabilities Data Buffer (10 extra w.r.t. Warp 4.5) */
LONG si[CAPS_DEVICE_POLYSET_POINTS - CAPS_FAMILY + 1];
int i = 0, j = 0, how = DevCap_dc;
HDC hScreenDC;
DEVOPENSTRUC doStruc= {0L, (PSZ)"DISPLAY", NULL, 0L, 0L, 0L, 0L, 0L, 0L};
ULONG rc1 = NO_ERROR;
HWND hwnd;
static volatile int devcap_loaded;
if (!devcap_loaded) {
*(PFN*)&pWinOpenWindowDC = loadByOrdinal(ORD_WinOpenWindowDC, 0);
*(PFN*)&pDevOpenDC = loadByOrdinal(ORD_DevOpenDC, 0);
*(PFN*)&pDevCloseDC = loadByOrdinal(ORD_DevCloseDC, 0);
*(PFN*)&pDevQueryCaps = loadByOrdinal(ORD_DevQueryCaps, 0);
devcap_loaded = 1;
}
if (items >= 2)
how = SvIV(ST(1));
if (!items) { /* Get device contents from PM */
hScreenDC = pDevOpenDC(perl_hab_GET(), OD_MEMORY, (PSZ)"*", 0,
(PDEVOPENDATA)&doStruc, NULLHANDLE);
if (CheckWinError(hScreenDC))
croak_with_os2error("DevOpenDC() failed");
} else if (how == DevCap_dc)
hScreenDC = (HDC)SvIV(ST(0));
else { /* DevCap_hwnd */
if (!Perl_hmq)
Perl_croak(aTHX_ "Getting a window's device context without a message queue would lock PM");
hwnd = (HWND)SvIV(ST(0));
hScreenDC = pWinOpenWindowDC(hwnd); /* No need to DevCloseDC() */
if (CheckWinError(hScreenDC))
croak_with_os2error("WinOpenWindowDC() failed");
}
if (CheckWinError(pDevQueryCaps(hScreenDC,
CAPS_FAMILY, /* W3 documented caps */
CAPS_DEVICE_POLYSET_POINTS
- 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");
XSRETURN(j);
}
}
LONG (*pWinQuerySysValue) (HWND hwndDesktop, LONG iSysValue);
BOOL (*pWinSetSysValue) (HWND hwndDesktop, LONG iSysValue, LONG lValue);
const char * const sv_keys[] = {
"SWAPBUTTON",
"DBLCLKTIME",
"CXDBLCLK",
"CYDBLCLK",
"CXSIZEBORDER",
"CYSIZEBORDER",
"ALARM",
"7",
"8",
"CURSORRATE",
"FIRSTSCROLLRATE",
"SCROLLRATE",
"NUMBEREDLISTS",
"WARNINGFREQ",
"NOTEFREQ",
"ERRORFREQ",
"WARNINGDURATION",
"NOTEDURATION",
"ERRORDURATION",
"19",
"CXSCREEN",
"CYSCREEN",
"CXVSCROLL",
"CYHSCROLL",
"CYVSCROLLARROW",
"CXHSCROLLARROW",
"CXBORDER",
"CYBORDER",
"CXDLGFRAME",
"CYDLGFRAME",
"CYTITLEBAR",
"CYVSLIDER",
"CXHSLIDER",
"CXMINMAXBUTTON",
"CYMINMAXBUTTON",
"CYMENU",
"CXFULLSCREEN",
"CYFULLSCREEN",
"CXICON",
"CYICON",
"CXPOINTER",
"CYPOINTER",
"DEBUG",
"CPOINTERBUTTONS",
"POINTERLEVEL",
"CURSORLEVEL",
"TRACKRECTLEVEL",
"CTIMERS",
"MOUSEPRESENT",
"CXALIGN",
"CYALIGN",
"DESKTOPWORKAREAYTOP",
"DESKTOPWORKAREAYBOTTOM",
"DESKTOPWORKAREAXRIGHT",
"DESKTOPWORKAREAXLEFT",
"55",
"NOTRESERVED",
"EXTRAKEYBEEP",
"SETLIGHTS",
"INSERTMODE",
"60",
"61",
"62",
"63",
"MENUROLLDOWNDELAY",
"MENUROLLUPDELAY",
"ALTMNEMONIC",
"TASKLISTMOUSEACCESS",
"CXICONTEXTWIDTH",
"CICONTEXTLINES",
"CHORDTIME",
"CXCHORD",
"CYCHORD",
"CXMOTIONSTART",
"CYMOTIONSTART",
"BEGINDRAG",
"ENDDRAG",
"SINGLESELECT",
"OPEN",
"CONTEXTMENU",
"CONTEXTHELP",
"TEXTEDIT",
"BEGINSELECT",
"ENDSELECT",
"BEGINDRAGKB",
"ENDDRAGKB",
"SELECTKB",
"OPENKB",
"CONTEXTMENUKB",
"CONTEXTHELPKB",
"TEXTEDITKB",
"BEGINSELECTKB",
"ENDSELECTKB",
"ANIMATION",
"ANIMATIONSPEED",
"MONOICONS",
"KBDALTERED",
"PRINTSCREEN", /* 97, the last one on one of the DDK header */
"LOCKSTARTINPUT",
"DYNAMICDRAG",
"100",
"101",
"102",
"103",
"104",
"105",
"106",
"107",
/* "CSYSVALUES",*/
/* In recent DDK the limit is 108 */
};
XS(XS_OS2_SysValues)
{
dXSARGS;
if (items > 2)
Perl_croak_nocontext("Usage: OS2::SysValues(which = -1, hwndDesktop = HWND_DESKTOP)");
{
int i = 0, j = 0, which = -1;
HWND hwnd = HWND_DESKTOP;
static volatile int sv_loaded;
LONG RETVAL;
if (!sv_loaded) {
*(PFN*)&pWinQuerySysValue = loadByOrdinal(ORD_WinQuerySysValue, 0);
sv_loaded = 1;
}
if (items == 2)
hwnd = (HWND)SvIV(ST(1));
if (items >= 1)
which = (int)SvIV(ST(0));
if (which == -1) {
EXTEND(SP,2*C_ARRAY_LENGTH(sv_keys));
while (i < C_ARRAY_LENGTH(sv_keys)) {
ResetWinError();
RETVAL = pWinQuerySysValue(hwnd, i);
if ( !RETVAL
&& !(sv_keys[i][0] >= '0' && sv_keys[i][0] <= '9'
&& i <= SV_PRINTSCREEN) ) {
FillWinError;
if (Perl_rc) {
if (i > SV_PRINTSCREEN)
break; /* May be not present on older systems */
croak_with_os2error("SysValues():");
}
}
ST(j) = sv_newmortal();
sv_setpv(ST(j++), sv_keys[i]);
ST(j) = sv_newmortal();
sv_setiv(ST(j++), RETVAL);
i++;
}
XSRETURN(2 * i);
} else {
dXSTARG;
ResetWinError();
RETVAL = pWinQuerySysValue(hwnd, which);
if (!RETVAL) {
FillWinError;
if (Perl_rc)
croak_with_os2error("SysValues():");
}
XSprePUSH; PUSHi((IV)RETVAL);
}
}
}
XS(XS_OS2_SysValues_set)
{
dXSARGS;
if (items < 2 || items > 3)
Perl_croak_nocontext("Usage: OS2::SysValues_set(which, val, hwndDesktop = HWND_DESKTOP)");
{
int which = (int)SvIV(ST(0));
LONG val = (LONG)SvIV(ST(1));
HWND hwnd = HWND_DESKTOP;
static volatile int svs_loaded;
if (!svs_loaded) {
*(PFN*)&pWinSetSysValue = loadByOrdinal(ORD_WinSetSysValue, 0);
svs_loaded = 1;
}
if (items == 3)
hwnd = (HWND)SvIV(ST(2));
if (CheckWinError(pWinSetSysValue(hwnd, which, val)))
croak_with_os2error("SysValues_set()");
}
XSRETURN_YES;
}
#define QSV_MAX_WARP3 QSV_MAX_COMP_LENGTH
static const char * const si_fields[] = {
"MAX_PATH_LENGTH",
"MAX_TEXT_SESSIONS",
"MAX_PM_SESSIONS",
"MAX_VDM_SESSIONS",
"BOOT_DRIVE",
"DYN_PRI_VARIATION",
"MAX_WAIT",
"MIN_SLICE",
"MAX_SLICE",
"PAGE_SIZE",
"VERSION_MAJOR",
"VERSION_MINOR",
"VERSION_REVISION",
"MS_COUNT",
"TIME_LOW",
"TIME_HIGH",
"TOTPHYSMEM",
"TOTRESMEM",
"TOTAVAILMEM",
"MAXPRMEM",
"MAXSHMEM",
"TIMER_INTERVAL",
"MAX_COMP_LENGTH",
"FOREGROUND_FS_SESSION",
"FOREGROUND_PROCESS", /* Warp 3 toolkit defines up to this */
"NUMPROCESSORS",
"MAXHPRMEM",
"MAXHSHMEM",
"MAXPROCESSES",
"VIRTUALADDRESSLIMIT",
"INT10ENABLED", /* From $TOOLKIT-ddk\DDK\video\rel\os2c\include\base\os2\bsedos.h */
};
XS(XS_OS2_SysInfo)
{
dXSARGS;
if (items != 0)
Perl_croak_nocontext("Usage: OS2::SysInfo()");
{
/* System Information Data Buffer (10 extra w.r.t. Warp 4.5) */
ULONG si[C_ARRAY_LENGTH(si_fields) + 10];
APIRET rc = NO_ERROR; /* Return code */
int i = 0, j = 0, last = QSV_MAX_WARP3;
if (CheckOSError(DosQuerySysInfo(1L, /* Request documented system */
last, /* info for Warp 3 */
(PVOID)si,
sizeof(si))))
croak_with_os2error("DosQuerySysInfo() failed");
while (++last <= C_ARRAY_LENGTH(si)) {
if (CheckOSError(DosQuerySysInfo(last, last, /* One entry only */
(PVOID)(si+last-1),
sizeof(*si)))) {
if (Perl_rc != ERROR_INVALID_PARAMETER)
croak_with_os2error("DosQuerySysInfo() failed");
break;
}
}
last--; /* Count of successfully processed offsets */
EXTEND(SP,2*last);
while (i < last) {
ST(j) = sv_newmortal();
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_setuv(ST(j++), si[i]);
i++;
}
XSRETURN(2 * last);
}
}
XS(XS_OS2_SysInfoFor)
{
dXSARGS;
int count = (items == 2 ? (int)SvIV(ST(1)) : 1);
if (items < 1 || items > 2)
Perl_croak_nocontext("Usage: OS2::SysInfoFor(id[,count])");
{
/* System Information Data Buffer (10 extra w.r.t. Warp 4.5) */
ULONG si[C_ARRAY_LENGTH(si_fields) + 10];
APIRET rc = NO_ERROR; /* Return code */
int i = 0;
int start = (int)SvIV(ST(0));
if (count > C_ARRAY_LENGTH(si) || count <= 0)
Perl_croak(aTHX_ "unexpected count %d for OS2::SysInfoFor()", count);
if (CheckOSError(DosQuerySysInfo(start,
start + count - 1,
(PVOID)si,
sizeof(si))))
croak_with_os2error("DosQuerySysInfo() failed");
EXTEND(SP,count);
while (i < count) {
ST(i) = sv_newmortal();
sv_setiv(ST(i), si[i]);
i++;
}
}
XSRETURN(count);
}
XS(XS_OS2_BootDrive)
{
dXSARGS;
if (items != 0)
Perl_croak_nocontext("Usage: OS2::BootDrive()");
{
ULONG si[1] = {0}; /* System Information Data Buffer */
APIRET rc = NO_ERROR; /* Return code */
char c;
dXSTARG;
if (CheckOSError(DosQuerySysInfo(QSV_BOOT_DRIVE, QSV_BOOT_DRIVE,
(PVOID)si, sizeof(si))))
croak_with_os2error("DosQuerySysInfo() failed");
c = 'a' - 1 + si[0];
sv_setpvn(TARG, &c, 1);
XSprePUSH; PUSHTARG;
}
XSRETURN(1);
}
XS(XS_OS2_Beep)
{
dXSARGS;
if (items > 2) /* Defaults as for WinAlarm(ERROR) */
Perl_croak_nocontext("Usage: OS2::Beep(freq = 440, ms = 100)");
{
ULONG freq = (items > 0 ? (ULONG)SvUV(ST(0)) : 440);
ULONG ms = (items > 1 ? (ULONG)SvUV(ST(1)) : 100);
ULONG rc;
if (CheckOSError(DosBeep(freq, ms)))
croak_with_os2error("SysValues_set()");
}
XSRETURN_YES;
}
XS(XS_OS2_MorphPM)
{
dXSARGS;
if (items != 1)
Perl_croak_nocontext("Usage: OS2::MorphPM(serve)");
{
bool serve = SvOK(ST(0));
unsigned long pmq = perl_hmq_GET(serve);
dXSTARG;
XSprePUSH; PUSHi((IV)pmq);
}
XSRETURN(1);
}
XS(XS_OS2_UnMorphPM)
{
dXSARGS;
if (items != 1)
Perl_croak_nocontext("Usage: OS2::UnMorphPM(serve)");
{
bool serve = SvOK(ST(0));
perl_hmq_UNSET(serve);
}
XSRETURN(0);
}
XS(XS_OS2_Serve_Messages)
{
dXSARGS;
if (items != 1)
Perl_croak_nocontext("Usage: OS2::Serve_Messages(force)");
{
bool force = SvOK(ST(0));
unsigned long cnt = Perl_Serve_Messages(force);
dXSTARG;
XSprePUSH; PUSHi((IV)cnt);
}
XSRETURN(1);
}
XS(XS_OS2_Process_Messages)
{
dXSARGS;
if (items < 1 || items > 2)
Perl_croak_nocontext("Usage: OS2::Process_Messages(force [, cnt])");
{
bool force = SvOK(ST(0));
unsigned long cnt;
dXSTARG;
if (items == 2) {
I32 cntr;
SV *sv = ST(1);
(void)SvIV(sv); /* Force SvIVX */
if (!SvIOK(sv))
Perl_croak_nocontext("Can't upgrade count to IV");
cntr = SvIVX(sv);
cnt = Perl_Process_Messages(force, &cntr);
SvIVX(sv) = cntr;
} else {
cnt = Perl_Process_Messages(force, NULL);
}
XSprePUSH; PUSHi((IV)cnt);
}
XSRETURN(1);
}
XS(XS_Cwd_current_drive)
{
dXSARGS;
if (items != 0)
Perl_croak_nocontext("Usage: Cwd::current_drive()");
{
char RETVAL;
dXSTARG;
RETVAL = current_drive();
sv_setpvn(TARG, (char *)&RETVAL, 1);
XSprePUSH; PUSHTARG;
}
XSRETURN(1);
}
XS(XS_Cwd_sys_chdir)
{
dXSARGS;
if (items != 1)
Perl_croak_nocontext("Usage: Cwd::sys_chdir(path)");
{
STRLEN n_a;
char * path = (char *)SvPV(ST(0),n_a);
bool RETVAL;
RETVAL = sys_chdir(path);
ST(0) = boolSV(RETVAL);
if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
}
XSRETURN(1);
}
XS(XS_Cwd_change_drive)
{
dXSARGS;
if (items != 1)
Perl_croak_nocontext("Usage: Cwd::change_drive(d)");
{
STRLEN n_a;
char d = (char)*SvPV(ST(0),n_a);
bool RETVAL;
RETVAL = change_drive(d);
ST(0) = boolSV(RETVAL);
if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
}
XSRETURN(1);
}
XS(XS_Cwd_sys_is_absolute)
{
dXSARGS;
if (items != 1)
Perl_croak_nocontext("Usage: Cwd::sys_is_absolute(path)");
{
STRLEN n_a;
char * path = (char *)SvPV(ST(0),n_a);
bool RETVAL;
RETVAL = sys_is_absolute(path);
ST(0) = boolSV(RETVAL);
if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
}
XSRETURN(1);
}
XS(XS_Cwd_sys_is_rooted)
{
dXSARGS;
if (items != 1)
Perl_croak_nocontext("Usage: Cwd::sys_is_rooted(path)");
{
STRLEN n_a;
char * path = (char *)SvPV(ST(0),n_a);
bool RETVAL;
RETVAL = sys_is_rooted(path);
ST(0) = boolSV(RETVAL);
if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
}
XSRETURN(1);
}
XS(XS_Cwd_sys_is_relative)
{
dXSARGS;
if (items != 1)
Perl_croak_nocontext("Usage: Cwd::sys_is_relative(path)");
{
STRLEN n_a;
char * path = (char *)SvPV(ST(0),n_a);
bool RETVAL;
RETVAL = sys_is_relative(path);
ST(0) = boolSV(RETVAL);
if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
}
XSRETURN(1);
}
XS(XS_Cwd_sys_cwd)
{
dXSARGS;
if (items != 0)
Perl_croak_nocontext("Usage: Cwd::sys_cwd()");
{
char p[MAXPATHLEN];
char * RETVAL;
/* Can't use TARG, since tainting behaves differently */
RETVAL = _getcwd2(p, MAXPATHLEN);
ST(0) = sv_newmortal();
sv_setpv(ST(0), RETVAL);
#ifndef INCOMPLETE_TAINTS
SvTAINTED_on(ST(0));
#endif
}
XSRETURN(1);
}
XS(XS_Cwd_sys_abspath)
{
dXSARGS;
if (items > 2)
Perl_croak_nocontext("Usage: Cwd::sys_abspath(path = '.', dir = NULL)");
{
STRLEN n_a;
char * path = items ? (char *)SvPV(ST(0),n_a) : ".";
char * dir, *s, *t, *e;
char p[MAXPATHLEN];
char * RETVAL;
int l;
SV *sv;
if (items < 2)
dir = NULL;
else {
dir = (char *)SvPV(ST(1),n_a);
}
if (path[0] == '.' && (path[1] == '/' || path[1] == '\\')) {
path += 2;
}
if (dir == NULL) {
if (_abspath(p, path, MAXPATHLEN) == 0) {
RETVAL = p;
} else {
RETVAL = NULL;
}
} else {
/* Absolute with drive: */
if ( sys_is_absolute(path) ) {
if (_abspath(p, path, MAXPATHLEN) == 0) {
RETVAL = p;
} else {
RETVAL = NULL;
}
} else if (path[0] == '/' || path[0] == '\\') {
/* Rooted, but maybe on different drive. */
if (isALPHA(dir[0]) && dir[1] == ':' ) {
char p1[MAXPATHLEN];
/* Need to prepend the drive. */
p1[0] = dir[0];
p1[1] = dir[1];
Copy(path, p1 + 2, strlen(path) + 1, char);
RETVAL = p;
if (_abspath(p, p1, MAXPATHLEN) == 0) {
RETVAL = p;
} else {
RETVAL = NULL;
}
} else if (_abspath(p, path, MAXPATHLEN) == 0) {
RETVAL = p;
} else {
RETVAL = NULL;
}
} else {
/* Either path is relative, or starts with a drive letter. */
/* If the path starts with a drive letter, then dir is
relevant only if
a/b) it is absolute/x:relative on the same drive.
c) path is on current drive, and dir is rooted
In all the cases it is safe to drop the drive part
of the path. */
if ( !sys_is_relative(path) ) {
if ( ( ( sys_is_absolute(dir)
|| (isALPHA(dir[0]) && dir[1] == ':'
&& strnicmp(dir, path,1) == 0))
&& strnicmp(dir, path,1) == 0)
|| ( !(isALPHA(dir[0]) && dir[1] == ':')
&& toupper(path[0]) == current_drive())) {
path += 2;
} else if (_abspath(p, path, MAXPATHLEN) == 0) {
RETVAL = p; goto done;
} else {
RETVAL = NULL; goto done;
}
}
{
/* Need to prepend the absolute path of dir. */
char p1[MAXPATHLEN];
if (_abspath(p1, dir, MAXPATHLEN) == 0) {
int l = strlen(p1);
if (p1[ l - 1 ] != '/') {
p1[ l ] = '/';
l++;
}
Copy(path, p1 + l, strlen(path) + 1, char);
if (_abspath(p, p1, MAXPATHLEN) == 0) {
RETVAL = p;
} else {
RETVAL = NULL;
}
} else {
RETVAL = NULL;
}
}
done:
}
}
if (!RETVAL)
XSRETURN_EMPTY;
/* Backslashes are already converted to slashes. */
/* Remove trailing slashes */
l = strlen(RETVAL);
while (l > 0 && RETVAL[l-1] == '/')
l--;
ST(0) = sv_newmortal();
sv_setpvn( sv = (SV*)ST(0), RETVAL, l);
/* Remove duplicate slashes, skipping the first three, which
may be parts of a server-based path */
s = t = 3 + SvPV_force(sv, n_a);
e = SvEND(sv);
/* Do not worry about multibyte chars here, this would contradict the
eventual UTFization, and currently most other places break too... */
while (s < e) {
if (s[0] == t[-1] && s[0] == '/')
s++; /* Skip duplicate / */
else
*t++ = *s++;
}
if (t < e) {
*t = 0;
SvCUR_set(sv, t - SvPVX(sv));
}
#ifndef INCOMPLETE_TAINTS
if (!items)
SvTAINTED_on(ST(0));
#endif
}
XSRETURN(1);
}
typedef APIRET (*PELP)(PSZ path, ULONG type);
/* Kernels after 2000/09/15 understand this too: */
#ifndef LIBPATHSTRICT
# define LIBPATHSTRICT 3
#endif
APIRET
ExtLIBPATH(ULONG ord, PSZ path, IV type, int fatal)
{
ULONG what;
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)
what = BEGIN_LIBPATH;
else
what = LIBPATHSTRICT;
return (*(PELP)f)(path, what);
}
#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;
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: OS2::extLibpath(type = 0)");
{
IV type;
char to[1024];
U32 rc;
char * RETVAL;
dXSTARG;
STRLEN l;
if (items < 1)
type = 0;
else {
type = SvIV(ST(0));
}
to[0] = 1; to[1] = 0; /* Sometimes no error reported */
RETVAL = extLibpath(to, type, 1); /* Make errors fatal */
if (RETVAL && RETVAL[0] == 1 && RETVAL[1] == 0)
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;
}
XSRETURN(1);
}
XS(XS_Cwd_extLibpath_set)
{
dXSARGS;
if (items < 1 || items > 2)
Perl_croak_nocontext("Usage: OS2::extLibpath_set(s, type = 0)");
{
STRLEN n_a;
char * s = (char *)SvPV(ST(0),n_a);
IV type;
U32 rc;
bool RETVAL;
if (items < 2)
type = 0;
else {
type = SvIV(ST(1));
}
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,
ULONG * Offset, ULONG Address);
*/
DeclOSFuncByORD(APIRET, _DosQueryModFromEIP,ORD_DosQueryModFromEIP,
(HMODULE * hmod, ULONG * obj, ULONG BufLen, PCHAR Buf,
ULONG * Offset, ULONG Address),
(hmod, obj, BufLen, Buf, Offset, Address))
static SV*
module_name_at(void *pp, enum module_name_how how)
{
dTHX;
char buf[MAXPATHLEN];
char *p = buf;
HMODULE mod;
ULONG obj, offset, rc, addr = (ULONG)pp;
if (how & mod_name_HMODULE) {
if ((how & ~mod_name_HMODULE) == mod_name_shortname)
Perl_croak(aTHX_ "Can't get short module name from a handle");
mod = (HMODULE)pp;
how &= ~mod_name_HMODULE;
} else if (!_DosQueryModFromEIP(&mod, &obj, sizeof(buf), buf, &offset, addr))
return &PL_sv_undef;
if (how == mod_name_handle)
return newSVuv(mod);
/* Full name... */
if ( how != mod_name_shortname
&& CheckOSError(DosQueryModuleName(mod, sizeof(buf), buf)) )
return &PL_sv_undef;
while (*p) {
if (*p == '\\')
*p = '/';
p++;
}
return newSVpv(buf, 0);
}
static SV*
module_name_of_cv(SV *cv, enum module_name_how how)
{
if (!cv || !SvROK(cv) || SvTYPE(SvRV(cv)) != SVt_PVCV || !CvXSUB(SvRV(cv))) {
dTHX;
if (how & mod_name_C_function)
return module_name_at((void*)SvIV(cv), how & ~mod_name_C_function);
else if (how & mod_name_HMODULE)
return module_name_at((void*)SvIV(cv), how);
Perl_croak(aTHX_ "Not an XSUB reference");
}
return module_name_at(CvXSUB(SvRV(cv)), how);
}
XS(XS_OS2_DLLname)
{
dXSARGS;
if (items > 2)
Perl_croak(aTHX_ "Usage: OS2::DLLname( [ how, [\\&xsub] ] )");
{
SV * RETVAL;
int how;
if (items < 1)
how = mod_name_full;
else {
how = (int)SvIV(ST(0));
}
if (items < 2)
RETVAL = module_name(how);
else
RETVAL = module_name_of_cv(ST(1), how);
ST(0) = RETVAL;
sv_2mortal(ST(0));
}
XSRETURN(1);
}
DeclOSFuncByORD(INT, _Dos32QueryHeaderInfo, ORD_Dos32QueryHeaderInfo,
(ULONG r1, ULONG r2, PVOID buf, ULONG szbuf, ULONG fnum),
(r1, r2, buf, szbuf, fnum))
XS(XS_OS2__headerInfo)
{
dXSARGS;
if (items > 4 || items < 2)
Perl_croak(aTHX_ "Usage: OS2::_headerInfo(req,size[,handle,[offset]])");
{
ULONG req = (ULONG)SvIV(ST(0));
STRLEN size = (STRLEN)SvIV(ST(1)), n_a;
ULONG handle = (items >= 3 ? (ULONG)SvIV(ST(2)) : 0);
ULONG offset = (items >= 4 ? (ULONG)SvIV(ST(3)) : 0);
if (size <= 0)
Perl_croak(aTHX_ "OS2::_headerInfo(): unexpected size: %d", (int)size);
ST(0) = newSVpvn("",0);
SvGROW(ST(0), size + 1);
sv_2mortal(ST(0));
if (!_Dos32QueryHeaderInfo(handle, offset, SvPV(ST(0), n_a), size, req))
Perl_croak(aTHX_ "OS2::_headerInfo(%ld,%ld,%ld,%ld) error: %s",
req, size, handle, offset, os2error(Perl_rc));
SvCUR_set(ST(0), size);
*SvEND(ST(0)) = 0;
}
XSRETURN(1);
}
#define DQHI_QUERYLIBPATHSIZE 4
#define DQHI_QUERYLIBPATH 5
XS(XS_OS2_libPath)
{
dXSARGS;
if (items != 0)
Perl_croak(aTHX_ "Usage: OS2::libPath()");
{
ULONG size;
STRLEN n_a;
if (!_Dos32QueryHeaderInfo(0, 0, &size, sizeof(size),
DQHI_QUERYLIBPATHSIZE))
Perl_croak(aTHX_ "OS2::_headerInfo(%ld,%ld,%ld,%ld) error: %s",
DQHI_QUERYLIBPATHSIZE, sizeof(size), 0, 0,
os2error(Perl_rc));
ST(0) = newSVpvn("",0);
SvGROW(ST(0), size + 1);
sv_2mortal(ST(0));
/* We should be careful: apparently, this entry point does not
pay attention to the size argument, so may overwrite
unrelated data! */
if (!_Dos32QueryHeaderInfo(0, 0, SvPV(ST(0), n_a), size,
DQHI_QUERYLIBPATH))
Perl_croak(aTHX_ "OS2::_headerInfo(%ld,%ld,%ld,%ld) error: %s",
DQHI_QUERYLIBPATH, size, 0, 0, os2error(Perl_rc));
SvCUR_set(ST(0), size);
*SvEND(ST(0)) = 0;
}
XSRETURN(1);
}
#define get_control87() _control87(0,0)
#define set_control87 _control87
XS(XS_OS2__control87)
{
dXSARGS;
if (items != 2)
Perl_croak(aTHX_ "Usage: OS2::_control87(new,mask)");
{
unsigned new = (unsigned)SvIV(ST(0));
unsigned mask = (unsigned)SvIV(ST(1));
unsigned RETVAL;
dXSTARG;
RETVAL = _control87(new, mask);
XSprePUSH; PUSHi((IV)RETVAL);
}
XSRETURN(1);
}
XS(XS_OS2_mytype)
{
dXSARGS;
int which = 0;
if (items < 0 || items > 1)
Perl_croak(aTHX_ "Usage: OS2::mytype([which])");
if (items == 1)
which = (int)SvIV(ST(0));
{
unsigned RETVAL;
dXSTARG;
switch (which) {
case 0:
RETVAL = os2_mytype; /* Reset after fork */
break;
case 1:
RETVAL = os2_mytype_ini; /* Before any fork */
break;
case 2:
RETVAL = Perl_os2_initial_mode; /* Before first morphing */
break;
case 3:
RETVAL = my_type(); /* Morphed type */
break;
default:
Perl_croak(aTHX_ "OS2::mytype(which): unknown which=%d", which);
}
XSprePUSH; PUSHi((IV)RETVAL);
}
XSRETURN(1);
}
XS(XS_OS2_mytype_set)
{
dXSARGS;
int type;
if (items == 1)
type = (int)SvIV(ST(0));
else
Perl_croak(aTHX_ "Usage: OS2::mytype_set(type)");
my_type_set(type);
XSRETURN_YES;
}
XS(XS_OS2_get_control87)
{
dXSARGS;
if (items != 0)
Perl_croak(aTHX_ "Usage: OS2::get_control87()");
{
unsigned RETVAL;
dXSTARG;
RETVAL = get_control87();
XSprePUSH; PUSHi((IV)RETVAL);
}
XSRETURN(1);
}
XS(XS_OS2_set_control87)
{
dXSARGS;
if (items < 0 || items > 2)
Perl_croak(aTHX_ "Usage: OS2::set_control87(new=MCW_EM, mask=MCW_EM)");
{
unsigned new;
unsigned mask;
unsigned RETVAL;
dXSTARG;
if (items < 1)
new = MCW_EM;
else {
new = (unsigned)SvIV(ST(0));
}
if (items < 2)
mask = MCW_EM;
else {
mask = (unsigned)SvIV(ST(1));
}
RETVAL = set_control87(new, mask);
XSprePUSH; PUSHi((IV)RETVAL);
}
XSRETURN(1);
}
XS(XS_OS2_incrMaxFHandles) /* DosSetRelMaxFH */
{
dXSARGS;
if (items < 0 || items > 1)
Perl_croak(aTHX_ "Usage: OS2::incrMaxFHandles(delta = 0)");
{
LONG delta;
ULONG RETVAL, rc;
dXSTARG;
if (items < 1)
delta = 0;
else
delta = (LONG)SvIV(ST(0));
if (CheckOSError(DosSetRelMaxFH(&delta, &RETVAL)))
croak_with_os2error("OS2::incrMaxFHandles(): DosSetRelMaxFH() error");
XSprePUSH; PUSHu((UV)RETVAL);
}
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_nolen(ST(0)) : 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 = NULL;
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);
mPUSHu(PipeState);
/* Bytes (available/in-message) */
mPUSHi(BytesAvail.cbpipe);
mPUSHi(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);
mPUSHp(b.pInfo.szName, size);
mPUSHu(b.id);
mPUSHi(b.pInfo.cbOut);
mPUSHi(b.pInfo.cbIn);
mPUSHi(b.pInfo.cbMaxInst);
mPUSHi(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_nolen(ST(0)) : 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)
{
char *file = __FILE__;
{
GV *gv;
if (_emx_env & 0x200) { /* OS/2 */
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);
newXS("OS2::SysInfo", XS_OS2_SysInfo, file);
newXSproto("OS2::DevCap", XS_OS2_DevCap, file, ";$$");
newXSproto("OS2::SysInfoFor", XS_OS2_SysInfoFor, file, "$;$");
newXS("OS2::BootDrive", XS_OS2_BootDrive, file);
newXS("OS2::MorphPM", XS_OS2_MorphPM, file);
newXS("OS2::UnMorphPM", XS_OS2_UnMorphPM, file);
newXS("OS2::Serve_Messages", XS_OS2_Serve_Messages, file);
newXS("OS2::Process_Messages", XS_OS2_Process_Messages, file);
newXS("DynaLoader::mod2fname", XS_DynaLoader_mod2fname, file);
newXS("Cwd::current_drive", XS_Cwd_current_drive, file);
newXS("Cwd::sys_chdir", XS_Cwd_sys_chdir, file);
newXS("Cwd::change_drive", XS_Cwd_change_drive, file);
newXS("Cwd::sys_is_absolute", XS_Cwd_sys_is_absolute, file);
newXS("Cwd::sys_is_rooted", XS_Cwd_sys_is_rooted, file);
newXS("Cwd::sys_is_relative", XS_Cwd_sys_is_relative, file);
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, ";$$");
newXSproto("OS2::DLLname", XS_OS2_DLLname, file, ";$$");
newXSproto("OS2::mytype", XS_OS2_mytype, file, ";$");
newXSproto("OS2::mytype_set", XS_OS2_mytype_set, file, "$");
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());
gv = gv_fetchpv("OS2::emx_rev", TRUE, SVt_PV);
GvMULTI_on(gv);
sv_setiv(GvSV(gv), _emx_rev);
sv_setpv(GvSV(gv), _emx_vprt);
SvIOK_on(GvSV(gv));
gv = gv_fetchpv("OS2::emx_env", TRUE, SVt_PV);
GvMULTI_on(gv);
sv_setiv(GvSV(gv), _emx_env);
gv = gv_fetchpv("OS2::os_ver", TRUE, SVt_PV);
GvMULTI_on(gv);
sv_setnv(GvSV(gv), _osmajor + 0.001 * _osminor);
gv = gv_fetchpv("OS2::nsyserror", TRUE, SVt_PV);
GvMULTI_on(gv);
sv_setiv(GvSV(gv), 1); /* DEFAULT: Show number on syserror */
}
return 0;
}
extern void _emx_init(void*);
static void jmp_out_of_atexit(void);
#define FORCE_EMX_INIT_CONTRACT_ARGV 1
#define FORCE_EMX_INIT_INSTALL_ATEXIT 2
static void
my_emx_init(void *layout) {
static volatile void *old_esp = 0; /* Cannot be on stack! */
/* Can't just call emx_init(), since it moves the stack pointer */
/* It also busts a lot of registers, so be extra careful */
__asm__( "pushf\n"
"pusha\n"
"movl %%esp, %1\n"
"push %0\n"
"call __emx_init\n"
"movl %1, %%esp\n"
"popa\n"
"popf\n" : : "r" (layout), "m" (old_esp) );
}
struct layout_table_t {
ULONG text_base;
ULONG text_end;
ULONG data_base;
ULONG data_end;
ULONG bss_base;
ULONG bss_end;
ULONG heap_base;
ULONG heap_end;
ULONG heap_brk;
ULONG heap_off;
ULONG os2_dll;
ULONG stack_base;
ULONG stack_end;
ULONG flags;
ULONG reserved[2];
char options[64];
};
static ULONG
my_os_version() {
static ULONG osv_res; /* Cannot be on stack! */
/* Can't just call __os_version(), since it does not follow C
calling convention: it busts a lot of registers, so be extra careful */
__asm__( "pushf\n"
"pusha\n"
"call ___os_version\n"
"movl %%eax, %0\n"
"popa\n"
"popf\n" : "=m" (osv_res) );
return osv_res;
}
static void
force_init_emx_runtime(EXCEPTIONREGISTRATIONRECORD *preg, ULONG flags)
{
/* Calling emx_init() will bust the top of stack: it installs an
exception handler and puts argv data there. */
char *oldarg, *oldenv;
void *oldstackend, *oldstack;
PPIB pib;
PTIB tib;
ULONG rc, error = 0, out;
char buf[512];
static struct layout_table_t layout_table;
struct {
char buf[48*1024]; /* _emx_init() requires 32K, cmd.exe has 64K only */
double alignment1;
EXCEPTIONREGISTRATIONRECORD xreg;
} *newstack;
char *s;
layout_table.os2_dll = (ULONG)&os2_dll_fake;
layout_table.flags = 0x02000002; /* flags: application, OMF */
DosGetInfoBlocks(&tib, &pib);
oldarg = pib->pib_pchcmd;
oldenv = pib->pib_pchenv;
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 */
pib->pib_pchcmd = "\0"; /* Ended by an extra \0. */
}
newstack = alloca(sizeof(*newstack));
/* Emulate the stack probe */
s = ((char*)newstack) + sizeof(*newstack);
while (s > (char*)newstack) {
s[-1] = 0;
s -= 4096;
}
/* Reassigning stack is documented to work */
tib->tib_pstack = (void*)newstack;
tib->tib_pstacklimit = (void*)((char*)newstack + sizeof(*newstack));
/* Can't just call emx_init(), since it moves the stack pointer */
my_emx_init((void*)&layout_table);
/* Remove the exception handler, cannot use it - too low on the stack.
Check whether it is inside the new stack. */
buf[0] = 0;
if (tib->tib_pexchain >= tib->tib_pstacklimit
|| tib->tib_pexchain < tib->tib_pstack) {
error = 1;
sprintf(buf,
"panic: ExceptionHandler misplaced: not %#lx <= %#lx < %#lx\n",
(unsigned long)tib->tib_pstack,
(unsigned long)tib->tib_pexchain,
(unsigned long)tib->tib_pstacklimit);
goto finish;
}
if (tib->tib_pexchain != &(newstack->xreg)) {
sprintf(buf, "ExceptionHandler misplaced: %#lx != %#lx\n",
(unsigned long)tib->tib_pexchain,
(unsigned long)&(newstack->xreg));
}
rc = DosUnsetExceptionHandler((EXCEPTIONREGISTRATIONRECORD *)tib->tib_pexchain);
if (rc)
sprintf(buf + strlen(buf),
"warning: DosUnsetExceptionHandler rc=%#lx=%lu\n", rc, rc);
if (preg) {
/* ExceptionRecords should be on stack, in a correct order. Sigh... */
preg->prev_structure = 0;
preg->ExceptionHandler = _emx_exception;
rc = DosSetExceptionHandler(preg);
if (rc) {
sprintf(buf + strlen(buf),
"warning: DosSetExceptionHandler rc=%#lx=%lu\n", rc, rc);
DosWrite(2, buf, strlen(buf), &out);
emx_exception_init = 1; /* Do it around spawn*() calls */
}
} else
emx_exception_init = 1; /* Do it around spawn*() calls */
finish:
/* Restore the damage */
pib->pib_pchcmd = oldarg;
pib->pib_pchcmd = oldenv;
tib->tib_pstacklimit = oldstackend;
tib->tib_pstack = oldstack;
emx_runtime_init = 1;
if (buf[0])
DosWrite(2, buf, strlen(buf), &out);
if (error)
exit(56);
}
static void
jmp_out_of_atexit(void)
{
if (longjmp_at_exit)
longjmp(at_exit_buf, 1);
}
extern void _CRT_term(void);
void
Perl_OS2_term(void **p, int exitstatus, int flags)
{
if (!emx_runtime_secondary)
return;
/* The principal executable is not running the same CRTL, so there
is nobody to shutdown *this* CRTL except us... */
if (flags & FORCE_EMX_DEINIT_EXIT) {
if (p && !emx_exception_init)
DosUnsetExceptionHandler((EXCEPTIONREGISTRATIONRECORD *)p);
/* Do not run the executable's CRTL's termination routines */
exit(exitstatus); /* Run at-exit, flush buffers, etc */
}
/* Run at-exit list, and jump out at the end */
if ((flags & FORCE_EMX_DEINIT_RUN_ATEXIT) && !setjmp(at_exit_buf)) {
longjmp_at_exit = 1;
exit(exitstatus); /* The first pass through "if" */
}
/* Get here if we managed to jump out of exit(), or did not run atexit. */
longjmp_at_exit = 0; /* Maybe exit() is called again? */
#if 0 /* _atexit_n is not exported */
if (flags & FORCE_EMX_DEINIT_RUN_ATEXIT)
_atexit_n = 0; /* Remove the atexit() handlers */
#endif
/* Will segfault on program termination if we leave this dangling... */
if (p && !emx_exception_init)
DosUnsetExceptionHandler((EXCEPTIONREGISTRATIONRECORD *)p);
/* Typically there is no need to do this, done from _DLL_InitTerm() */
if (flags & FORCE_EMX_DEINIT_CRT_TERM)
_CRT_term(); /* Flush buffers, etc. */
/* Now it is a good time to call exit() in the caller's CRTL... */
}
#include <emx/startup.h>
extern ULONG __os_version(); /* See system.doc */
void
check_emx_runtime(char **env, EXCEPTIONREGISTRATIONRECORD *preg)
{
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;
/* If _environ is not set, this code sits in a DLL which
uses a CRT DLL which not compatible with the executable's
CRT library. Some parts of the DLL are not initialized.
*/
if (_environ != NULL)
return; /* Properly initialized */
/* It is not DOS, so we may use OS/2 API now */
/* Some data we manipulate is static; protect ourselves from
calling the same API from a different thread. */
DosEnterMustComplete(&count);
rc1 = DosEnterCritSec();
if (!hmtx_emx_init)
rc = DosCreateMutexSem(NULL, &hmtx_emx_init, 0, TRUE); /*Create owned*/
else
maybe_inited = 1;
if (rc != NO_ERROR)
hmtx_emx_init = NULLHANDLE;
if (rc1 == NO_ERROR)
DosExitCritSec();
DosExitMustComplete(&count);
while (maybe_inited) { /* Other thread did or is doing the same now */
if (emx_init_done)
return;
rc = DosRequestMutexSem(hmtx_emx_init,
(ULONG) SEM_INDEFINITE_WAIT); /* Timeout (none) */
if (rc == ERROR_INTERRUPT)
continue;
if (rc != NO_ERROR) {
char buf[80];
ULONG out;
sprintf(buf,
"panic: EMX backdoor init: DosRequestMutexSem error: %lu=%#lx\n", rc, rc);
DosWrite(2, buf, strlen(buf), &out);
return;
}
DosReleaseMutexSem(hmtx_emx_init);
return;
}
/* If the executable does not use EMX.DLL, EMX.DLL is not completely
initialized either. Uninitialized EMX.DLL returns 0 in the low
nibble of __os_version(). */
v_emx = my_os_version();
/* _osmajor and _osminor are normally set in _DLL_InitTerm of CRT DLL
(=>_CRT_init=>_entry2) via a call to __os_version(), then
reset when the EXE initialization code calls _text=>_init=>_entry2.
The first time they are wrongly set to 0; the second time the
EXE initialization code had already called emx_init=>initialize1
which correctly set version_major, version_minor used by
__os_version(). */
v_crt = (_osmajor | _osminor);
if ((_emx_env & 0x200) && !(v_emx & 0xFFFF)) { /* OS/2, EMX uninit. */
force_init_emx_runtime( preg,
FORCE_EMX_INIT_CONTRACT_ARGV
| FORCE_EMX_INIT_INSTALL_ATEXIT );
emx_wasnt_initialized = 1;
/* Update CRTL data basing on now-valid EMX runtime data */
if (!v_crt) { /* The only wrong data are the versions. */
v_emx = my_os_version(); /* *Now* it works */
*(unsigned char *)&_osmajor = v_emx & 0xFF; /* Cast out const */
*(unsigned char *)&_osminor = (v_emx>>8) & 0xFF;
}
}
emx_runtime_secondary = 1;
/* if (flags & FORCE_EMX_INIT_INSTALL_ATEXIT) */
atexit(jmp_out_of_atexit); /* Allow run of atexit() w/o exit() */
if (env == NULL) { /* Fetch from the process info block */
int c = 0;
PPIB pib;
PTIB tib;
char *e, **ep;
DosGetInfoBlocks(&tib, &pib);
e = pib->pib_pchenv;
while (*e) { /* Get count */
c++;
e = e + strlen(e) + 1;
}
Newx(env, c + 1, char*);
ep = env;
e = pib->pib_pchenv;
while (c--) {
*ep++ = e;
e = e + strlen(e) + 1;
}
*ep = NULL;
}
_environ = _org_environ = env;
emx_init_done = 1;
if (hmtx_emx_init)
DosReleaseMutexSem(hmtx_emx_init);
}
#define ENTRY_POINT 0x10000
static int
exe_is_aout(void)
{
struct layout_table_t *layout;
if (emx_wasnt_initialized)
return 0;
/* Now we know that the principal executable is an EMX application
- unless somebody did already play with delayed initialization... */
/* With EMX applications to determine whether it is AOUT one needs
to examine the start of the executable to find "layout" */
if ( *(unsigned char*)ENTRY_POINT != 0x68 /* PUSH n */
|| *(unsigned char*)(ENTRY_POINT+5) != 0xe8 /* CALL */
|| *(unsigned char*)(ENTRY_POINT+10) != 0xeb /* JMP */
|| *(unsigned char*)(ENTRY_POINT+12) != 0xe8) /* CALL */
return 0; /* ! EMX executable */
/* Fix alignment */
Copy((char*)(ENTRY_POINT+1), &layout, 1, struct layout_table_t*);
return !(layout->flags & 2);
}
void
Perl_OS2_init(char **env)
{
Perl_OS2_init3(env, 0, 0);
}
void
Perl_OS2_init3(char **env, void **preg, int flags)
{
char *shell, *s;
ULONG rc;
_uflags (_UF_SBRK_MODEL, _UF_SBRK_ARBITRARY);
MALLOC_INIT;
check_emx_runtime(env, (EXCEPTIONREGISTRATIONRECORD *)preg);
settmppath();
OS2_Perl_data.xs_init = &Xs_OS2_init;
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;
while (l && (shell[l-1] == '/' || shell[l-1] == '\\'))
l--;
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++) {
if (PL_sh_path[i] == '\\') PL_sh_path[i] = '/';
}
}
#if defined(USE_5005THREADS) || defined(USE_ITHREADS)
MUTEX_INIT(&start_thread_mutex);
MUTEX_INIT(&perlos2_state_mutex);
#endif
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
char *
my_tmpnam (char *str)
{
char *p = getenv("TMP"), *tpath;
if (!p) p = getenv("TEMP");
tpath = tempnam(p, "pltmp");
if (str && tpath) {
strcpy(str, tpath);
return str;
}
return tpath;
}
FILE *
my_tmpfile ()
{
struct stat s;
stat(".", &s);
if (s.st_mode & S_IWOTH) {
return tmpfile();
}
return fopen(my_tmpnam(NULL), "w+b"); /* Race condition, but
grants TMP. */
}
#undef rmdir
/* EMX flavors do not tolerate trailing slashes. t/op/mkdir.t has many
trailing slashes, so we need to support this as well. */
int
my_rmdir (__const__ char *s)
{
char b[MAXPATHLEN];
char *buf = b;
STRLEN l = strlen(s);
int rc;
if (s[l-1] == '/' || s[l-1] == '\\') { /* EMX mkdir fails... */
if (l >= sizeof b)
Newx(buf, l + 1, char);
strcpy(buf,s);
while (l > 1 && (s[l-1] == '/' || s[l-1] == '\\'))
l--;
buf[l] = 0;
s = buf;
}
rc = rmdir(s);
if (b != buf)
Safefree(buf);
return rc;
}
#undef mkdir
int
my_mkdir (__const__ char *s, long perm)
{
char b[MAXPATHLEN];
char *buf = b;
STRLEN l = strlen(s);
int rc;
if (s[l-1] == '/' || s[l-1] == '\\') { /* EMX mkdir fails... */
if (l >= sizeof b)
Newx(buf, l + 1, char);
strcpy(buf,s);
while (l > 1 && (s[l-1] == '/' || s[l-1] == '\\'))
l--;
buf[l] = 0;
s = buf;
}
rc = mkdir(s, perm);
if (b != buf)
Safefree(buf);
return rc;
}
#undef flock
/* This code was contributed by Rocco Caputo. */
int
my_flock(int handle, int o)
{
FILELOCK rNull, rFull;
ULONG timeout, handle_type, flag_word;
APIRET rc;
int blocking, shared;
static int use_my_flock = -1;
if (use_my_flock == -1) {
MUTEX_LOCK(&perlos2_state_mutex);
if (use_my_flock == -1) {
char *s = getenv("USE_PERL_FLOCK");
if (s)
use_my_flock = atoi(s);
else
use_my_flock = 1;
}
MUTEX_UNLOCK(&perlos2_state_mutex);
}
if (!(_emx_env & 0x200) || !use_my_flock)
return flock(handle, o); /* Delegate to EMX. */
/* is this a file? */
if ((DosQueryHType(handle, &handle_type, &flag_word) != 0) ||
(handle_type & 0xFF))
{
errno = EBADF;
return -1;
}
/* set lock/unlock ranges */
rNull.lOffset = rNull.lRange = rFull.lOffset = 0;
rFull.lRange = 0x7FFFFFFF;
/* set timeout for blocking */
timeout = ((blocking = !(o & LOCK_NB))) ? 100 : 1;
/* shared or exclusive? */
shared = (o & LOCK_SH) ? 1 : 0;
/* do not block the unlock */
if (o & (LOCK_UN | LOCK_SH | LOCK_EX)) {
rc = DosSetFileLocks(handle, &rFull, &rNull, timeout, shared);
switch (rc) {
case 0:
errno = 0;
return 0;
case ERROR_INVALID_HANDLE:
errno = EBADF;
return -1;
case ERROR_SHARING_BUFFER_EXCEEDED:
errno = ENOLCK;
return -1;
case ERROR_LOCK_VIOLATION:
break; /* not an error */
case ERROR_INVALID_PARAMETER:
case ERROR_ATOMIC_LOCK_NOT_SUPPORTED:
case ERROR_READ_LOCKS_NOT_SUPPORTED:
errno = EINVAL;
return -1;
case ERROR_INTERRUPT:
errno = EINTR;
return -1;
default:
errno = EINVAL;
return -1;
}
}
/* lock may block */
if (o & (LOCK_SH | LOCK_EX)) {
/* for blocking operations */
for (;;) {
rc =
DosSetFileLocks(
handle,
&rNull,
&rFull,
timeout,
shared
);
switch (rc) {
case 0:
errno = 0;
return 0;
case ERROR_INVALID_HANDLE:
errno = EBADF;
return -1;
case ERROR_SHARING_BUFFER_EXCEEDED:
errno = ENOLCK;
return -1;
case ERROR_LOCK_VIOLATION:
if (!blocking) {
errno = EWOULDBLOCK;
return -1;
}
break;
case ERROR_INVALID_PARAMETER:
case ERROR_ATOMIC_LOCK_NOT_SUPPORTED:
case ERROR_READ_LOCKS_NOT_SUPPORTED:
errno = EINVAL;
return -1;
case ERROR_INTERRUPT:
errno = EINTR;
return -1;
default:
errno = EINVAL;
return -1;
}
/* give away timeslice */
DosSleep(1);
}
}
errno = 0;
return 0;
}
static int
use_my_pwent(void)
{
if (_my_pwent == -1) {
char *s = getenv("USE_PERL_PWENT");
if (s)
_my_pwent = atoi(s);
else
_my_pwent = 1;
}
return _my_pwent;
}
#undef setpwent
#undef getpwent
#undef endpwent
void
my_setpwent(void)
{
if (!use_my_pwent()) {
setpwent(); /* Delegate to EMX. */
return;
}
pwent_cnt = 0;
}
void
my_endpwent(void)
{
if (!use_my_pwent()) {
endpwent(); /* Delegate to EMX. */
return;
}
}
struct passwd *
my_getpwent (void)
{
if (!use_my_pwent())
return getpwent(); /* Delegate to EMX. */
if (pwent_cnt++)
return 0; /* Return one entry only */
return getpwuid(0);
}
void
setgrent(void)
{
grent_cnt = 0;
}
void
endgrent(void)
{
}
struct group *
getgrent (void)
{
if (grent_cnt++)
return 0; /* Return one entry only */
return getgrgid(0);
}
#undef getpwuid
#undef getpwnam
/* Too long to be a crypt() of anything, so it is not-a-valid pw_passwd. */
static const char pw_p[] = "Jf0Wb/BzMFvk7K7lrzK";
static struct passwd *
passw_wrap(struct passwd *p)
{
char *s;
if (!p || (p->pw_passwd && *p->pw_passwd)) /* Not a dangerous password */
return p;
pw = *p;
s = getenv("PW_PASSWD");
if (!s)
s = (char*)pw_p; /* Make match impossible */
pw.pw_passwd = s;
return &pw;
}
struct passwd *
my_getpwuid (uid_t id)
{
return passw_wrap(getpwuid(id));
}
struct passwd *
my_getpwnam (__const__ char *n)
{
return passw_wrap(getpwnam(n));
}
char *
gcvt_os2 (double value, int digits, char *buffer)
{
double absv = value > 0 ? value : -value;
/* EMX implementation is lousy between 0.1 and 0.0001 (uses exponents below
0.1), 1-digit stuff is ok below 0.001; multi-digit below 0.0001. */
int buggy;
absv *= 10000;
buggy = (absv < 1000 && (absv >= 10 || (absv > 1 && floor(absv) != absv)));
if (buggy) {
char pat[12];
sprintf(pat, "%%.%dg", digits);
sprintf(buffer, pat, value);
return buffer;
}
return gcvt (value, digits, buffer);
}
#undef fork
int fork_with_resources()
{
#if (defined(USE_5005THREADS) || defined(USE_ITHREADS)) && !defined(USE_SLOW_THREAD_SPECIFIC)
dTHX;
void *ctx = PERL_GET_CONTEXT;
#endif
unsigned fpflag = _control87(0,0);
int rc = fork();
if (rc == 0) { /* child */
#if (defined(USE_5005THREADS) || defined(USE_ITHREADS)) && !defined(USE_SLOW_THREAD_SPECIFIC)
ALLOC_THREAD_KEY; /* Acquire the thread-local memory */
PERL_SET_CONTEXT(ctx); /* Reinit the thread-local memory */
#endif
{ /* Reload loaded-on-demand DLLs */
struct dll_handle_t *dlls = dll_handles;
while (dlls->modname) {
char dllname[260], fail[260];
ULONG rc;
if (!dlls->handle) { /* Was not loaded */
dlls++;
continue;
}
/* It was loaded in the parent. We need to reload it. */
rc = DosQueryModuleName(dlls->handle, sizeof(dllname), dllname);
if (rc) {
Perl_warn_nocontext("Can't find DLL name for the module `%s' by the handle %d, rc=%lu=%#lx",
dlls->modname, (int)dlls->handle, rc, rc);
dlls++;
continue;
}
rc = DosLoadModule(fail, sizeof fail, dllname, &dlls->handle);
if (rc)
Perl_warn_nocontext("Can't load DLL `%s', possible problematic module `%s'",
dllname, fail);
dlls++;
}
}
{ /* Support message queue etc. */
os2_mytype = my_type();
/* Apparently, subprocesses (in particular, fork()) do not
inherit the morphed state, so os2_mytype is the same as
os2_mytype_ini. */
if (Perl_os2_initial_mode != -1
&& Perl_os2_initial_mode != os2_mytype) {
/* XXXX ??? */
}
}
if (Perl_HAB_set)
(void)_obtain_Perl_HAB;
if (Perl_hmq_refcnt) {
if (my_type() != 3)
my_type_set(3);
Create_HMQ(Perl_hmq_servers != 0,
"Cannot create a message queue on fork");
}
/* We may have loaded some modules */
_control87(fpflag, MCW_EM); /* Some modules reset FP flags on (un)load */
}
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;
}