xref: /openbsd-src/gnu/usr.bin/perl/os2/os2.c (revision 33b4f39fbeffad07bc3206f173cff9f3c9901cd1)
1 #define INCL_DOS
2 #define INCL_NOPM
3 #define INCL_DOSFILEMGR
4 #define INCL_DOSMEMMGR
5 #define INCL_DOSERRORS
6 #define INCL_WINERRORS
7 #define INCL_WINSYS
8 /* These 3 are needed for compile if os2.h includes os2tk.h, not os2emx.h */
9 #define INCL_DOSPROCESS
10 #define SPU_DISABLESUPPRESSION          0
11 #define SPU_ENABLESUPPRESSION           1
12 #include <os2.h>
13 #include "dlfcn.h"
14 #include <emx/syscalls.h>
15 #include <sys/emxload.h>
16 
17 #include <sys/uflags.h>
18 
19 /*
20  * Various Unix compatibility functions for OS/2
21  */
22 
23 #include <stdio.h>
24 #include <errno.h>
25 #include <limits.h>
26 #include <process.h>
27 #include <fcntl.h>
28 #include <pwd.h>
29 #include <grp.h>
30 
31 #define PERLIO_NOT_STDIO 0
32 
33 #include "EXTERN.h"
34 #include "perl.h"
35 
36 enum module_name_how { mod_name_handle, mod_name_shortname, mod_name_full,
37   mod_name_C_function = 0x100, mod_name_HMODULE = 0x200};
38 
39 /* Find module name to which *this* subroutine is compiled */
40 #define module_name(how)	module_name_at(&module_name_at, how)
41 
42 static SV* module_name_at(void *pp, enum module_name_how how);
43 
44 void
45 croak_with_os2error(char *s)
46 {
47     Perl_croak_nocontext("%s: %s", s, os2error(Perl_rc));
48 }
49 
50 struct PMWIN_entries_t PMWIN_entries;
51 
52 /*****************************************************************************/
53 /* 2.1 would not resolve symbols on demand, and has no ExtLIBPATH. */
54 
55 struct dll_handle_t {
56     const char *modname;
57     HMODULE handle;
58     int requires_pm;
59 };
60 
61 static struct dll_handle_t dll_handles[] = {
62     {"doscalls", 0, 0},
63     {"tcp32dll", 0, 0},
64     {"pmwin", 0, 1},
65     {"rexx", 0, 0},
66     {"rexxapi", 0, 0},
67     {"sesmgr", 0, 0},
68     {"pmshapi", 0, 1},
69     {"pmwp", 0, 1},
70     {"pmgpi", 0, 1},
71     {NULL, 0},
72 };
73 
74 enum dll_handle_e {
75     dll_handle_doscalls,
76     dll_handle_tcp32dll,
77     dll_handle_pmwin,
78     dll_handle_rexx,
79     dll_handle_rexxapi,
80     dll_handle_sesmgr,
81     dll_handle_pmshapi,
82     dll_handle_pmwp,
83     dll_handle_pmgpi,
84     dll_handle_LAST,
85 };
86 
87 #define doscalls_handle		(dll_handles[dll_handle_doscalls])
88 #define tcp_handle		(dll_handles[dll_handle_tcp32dll])
89 #define pmwin_handle		(dll_handles[dll_handle_pmwin])
90 #define rexx_handle		(dll_handles[dll_handle_rexx])
91 #define rexxapi_handle		(dll_handles[dll_handle_rexxapi])
92 #define sesmgr_handle		(dll_handles[dll_handle_sesmgr])
93 #define pmshapi_handle		(dll_handles[dll_handle_pmshapi])
94 #define pmwp_handle		(dll_handles[dll_handle_pmwp])
95 #define pmgpi_handle		(dll_handles[dll_handle_pmgpi])
96 
97 /*  The following local-scope data is not yet included:
98        fargs.140			// const => OK
99        ino.165				// locked - and the access is almost cosmetic
100        layout_table.260			// startup only, locked
101        osv_res.257			// startup only, locked
102        old_esp.254			// startup only, locked
103        priors				// const ==> OK
104        use_my_flock.283			// locked
105        emx_init_done.268		// locked
106        dll_handles			// locked
107        hmtx_emx_init.267		// THIS is the lock for startup
108        perlos2_state_mutex		// THIS is the lock for all the rest
109 BAD:
110        perlos2_state			// see below
111 */
112 /*  The following global-scope data is not yet included:
113        OS2_Perl_data
114        pthreads_states			// const now?
115        start_thread_mutex
116        thread_join_count		// protected
117        thread_join_data			// protected
118        tmppath
119 
120        pDosVerifyPidTid
121 
122        Perl_OS2_init3() - should it be protected?
123 */
124 OS2_Perl_data_t OS2_Perl_data;
125 
126 static struct perlos2_state_t {
127   int po2__my_pwent;				/* = -1; */
128   int po2_DOS_harderr_state;			/* = -1;    */
129   signed char po2_DOS_suppression_state;	/* = -1;    */
130 
131   PFN po2_ExtFCN[ORD_NENTRIES];	/* Labeled by ord ORD_*. */
132 /*  struct PMWIN_entries_t po2_PMWIN_entries; */
133 
134   int po2_emx_wasnt_initialized;
135 
136   char po2_fname[9];
137   int po2_rmq_cnt;
138 
139   int po2_grent_cnt;
140 
141   char *po2_newp;
142   char *po2_oldp;
143   int po2_newl;
144   int po2_oldl;
145   int po2_notfound;
146   char po2_mangle_ret[STATIC_FILE_LENGTH+1];
147   ULONG po2_os2_dll_fake;
148   ULONG po2_os2_mytype;
149   ULONG po2_os2_mytype_ini;
150   int po2_pidtid_lookup;
151   struct passwd po2_pw;
152 
153   int po2_pwent_cnt;
154   char po2_pthreads_state_buf[80];
155   char po2_os2error_buf[300];
156 /* There is no big sense to make it thread-specific, since signals
157    are delivered to thread 1 only.  XXXX Maybe make it into an array? */
158   int po2_spawn_pid;
159   int po2_spawn_killed;
160 
161   jmp_buf po2_at_exit_buf;
162   int po2_longjmp_at_exit;
163   int po2_emx_runtime_init;		/* If 1, we need to manually init it */
164   int po2_emx_exception_init;		/* If 1, we need to manually set it */
165   int po2_emx_runtime_secondary;
166   char* (*po2_perllib_mangle_installed)(char *s, unsigned int l);
167   char* po2_perl_sh_installed;
168   PGINFOSEG po2_gTable;
169   PLINFOSEG po2_lTable;
170 } perlos2_state = {
171     -1,					/* po2__my_pwent */
172     -1,					/* po2_DOS_harderr_state */
173     -1,					/* po2_DOS_suppression_state */
174 };
175 
176 #define Perl_po2()		(&perlos2_state)
177 
178 #define ExtFCN			(Perl_po2()->po2_ExtFCN)
179 /* #define PMWIN_entries		(Perl_po2()->po2_PMWIN_entries) */
180 #define emx_wasnt_initialized	(Perl_po2()->po2_emx_wasnt_initialized)
181 #define fname			(Perl_po2()->po2_fname)
182 #define rmq_cnt			(Perl_po2()->po2_rmq_cnt)
183 #define grent_cnt		(Perl_po2()->po2_grent_cnt)
184 #define newp			(Perl_po2()->po2_newp)
185 #define oldp			(Perl_po2()->po2_oldp)
186 #define newl			(Perl_po2()->po2_newl)
187 #define oldl			(Perl_po2()->po2_oldl)
188 #define notfound		(Perl_po2()->po2_notfound)
189 #define mangle_ret		(Perl_po2()->po2_mangle_ret)
190 #define os2_dll_fake		(Perl_po2()->po2_os2_dll_fake)
191 #define os2_mytype		(Perl_po2()->po2_os2_mytype)
192 #define os2_mytype_ini		(Perl_po2()->po2_os2_mytype_ini)
193 #define pidtid_lookup		(Perl_po2()->po2_pidtid_lookup)
194 #define pw			(Perl_po2()->po2_pw)
195 #define pwent_cnt		(Perl_po2()->po2_pwent_cnt)
196 #define _my_pwent		(Perl_po2()->po2__my_pwent)
197 #define pthreads_state_buf	(Perl_po2()->po2_pthreads_state_buf)
198 #define os2error_buf		(Perl_po2()->po2_os2error_buf)
199 /* There is no big sense to make it thread-specific, since signals
200    are delivered to thread 1 only.  XXXX Maybe make it into an array? */
201 #define spawn_pid		(Perl_po2()->po2_spawn_pid)
202 #define spawn_killed		(Perl_po2()->po2_spawn_killed)
203 #define DOS_harderr_state	(Perl_po2()->po2_DOS_harderr_state)
204 #define DOS_suppression_state		(Perl_po2()->po2_DOS_suppression_state)
205 
206 #define at_exit_buf		(Perl_po2()->po2_at_exit_buf)
207 #define longjmp_at_exit		(Perl_po2()->po2_longjmp_at_exit)
208 #define emx_runtime_init	(Perl_po2()->po2_emx_runtime_init)
209 #define emx_exception_init	(Perl_po2()->po2_emx_exception_init)
210 #define emx_runtime_secondary	(Perl_po2()->po2_emx_runtime_secondary)
211 #define perllib_mangle_installed	(Perl_po2()->po2_perllib_mangle_installed)
212 #define perl_sh_installed	(Perl_po2()->po2_perl_sh_installed)
213 #define gTable			(Perl_po2()->po2_gTable)
214 #define lTable			(Perl_po2()->po2_lTable)
215 
216 const Perl_PFN * const pExtFCN = (Perl_po2()->po2_ExtFCN);
217 
218 #if defined(USE_5005THREADS) || defined(USE_ITHREADS)
219 
220 typedef void (*emx_startroutine)(void *);
221 typedef void* (*pthreads_startroutine)(void *);
222 
223 enum pthreads_state {
224     pthreads_st_none = 0,
225     pthreads_st_run,
226     pthreads_st_exited,
227     pthreads_st_detached,
228     pthreads_st_waited,
229     pthreads_st_norun,
230     pthreads_st_exited_waited,
231 };
232 const char * const pthreads_states[] = {
233     "uninit",
234     "running",
235     "exited",
236     "detached",
237     "waited for",
238     "could not start",
239     "exited, then waited on",
240 };
241 
242 enum pthread_exists { pthread_not_existant = -0xff };
243 
244 static const char*
245 pthreads_state_string(enum pthreads_state state)
246 {
247   if (state < 0 || state >= sizeof(pthreads_states)/sizeof(*pthreads_states)) {
248     snprintf(pthreads_state_buf, sizeof(pthreads_state_buf),
249 	     "unknown thread state %d", (int)state);
250     return pthreads_state_buf;
251   }
252   return pthreads_states[state];
253 }
254 
255 typedef struct {
256     void *status;
257     perl_cond cond;
258     enum pthreads_state state;
259 } thread_join_t;
260 
261 thread_join_t *thread_join_data;
262 int thread_join_count;
263 perl_mutex start_thread_mutex;
264 static perl_mutex perlos2_state_mutex;
265 
266 
267 int
268 pthread_join(perl_os_thread tid, void **status)
269 {
270     MUTEX_LOCK(&start_thread_mutex);
271     if (tid < 1 || tid >= thread_join_count) {
272 	MUTEX_UNLOCK(&start_thread_mutex);
273 	if (tid != pthread_not_existant)
274 	    Perl_croak_nocontext("panic: join with a thread with strange ordinal %d", (int)tid);
275 	Perl_warn_nocontext("panic: join with a thread which could not start");
276 	*status = 0;
277 	return 0;
278     }
279     switch (thread_join_data[tid].state) {
280     case pthreads_st_exited:
281 	thread_join_data[tid].state = pthreads_st_exited_waited;
282 	*status = thread_join_data[tid].status;
283 	MUTEX_UNLOCK(&start_thread_mutex);
284 	COND_SIGNAL(&thread_join_data[tid].cond);
285 	break;
286     case pthreads_st_waited:
287 	MUTEX_UNLOCK(&start_thread_mutex);
288 	Perl_croak_nocontext("join with a thread with a waiter");
289 	break;
290     case pthreads_st_norun:
291     {
292 	int state = (int)thread_join_data[tid].status;
293 
294 	thread_join_data[tid].state = pthreads_st_none;
295 	MUTEX_UNLOCK(&start_thread_mutex);
296 	Perl_croak_nocontext("panic: join with a thread which could not run"
297 			     " due to attempt of tid reuse (state='%s')",
298 			     pthreads_state_string(state));
299 	break;
300     }
301     case pthreads_st_run:
302     {
303 	perl_cond cond;
304 
305 	thread_join_data[tid].state = pthreads_st_waited;
306 	thread_join_data[tid].status = (void *)status;
307 	COND_INIT(&thread_join_data[tid].cond);
308 	cond = thread_join_data[tid].cond;
309 	COND_WAIT(&thread_join_data[tid].cond, &start_thread_mutex);
310 	COND_DESTROY(&cond);
311 	MUTEX_UNLOCK(&start_thread_mutex);
312 	break;
313     }
314     default:
315 	MUTEX_UNLOCK(&start_thread_mutex);
316 	Perl_croak_nocontext("panic: join with thread in unknown thread state: '%s'",
317 	      pthreads_state_string(thread_join_data[tid].state));
318 	break;
319     }
320     return 0;
321 }
322 
323 typedef struct {
324   pthreads_startroutine sub;
325   void *arg;
326   void *ctx;
327 } pthr_startit;
328 
329 /* The lock is used:
330 	a) Since we temporarily usurp the caller interp, so malloc() may
331 	   use it to decide on debugging the call;
332 	b) Since *args is on the caller's stack.
333  */
334 void
335 pthread_startit(void *arg1)
336 {
337     /* Thread is already started, we need to transfer control only */
338     pthr_startit args = *(pthr_startit *)arg1;
339     int tid = pthread_self();
340     void *rc;
341     int state;
342 
343     if (tid <= 1) {
344 	/* Can't croak, the setjmp() is not in scope... */
345 	char buf[80];
346 
347 	snprintf(buf, sizeof(buf),
348 		 "panic: thread with strange ordinal %d created\n\r", tid);
349 	write(2,buf,strlen(buf));
350 	MUTEX_UNLOCK(&start_thread_mutex);
351 	return;
352     }
353     /* Until args.sub resets it, makes debugging Perl_malloc() work: */
354     PERL_SET_CONTEXT(0);
355     if (tid >= thread_join_count) {
356 	int oc = thread_join_count;
357 
358 	thread_join_count = tid + 5 + tid/5;
359 	if (thread_join_data) {
360 	    Renew(thread_join_data, thread_join_count, thread_join_t);
361 	    Zero(thread_join_data + oc, thread_join_count - oc, thread_join_t);
362 	} else {
363 	    Newz(1323, thread_join_data, thread_join_count, thread_join_t);
364 	}
365     }
366     if (thread_join_data[tid].state != pthreads_st_none) {
367 	/* Can't croak, the setjmp() is not in scope... */
368 	char buf[80];
369 
370 	snprintf(buf, sizeof(buf),
371 		 "panic: attempt to reuse thread id %d (state='%s')\n\r",
372 		 tid, pthreads_state_string(thread_join_data[tid].state));
373 	write(2,buf,strlen(buf));
374 	thread_join_data[tid].status = (void*)thread_join_data[tid].state;
375 	thread_join_data[tid].state = pthreads_st_norun;
376 	MUTEX_UNLOCK(&start_thread_mutex);
377 	return;
378     }
379     thread_join_data[tid].state = pthreads_st_run;
380     /* Now that we copied/updated the guys, we may release the caller... */
381     MUTEX_UNLOCK(&start_thread_mutex);
382     rc = (*args.sub)(args.arg);
383     MUTEX_LOCK(&start_thread_mutex);
384     switch (thread_join_data[tid].state) {
385     case pthreads_st_waited:
386 	COND_SIGNAL(&thread_join_data[tid].cond);
387 	thread_join_data[tid].state = pthreads_st_none;
388 	*((void**)thread_join_data[tid].status) = rc;
389 	break;
390     case pthreads_st_detached:
391 	thread_join_data[tid].state = pthreads_st_none;
392 	break;
393     case pthreads_st_run:
394 	/* Somebody can wait on us; cannot exit, since OS can reuse the tid
395 	   and our waiter will get somebody else's status. */
396 	thread_join_data[tid].state = pthreads_st_exited;
397 	thread_join_data[tid].status = rc;
398 	COND_INIT(&thread_join_data[tid].cond);
399 	COND_WAIT(&thread_join_data[tid].cond, &start_thread_mutex);
400 	COND_DESTROY(&thread_join_data[tid].cond);
401 	thread_join_data[tid].state = pthreads_st_none;	/* Ready to reuse */
402 	break;
403     default:
404 	state = thread_join_data[tid].state;
405 	MUTEX_UNLOCK(&start_thread_mutex);
406 	Perl_croak_nocontext("panic: unexpected thread state on exit: '%s'",
407 			     pthreads_state_string(state));
408     }
409     MUTEX_UNLOCK(&start_thread_mutex);
410 }
411 
412 int
413 pthread_create(perl_os_thread *tidp, const pthread_attr_t *attr,
414 	       void *(*start_routine)(void*), void *arg)
415 {
416     dTHX;
417     pthr_startit args;
418 
419     args.sub = (void*)start_routine;
420     args.arg = arg;
421     args.ctx = PERL_GET_CONTEXT;
422 
423     MUTEX_LOCK(&start_thread_mutex);
424     /* Test suite creates 31 extra threads;
425        on machine without shared-memory-hogs this stack sizeis OK with 31: */
426     *tidp = _beginthread(pthread_startit, /*stack*/ NULL,
427 			 /*stacksize*/ 4*1024*1024, (void*)&args);
428     if (*tidp == -1) {
429 	*tidp = pthread_not_existant;
430 	MUTEX_UNLOCK(&start_thread_mutex);
431 	return EINVAL;
432     }
433     MUTEX_LOCK(&start_thread_mutex);		/* Wait for init to proceed */
434     MUTEX_UNLOCK(&start_thread_mutex);
435     return 0;
436 }
437 
438 int
439 pthread_detach(perl_os_thread tid)
440 {
441     MUTEX_LOCK(&start_thread_mutex);
442     if (tid < 1 || tid >= thread_join_count) {
443 	MUTEX_UNLOCK(&start_thread_mutex);
444 	if (tid != pthread_not_existant)
445 	    Perl_croak_nocontext("panic: detach of a thread with strange ordinal %d", (int)tid);
446 	Perl_warn_nocontext("detach of a thread which could not start");
447 	return 0;
448     }
449     switch (thread_join_data[tid].state) {
450     case pthreads_st_waited:
451 	MUTEX_UNLOCK(&start_thread_mutex);
452 	Perl_croak_nocontext("detach on a thread with a waiter");
453 	break;
454     case pthreads_st_run:
455 	thread_join_data[tid].state = pthreads_st_detached;
456 	MUTEX_UNLOCK(&start_thread_mutex);
457 	break;
458     case pthreads_st_exited:
459 	MUTEX_UNLOCK(&start_thread_mutex);
460 	COND_SIGNAL(&thread_join_data[tid].cond);
461 	break;
462     case pthreads_st_detached:
463 	MUTEX_UNLOCK(&start_thread_mutex);
464 	Perl_warn_nocontext("detach on an already detached thread");
465 	break;
466     case pthreads_st_norun:
467     {
468 	int state = (int)thread_join_data[tid].status;
469 
470 	thread_join_data[tid].state = pthreads_st_none;
471 	MUTEX_UNLOCK(&start_thread_mutex);
472 	Perl_croak_nocontext("panic: detaching thread which could not run"
473 			     " due to attempt of tid reuse (state='%s')",
474 			     pthreads_state_string(state));
475 	break;
476     }
477     default:
478 	MUTEX_UNLOCK(&start_thread_mutex);
479 	Perl_croak_nocontext("panic: detach of a thread with unknown thread state: '%s'",
480 	      pthreads_state_string(thread_join_data[tid].state));
481 	break;
482     }
483     return 0;
484 }
485 
486 /* This is a very bastardized version; may be OK due to edge trigger of Wait */
487 int
488 os2_cond_wait(perl_cond *c, perl_mutex *m)
489 {
490     int rc;
491     STRLEN n_a;
492     if ((rc = DosResetEventSem(*c,&n_a)) && (rc != ERROR_ALREADY_RESET))
493 	Perl_rc = rc, croak_with_os2error("panic: COND_WAIT-reset");
494     if (m) MUTEX_UNLOCK(m);
495     if (CheckOSError(DosWaitEventSem(*c,SEM_INDEFINITE_WAIT))
496 	&& (rc != ERROR_INTERRUPT))
497 	croak_with_os2error("panic: COND_WAIT");
498     if (rc == ERROR_INTERRUPT)
499 	errno = EINTR;
500     if (m) MUTEX_LOCK(m);
501     return 0;
502 }
503 #endif
504 
505 static int exe_is_aout(void);
506 
507 /* This should match enum entries_ordinals defined in os2ish.h. */
508 static const struct {
509     struct dll_handle_t *dll;
510     const char *entryname;
511     int entrypoint;
512 } loadOrdinals[] = {
513   {&doscalls_handle, NULL, 874},	/* DosQueryExtLibpath */
514   {&doscalls_handle, NULL, 873},	/* DosSetExtLibpath */
515   {&doscalls_handle, NULL, 460},	/* DosVerifyPidTid */
516   {&tcp_handle, "SETHOSTENT", 0},
517   {&tcp_handle, "SETNETENT" , 0},
518   {&tcp_handle, "SETPROTOENT", 0},
519   {&tcp_handle, "SETSERVENT", 0},
520   {&tcp_handle, "GETHOSTENT", 0},
521   {&tcp_handle, "GETNETENT" , 0},
522   {&tcp_handle, "GETPROTOENT", 0},
523   {&tcp_handle, "GETSERVENT", 0},
524   {&tcp_handle, "ENDHOSTENT", 0},
525   {&tcp_handle, "ENDNETENT", 0},
526   {&tcp_handle, "ENDPROTOENT", 0},
527   {&tcp_handle, "ENDSERVENT", 0},
528   {&pmwin_handle, NULL, 763},		/* WinInitialize */
529   {&pmwin_handle, NULL, 716},		/* WinCreateMsgQueue */
530   {&pmwin_handle, NULL, 726},		/* WinDestroyMsgQueue */
531   {&pmwin_handle, NULL, 918},		/* WinPeekMsg */
532   {&pmwin_handle, NULL, 915},		/* WinGetMsg */
533   {&pmwin_handle, NULL, 912},		/* WinDispatchMsg */
534   {&pmwin_handle, NULL, 753},		/* WinGetLastError */
535   {&pmwin_handle, NULL, 705},		/* WinCancelShutdown */
536 	/* These are needed in extensions.
537 	   How to protect PMSHAPI: it comes through EMX functions? */
538   {&rexx_handle,    "RexxStart", 0},
539   {&rexx_handle,    "RexxVariablePool", 0},
540   {&rexxapi_handle, "RexxRegisterFunctionExe", 0},
541   {&rexxapi_handle, "RexxDeregisterFunction", 0},
542   {&sesmgr_handle,  "DOSSMSETTITLE", 0}, /* Would not work runtime-loaded */
543   {&pmshapi_handle, "PRF32QUERYPROFILESIZE", 0},
544   {&pmshapi_handle, "PRF32OPENPROFILE", 0},
545   {&pmshapi_handle, "PRF32CLOSEPROFILE", 0},
546   {&pmshapi_handle, "PRF32QUERYPROFILE", 0},
547   {&pmshapi_handle, "PRF32RESET", 0},
548   {&pmshapi_handle, "PRF32QUERYPROFILEDATA", 0},
549   {&pmshapi_handle, "PRF32WRITEPROFILEDATA", 0},
550 
551   /* At least some of these do not work by name, since they need
552 	WIN32 instead of WIN... */
553 #if 0
554   These were generated with
555     nm I:\emx\lib\os2.a  | fgrep -f API-list | grep = > API-list-entries
556     perl -wnle "next unless /^0+\s+E\s+_(\w+)=(\w+).(\d+)/; print qq(    ORD_$1,)" API-list-entries > API-list-ORD_
557     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
558 #endif
559   {&pmshapi_handle, NULL, 123},		/* WinChangeSwitchEntry */
560   {&pmshapi_handle, NULL, 124},		/* WinQuerySwitchEntry */
561   {&pmshapi_handle, NULL, 125},		/* WinQuerySwitchHandle */
562   {&pmshapi_handle, NULL, 126},		/* WinQuerySwitchList */
563   {&pmshapi_handle, NULL, 131},		/* WinSwitchToProgram */
564   {&pmwin_handle, NULL, 702},		/* WinBeginEnumWindows */
565   {&pmwin_handle, NULL, 737},		/* WinEndEnumWindows */
566   {&pmwin_handle, NULL, 740},		/* WinEnumDlgItem */
567   {&pmwin_handle, NULL, 756},		/* WinGetNextWindow */
568   {&pmwin_handle, NULL, 768},		/* WinIsChild */
569   {&pmwin_handle, NULL, 799},		/* WinQueryActiveWindow */
570   {&pmwin_handle, NULL, 805},		/* WinQueryClassName */
571   {&pmwin_handle, NULL, 817},		/* WinQueryFocus */
572   {&pmwin_handle, NULL, 834},		/* WinQueryWindow */
573   {&pmwin_handle, NULL, 837},		/* WinQueryWindowPos */
574   {&pmwin_handle, NULL, 838},		/* WinQueryWindowProcess */
575   {&pmwin_handle, NULL, 841},		/* WinQueryWindowText */
576   {&pmwin_handle, NULL, 842},		/* WinQueryWindowTextLength */
577   {&pmwin_handle, NULL, 860},		/* WinSetFocus */
578   {&pmwin_handle, NULL, 875},		/* WinSetWindowPos */
579   {&pmwin_handle, NULL, 877},		/* WinSetWindowText */
580   {&pmwin_handle, NULL, 883},		/* WinShowWindow */
581   {&pmwin_handle, NULL, 772},		/* WinIsWindow */
582   {&pmwin_handle, NULL, 899},		/* WinWindowFromId */
583   {&pmwin_handle, NULL, 900},		/* WinWindowFromPoint */
584   {&pmwin_handle, NULL, 919},		/* WinPostMsg */
585   {&pmwin_handle, NULL, 735},		/* WinEnableWindow */
586   {&pmwin_handle, NULL, 736},		/* WinEnableWindowUpdate */
587   {&pmwin_handle, NULL, 773},		/* WinIsWindowEnabled */
588   {&pmwin_handle, NULL, 774},		/* WinIsWindowShowing */
589   {&pmwin_handle, NULL, 775},		/* WinIsWindowVisible */
590   {&pmwin_handle, NULL, 839},		/* WinQueryWindowPtr */
591   {&pmwin_handle, NULL, 843},		/* WinQueryWindowULong */
592   {&pmwin_handle, NULL, 844},		/* WinQueryWindowUShort */
593   {&pmwin_handle, NULL, 874},		/* WinSetWindowBits */
594   {&pmwin_handle, NULL, 876},		/* WinSetWindowPtr */
595   {&pmwin_handle, NULL, 878},		/* WinSetWindowULong */
596   {&pmwin_handle, NULL, 879},		/* WinSetWindowUShort */
597   {&pmwin_handle, NULL, 813},		/* WinQueryDesktopWindow */
598   {&pmwin_handle, NULL, 851},		/* WinSetActiveWindow */
599   {&doscalls_handle, NULL, 360},	/* DosQueryModFromEIP */
600   {&doscalls_handle, NULL, 582},	/* Dos32QueryHeaderInfo */
601   {&doscalls_handle, NULL, 362},	/* DosTmrQueryFreq */
602   {&doscalls_handle, NULL, 363},	/* DosTmrQueryTime */
603   {&pmwp_handle, NULL, 262},		/* WinQueryActiveDesktopPathname */
604   {&pmwin_handle, NULL, 765},		/* WinInvalidateRect */
605   {&pmwin_handle, NULL, 906},		/* WinCreateFrameControl */
606   {&pmwin_handle, NULL, 807},		/* WinQueryClipbrdFmtInfo */
607   {&pmwin_handle, NULL, 808},		/* WinQueryClipbrdOwner */
608   {&pmwin_handle, NULL, 809},		/* WinQueryClipbrdViewer */
609   {&pmwin_handle, NULL, 806},		/* WinQueryClipbrdData */
610   {&pmwin_handle, NULL, 793},		/* WinOpenClipbrd */
611   {&pmwin_handle, NULL, 707},		/* WinCloseClipbrd */
612   {&pmwin_handle, NULL, 854},		/* WinSetClipbrdData */
613   {&pmwin_handle, NULL, 855},		/* WinSetClipbrdOwner */
614   {&pmwin_handle, NULL, 856},		/* WinSetClipbrdViewer */
615   {&pmwin_handle, NULL, 739},		/* WinEnumClipbrdFmts  */
616   {&pmwin_handle, NULL, 733},		/* WinEmptyClipbrd */
617   {&pmwin_handle, NULL, 700},		/* WinAddAtom */
618   {&pmwin_handle, NULL, 744},		/* WinFindAtom */
619   {&pmwin_handle, NULL, 721},		/* WinDeleteAtom */
620   {&pmwin_handle, NULL, 803},		/* WinQueryAtomUsage */
621   {&pmwin_handle, NULL, 802},		/* WinQueryAtomName */
622   {&pmwin_handle, NULL, 801},		/* WinQueryAtomLength */
623   {&pmwin_handle, NULL, 830},		/* WinQuerySystemAtomTable */
624   {&pmwin_handle, NULL, 714},		/* WinCreateAtomTable */
625   {&pmwin_handle, NULL, 724},		/* WinDestroyAtomTable */
626   {&pmwin_handle, NULL, 794},		/* WinOpenWindowDC */
627   {&pmgpi_handle, NULL, 610},		/* DevOpenDC */
628   {&pmgpi_handle, NULL, 606},		/* DevQueryCaps */
629   {&pmgpi_handle, NULL, 604},		/* DevCloseDC */
630   {&pmwin_handle, NULL, 789},		/* WinMessageBox */
631   {&pmwin_handle, NULL, 1015},		/* WinMessageBox2 */
632   {&pmwin_handle, NULL, 829},		/* WinQuerySysValue */
633   {&pmwin_handle, NULL, 873},		/* WinSetSysValue */
634   {&pmwin_handle, NULL, 701},		/* WinAlarm */
635   {&pmwin_handle, NULL, 745},		/* WinFlashWindow */
636   {&pmwin_handle, NULL, 780},		/* WinLoadPointer */
637   {&pmwin_handle, NULL, 828},		/* WinQuerySysPointer */
638   {&doscalls_handle, NULL, 417},	/* DosReplaceModule */
639   {&doscalls_handle, NULL, 976},	/* DosPerfSysCall */
640   {&rexxapi_handle, "RexxRegisterSubcomExe", 0},
641 };
642 
643 HMODULE
644 loadModule(const char *modname, int fail)
645 {
646     HMODULE h = (HMODULE)dlopen(modname, 0);
647 
648     if (!h && fail)
649 	Perl_croak_nocontext("Error loading module '%s': %s",
650 			     modname, dlerror());
651     return h;
652 }
653 
654 /* const char* const ptypes[] = { "FS", "DOS", "VIO", "PM", "DETACH" }; */
655 
656 static int
657 my_type()
658 {
659     int rc;
660     TIB *tib;
661     PIB *pib;
662 
663     if (!(_emx_env & 0x200)) return 1; /* not OS/2. */
664     if (CheckOSError(DosGetInfoBlocks(&tib, &pib)))
665 	return -1;
666 
667     return (pib->pib_ultype);
668 }
669 
670 static void
671 my_type_set(int type)
672 {
673     int rc;
674     TIB *tib;
675     PIB *pib;
676 
677     if (!(_emx_env & 0x200))
678 	Perl_croak_nocontext("Can't set type on DOS"); /* not OS/2. */
679     if (CheckOSError(DosGetInfoBlocks(&tib, &pib)))
680 	croak_with_os2error("Error getting info blocks");
681     pib->pib_ultype = type;
682 }
683 
684 PFN
685 loadByOrdinal(enum entries_ordinals ord, int fail)
686 {
687     if (sizeof(loadOrdinals)/sizeof(loadOrdinals[0]) != ORD_NENTRIES)
688 	    Perl_croak_nocontext(
689 		 "Wrong size of loadOrdinals array: expected %d, actual %d",
690 		 sizeof(loadOrdinals)/sizeof(loadOrdinals[0]), ORD_NENTRIES);
691     if (ExtFCN[ord] == NULL) {
692 	PFN fcn = (PFN)-1;
693 	APIRET rc;
694 
695 	if (!loadOrdinals[ord].dll->handle) {
696 	    if (loadOrdinals[ord].dll->requires_pm && my_type() < 2) { /* FS */
697 		char *s = getenv("PERL_ASIF_PM");
698 
699 		if (!s || !atoi(s)) {
700 		    /* The module will not function well without PM.
701 		       The usual way to detect PM is the existence of the mutex
702 		       \SEM32\PMDRAG.SEM. */
703 		    HMTX hMtx = 0;
704 
705 		    if (CheckOSError(DosOpenMutexSem("\\SEM32\\PMDRAG.SEM",
706 						     &hMtx)))
707 			Perl_croak_nocontext("Looks like we have no PM; will not load DLL %s without $ENV{PERL_ASIF_PM}",
708 					     loadOrdinals[ord].dll->modname);
709 		    DosCloseMutexSem(hMtx);
710 		}
711 	    }
712 	    MUTEX_LOCK(&perlos2_state_mutex);
713 	    loadOrdinals[ord].dll->handle
714 		= loadModule(loadOrdinals[ord].dll->modname, fail);
715 	    MUTEX_UNLOCK(&perlos2_state_mutex);
716 	}
717 	if (!loadOrdinals[ord].dll->handle)
718 	    return 0;			/* Possible with FAIL==0 only */
719 	if (CheckOSError(DosQueryProcAddr(loadOrdinals[ord].dll->handle,
720 					  loadOrdinals[ord].entrypoint,
721 					  loadOrdinals[ord].entryname,&fcn))) {
722 	    char buf[20], *s = (char*)loadOrdinals[ord].entryname;
723 
724 	    if (!fail)
725 		return 0;
726 	    if (!s)
727 		sprintf(s = buf, "%d", loadOrdinals[ord].entrypoint);
728 	    Perl_croak_nocontext(
729 		 "This version of OS/2 does not support %s.%s",
730 		 loadOrdinals[ord].dll->modname, s);
731 	}
732 	ExtFCN[ord] = fcn;
733     }
734     if ((long)ExtFCN[ord] == -1)
735 	Perl_croak_nocontext("panic queryaddr");
736     return ExtFCN[ord];
737 }
738 
739 void
740 init_PMWIN_entries(void)
741 {
742     int i;
743 
744     for (i = ORD_WinInitialize; i <= ORD_WinCancelShutdown; i++)
745 	((PFN*)&PMWIN_entries)[i - ORD_WinInitialize] = loadByOrdinal(i, 1);
746 }
747 
748 /*****************************************************/
749 /* socket forwarders without linking with tcpip DLLs */
750 
751 DeclFuncByORD(struct hostent *,  gethostent,  ORD_GETHOSTENT,  (void), ())
752 DeclFuncByORD(struct netent  *,  getnetent,   ORD_GETNETENT,   (void), ())
753 DeclFuncByORD(struct protoent *, getprotoent, ORD_GETPROTOENT, (void), ())
754 DeclFuncByORD(struct servent *,  getservent,  ORD_GETSERVENT,  (void), ())
755 
756 DeclVoidFuncByORD(sethostent,  ORD_SETHOSTENT,  (int x), (x))
757 DeclVoidFuncByORD(setnetent,   ORD_SETNETENT,   (int x), (x))
758 DeclVoidFuncByORD(setprotoent, ORD_SETPROTOENT, (int x), (x))
759 DeclVoidFuncByORD(setservent,  ORD_SETSERVENT,  (int x), (x))
760 
761 DeclVoidFuncByORD(endhostent,  ORD_ENDHOSTENT,  (void), ())
762 DeclVoidFuncByORD(endnetent,   ORD_ENDNETENT,   (void), ())
763 DeclVoidFuncByORD(endprotoent, ORD_ENDPROTOENT, (void), ())
764 DeclVoidFuncByORD(endservent,  ORD_ENDSERVENT,  (void), ())
765 
766 /* priorities */
767 static const signed char priors[] = {0, 1, 3, 2}; /* Last two interchanged,
768 						     self inverse. */
769 #define QSS_INI_BUFFER 1024
770 
771 ULONG (*pDosVerifyPidTid) (PID pid, TID tid);
772 
773 PQTOPLEVEL
774 get_sysinfo(ULONG pid, ULONG flags)
775 {
776     char *pbuffer;
777     ULONG rc, buf_len = QSS_INI_BUFFER;
778     PQTOPLEVEL psi;
779 
780     if (pid) {
781 	if (!pidtid_lookup) {
782 	    pidtid_lookup = 1;
783 	    *(PFN*)&pDosVerifyPidTid = loadByOrdinal(ORD_DosVerifyPidTid, 0);
784 	}
785 	if (pDosVerifyPidTid) {	/* Warp3 or later */
786 	    /* Up to some fixpak QuerySysState() kills the system if a non-existent
787 	       pid is used. */
788 	    if (CheckOSError(pDosVerifyPidTid(pid, 1)))
789 		return 0;
790         }
791     }
792     New(1322, pbuffer, buf_len, char);
793     /* QSS_PROCESS | QSS_MODULE | QSS_SEMAPHORES | QSS_SHARED */
794     rc = QuerySysState(flags, pid, pbuffer, buf_len);
795     while (rc == ERROR_BUFFER_OVERFLOW) {
796 	Renew(pbuffer, buf_len *= 2, char);
797 	rc = QuerySysState(flags, pid, pbuffer, buf_len);
798     }
799     if (rc) {
800 	FillOSError(rc);
801 	Safefree(pbuffer);
802 	return 0;
803     }
804     psi = (PQTOPLEVEL)pbuffer;
805     if (psi && pid && psi->procdata && pid != psi->procdata->pid) {
806       Safefree(psi);
807       Perl_croak_nocontext("panic: wrong pid in sysinfo");
808     }
809     return psi;
810 }
811 
812 #define PRIO_ERR 0x1111
813 
814 static ULONG
815 sys_prio(pid)
816 {
817   ULONG prio;
818   PQTOPLEVEL psi;
819 
820   if (!pid)
821       return PRIO_ERR;
822   psi = get_sysinfo(pid, QSS_PROCESS);
823   if (!psi)
824       return PRIO_ERR;
825   prio = psi->procdata->threads->priority;
826   Safefree(psi);
827   return prio;
828 }
829 
830 int
831 setpriority(int which, int pid, int val)
832 {
833   ULONG rc, prio = sys_prio(pid);
834 
835   if (!(_emx_env & 0x200)) return 0; /* Nop if not OS/2. */
836   if (priors[(32 - val) >> 5] + 1 == (prio >> 8)) {
837       /* Do not change class. */
838       return CheckOSError(DosSetPriority((pid < 0)
839 					 ? PRTYS_PROCESSTREE : PRTYS_PROCESS,
840 					 0,
841 					 (32 - val) % 32 - (prio & 0xFF),
842 					 abs(pid)))
843       ? -1 : 0;
844   } else /* if ((32 - val) % 32 == (prio & 0xFF)) */ {
845       /* Documentation claims one can change both class and basevalue,
846        * but I find it wrong. */
847       /* Change class, but since delta == 0 denotes absolute 0, correct. */
848       if (CheckOSError(DosSetPriority((pid < 0)
849 				      ? PRTYS_PROCESSTREE : PRTYS_PROCESS,
850 				      priors[(32 - val) >> 5] + 1,
851 				      0,
852 				      abs(pid))))
853 	  return -1;
854       if ( ((32 - val) % 32) == 0 ) return 0;
855       return CheckOSError(DosSetPriority((pid < 0)
856 					 ? PRTYS_PROCESSTREE : PRTYS_PROCESS,
857 					 0,
858 					 (32 - val) % 32,
859 					 abs(pid)))
860 	  ? -1 : 0;
861   }
862 }
863 
864 int
865 getpriority(int which /* ignored */, int pid)
866 {
867   ULONG ret;
868 
869   if (!(_emx_env & 0x200)) return 0; /* Nop if not OS/2. */
870   ret = sys_prio(pid);
871   if (ret == PRIO_ERR) {
872       return -1;
873   }
874   return (1 - priors[((ret >> 8) - 1)])*32 - (ret & 0xFF);
875 }
876 
877 /*****************************************************************************/
878 /* spawn */
879 
880 
881 
882 static Signal_t
883 spawn_sighandler(int sig)
884 {
885     /* Some programs do not arrange for the keyboard signals to be
886        delivered to them.  We need to deliver the signal manually. */
887     /* We may get a signal only if
888        a) kid does not receive keyboard signal: deliver it;
889        b) kid already died, and we get a signal.  We may only hope
890           that the pid number was not reused.
891      */
892 
893     if (spawn_killed)
894 	sig = SIGKILL;			/* Try harder. */
895     kill(spawn_pid, sig);
896     spawn_killed = 1;
897 }
898 
899 static int
900 result(pTHX_ int flag, int pid)
901 {
902 	int r, status;
903 	Signal_t (*ihand)();     /* place to save signal during system() */
904 	Signal_t (*qhand)();     /* place to save signal during system() */
905 #ifndef __EMX__
906 	RESULTCODES res;
907 	int rpid;
908 #endif
909 
910 	if (pid < 0 || flag != 0)
911 		return pid;
912 
913 #ifdef __EMX__
914 	spawn_pid = pid;
915 	spawn_killed = 0;
916 	ihand = rsignal(SIGINT, &spawn_sighandler);
917 	qhand = rsignal(SIGQUIT, &spawn_sighandler);
918 	do {
919 	    r = wait4pid(pid, &status, 0);
920 	} while (r == -1 && errno == EINTR);
921 	rsignal(SIGINT, ihand);
922 	rsignal(SIGQUIT, qhand);
923 
924 	PL_statusvalue = (U16)status;
925 	if (r < 0)
926 		return -1;
927 	return status & 0xFFFF;
928 #else
929 	ihand = rsignal(SIGINT, SIG_IGN);
930 	r = DosWaitChild(DCWA_PROCESS, DCWW_WAIT, &res, &rpid, pid);
931 	rsignal(SIGINT, ihand);
932 	PL_statusvalue = res.codeResult << 8 | res.codeTerminate;
933 	if (r)
934 		return -1;
935 	return PL_statusvalue;
936 #endif
937 }
938 
939 enum execf_t {
940   EXECF_SPAWN,
941   EXECF_EXEC,
942   EXECF_TRUEEXEC,
943   EXECF_SPAWN_NOWAIT,
944   EXECF_SPAWN_BYFLAG,
945   EXECF_SYNC
946 };
947 
948 static ULONG
949 file_type(char *path)
950 {
951     int rc;
952     ULONG apptype;
953 
954     if (!(_emx_env & 0x200))
955 	Perl_croak_nocontext("file_type not implemented on DOS"); /* not OS/2. */
956     if (CheckOSError(DosQueryAppType(path, &apptype))) {
957 	switch (rc) {
958 	case ERROR_FILE_NOT_FOUND:
959 	case ERROR_PATH_NOT_FOUND:
960 	    return -1;
961 	case ERROR_ACCESS_DENIED:	/* Directory with this name found? */
962 	    return -3;
963 	default:			/* Found, but not an
964 					   executable, or some other
965 					   read error. */
966 	    return -2;
967 	}
968     }
969     return apptype;
970 }
971 
972 /* Spawn/exec a program, revert to shell if needed. */
973 /* global PL_Argv[] contains arguments. */
974 
975 extern ULONG _emx_exception (	EXCEPTIONREPORTRECORD *,
976 				EXCEPTIONREGISTRATIONRECORD *,
977                                 CONTEXTRECORD *,
978                                 void *);
979 
980 int
981 do_spawn_ve(pTHX_ SV *really, U32 flag, U32 execf, char *inicmd, U32 addflag)
982 {
983 	int trueflag = flag;
984 	int rc, pass = 1;
985 	char *real_name = NULL;			/* Shut down the warning */
986 	char const * args[4];
987 	static const char * const fargs[4]
988 	    = { "/bin/sh", "-c", "\"$@\"", "spawn-via-shell", };
989 	const char * const *argsp = fargs;
990 	int nargs = 4;
991 	int force_shell;
992  	int new_stderr = -1, nostderr = 0;
993 	int fl_stderr = 0;
994 	STRLEN n_a;
995 	char *buf;
996 	PerlIO *file;
997 
998 	if (flag == P_WAIT)
999 		flag = P_NOWAIT;
1000 	if (really && !*(real_name = SvPV(really, n_a)))
1001 	    really = Nullsv;
1002 
1003       retry:
1004 	if (strEQ(PL_Argv[0],"/bin/sh"))
1005 	    PL_Argv[0] = PL_sh_path;
1006 
1007 	/* We should check PERL_SH* and PERLLIB_* as well? */
1008 	if (!really || pass >= 2)
1009 	    real_name = PL_Argv[0];
1010 	if (real_name[0] != '/' && real_name[0] != '\\'
1011 	    && !(real_name[0] && real_name[1] == ':'
1012 		 && (real_name[2] == '/' || real_name[2] != '\\'))
1013 	    ) /* will spawnvp use PATH? */
1014 	    TAINT_ENV();	/* testing IFS here is overkill, probably */
1015 
1016       reread:
1017 	force_shell = 0;
1018 	if (_emx_env & 0x200) { /* OS/2. */
1019 	    int type = file_type(real_name);
1020 	  type_again:
1021 	    if (type == -1) {		/* Not found */
1022 		errno = ENOENT;
1023 		rc = -1;
1024 		goto do_script;
1025 	    }
1026 	    else if (type == -2) {		/* Not an EXE */
1027 		errno = ENOEXEC;
1028 		rc = -1;
1029 		goto do_script;
1030 	    }
1031 	    else if (type == -3) {		/* Is a directory? */
1032 		/* Special-case this */
1033 		char tbuf[512];
1034 		int l = strlen(real_name);
1035 
1036 		if (l + 5 <= sizeof tbuf) {
1037 		    strcpy(tbuf, real_name);
1038 		    strcpy(tbuf + l, ".exe");
1039 		    type = file_type(tbuf);
1040 		    if (type >= -3)
1041 			goto type_again;
1042 		}
1043 
1044 		errno = ENOEXEC;
1045 		rc = -1;
1046 		goto do_script;
1047 	    }
1048 	    switch (type & 7) {
1049 		/* Ignore WINDOWCOMPAT and FAPI, start them the same type we are. */
1050 	    case FAPPTYP_WINDOWAPI:
1051 	    {	/* Apparently, kids are started basing on startup type, not the morphed type */
1052 		if (os2_mytype != 3) {	/* not PM */
1053 		    if (flag == P_NOWAIT)
1054 			flag = P_PM;
1055 		    else if ((flag & 7) != P_PM && (flag & 7) != P_SESSION && ckWARN(WARN_EXEC))
1056 			Perl_warner(aTHX_ packWARN(WARN_EXEC), "Starting PM process with flag=%d, mytype=%d",
1057 			     flag, os2_mytype);
1058 		}
1059 	    }
1060 	    break;
1061 	    case FAPPTYP_NOTWINDOWCOMPAT:
1062 	    {
1063 		if (os2_mytype != 0) {	/* not full screen */
1064 		    if (flag == P_NOWAIT)
1065 			flag = P_SESSION;
1066 		    else if ((flag & 7) != P_SESSION && ckWARN(WARN_EXEC))
1067 			Perl_warner(aTHX_ packWARN(WARN_EXEC), "Starting Full Screen process with flag=%d, mytype=%d",
1068 			     flag, os2_mytype);
1069 		}
1070 	    }
1071 	    break;
1072 	    case FAPPTYP_NOTSPEC:
1073 		/* Let the shell handle this... */
1074 		force_shell = 1;
1075 		buf = "";		/* Pacify a warning */
1076 		file = 0;		/* Pacify a warning */
1077 		goto doshell_args;
1078 		break;
1079 	    }
1080 	}
1081 
1082 	if (addflag) {
1083 	    addflag = 0;
1084 	    new_stderr = dup(2);		/* Preserve stderr */
1085 	    if (new_stderr == -1) {
1086 		if (errno == EBADF)
1087 		    nostderr = 1;
1088 		else {
1089 		    rc = -1;
1090 		    goto finish;
1091 		}
1092 	    } else
1093 		fl_stderr = fcntl(2, F_GETFD);
1094 	    rc = dup2(1,2);
1095 	    if (rc == -1)
1096 		goto finish;
1097 	    fcntl(new_stderr, F_SETFD, FD_CLOEXEC);
1098 	}
1099 
1100 #if 0
1101 	rc = result(aTHX_ trueflag, spawnvp(flag,real_name,PL_Argv));
1102 #else
1103 	if (execf == EXECF_TRUEEXEC)
1104 	    rc = execvp(real_name,PL_Argv);
1105 	else if (execf == EXECF_EXEC)
1106 	    rc = spawnvp(trueflag | P_OVERLAY,real_name,PL_Argv);
1107 	else if (execf == EXECF_SPAWN_NOWAIT)
1108 	    rc = spawnvp(flag,real_name,PL_Argv);
1109         else if (execf == EXECF_SYNC)
1110 	    rc = spawnvp(trueflag,real_name,PL_Argv);
1111         else				/* EXECF_SPAWN, EXECF_SPAWN_BYFLAG */
1112 	    rc = result(aTHX_ trueflag,
1113 			spawnvp(flag,real_name,PL_Argv));
1114 #endif
1115 	if (rc < 0 && pass == 1) {
1116 	      do_script:
1117 	  if (real_name == PL_Argv[0]) {
1118 	    int err = errno;
1119 
1120 	    if (err == ENOENT || err == ENOEXEC) {
1121 		/* No such file, or is a script. */
1122 		/* Try adding script extensions to the file name, and
1123 		   search on PATH. */
1124 		char *scr = find_script(PL_Argv[0], TRUE, NULL, 0);
1125 
1126 		if (scr) {
1127 		    char *s = 0, *s1;
1128 		    SV *scrsv = sv_2mortal(newSVpv(scr, 0));
1129 		    SV *bufsv = sv_newmortal();
1130 
1131                     Safefree(scr);
1132 		    scr = SvPV(scrsv, n_a); /* free()ed later */
1133 
1134 		    file = PerlIO_open(scr, "r");
1135 		    PL_Argv[0] = scr;
1136 		    if (!file)
1137 			goto panic_file;
1138 
1139 		    buf = sv_gets(bufsv, file, 0 /* No append */);
1140 		    if (!buf)
1141 			buf = "";	/* XXX Needed? */
1142 		    if (!buf[0]) {	/* Empty... */
1143 			PerlIO_close(file);
1144 			/* Special case: maybe from -Zexe build, so
1145 			   there is an executable around (contrary to
1146 			   documentation, DosQueryAppType sometimes (?)
1147 			   does not append ".exe", so we could have
1148 			   reached this place). */
1149 			sv_catpv(scrsv, ".exe");
1150 	                PL_Argv[0] = scr = SvPV(scrsv, n_a);	/* Reload */
1151 			if (PerlLIO_stat(scr,&PL_statbuf) >= 0
1152 			    && !S_ISDIR(PL_statbuf.st_mode)) {	/* Found */
1153 				real_name = scr;
1154 				pass++;
1155 				goto reread;
1156 			} else {		/* Restore */
1157 				SvCUR_set(scrsv, SvCUR(scrsv) - 4);
1158 				*SvEND(scrsv) = 0;
1159 			}
1160 		    }
1161 		    if (PerlIO_close(file) != 0) { /* Failure */
1162 		      panic_file:
1163 			if (ckWARN(WARN_EXEC))
1164 			   Perl_warner(aTHX_ packWARN(WARN_EXEC), "Error reading \"%s\": %s",
1165 			     scr, Strerror(errno));
1166 			buf = "";	/* Not #! */
1167 			goto doshell_args;
1168 		    }
1169 		    if (buf[0] == '#') {
1170 			if (buf[1] == '!')
1171 			    s = buf + 2;
1172 		    } else if (buf[0] == 'e') {
1173 			if (strnEQ(buf, "extproc", 7)
1174 			    && isSPACE(buf[7]))
1175 			    s = buf + 8;
1176 		    } else if (buf[0] == 'E') {
1177 			if (strnEQ(buf, "EXTPROC", 7)
1178 			    && isSPACE(buf[7]))
1179 			    s = buf + 8;
1180 		    }
1181 		    if (!s) {
1182 			buf = "";	/* Not #! */
1183 			goto doshell_args;
1184 		    }
1185 
1186 		    s1 = s;
1187 		    nargs = 0;
1188 		    argsp = args;
1189 		    while (1) {
1190 			/* Do better than pdksh: allow a few args,
1191 			   strip trailing whitespace.  */
1192 			while (isSPACE(*s))
1193 			    s++;
1194 			if (*s == 0)
1195 			    break;
1196 			if (nargs == 4) {
1197 			    nargs = -1;
1198 			    break;
1199 			}
1200 			args[nargs++] = s;
1201 			while (*s && !isSPACE(*s))
1202 			    s++;
1203 			if (*s == 0)
1204 			    break;
1205 			*s++ = 0;
1206 		    }
1207 		    if (nargs == -1) {
1208 			Perl_warner(aTHX_ packWARN(WARN_EXEC), "Too many args on %.*s line of \"%s\"",
1209 			     s1 - buf, buf, scr);
1210 			nargs = 4;
1211 			argsp = fargs;
1212 		    }
1213 		    /* Can jump from far, buf/file invalid if force_shell: */
1214 		  doshell_args:
1215 		    {
1216 			char **a = PL_Argv;
1217 			const char *exec_args[2];
1218 
1219 			if (force_shell
1220 			    || (!buf[0] && file)) { /* File without magic */
1221 			    /* In fact we tried all what pdksh would
1222 			       try.  There is no point in calling
1223 			       pdksh, we may just emulate its logic. */
1224 			    char *shell = getenv("EXECSHELL");
1225 			    char *shell_opt = NULL;
1226 
1227 			    if (!shell) {
1228 				char *s;
1229 
1230 				shell_opt = "/c";
1231 				shell = getenv("OS2_SHELL");
1232 				if (inicmd) { /* No spaces at start! */
1233 				    s = inicmd;
1234 				    while (*s && !isSPACE(*s)) {
1235 					if (*s++ == '/') {
1236 					    inicmd = NULL; /* Cannot use */
1237 					    break;
1238 					}
1239 				    }
1240 				}
1241 				if (!inicmd) {
1242 				    s = PL_Argv[0];
1243 				    while (*s) {
1244 					/* Dosish shells will choke on slashes
1245 					   in paths, fortunately, this is
1246 					   important for zeroth arg only. */
1247 					if (*s == '/')
1248 					    *s = '\\';
1249 					s++;
1250 				    }
1251 				}
1252 			    }
1253 			    /* If EXECSHELL is set, we do not set */
1254 
1255 			    if (!shell)
1256 				shell = ((_emx_env & 0x200)
1257 					 ? "c:/os2/cmd.exe"
1258 					 : "c:/command.com");
1259 			    nargs = shell_opt ? 2 : 1;	/* shell file args */
1260 			    exec_args[0] = shell;
1261 			    exec_args[1] = shell_opt;
1262 			    argsp = exec_args;
1263 			    if (nargs == 2 && inicmd) {
1264 				/* Use the original cmd line */
1265 				/* XXXX This is good only until we refuse
1266 				        quoted arguments... */
1267 				PL_Argv[0] = inicmd;
1268 				PL_Argv[1] = Nullch;
1269 			    }
1270 			} else if (!buf[0] && inicmd) { /* No file */
1271 			    /* Start with the original cmdline. */
1272 			    /* XXXX This is good only until we refuse
1273 			            quoted arguments... */
1274 
1275 			    PL_Argv[0] = inicmd;
1276 			    PL_Argv[1] = Nullch;
1277 			    nargs = 2;	/* shell -c */
1278 			}
1279 
1280 			while (a[1])		/* Get to the end */
1281 			    a++;
1282 			a++;			/* Copy finil NULL too */
1283 			while (a >= PL_Argv) {
1284 			    *(a + nargs) = *a;	/* PL_Argv was preallocated to be
1285 						   long enough. */
1286 			    a--;
1287 			}
1288 			while (--nargs >= 0) /* XXXX Discard const... */
1289 			    PL_Argv[nargs] = (char*)argsp[nargs];
1290 			/* Enable pathless exec if #! (as pdksh). */
1291 			pass = (buf[0] == '#' ? 2 : 3);
1292 			goto retry;
1293 		    }
1294 		}
1295 		/* Not found: restore errno */
1296 		errno = err;
1297 	    }
1298 	  } else if (errno == ENOEXEC) { /* Cannot transfer `real_name' via shell. */
1299 		if (rc < 0 && ckWARN(WARN_EXEC))
1300 		    Perl_warner(aTHX_ packWARN(WARN_EXEC), "Can't %s script `%s' with ARGV[0] being `%s'",
1301 			 ((execf != EXECF_EXEC && execf != EXECF_TRUEEXEC)
1302 			  ? "spawn" : "exec"),
1303 			 real_name, PL_Argv[0]);
1304 		goto warned;
1305 	  } else if (errno == ENOENT) { /* Cannot transfer `real_name' via shell. */
1306 		if (rc < 0 && ckWARN(WARN_EXEC))
1307 		    Perl_warner(aTHX_ packWARN(WARN_EXEC), "Can't %s `%s' with ARGV[0] being `%s' (looking for executables only, not found)",
1308 			 ((execf != EXECF_EXEC && execf != EXECF_TRUEEXEC)
1309 			  ? "spawn" : "exec"),
1310 			 real_name, PL_Argv[0]);
1311 		goto warned;
1312 	  }
1313 	} else if (rc < 0 && pass == 2 && errno == ENOENT) { /* File not found */
1314 	    char *no_dir = strrchr(PL_Argv[0], '/');
1315 
1316 	    /* Do as pdksh port does: if not found with /, try without
1317 	       path. */
1318 	    if (no_dir) {
1319 		PL_Argv[0] = no_dir + 1;
1320 		pass++;
1321 		goto retry;
1322 	    }
1323 	}
1324 	if (rc < 0 && ckWARN(WARN_EXEC))
1325 	    Perl_warner(aTHX_ packWARN(WARN_EXEC), "Can't %s \"%s\": %s\n",
1326 		 ((execf != EXECF_EXEC && execf != EXECF_TRUEEXEC)
1327 		  ? "spawn" : "exec"),
1328 		 real_name, Strerror(errno));
1329       warned:
1330 	if (rc < 0 && (execf != EXECF_SPAWN_NOWAIT)
1331 	    && ((trueflag & 0xFF) == P_WAIT))
1332 	    rc = -1;
1333 
1334   finish:
1335     if (new_stderr != -1) {	/* How can we use error codes? */
1336 	dup2(new_stderr, 2);
1337 	close(new_stderr);
1338 	fcntl(2, F_SETFD, fl_stderr);
1339     } else if (nostderr)
1340        close(2);
1341     return rc;
1342 }
1343 
1344 /* Try converting 1-arg form to (usually shell-less) multi-arg form. */
1345 int
1346 do_spawn3(pTHX_ char *cmd, int execf, int flag)
1347 {
1348     register char **a;
1349     register char *s;
1350     char *shell, *copt, *news = NULL;
1351     int rc, seenspace = 0, mergestderr = 0;
1352 
1353 #ifdef TRYSHELL
1354     if ((shell = getenv("EMXSHELL")) != NULL)
1355     	copt = "-c";
1356     else if ((shell = getenv("SHELL")) != NULL)
1357     	copt = "-c";
1358     else if ((shell = getenv("COMSPEC")) != NULL)
1359     	copt = "/C";
1360     else
1361     	shell = "cmd.exe";
1362 #else
1363     /* Consensus on perl5-porters is that it is _very_ important to
1364        have a shell which will not change between computers with the
1365        same architecture, to avoid "action on a distance".
1366        And to have simple build, this shell should be sh. */
1367     shell = PL_sh_path;
1368     copt = "-c";
1369 #endif
1370 
1371     while (*cmd && isSPACE(*cmd))
1372 	cmd++;
1373 
1374     if (strnEQ(cmd,"/bin/sh",7) && isSPACE(cmd[7])) {
1375 	STRLEN l = strlen(PL_sh_path);
1376 
1377 	New(1302, news, strlen(cmd) - 7 + l + 1, char);
1378 	strcpy(news, PL_sh_path);
1379 	strcpy(news + l, cmd + 7);
1380 	cmd = news;
1381     }
1382 
1383     /* save an extra exec if possible */
1384     /* see if there are shell metacharacters in it */
1385 
1386     if (*cmd == '.' && isSPACE(cmd[1]))
1387 	goto doshell;
1388 
1389     if (strnEQ(cmd,"exec",4) && isSPACE(cmd[4]))
1390 	goto doshell;
1391 
1392     for (s = cmd; *s && isALPHA(*s); s++) ;	/* catch VAR=val gizmo */
1393     if (*s == '=')
1394 	goto doshell;
1395 
1396     for (s = cmd; *s; s++) {
1397 	if (*s != ' ' && !isALPHA(*s) && strchr("$&*(){}[]'\";\\|?<>~`\n",*s)) {
1398 	    if (*s == '\n' && s[1] == '\0') {
1399 		*s = '\0';
1400 		break;
1401 	    } else if (*s == '\\' && !seenspace) {
1402 		continue;		/* Allow backslashes in names */
1403 	    } else if (*s == '>' && s >= cmd + 3
1404 			&& s[-1] == '2' && s[1] == '&' && s[2] == '1'
1405 			&& isSPACE(s[-2]) ) {
1406 		char *t = s + 3;
1407 
1408 		while (*t && isSPACE(*t))
1409 		    t++;
1410 		if (!*t) {
1411 		    s[-2] = '\0';
1412 		    mergestderr = 1;
1413 		    break;		/* Allow 2>&1 as the last thing */
1414 		}
1415 	    }
1416 	    /* We do not convert this to do_spawn_ve since shell
1417 	       should be smart enough to start itself gloriously. */
1418 	  doshell:
1419 	    if (execf == EXECF_TRUEEXEC)
1420                 rc = execl(shell,shell,copt,cmd,(char*)0);
1421 	    else if (execf == EXECF_EXEC)
1422                 rc = spawnl(P_OVERLAY,shell,shell,copt,cmd,(char*)0);
1423 	    else if (execf == EXECF_SPAWN_NOWAIT)
1424                 rc = spawnl(P_NOWAIT,shell,shell,copt,cmd,(char*)0);
1425 	    else if (execf == EXECF_SPAWN_BYFLAG)
1426                 rc = spawnl(flag,shell,shell,copt,cmd,(char*)0);
1427 	    else {
1428 		/* In the ak code internal P_NOWAIT is P_WAIT ??? */
1429 		if (execf == EXECF_SYNC)
1430 		   rc = spawnl(P_WAIT,shell,shell,copt,cmd,(char*)0);
1431 		else
1432 		   rc = result(aTHX_ P_WAIT,
1433 			       spawnl(P_NOWAIT,shell,shell,copt,cmd,(char*)0));
1434 		if (rc < 0 && ckWARN(WARN_EXEC))
1435 		    Perl_warner(aTHX_ packWARN(WARN_EXEC), "Can't %s \"%s\": %s",
1436 			 (execf == EXECF_SPAWN ? "spawn" : "exec"),
1437 			 shell, Strerror(errno));
1438 		if (rc < 0)
1439 		    rc = -1;
1440 	    }
1441 	    if (news)
1442 		Safefree(news);
1443 	    return rc;
1444 	} else if (*s == ' ' || *s == '\t') {
1445 	    seenspace = 1;
1446 	}
1447     }
1448 
1449     /* cmd="a" may lead to "sh", "-c", "\"$@\"", "a", "a.cmd", NULL */
1450     New(1303,PL_Argv, (s - cmd + 11) / 2, char*);
1451     PL_Cmd = savepvn(cmd, s-cmd);
1452     a = PL_Argv;
1453     for (s = PL_Cmd; *s;) {
1454 	while (*s && isSPACE(*s)) s++;
1455 	if (*s)
1456 	    *(a++) = s;
1457 	while (*s && !isSPACE(*s)) s++;
1458 	if (*s)
1459 	    *s++ = '\0';
1460     }
1461     *a = Nullch;
1462     if (PL_Argv[0])
1463 	rc = do_spawn_ve(aTHX_ NULL, flag, execf, cmd, mergestderr);
1464     else
1465     	rc = -1;
1466     if (news)
1467 	Safefree(news);
1468     do_execfree();
1469     return rc;
1470 }
1471 
1472 /* Array spawn/exec.  */
1473 int
1474 os2_aspawn4(pTHX_ SV *really, register SV **vmark, register SV **vsp, int execing)
1475 {
1476     register SV **mark = (SV **)vmark;
1477     register SV **sp = (SV **)vsp;
1478     register char **a;
1479     int rc;
1480     int flag = P_WAIT, flag_set = 0;
1481     STRLEN n_a;
1482 
1483     if (sp > mark) {
1484 	New(1301,PL_Argv, sp - mark + 3, char*);
1485 	a = PL_Argv;
1486 
1487 	if (mark < sp && SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
1488 		++mark;
1489 		flag = SvIVx(*mark);
1490 		flag_set = 1;
1491 
1492 	}
1493 
1494 	while (++mark <= sp) {
1495 	    if (*mark)
1496 		*a++ = SvPVx(*mark, n_a);
1497 	    else
1498 		*a++ = "";
1499 	}
1500 	*a = Nullch;
1501 
1502 	if ( flag_set && (a == PL_Argv + 1)
1503 	     && !really && !execing ) { 		/* One arg? */
1504 	    rc = do_spawn3(aTHX_ a[-1], EXECF_SPAWN_BYFLAG, flag);
1505 	} else
1506 	    rc = do_spawn_ve(aTHX_ really, flag,
1507 			     (execing ? EXECF_EXEC : EXECF_SPAWN), NULL, 0);
1508     } else
1509     	rc = -1;
1510     do_execfree();
1511     return rc;
1512 }
1513 
1514 /* Array spawn.  */
1515 int
1516 os2_do_aspawn(pTHX_ SV *really, register SV **vmark, register SV **vsp)
1517 {
1518     return os2_aspawn4(aTHX_ really, vmark, vsp, 0);
1519 }
1520 
1521 /* Array exec.  */
1522 bool
1523 Perl_do_aexec(pTHX_ SV* really, SV** vmark, SV** vsp)
1524 {
1525     return os2_aspawn4(aTHX_ really, vmark, vsp, 1);
1526 }
1527 
1528 int
1529 os2_do_spawn(pTHX_ char *cmd)
1530 {
1531     return do_spawn3(aTHX_ cmd, EXECF_SPAWN, 0);
1532 }
1533 
1534 int
1535 do_spawn_nowait(pTHX_ char *cmd)
1536 {
1537     return do_spawn3(aTHX_ cmd, EXECF_SPAWN_NOWAIT,0);
1538 }
1539 
1540 bool
1541 Perl_do_exec(pTHX_ char *cmd)
1542 {
1543     do_spawn3(aTHX_ cmd, EXECF_EXEC, 0);
1544     return FALSE;
1545 }
1546 
1547 bool
1548 os2exec(pTHX_ char *cmd)
1549 {
1550     return do_spawn3(aTHX_ cmd, EXECF_TRUEEXEC, 0);
1551 }
1552 
1553 PerlIO *
1554 my_syspopen(pTHX_ char *cmd, char *mode)
1555 {
1556 #ifndef USE_POPEN
1557     int p[2];
1558     register I32 this, that, newfd;
1559     register I32 pid;
1560     SV *sv;
1561     int fh_fl = 0;			/* Pacify the warning */
1562 
1563     /* `this' is what we use in the parent, `that' in the child. */
1564     this = (*mode == 'w');
1565     that = !this;
1566     if (PL_tainting) {
1567 	taint_env();
1568 	taint_proper("Insecure %s%s", "EXEC");
1569     }
1570     if (pipe(p) < 0)
1571 	return Nullfp;
1572     /* Now we need to spawn the child. */
1573     if (p[this] == (*mode == 'r')) {	/* if fh 0/1 was initially closed. */
1574 	int new = dup(p[this]);
1575 
1576 	if (new == -1)
1577 	    goto closepipes;
1578 	close(p[this]);
1579 	p[this] = new;
1580     }
1581     newfd = dup(*mode == 'r');		/* Preserve std* */
1582     if (newfd == -1) {
1583 	/* This cannot happen due to fh being bad after pipe(), since
1584 	   pipe() should have created fh 0 and 1 even if they were
1585 	   initially closed.  But we closed p[this] before.  */
1586 	if (errno != EBADF) {
1587 	  closepipes:
1588 	    close(p[0]);
1589 	    close(p[1]);
1590 	    return Nullfp;
1591 	}
1592     } else
1593 	fh_fl = fcntl(*mode == 'r', F_GETFD);
1594     if (p[that] != (*mode == 'r')) {	/* if fh 0/1 was initially closed. */
1595 	dup2(p[that], *mode == 'r');
1596 	close(p[that]);
1597     }
1598     /* Where is `this' and newfd now? */
1599     fcntl(p[this], F_SETFD, FD_CLOEXEC);
1600     if (newfd != -1)
1601 	fcntl(newfd, F_SETFD, FD_CLOEXEC);
1602     pid = do_spawn_nowait(aTHX_ cmd);
1603     if (newfd == -1)
1604 	close(*mode == 'r');		/* It was closed initially */
1605     else if (newfd != (*mode == 'r')) {	/* Probably this check is not needed */
1606 	dup2(newfd, *mode == 'r');	/* Return std* back. */
1607 	close(newfd);
1608 	fcntl(*mode == 'r', F_SETFD, fh_fl);
1609     } else
1610 	fcntl(*mode == 'r', F_SETFD, fh_fl);
1611     if (p[that] == (*mode == 'r'))
1612 	close(p[that]);
1613     if (pid == -1) {
1614 	close(p[this]);
1615 	return Nullfp;
1616     }
1617     if (p[that] < p[this]) {		/* Make fh as small as possible */
1618 	dup2(p[this], p[that]);
1619 	close(p[this]);
1620 	p[this] = p[that];
1621     }
1622     sv = *av_fetch(PL_fdpid,p[this],TRUE);
1623     (void)SvUPGRADE(sv,SVt_IV);
1624     SvIVX(sv) = pid;
1625     PL_forkprocess = pid;
1626     return PerlIO_fdopen(p[this], mode);
1627 
1628 #else  /* USE_POPEN */
1629 
1630     PerlIO *res;
1631     SV *sv;
1632 
1633 #  ifdef TRYSHELL
1634     res = popen(cmd, mode);
1635 #  else
1636     char *shell = getenv("EMXSHELL");
1637 
1638     my_setenv("EMXSHELL", PL_sh_path);
1639     res = popen(cmd, mode);
1640     my_setenv("EMXSHELL", shell);
1641 #  endif
1642     sv = *av_fetch(PL_fdpid, PerlIO_fileno(res), TRUE);
1643     (void)SvUPGRADE(sv,SVt_IV);
1644     SvIVX(sv) = -1;			/* A cooky. */
1645     return res;
1646 
1647 #endif /* USE_POPEN */
1648 
1649 }
1650 
1651 /******************************************************************/
1652 
1653 #ifndef HAS_FORK
1654 int
1655 fork(void)
1656 {
1657     Perl_croak_nocontext(PL_no_func, "Unsupported function fork");
1658     errno = EINVAL;
1659     return -1;
1660 }
1661 #endif
1662 
1663 /*******************************************************************/
1664 /* not implemented in EMX 0.9d */
1665 
1666 char *	ctermid(char *s)	{ return 0; }
1667 
1668 #ifdef MYTTYNAME /* was not in emx0.9a */
1669 void *	ttyname(x)	{ return 0; }
1670 #endif
1671 
1672 /*****************************************************************************/
1673 /* not implemented in C Set++ */
1674 
1675 #ifndef __EMX__
1676 int	setuid(x)	{ errno = EINVAL; return -1; }
1677 int	setgid(x)	{ errno = EINVAL; return -1; }
1678 #endif
1679 
1680 /*****************************************************************************/
1681 /* stat() hack for char/block device */
1682 
1683 #if OS2_STAT_HACK
1684 
1685 enum os2_stat_extra {	/* EMX 0.9d fix 4 defines up to 0100000 */
1686   os2_stat_archived	= 0x1000000,	/* 0100000000 */
1687   os2_stat_hidden	= 0x2000000,	/* 0200000000 */
1688   os2_stat_system	= 0x4000000,	/* 0400000000 */
1689   os2_stat_force	= 0x8000000,	/* Do not ignore flags on chmod */
1690 };
1691 
1692 #define OS2_STAT_SPECIAL (os2_stat_system | os2_stat_archived | os2_stat_hidden)
1693 
1694 static void
1695 massage_os2_attr(struct stat *st)
1696 {
1697     if ( ((st->st_mode & S_IFMT) != S_IFREG
1698 	  && (st->st_mode & S_IFMT) != S_IFDIR)
1699          || !(st->st_attr & (FILE_ARCHIVED | FILE_HIDDEN | FILE_SYSTEM)))
1700 	return;
1701 
1702     if ( st->st_attr & FILE_ARCHIVED )
1703 	st->st_mode |= (os2_stat_archived | os2_stat_force);
1704     if ( st->st_attr & FILE_HIDDEN )
1705 	st->st_mode |= (os2_stat_hidden | os2_stat_force);
1706     if ( st->st_attr & FILE_SYSTEM )
1707 	st->st_mode |= (os2_stat_system | os2_stat_force);
1708 }
1709 
1710     /* First attempt used DosQueryFSAttach which crashed the system when
1711        used with 5.001. Now just look for /dev/. */
1712 int
1713 os2_stat(const char *name, struct stat *st)
1714 {
1715     static int ino = SHRT_MAX;
1716     STRLEN l = strlen(name);
1717 
1718     if ( ( l < 8 || l > 9) || strnicmp(name, "/dev/", 5) != 0
1719          || (    stricmp(name + 5, "con") != 0
1720 	      && stricmp(name + 5, "tty") != 0
1721 	      && stricmp(name + 5, "nul") != 0
1722 	      && stricmp(name + 5, "null") != 0) ) {
1723 	int s = stat(name, st);
1724 
1725 	if (s)
1726 	    return s;
1727 	massage_os2_attr(st);
1728 	return 0;
1729     }
1730 
1731     memset(st, 0, sizeof *st);
1732     st->st_mode = S_IFCHR|0666;
1733     MUTEX_LOCK(&perlos2_state_mutex);
1734     st->st_ino = (ino-- & 0x7FFF);
1735     MUTEX_UNLOCK(&perlos2_state_mutex);
1736     st->st_nlink = 1;
1737     return 0;
1738 }
1739 
1740 int
1741 os2_fstat(int handle, struct stat *st)
1742 {
1743     int s = fstat(handle, st);
1744 
1745     if (s)
1746 	return s;
1747     massage_os2_attr(st);
1748     return 0;
1749 }
1750 
1751 #undef chmod
1752 int
1753 os2_chmod (const char *name, int pmode)	/* Modelled after EMX src/lib/io/chmod.c */
1754 {
1755     int attr, rc;
1756 
1757     if (!(pmode & os2_stat_force))
1758 	return chmod(name, pmode);
1759 
1760     attr = __chmod (name, 0, 0);           /* Get attributes */
1761     if (attr < 0)
1762 	return -1;
1763     if (pmode & S_IWRITE)
1764 	attr &= ~FILE_READONLY;
1765     else
1766 	attr |= FILE_READONLY;
1767     /* New logic */
1768     attr &= ~(FILE_ARCHIVED | FILE_HIDDEN | FILE_SYSTEM);
1769 
1770     if ( pmode & os2_stat_archived )
1771         attr |= FILE_ARCHIVED;
1772     if ( pmode & os2_stat_hidden )
1773         attr |= FILE_HIDDEN;
1774     if ( pmode & os2_stat_system )
1775         attr |= FILE_SYSTEM;
1776 
1777     rc = __chmod (name, 1, attr);
1778     if (rc >= 0) rc = 0;
1779     return rc;
1780 }
1781 
1782 #endif
1783 
1784 #ifdef USE_PERL_SBRK
1785 
1786 /* SBRK() emulation, mostly moved to malloc.c. */
1787 
1788 void *
1789 sys_alloc(int size) {
1790     void *got;
1791     APIRET rc = DosAllocMem(&got, size, PAG_COMMIT | PAG_WRITE);
1792 
1793     if (rc == ERROR_NOT_ENOUGH_MEMORY) {
1794 	return (void *) -1;
1795     } else if ( rc )
1796 	Perl_croak_nocontext("Got an error from DosAllocMem: %li", (long)rc);
1797     return got;
1798 }
1799 
1800 #endif /* USE_PERL_SBRK */
1801 
1802 /* tmp path */
1803 
1804 const char *tmppath = TMPPATH1;
1805 
1806 void
1807 settmppath()
1808 {
1809     char *p = getenv("TMP"), *tpath;
1810     int len;
1811 
1812     if (!p) p = getenv("TEMP");
1813     if (!p) p = getenv("TMPDIR");
1814     if (!p) return;
1815     len = strlen(p);
1816     tpath = (char *)malloc(len + strlen(TMPPATH1) + 2);
1817     if (tpath) {
1818 	strcpy(tpath, p);
1819 	tpath[len] = '/';
1820 	strcpy(tpath + len + 1, TMPPATH1);
1821 	tmppath = tpath;
1822     }
1823 }
1824 
1825 #include "XSUB.h"
1826 
1827 XS(XS_File__Copy_syscopy)
1828 {
1829     dXSARGS;
1830     if (items < 2 || items > 3)
1831 	Perl_croak_nocontext("Usage: File::Copy::syscopy(src,dst,flag=0)");
1832     {
1833 	STRLEN n_a;
1834 	char *	src = (char *)SvPV(ST(0),n_a);
1835 	char *	dst = (char *)SvPV(ST(1),n_a);
1836 	U32	flag;
1837 	int	RETVAL, rc;
1838 	dXSTARG;
1839 
1840 	if (items < 3)
1841 	    flag = 0;
1842 	else {
1843 	    flag = (unsigned long)SvIV(ST(2));
1844 	}
1845 
1846 	RETVAL = !CheckOSError(DosCopy(src, dst, flag));
1847 	XSprePUSH; PUSHi((IV)RETVAL);
1848     }
1849     XSRETURN(1);
1850 }
1851 
1852 /* APIRET APIENTRY DosReplaceModule (PCSZ pszOld, PCSZ pszNew, PCSZ pszBackup); */
1853 
1854 DeclOSFuncByORD(ULONG,replaceModule,ORD_DosReplaceModule,
1855 		(char *old, char *new, char *backup), (old, new, backup))
1856 
1857 XS(XS_OS2_replaceModule); /* prototype to pass -Wmissing-prototypes */
1858 XS(XS_OS2_replaceModule)
1859 {
1860     dXSARGS;
1861     if (items < 1 || items > 3)
1862 	Perl_croak(aTHX_ "Usage: OS2::replaceModule(target [, source [, backup]])");
1863     {
1864 	char *	target = (char *)SvPV_nolen(ST(0));
1865 	char *	source = (items < 2) ? Nullch : (char *)SvPV_nolen(ST(1));
1866 	char *	backup = (items < 3) ? Nullch : (char *)SvPV_nolen(ST(2));
1867 
1868 	if (!replaceModule(target, source, backup))
1869 	    croak_with_os2error("replaceModule() error");
1870     }
1871     XSRETURN_EMPTY;
1872 }
1873 
1874 /* APIRET APIENTRY DosPerfSysCall(ULONG ulCommand, ULONG ulParm1,
1875                                   ULONG ulParm2, ULONG ulParm3); */
1876 
1877 DeclOSFuncByORD(ULONG,perfSysCall,ORD_DosPerfSysCall,
1878 		(ULONG ulCommand, ULONG ulParm1, ULONG ulParm2, ULONG ulParm3),
1879 		(ulCommand, ulParm1, ulParm2, ulParm3))
1880 
1881 #ifndef CMD_KI_RDCNT
1882 #  define CMD_KI_RDCNT	0x63
1883 #endif
1884 #ifndef CMD_KI_GETQTY
1885 #  define CMD_KI_GETQTY 0x41
1886 #endif
1887 #ifndef QSV_NUMPROCESSORS
1888 #  define QSV_NUMPROCESSORS         26
1889 #endif
1890 
1891 typedef unsigned long long myCPUUTIL[4];	/* time/idle/busy/intr */
1892 
1893 /*
1894 NO_OUTPUT ULONG
1895 perfSysCall(ULONG ulCommand, ULONG ulParm1, ULONG ulParm2, ULONG ulParm3)
1896     PREINIT:
1897 	ULONG rc;
1898     POSTCALL:
1899 	if (!RETVAL)
1900 	    croak_with_os2error("perfSysCall() error");
1901  */
1902 
1903 static int
1904 numprocessors(void)
1905 {
1906     ULONG res;
1907 
1908     if (DosQuerySysInfo(QSV_NUMPROCESSORS, QSV_NUMPROCESSORS, (PVOID)&res, sizeof(res)))
1909 	return 1;			/* Old system? */
1910     return res;
1911 }
1912 
1913 XS(XS_OS2_perfSysCall); /* prototype to pass -Wmissing-prototypes */
1914 XS(XS_OS2_perfSysCall)
1915 {
1916     dXSARGS;
1917     if (items < 0 || items > 4)
1918 	Perl_croak(aTHX_ "Usage: OS2::perfSysCall(ulCommand = CMD_KI_RDCNT, ulParm1= 0, ulParm2= 0, ulParm3= 0)");
1919     SP -= items;
1920     {
1921 	dXSTARG;
1922 	ULONG RETVAL, ulCommand, ulParm1, ulParm2, ulParm3, res;
1923 	myCPUUTIL u[64];
1924 	int total = 0, tot2 = 0;
1925 
1926 	if (items < 1)
1927 	    ulCommand = CMD_KI_RDCNT;
1928 	else {
1929 	    ulCommand = (ULONG)SvUV(ST(0));
1930 	}
1931 
1932 	if (items < 2) {
1933 	    total = (ulCommand == CMD_KI_RDCNT ? numprocessors() : 0);
1934 	    ulParm1 = (total ? (ULONG)u : 0);
1935 
1936 	    if (total > C_ARRAY_LENGTH(u))
1937 		croak("Unexpected number of processors: %d", total);
1938 	} else {
1939 	    ulParm1 = (ULONG)SvUV(ST(1));
1940 	}
1941 
1942 	if (items < 3) {
1943 	    tot2 = (ulCommand == CMD_KI_GETQTY);
1944 	    ulParm2 = (tot2 ? (ULONG)&res : 0);
1945 	} else {
1946 	    ulParm2 = (ULONG)SvUV(ST(2));
1947 	}
1948 
1949 	if (items < 4)
1950 	    ulParm3 = 0;
1951 	else {
1952 	    ulParm3 = (ULONG)SvUV(ST(3));
1953 	}
1954 
1955 	RETVAL = perfSysCall(ulCommand, ulParm1, ulParm2, ulParm3);
1956 	if (!RETVAL)
1957 	    croak_with_os2error("perfSysCall() error");
1958 	if (total) {
1959 	    int i,j;
1960 
1961 	    if (GIMME_V != G_ARRAY) {
1962 		PUSHn(u[0][0]);		/* Total ticks on the first processor */
1963 		XSRETURN(1);
1964 	    }
1965 	    for (i=0; i < total; i++)
1966 		for (j=0; j < 4; j++)
1967 		    PUSHs(sv_2mortal(newSVnv(u[i][j])));
1968 	    XSRETURN(4*total);
1969 	}
1970 	if (tot2) {
1971 	    PUSHu(res);
1972 	    XSRETURN(1);
1973 	}
1974     }
1975     XSRETURN_EMPTY;
1976 }
1977 
1978 #define PERL_PATCHLEVEL_H_IMPLICIT	/* Do not init local_patches. */
1979 #include "patchlevel.h"
1980 #undef PERL_PATCHLEVEL_H_IMPLICIT
1981 
1982 char *
1983 mod2fname(pTHX_ SV *sv)
1984 {
1985     int pos = 6, len, avlen;
1986     unsigned int sum = 0;
1987     char *s;
1988     STRLEN n_a;
1989 
1990     if (!SvROK(sv)) Perl_croak_nocontext("Not a reference given to mod2fname");
1991     sv = SvRV(sv);
1992     if (SvTYPE(sv) != SVt_PVAV)
1993       Perl_croak_nocontext("Not array reference given to mod2fname");
1994 
1995     avlen = av_len((AV*)sv);
1996     if (avlen < 0)
1997       Perl_croak_nocontext("Empty array reference given to mod2fname");
1998 
1999     s = SvPV(*av_fetch((AV*)sv, avlen, FALSE), n_a);
2000     strncpy(fname, s, 8);
2001     len = strlen(s);
2002     if (len < 6) pos = len;
2003     while (*s) {
2004 	sum = 33 * sum + *(s++);	/* Checksumming first chars to
2005 					 * get the capitalization into c.s. */
2006     }
2007     avlen --;
2008     while (avlen >= 0) {
2009 	s = SvPV(*av_fetch((AV*)sv, avlen, FALSE), n_a);
2010 	while (*s) {
2011 	    sum = 33 * sum + *(s++);	/* 7 is primitive mod 13. */
2012 	}
2013 	avlen --;
2014     }
2015    /* We always load modules as *specific* DLLs, and with the full name.
2016       When loading a specific DLL by its full name, one cannot get a
2017       different DLL, even if a DLL with the same basename is loaded already.
2018       Thus there is no need to include the version into the mangling scheme. */
2019 #if 0
2020     sum += PERL_VERSION * 200 + PERL_SUBVERSION * 2;  /* Up to 5.6.1 */
2021 #else
2022 #  ifndef COMPATIBLE_VERSION_SUM  /* Binary compatibility with the 5.00553 binary */
2023 #    define COMPATIBLE_VERSION_SUM (5 * 200 + 53 * 2)
2024 #  endif
2025     sum += COMPATIBLE_VERSION_SUM;
2026 #endif
2027     fname[pos] = 'A' + (sum % 26);
2028     fname[pos + 1] = 'A' + (sum / 26 % 26);
2029     fname[pos + 2] = '\0';
2030     return (char *)fname;
2031 }
2032 
2033 XS(XS_DynaLoader_mod2fname)
2034 {
2035     dXSARGS;
2036     if (items != 1)
2037 	Perl_croak_nocontext("Usage: DynaLoader::mod2fname(sv)");
2038     {
2039 	SV *	sv = ST(0);
2040 	char *	RETVAL;
2041 	dXSTARG;
2042 
2043 	RETVAL = mod2fname(aTHX_ sv);
2044 	sv_setpv(TARG, RETVAL);
2045 	XSprePUSH; PUSHTARG;
2046     }
2047     XSRETURN(1);
2048 }
2049 
2050 char *
2051 os2error(int rc)
2052 {
2053 	dTHX;
2054 	ULONG len;
2055 	char *s;
2056 	int number = SvTRUE(get_sv("OS2::nsyserror", TRUE));
2057 
2058         if (!(_emx_env & 0x200)) return ""; /* Nop if not OS/2. */
2059 	if (rc == 0)
2060 		return "";
2061 	if (number) {
2062 	    sprintf(os2error_buf, "SYS%04d=%#x: ", rc, rc);
2063 	    s = os2error_buf + strlen(os2error_buf);
2064 	} else
2065 	    s = os2error_buf;
2066 	if (DosGetMessage(NULL, 0, s, sizeof(os2error_buf) - 1 - (s-os2error_buf),
2067 			  rc, "OSO001.MSG", &len)) {
2068 	    char *name = "";
2069 
2070 	    if (!number) {
2071 		sprintf(os2error_buf, "SYS%04d=%#x: ", rc, rc);
2072 		s = os2error_buf + strlen(os2error_buf);
2073 	    }
2074 	    switch (rc) {
2075 	    case PMERR_INVALID_HWND:
2076 		name = "PMERR_INVALID_HWND";
2077 		break;
2078 	    case PMERR_INVALID_HMQ:
2079 		name = "PMERR_INVALID_HMQ";
2080 		break;
2081 	    case PMERR_CALL_FROM_WRONG_THREAD:
2082 		name = "PMERR_CALL_FROM_WRONG_THREAD";
2083 		break;
2084 	    case PMERR_NO_MSG_QUEUE:
2085 		name = "PMERR_NO_MSG_QUEUE";
2086 		break;
2087 	    case PMERR_NOT_IN_A_PM_SESSION:
2088 		name = "PMERR_NOT_IN_A_PM_SESSION";
2089 		break;
2090 	    }
2091 	    sprintf(s, "%s%s[No description found in OSO001.MSG]",
2092 		    name, (*name ? "=" : ""));
2093 	} else {
2094 		s[len] = '\0';
2095 		if (len && s[len - 1] == '\n')
2096 			s[--len] = 0;
2097 		if (len && s[len - 1] == '\r')
2098 			s[--len] = 0;
2099 		if (len && s[len - 1] == '.')
2100 			s[--len] = 0;
2101 		if (len >= 10 && number && strnEQ(s, os2error_buf, 7)
2102 		    && s[7] == ':' && s[8] == ' ')
2103 		    /* Some messages start with SYSdddd:, some not */
2104 		    Move(s + 9, s, (len -= 9) + 1, char);
2105 	}
2106 	return os2error_buf;
2107 }
2108 
2109 void
2110 ResetWinError(void)
2111 {
2112   WinError_2_Perl_rc;
2113 }
2114 
2115 void
2116 CroakWinError(int die, char *name)
2117 {
2118   FillWinError;
2119   if (die && Perl_rc)
2120     croak_with_os2error(name ? name : "Win* API call");
2121 }
2122 
2123 static char *
2124 dllname2buffer(pTHX_ char *buf, STRLEN l)
2125 {
2126     char *o;
2127     STRLEN ll;
2128     SV *dll = Nullsv;
2129 
2130     dll = module_name(mod_name_full);
2131     o = SvPV(dll, ll);
2132     if (ll < l)
2133        memcpy(buf,o,ll);
2134     SvREFCNT_dec(dll);
2135     return (ll >= l ? "???" : buf);
2136 }
2137 
2138 static char *
2139 execname2buffer(char *buf, STRLEN l, char *oname)
2140 {
2141   char *p, *orig = oname, ok = oname != NULL;
2142 
2143   if (_execname(buf, l) != 0) {
2144     if (!oname || strlen(oname) >= l)
2145       return oname;
2146     strcpy(buf, oname);
2147     ok = 0;
2148   }
2149   p = buf;
2150   while (*p) {
2151     if (*p == '\\')
2152 	*p = '/';
2153     if (*p == '/') {
2154 	if (ok && *oname != '/' && *oname != '\\')
2155 	    ok = 0;
2156     } else if (ok && tolower(*oname) != tolower(*p))
2157 	ok = 0;
2158     p++;
2159     oname++;
2160   }
2161   if (ok) { /* orig matches the real name.  Use orig: */
2162      strcpy(buf, orig);		/* _execname() is always uppercased */
2163      p = buf;
2164      while (*p) {
2165        if (*p == '\\')
2166            *p = '/';
2167        p++;
2168      }
2169   }
2170   return buf;
2171 }
2172 
2173 char *
2174 os2_execname(pTHX)
2175 {
2176   char buf[300], *p = execname2buffer(buf, sizeof buf, PL_origargv[0]);
2177 
2178   p = savepv(p);
2179   SAVEFREEPV(p);
2180   return p;
2181 }
2182 
2183 int
2184 Perl_OS2_handler_install(void *handler, enum Perlos2_handler how)
2185 {
2186     char *s, b[300];
2187 
2188     switch (how) {
2189       case Perlos2_handler_mangle:
2190 	perllib_mangle_installed = (char *(*)(char *s, unsigned int l))handler;
2191 	return 1;
2192       case Perlos2_handler_perl_sh:
2193 	s = (char *)handler;
2194 	s = dir_subst(s, strlen(s), b, sizeof b, 0, "handler_perl_sh");
2195 	perl_sh_installed = savepv(s);
2196 	return 1;
2197       case Perlos2_handler_perllib_from:
2198 	s = (char *)handler;
2199 	s = dir_subst(s, strlen(s), b, sizeof b, 0, "handler_perllib_from");
2200 	oldl = strlen(s);
2201 	oldp = savepv(s);
2202 	return 1;
2203       case Perlos2_handler_perllib_to:
2204 	s = (char *)handler;
2205 	s = dir_subst(s, strlen(s), b, sizeof b, 0, "handler_perllib_to");
2206 	newl = strlen(s);
2207 	newp = savepv(s);
2208 	strcpy(mangle_ret, newp);
2209 	s = mangle_ret - 1;
2210 	while (*++s)
2211 	    if (*s == '\\')
2212 		*s = '/';
2213 	return 1;
2214       default:
2215 	return 0;
2216     }
2217 }
2218 
2219 /* Returns a malloc()ed copy */
2220 char *
2221 dir_subst(char *s, unsigned int l, char *b, unsigned int bl, enum dir_subst_e flags, char *msg)
2222 {
2223     char *from, *to = b, *e = b; /* `to' assignment: shut down the warning */
2224     STRLEN froml = 0, tol = 0, rest = 0;	/* froml: likewise */
2225 
2226     if (l >= 2 && s[0] == '~') {
2227 	switch (s[1]) {
2228 	  case 'i': case 'I':
2229 	    from = "installprefix";	break;
2230 	  case 'd': case 'D':
2231 	    from = "dll";		break;
2232 	  case 'e': case 'E':
2233 	    from = "exe";		break;
2234 	  default:
2235 	    from = NULL;
2236 	    froml = l + 1;			/* Will not match */
2237 	    break;
2238 	}
2239 	if (from)
2240 	    froml = strlen(from) + 1;
2241 	if (l >= froml && strnicmp(s + 2, from + 1, froml - 2) == 0) {
2242 	    int strip = 1;
2243 
2244 	    switch (s[1]) {
2245 	      case 'i': case 'I':
2246 		strip = 0;
2247 		tol = strlen(INSTALL_PREFIX);
2248 		if (tol >= bl) {
2249 		    if (flags & dir_subst_fatal)
2250 			Perl_croak_nocontext("INSTALL_PREFIX too long: `%s'", INSTALL_PREFIX);
2251 		    else
2252 			return NULL;
2253 		}
2254 		memcpy(b, INSTALL_PREFIX, tol + 1);
2255 		to = b;
2256 		e = b + tol;
2257 		break;
2258 	      case 'd': case 'D':
2259 		if (flags & dir_subst_fatal) {
2260 		    dTHX;
2261 
2262 		    to = dllname2buffer(aTHX_ b, bl);
2263 		} else {				/* No Perl present yet */
2264 		    HMODULE self = find_myself();
2265 		    APIRET rc = DosQueryModuleName(self, bl, b);
2266 
2267 		    if (rc)
2268 			return 0;
2269 		    to = b - 1;
2270 		    while (*++to)
2271 			if (*to == '\\')
2272 			    *to = '/';
2273 		    to = b;
2274 		}
2275 		break;
2276 	      case 'e': case 'E':
2277 		if (flags & dir_subst_fatal) {
2278 		    dTHX;
2279 
2280 		    to = execname2buffer(b, bl, PL_origargv[0]);
2281 	        } else
2282 		    to = execname2buffer(b, bl, NULL);
2283 		break;
2284 	    }
2285 	    if (!to)
2286 		return NULL;
2287 	    if (strip) {
2288 		e = strrchr(to, '/');
2289 		if (!e && (flags & dir_subst_fatal))
2290 		    Perl_croak_nocontext("%s: Can't parse EXE/DLL name: '%s'", msg, to);
2291 		else if (!e)
2292 		    return NULL;
2293 		*e = 0;
2294 	    }
2295 	    s += froml; l -= froml;
2296 	    if (!l)
2297 		return to;
2298 	    if (!tol)
2299 		tol = strlen(to);
2300 
2301 	    while (l >= 3 && (s[0] == '/' || s[0] == '\\')
2302 		   && s[1] == '.' && s[2] == '.'
2303 		   && (l == 3 || s[3] == '/' || s[3] == '\\' || s[3] == ';')) {
2304 		e = strrchr(b, '/');
2305 		if (!e && (flags & dir_subst_fatal))
2306 			Perl_croak_nocontext("%s: Error stripping dirs from EXE/DLL/INSTALLDIR name", msg);
2307 		else if (!e)
2308 			return NULL;
2309 		*e = 0;
2310 		l -= 3; s += 3;
2311 	    }
2312 	    if (l && s[0] != '/' && s[0] != '\\' && s[0] != ';')
2313 		*e++ = '/';
2314 	}
2315     }						/* Else: copy as is */
2316     if (l && (flags & dir_subst_pathlike)) {
2317 	STRLEN i = 0;
2318 
2319 	while ( i < l - 2 && s[i] != ';')	/* May have ~char after `;' */
2320 	    i++;
2321 	if (i < l - 2) {			/* Found */
2322 	    rest = l - i - 1;
2323 	    l = i + 1;
2324 	}
2325     }
2326     if (e + l >= b + bl) {
2327 	if (flags & dir_subst_fatal)
2328 	    Perl_croak_nocontext("%s: name `%s%s' too long", msg, b, s);
2329 	else
2330 	    return NULL;
2331     }
2332     memcpy(e, s, l);
2333     if (rest) {
2334 	e = dir_subst(s + l, rest, e + l, bl - (e + l - b), flags, msg);
2335 	return e ? b : e;
2336     }
2337     e[l] = 0;
2338     return b;
2339 }
2340 
2341 char *
2342 perllib_mangle_with(char *s, unsigned int l, char *from, unsigned int froml, char *to, unsigned int tol)
2343 {
2344     if (!to)
2345 	return s;
2346     if (l == 0)
2347 	l = strlen(s);
2348     if (l < froml || strnicmp(from, s, froml) != 0)
2349 	return s;
2350     if (l + tol - froml > STATIC_FILE_LENGTH || tol > STATIC_FILE_LENGTH)
2351 	Perl_croak_nocontext("Malformed PERLLIB_PREFIX");
2352     if (to && to != mangle_ret)
2353 	memcpy(mangle_ret, to, tol);
2354     strcpy(mangle_ret + tol, s + froml);
2355     return mangle_ret;
2356 }
2357 
2358 char *
2359 perllib_mangle(char *s, unsigned int l)
2360 {
2361     char *name;
2362 
2363     if (perllib_mangle_installed && (name = perllib_mangle_installed(s,l)))
2364 	return name;
2365     if (!newp && !notfound) {
2366 	newp = getenv(name = "PERLLIB_" STRINGIFY(PERL_REVISION)
2367 		      STRINGIFY(PERL_VERSION) STRINGIFY(PERL_SUBVERSION)
2368 		      "_PREFIX");
2369 	if (!newp)
2370 	    newp = getenv(name = "PERLLIB_" STRINGIFY(PERL_REVISION)
2371 			  STRINGIFY(PERL_VERSION) "_PREFIX");
2372 	if (!newp)
2373 	    newp = getenv(name = "PERLLIB_" STRINGIFY(PERL_REVISION) "_PREFIX");
2374 	if (!newp)
2375 	    newp = getenv(name = "PERLLIB_PREFIX");
2376 	if (newp) {
2377 	    char *s, b[300];
2378 
2379 	    oldp = newp;
2380 	    while (*newp && !isSPACE(*newp) && *newp != ';')
2381 		newp++;			/* Skip old name. */
2382 	    oldl = newp - oldp;
2383 	    s = dir_subst(oldp, oldl, b, sizeof b, dir_subst_fatal, name);
2384 	    oldp = savepv(s);
2385 	    oldl = strlen(s);
2386 	    while (*newp && (isSPACE(*newp) || *newp == ';'))
2387 		newp++;			/* Skip whitespace. */
2388 	    Perl_OS2_handler_install((void *)newp, Perlos2_handler_perllib_to);
2389 	    if (newl == 0 || oldl == 0)
2390 		Perl_croak_nocontext("Malformed %s", name);
2391 	} else
2392 	    notfound = 1;
2393     }
2394     if (!newp)
2395 	return s;
2396     if (l == 0)
2397 	l = strlen(s);
2398     if (l < oldl || strnicmp(oldp, s, oldl) != 0)
2399 	return s;
2400     if (l + newl - oldl > STATIC_FILE_LENGTH || newl > STATIC_FILE_LENGTH)
2401 	Perl_croak_nocontext("Malformed PERLLIB_PREFIX");
2402     strcpy(mangle_ret + newl, s + oldl);
2403     return mangle_ret;
2404 }
2405 
2406 unsigned long
2407 Perl_hab_GET()			/* Needed if perl.h cannot be included */
2408 {
2409     return perl_hab_GET();
2410 }
2411 
2412 static void
2413 Create_HMQ(int serve, char *message)	/* Assumes morphing */
2414 {
2415     unsigned fpflag = _control87(0,0);
2416 
2417     init_PMWIN_entries();
2418     /* 64 messages if before OS/2 3.0, ignored otherwise */
2419     Perl_hmq = (*PMWIN_entries.CreateMsgQueue)(perl_hab_GET(), 64);
2420     if (!Perl_hmq) {
2421 	dTHX;
2422 
2423 	SAVEINT(rmq_cnt);		/* Allow catch()ing. */
2424 	if (rmq_cnt++)
2425 	    _exit(188);		/* Panic can try to create a window. */
2426 	CroakWinError(1, message ? message : "Cannot create a message queue");
2427     }
2428     if (serve != -1)
2429 	(*PMWIN_entries.CancelShutdown)(Perl_hmq, !serve);
2430     /* We may have loaded some modules */
2431     _control87(fpflag, MCW_EM); /* Some modules reset FP flags on (un)load */
2432 }
2433 
2434 #define REGISTERMQ_WILL_SERVE		1
2435 #define REGISTERMQ_IMEDIATE_UNMORPH	2
2436 
2437 HMQ
2438 Perl_Register_MQ(int serve)
2439 {
2440   if (Perl_hmq_refcnt <= 0) {
2441     PPIB pib;
2442     PTIB tib;
2443 
2444     Perl_hmq_refcnt = 0;		/* Be extra safe */
2445     DosGetInfoBlocks(&tib, &pib);
2446     if (!Perl_morph_refcnt) {
2447 	Perl_os2_initial_mode = pib->pib_ultype;
2448 	/* Try morphing into a PM application. */
2449 	if (pib->pib_ultype != 3)		/* 2 is VIO */
2450 	    pib->pib_ultype = 3;		/* 3 is PM */
2451     }
2452     Create_HMQ(-1,			/* We do CancelShutdown ourselves */
2453 	       "Cannot create a message queue, or morph to a PM application");
2454     if ((serve & REGISTERMQ_IMEDIATE_UNMORPH)) {
2455 	if (!Perl_morph_refcnt && Perl_os2_initial_mode != 3)
2456 	    pib->pib_ultype = Perl_os2_initial_mode;
2457     }
2458   }
2459     if (serve & REGISTERMQ_WILL_SERVE) {
2460 	if ( Perl_hmq_servers <= 0	/* Safe to inform us on shutdown, */
2461 	     && Perl_hmq_refcnt > 0 )	/* this was switched off before... */
2462 	    (*PMWIN_entries.CancelShutdown)(Perl_hmq, 0);
2463 	Perl_hmq_servers++;
2464     } else if (!Perl_hmq_servers)	/* Do not inform us on shutdown */
2465 	(*PMWIN_entries.CancelShutdown)(Perl_hmq, 1);
2466     Perl_hmq_refcnt++;
2467     if (!(serve & REGISTERMQ_IMEDIATE_UNMORPH))
2468 	Perl_morph_refcnt++;
2469     return Perl_hmq;
2470 }
2471 
2472 int
2473 Perl_Serve_Messages(int force)
2474 {
2475     int cnt = 0;
2476     QMSG msg;
2477 
2478     if (Perl_hmq_servers > 0 && !force)
2479 	return 0;
2480     if (Perl_hmq_refcnt <= 0)
2481 	Perl_croak_nocontext("No message queue");
2482     while ((*PMWIN_entries.PeekMsg)(Perl_hab, &msg, NULLHANDLE, 0, 0, PM_REMOVE)) {
2483 	cnt++;
2484 	if (msg.msg == WM_QUIT)
2485 	    Perl_croak_nocontext("QUITing...");
2486 	(*PMWIN_entries.DispatchMsg)(Perl_hab, &msg);
2487     }
2488     return cnt;
2489 }
2490 
2491 int
2492 Perl_Process_Messages(int force, I32 *cntp)
2493 {
2494     QMSG msg;
2495 
2496     if (Perl_hmq_servers > 0 && !force)
2497 	return 0;
2498     if (Perl_hmq_refcnt <= 0)
2499 	Perl_croak_nocontext("No message queue");
2500     while ((*PMWIN_entries.GetMsg)(Perl_hab, &msg, NULLHANDLE, 0, 0)) {
2501 	if (cntp)
2502 	    (*cntp)++;
2503 	(*PMWIN_entries.DispatchMsg)(Perl_hab, &msg);
2504 	if (msg.msg == WM_DESTROY)
2505 	    return -1;
2506 	if (msg.msg == WM_CREATE)
2507 	    return +1;
2508     }
2509     Perl_croak_nocontext("QUITing...");
2510 }
2511 
2512 void
2513 Perl_Deregister_MQ(int serve)
2514 {
2515     if (serve & REGISTERMQ_WILL_SERVE)
2516 	Perl_hmq_servers--;
2517 
2518     if (--Perl_hmq_refcnt <= 0) {
2519 	unsigned fpflag = _control87(0,0);
2520 
2521 	init_PMWIN_entries();			/* To be extra safe */
2522 	(*PMWIN_entries.DestroyMsgQueue)(Perl_hmq);
2523 	Perl_hmq = 0;
2524 	/* We may have (un)loaded some modules */
2525 	_control87(fpflag, MCW_EM); /* Some modules reset FP flags on (un)load */
2526     } else if ((serve & REGISTERMQ_WILL_SERVE) && Perl_hmq_servers <= 0)
2527 	(*PMWIN_entries.CancelShutdown)(Perl_hmq, 1); /* Last server exited */
2528     if (!(serve & REGISTERMQ_IMEDIATE_UNMORPH) && (--Perl_morph_refcnt <= 0)) {
2529 	/* Try morphing back from a PM application. */
2530 	PPIB pib;
2531 	PTIB tib;
2532 
2533 	DosGetInfoBlocks(&tib, &pib);
2534 	if (pib->pib_ultype == 3)		/* 3 is PM */
2535 	    pib->pib_ultype = Perl_os2_initial_mode;
2536 	else
2537 	    Perl_warn_nocontext("Unexpected program mode %d when morphing back from PM",
2538 				pib->pib_ultype);
2539     }
2540 }
2541 
2542 #define sys_is_absolute(path) ( isALPHA((path)[0]) && (path)[1] == ':' \
2543 				&& ((path)[2] == '/' || (path)[2] == '\\'))
2544 #define sys_is_rooted _fnisabs
2545 #define sys_is_relative _fnisrel
2546 #define current_drive _getdrive
2547 
2548 #undef chdir				/* Was _chdir2. */
2549 #define sys_chdir(p) (chdir(p) == 0)
2550 #define change_drive(d) (_chdrive(d), (current_drive() == toupper(d)))
2551 
2552 XS(XS_OS2_Error)
2553 {
2554     dXSARGS;
2555     if (items != 2)
2556 	Perl_croak_nocontext("Usage: OS2::Error(harderr, exception)");
2557     {
2558 	int	arg1 = SvIV(ST(0));
2559 	int	arg2 = SvIV(ST(1));
2560 	int	a = ((arg1 ? FERR_ENABLEHARDERR : FERR_DISABLEHARDERR)
2561 		     | (arg2 ? FERR_ENABLEEXCEPTION : FERR_DISABLEEXCEPTION));
2562 	int	RETVAL = ((arg1 ? 1 : 0) | (arg2 ? 2 : 0));
2563 	unsigned long rc;
2564 
2565 	if (CheckOSError(DosError(a)))
2566 	    Perl_croak_nocontext("DosError(%d) failed: %s", a, os2error(Perl_rc));
2567 	ST(0) = sv_newmortal();
2568 	if (DOS_harderr_state >= 0)
2569 	    sv_setiv(ST(0), DOS_harderr_state);
2570 	DOS_harderr_state = RETVAL;
2571     }
2572     XSRETURN(1);
2573 }
2574 
2575 XS(XS_OS2_Errors2Drive)
2576 {
2577     dXSARGS;
2578     if (items != 1)
2579 	Perl_croak_nocontext("Usage: OS2::Errors2Drive(drive)");
2580     {
2581 	STRLEN n_a;
2582 	SV  *sv = ST(0);
2583 	int	suppress = SvOK(sv);
2584 	char	*s = suppress ? SvPV(sv, n_a) : NULL;
2585 	char	drive = (s ? *s : 0);
2586 	unsigned long rc;
2587 
2588 	if (suppress && !isALPHA(drive))
2589 	    Perl_croak_nocontext("Non-char argument '%c' to OS2::Errors2Drive()", drive);
2590 	if (CheckOSError(DosSuppressPopUps((suppress
2591 					    ? SPU_ENABLESUPPRESSION
2592 					    : SPU_DISABLESUPPRESSION),
2593 					   drive)))
2594 	    Perl_croak_nocontext("DosSuppressPopUps(%c) failed: %s", drive,
2595 				 os2error(Perl_rc));
2596 	ST(0) = sv_newmortal();
2597 	if (DOS_suppression_state > 0)
2598 	    sv_setpvn(ST(0), &DOS_suppression_state, 1);
2599 	else if (DOS_suppression_state == 0)
2600 	    sv_setpvn(ST(0), "", 0);
2601 	DOS_suppression_state = drive;
2602     }
2603     XSRETURN(1);
2604 }
2605 
2606 int
2607 async_mssleep(ULONG ms, int switch_priority) {
2608   /* This is similar to DosSleep(), but has 8ms granularity in time-critical
2609      threads even on Warp3. */
2610   HEV     hevEvent1     = 0;			/* Event semaphore handle    */
2611   HTIMER  htimerEvent1  = 0;			/* Timer handle              */
2612   APIRET  rc            = NO_ERROR;		/* Return code               */
2613   int ret = 1;
2614   ULONG priority = 0, nesting;			/* Shut down the warnings */
2615   PPIB pib;
2616   PTIB tib;
2617   char *e = NULL;
2618   APIRET badrc;
2619 
2620   if (!(_emx_env & 0x200))	/* DOS */
2621     return !_sleep2(ms);
2622 
2623   os2cp_croak(DosCreateEventSem(NULL,	     /* Unnamed */
2624 				&hevEvent1,  /* Handle of semaphore returned */
2625 				DC_SEM_SHARED, /* Shared needed for DosAsyncTimer */
2626 				FALSE),      /* Semaphore is in RESET state  */
2627 	      "DosCreateEventSem");
2628 
2629   if (ms >= switch_priority)
2630     switch_priority = 0;
2631   if (switch_priority) {
2632     if (CheckOSError(DosGetInfoBlocks(&tib, &pib)))
2633 	switch_priority = 0;
2634     else {
2635 	/* In Warp3, to switch scheduling to 8ms step, one needs to do
2636 	   DosAsyncTimer() in time-critical thread.  On laters versions,
2637 	   more and more cases of wait-for-something are covered.
2638 
2639 	   It turns out that on Warp3fp42 it is the priority at the time
2640 	   of DosAsyncTimer() which matters.  Let's hope that this works
2641 	   with later versions too...		XXXX
2642 	 */
2643 	priority = (tib->tib_ptib2->tib2_ulpri);
2644 	if ((priority & 0xFF00) == 0x0300) /* already time-critical */
2645 	    switch_priority = 0;
2646 	/* Make us time-critical.  Just modifying TIB is not enough... */
2647 	/* tib->tib_ptib2->tib2_ulpri = 0x0300;*/
2648 	/* We do not want to run at high priority if a signal causes us
2649 	   to longjmp() out of this section... */
2650 	if (DosEnterMustComplete(&nesting))
2651 	    switch_priority = 0;
2652 	else
2653 	    DosSetPriority(PRTYS_THREAD, PRTYC_TIMECRITICAL, 0, 0);
2654     }
2655   }
2656 
2657   if ((badrc = DosAsyncTimer(ms,
2658 			     (HSEM) hevEvent1,	/* Semaphore to post        */
2659 			     &htimerEvent1)))	/* Timer handler (returned) */
2660      e = "DosAsyncTimer";
2661 
2662   if (switch_priority && tib->tib_ptib2->tib2_ulpri == 0x0300) {
2663 	/* Nobody switched priority while we slept...  Ignore errors... */
2664 	/* tib->tib_ptib2->tib2_ulpri = priority; */	/* Get back... */
2665 	if (!(rc = DosSetPriority(PRTYS_THREAD, (priority>>8) & 0xFF, 0, 0)))
2666 	    rc = DosSetPriority(PRTYS_THREAD, 0, priority & 0xFF, 0);
2667   }
2668   if (switch_priority)
2669       rc = DosExitMustComplete(&nesting);	/* Ignore errors */
2670 
2671   /* The actual blocking call is made with "normal" priority.  This way we
2672      should not bother with DosSleep(0) etc. to compensate for us interrupting
2673      higher-priority threads.  The goal is to prohibit the system spending too
2674      much time halt()ing, not to run us "no matter what". */
2675   if (!e)					/* Wait for AsyncTimer event */
2676       badrc = DosWaitEventSem(hevEvent1, SEM_INDEFINITE_WAIT);
2677 
2678   if (e) ;				/* Do nothing */
2679   else if (badrc == ERROR_INTERRUPT)
2680      ret = 0;
2681   else if (badrc)
2682      e = "DosWaitEventSem";
2683   if ((rc = DosCloseEventSem(hevEvent1)) && !e) { /* Get rid of semaphore */
2684      e = "DosCloseEventSem";
2685      badrc = rc;
2686   }
2687   if (e)
2688      os2cp_croak(badrc, e);
2689   return ret;
2690 }
2691 
2692 XS(XS_OS2_ms_sleep)		/* for testing only... */
2693 {
2694     dXSARGS;
2695     ULONG ms, lim;
2696 
2697     if (items > 2 || items < 1)
2698 	Perl_croak_nocontext("Usage: OS2::ms_sleep(wait_ms [, high_priority_limit])");
2699     ms = SvUV(ST(0));
2700     lim = items > 1 ? SvUV(ST(1)) : ms + 1;
2701     async_mssleep(ms, lim);
2702     XSRETURN_EMPTY;
2703 }
2704 
2705 ULONG (*pDosTmrQueryFreq) (PULONG);
2706 ULONG (*pDosTmrQueryTime) (unsigned long long *);
2707 
2708 XS(XS_OS2_Timer)
2709 {
2710     dXSARGS;
2711     static ULONG freq;
2712     unsigned long long count;
2713     ULONG rc;
2714 
2715     if (items != 0)
2716 	Perl_croak_nocontext("Usage: OS2::Timer()");
2717     if (!freq) {
2718 	*(PFN*)&pDosTmrQueryFreq = loadByOrdinal(ORD_DosTmrQueryFreq, 0);
2719 	*(PFN*)&pDosTmrQueryTime = loadByOrdinal(ORD_DosTmrQueryTime, 0);
2720 	MUTEX_LOCK(&perlos2_state_mutex);
2721 	if (!freq)
2722 	    if (CheckOSError(pDosTmrQueryFreq(&freq)))
2723 		croak_with_os2error("DosTmrQueryFreq");
2724 	MUTEX_UNLOCK(&perlos2_state_mutex);
2725     }
2726     if (CheckOSError(pDosTmrQueryTime(&count)))
2727 	croak_with_os2error("DosTmrQueryTime");
2728     {
2729 	dXSTARG;
2730 
2731 	XSprePUSH; PUSHn(((NV)count)/freq);
2732     }
2733     XSRETURN(1);
2734 }
2735 
2736 XS(XS_OS2_msCounter)
2737 {
2738     dXSARGS;
2739 
2740     if (items != 0)
2741 	Perl_croak_nocontext("Usage: OS2::msCounter()");
2742     {
2743 	dXSTARG;
2744 
2745 	XSprePUSH; PUSHu(msCounter());
2746     }
2747     XSRETURN(1);
2748 }
2749 
2750 XS(XS_OS2__InfoTable)
2751 {
2752     dXSARGS;
2753     int is_local = 0;
2754 
2755     if (items > 1)
2756 	Perl_croak_nocontext("Usage: OS2::_infoTable([isLocal])");
2757     if (items == 1)
2758 	is_local = (int)SvIV(ST(0));
2759     {
2760 	dXSTARG;
2761 
2762 	XSprePUSH; PUSHu(InfoTable(is_local));
2763     }
2764     XSRETURN(1);
2765 }
2766 
2767 static const char * const dc_fields[] = {
2768   "FAMILY",
2769   "IO_CAPS",
2770   "TECHNOLOGY",
2771   "DRIVER_VERSION",
2772   "WIDTH",
2773   "HEIGHT",
2774   "WIDTH_IN_CHARS",
2775   "HEIGHT_IN_CHARS",
2776   "HORIZONTAL_RESOLUTION",
2777   "VERTICAL_RESOLUTION",
2778   "CHAR_WIDTH",
2779   "CHAR_HEIGHT",
2780   "SMALL_CHAR_WIDTH",
2781   "SMALL_CHAR_HEIGHT",
2782   "COLORS",
2783   "COLOR_PLANES",
2784   "COLOR_BITCOUNT",
2785   "COLOR_TABLE_SUPPORT",
2786   "MOUSE_BUTTONS",
2787   "FOREGROUND_MIX_SUPPORT",
2788   "BACKGROUND_MIX_SUPPORT",
2789   "VIO_LOADABLE_FONTS",
2790   "WINDOW_BYTE_ALIGNMENT",
2791   "BITMAP_FORMATS",
2792   "RASTER_CAPS",
2793   "MARKER_HEIGHT",
2794   "MARKER_WIDTH",
2795   "DEVICE_FONTS",
2796   "GRAPHICS_SUBSET",
2797   "GRAPHICS_VERSION",
2798   "GRAPHICS_VECTOR_SUBSET",
2799   "DEVICE_WINDOWING",
2800   "ADDITIONAL_GRAPHICS",
2801   "PHYS_COLORS",
2802   "COLOR_INDEX",
2803   "GRAPHICS_CHAR_WIDTH",
2804   "GRAPHICS_CHAR_HEIGHT",
2805   "HORIZONTAL_FONT_RES",
2806   "VERTICAL_FONT_RES",
2807   "DEVICE_FONT_SIM",
2808   "LINEWIDTH_THICK",
2809   "DEVICE_POLYSET_POINTS",
2810 };
2811 
2812 enum {
2813     DevCap_dc, DevCap_hwnd
2814 };
2815 
2816 HDC (*pWinOpenWindowDC) (HWND hwnd);
2817 HMF (*pDevCloseDC) (HDC hdc);
2818 HDC (*pDevOpenDC) (HAB hab, LONG lType, PCSZ pszToken, LONG lCount,
2819     PDEVOPENDATA pdopData, HDC hdcComp);
2820 BOOL (*pDevQueryCaps) (HDC hdc, LONG lStart, LONG lCount, PLONG alArray);
2821 
2822 
2823 XS(XS_OS2_DevCap)
2824 {
2825     dXSARGS;
2826     if (items > 2)
2827 	Perl_croak_nocontext("Usage: OS2::DevCap()");
2828     {
2829 	/* Device Capabilities Data Buffer (10 extra w.r.t. Warp 4.5) */
2830 	LONG   si[CAPS_DEVICE_POLYSET_POINTS - CAPS_FAMILY + 1];
2831 	int i = 0, j = 0, how = DevCap_dc;
2832 	HDC hScreenDC;
2833 	DEVOPENSTRUC doStruc= {0L, (PSZ)"DISPLAY", NULL, 0L, 0L, 0L, 0L, 0L, 0L};
2834 	ULONG rc1 = NO_ERROR;
2835 	HWND hwnd;
2836 	static volatile int devcap_loaded;
2837 
2838 	if (!devcap_loaded) {
2839 	    *(PFN*)&pWinOpenWindowDC = loadByOrdinal(ORD_WinOpenWindowDC, 0);
2840 	    *(PFN*)&pDevOpenDC = loadByOrdinal(ORD_DevOpenDC, 0);
2841 	    *(PFN*)&pDevCloseDC = loadByOrdinal(ORD_DevCloseDC, 0);
2842 	    *(PFN*)&pDevQueryCaps = loadByOrdinal(ORD_DevQueryCaps, 0);
2843 	    devcap_loaded = 1;
2844 	}
2845 
2846 	if (items >= 2)
2847 	    how = SvIV(ST(1));
2848 	if (!items) {			/* Get device contents from PM */
2849 	    hScreenDC = pDevOpenDC(perl_hab_GET(), OD_MEMORY, (PSZ)"*", 0,
2850 				  (PDEVOPENDATA)&doStruc, NULLHANDLE);
2851 	    if (CheckWinError(hScreenDC))
2852 		croak_with_os2error("DevOpenDC() failed");
2853 	} else if (how == DevCap_dc)
2854 	    hScreenDC = (HDC)SvIV(ST(0));
2855 	else {				/* DevCap_hwnd */
2856 	    if (!Perl_hmq)
2857 		Perl_croak(aTHX_ "Getting a window's device context without a message queue would lock PM");
2858 	    hwnd = (HWND)SvIV(ST(0));
2859 	    hScreenDC = pWinOpenWindowDC(hwnd); /* No need to DevCloseDC() */
2860 	    if (CheckWinError(hScreenDC))
2861 		croak_with_os2error("WinOpenWindowDC() failed");
2862 	}
2863 	if (CheckWinError(pDevQueryCaps(hScreenDC,
2864 					CAPS_FAMILY, /* W3 documented caps */
2865 					CAPS_DEVICE_POLYSET_POINTS
2866 					  - CAPS_FAMILY + 1,
2867 					si)))
2868 	    rc1 = Perl_rc;
2869 	if (!items && CheckWinError(pDevCloseDC(hScreenDC)))
2870 	    Perl_warn_nocontext("DevCloseDC() failed: %s", os2error(Perl_rc));
2871 	if (rc1)
2872 	    Perl_rc = rc1, croak_with_os2error("DevQueryCaps() failed");
2873 	EXTEND(SP,2*(CAPS_DEVICE_POLYSET_POINTS - CAPS_FAMILY + 1));
2874 	while (i < CAPS_DEVICE_POLYSET_POINTS - CAPS_FAMILY + 1) {
2875 	    ST(j) = sv_newmortal();
2876 	    sv_setpv(ST(j++), dc_fields[i]);
2877 	    ST(j) = sv_newmortal();
2878 	    sv_setiv(ST(j++), si[i]);
2879 	    i++;
2880 	}
2881     }
2882     XSRETURN(2 * (CAPS_DEVICE_POLYSET_POINTS - CAPS_FAMILY + 1));
2883 }
2884 
2885 LONG (*pWinQuerySysValue) (HWND hwndDesktop, LONG iSysValue);
2886 BOOL (*pWinSetSysValue) (HWND hwndDesktop, LONG iSysValue, LONG lValue);
2887 
2888 const char * const sv_keys[] = {
2889   "SWAPBUTTON",
2890   "DBLCLKTIME",
2891   "CXDBLCLK",
2892   "CYDBLCLK",
2893   "CXSIZEBORDER",
2894   "CYSIZEBORDER",
2895   "ALARM",
2896   "7",
2897   "8",
2898   "CURSORRATE",
2899   "FIRSTSCROLLRATE",
2900   "SCROLLRATE",
2901   "NUMBEREDLISTS",
2902   "WARNINGFREQ",
2903   "NOTEFREQ",
2904   "ERRORFREQ",
2905   "WARNINGDURATION",
2906   "NOTEDURATION",
2907   "ERRORDURATION",
2908   "19",
2909   "CXSCREEN",
2910   "CYSCREEN",
2911   "CXVSCROLL",
2912   "CYHSCROLL",
2913   "CYVSCROLLARROW",
2914   "CXHSCROLLARROW",
2915   "CXBORDER",
2916   "CYBORDER",
2917   "CXDLGFRAME",
2918   "CYDLGFRAME",
2919   "CYTITLEBAR",
2920   "CYVSLIDER",
2921   "CXHSLIDER",
2922   "CXMINMAXBUTTON",
2923   "CYMINMAXBUTTON",
2924   "CYMENU",
2925   "CXFULLSCREEN",
2926   "CYFULLSCREEN",
2927   "CXICON",
2928   "CYICON",
2929   "CXPOINTER",
2930   "CYPOINTER",
2931   "DEBUG",
2932   "CPOINTERBUTTONS",
2933   "POINTERLEVEL",
2934   "CURSORLEVEL",
2935   "TRACKRECTLEVEL",
2936   "CTIMERS",
2937   "MOUSEPRESENT",
2938   "CXALIGN",
2939   "CYALIGN",
2940   "DESKTOPWORKAREAYTOP",
2941   "DESKTOPWORKAREAYBOTTOM",
2942   "DESKTOPWORKAREAXRIGHT",
2943   "DESKTOPWORKAREAXLEFT",
2944   "55",
2945   "NOTRESERVED",
2946   "EXTRAKEYBEEP",
2947   "SETLIGHTS",
2948   "INSERTMODE",
2949   "60",
2950   "61",
2951   "62",
2952   "63",
2953   "MENUROLLDOWNDELAY",
2954   "MENUROLLUPDELAY",
2955   "ALTMNEMONIC",
2956   "TASKLISTMOUSEACCESS",
2957   "CXICONTEXTWIDTH",
2958   "CICONTEXTLINES",
2959   "CHORDTIME",
2960   "CXCHORD",
2961   "CYCHORD",
2962   "CXMOTIONSTART",
2963   "CYMOTIONSTART",
2964   "BEGINDRAG",
2965   "ENDDRAG",
2966   "SINGLESELECT",
2967   "OPEN",
2968   "CONTEXTMENU",
2969   "CONTEXTHELP",
2970   "TEXTEDIT",
2971   "BEGINSELECT",
2972   "ENDSELECT",
2973   "BEGINDRAGKB",
2974   "ENDDRAGKB",
2975   "SELECTKB",
2976   "OPENKB",
2977   "CONTEXTMENUKB",
2978   "CONTEXTHELPKB",
2979   "TEXTEDITKB",
2980   "BEGINSELECTKB",
2981   "ENDSELECTKB",
2982   "ANIMATION",
2983   "ANIMATIONSPEED",
2984   "MONOICONS",
2985   "KBDALTERED",
2986   "PRINTSCREEN",		/* 97, the last one on one of the DDK header */
2987   "LOCKSTARTINPUT",
2988   "DYNAMICDRAG",
2989   "100",
2990   "101",
2991   "102",
2992   "103",
2993   "104",
2994   "105",
2995   "106",
2996   "107",
2997 /*  "CSYSVALUES",*/
2998 					/* In recent DDK the limit is 108 */
2999 };
3000 
3001 XS(XS_OS2_SysValues)
3002 {
3003     dXSARGS;
3004     if (items > 2)
3005 	Perl_croak_nocontext("Usage: OS2::SysValues(which = -1, hwndDesktop = HWND_DESKTOP)");
3006     {
3007 	int i = 0, j = 0, which = -1;
3008 	HWND hwnd = HWND_DESKTOP;
3009 	static volatile int sv_loaded;
3010 	LONG RETVAL;
3011 
3012 	if (!sv_loaded) {
3013 	    *(PFN*)&pWinQuerySysValue = loadByOrdinal(ORD_WinQuerySysValue, 0);
3014 	    sv_loaded = 1;
3015 	}
3016 
3017 	if (items == 2)
3018 	    hwnd = (HWND)SvIV(ST(1));
3019 	if (items >= 1)
3020 	    which = (int)SvIV(ST(0));
3021 	if (which == -1) {
3022 	    EXTEND(SP,2*C_ARRAY_LENGTH(sv_keys));
3023 	    while (i < C_ARRAY_LENGTH(sv_keys)) {
3024 		ResetWinError();
3025 		RETVAL = pWinQuerySysValue(hwnd, i);
3026 		if ( !RETVAL
3027 		     && !(sv_keys[i][0] >= '0' && sv_keys[i][0] <= '9'
3028 			  && i <= SV_PRINTSCREEN) ) {
3029 		    FillWinError;
3030 		    if (Perl_rc) {
3031 			if (i > SV_PRINTSCREEN)
3032 			    break; /* May be not present on older systems */
3033 			croak_with_os2error("SysValues():");
3034 		    }
3035 
3036 		}
3037 		ST(j) = sv_newmortal();
3038 		sv_setpv(ST(j++), sv_keys[i]);
3039 		ST(j) = sv_newmortal();
3040 		sv_setiv(ST(j++), RETVAL);
3041 		i++;
3042 	    }
3043 	    XSRETURN(2 * i);
3044 	} else {
3045 	    dXSTARG;
3046 
3047 	    ResetWinError();
3048 	    RETVAL = pWinQuerySysValue(hwnd, which);
3049 	    if (!RETVAL) {
3050 		FillWinError;
3051 		if (Perl_rc)
3052 		    croak_with_os2error("SysValues():");
3053 	    }
3054 	    XSprePUSH; PUSHi((IV)RETVAL);
3055 	}
3056     }
3057 }
3058 
3059 XS(XS_OS2_SysValues_set)
3060 {
3061     dXSARGS;
3062     if (items < 2 || items > 3)
3063 	Perl_croak_nocontext("Usage: OS2::SysValues_set(which, val, hwndDesktop = HWND_DESKTOP)");
3064     {
3065 	int which = (int)SvIV(ST(0));
3066 	LONG val = (LONG)SvIV(ST(1));
3067 	HWND hwnd = HWND_DESKTOP;
3068 	static volatile int svs_loaded;
3069 
3070 	if (!svs_loaded) {
3071 	    *(PFN*)&pWinSetSysValue = loadByOrdinal(ORD_WinSetSysValue, 0);
3072 	    svs_loaded = 1;
3073 	}
3074 
3075 	if (items == 3)
3076 	    hwnd = (HWND)SvIV(ST(2));
3077 	if (CheckWinError(pWinSetSysValue(hwnd, which, val)))
3078 	    croak_with_os2error("SysValues_set()");
3079     }
3080     XSRETURN_EMPTY;
3081 }
3082 
3083 #define QSV_MAX_WARP3				QSV_MAX_COMP_LENGTH
3084 
3085 static const char * const si_fields[] = {
3086   "MAX_PATH_LENGTH",
3087   "MAX_TEXT_SESSIONS",
3088   "MAX_PM_SESSIONS",
3089   "MAX_VDM_SESSIONS",
3090   "BOOT_DRIVE",
3091   "DYN_PRI_VARIATION",
3092   "MAX_WAIT",
3093   "MIN_SLICE",
3094   "MAX_SLICE",
3095   "PAGE_SIZE",
3096   "VERSION_MAJOR",
3097   "VERSION_MINOR",
3098   "VERSION_REVISION",
3099   "MS_COUNT",
3100   "TIME_LOW",
3101   "TIME_HIGH",
3102   "TOTPHYSMEM",
3103   "TOTRESMEM",
3104   "TOTAVAILMEM",
3105   "MAXPRMEM",
3106   "MAXSHMEM",
3107   "TIMER_INTERVAL",
3108   "MAX_COMP_LENGTH",
3109   "FOREGROUND_FS_SESSION",
3110   "FOREGROUND_PROCESS",			/* Warp 3 toolkit defines up to this */
3111   "NUMPROCESSORS",
3112   "MAXHPRMEM",
3113   "MAXHSHMEM",
3114   "MAXPROCESSES",
3115   "VIRTUALADDRESSLIMIT",
3116   "INT10ENABLED",			/* From $TOOLKIT-ddk\DDK\video\rel\os2c\include\base\os2\bsedos.h */
3117 };
3118 
3119 XS(XS_OS2_SysInfo)
3120 {
3121     dXSARGS;
3122     if (items != 0)
3123 	Perl_croak_nocontext("Usage: OS2::SysInfo()");
3124     {
3125 	/* System Information Data Buffer (10 extra w.r.t. Warp 4.5) */
3126 	ULONG   si[C_ARRAY_LENGTH(si_fields) + 10];
3127 	APIRET  rc	= NO_ERROR;	/* Return code            */
3128 	int i = 0, j = 0, last = QSV_MAX_WARP3;
3129 
3130 	if (CheckOSError(DosQuerySysInfo(1L, /* Request documented system */
3131 					 last, /* info for Warp 3 */
3132 					 (PVOID)si,
3133 					 sizeof(si))))
3134 	    croak_with_os2error("DosQuerySysInfo() failed");
3135 	while (last++ <= C_ARRAY_LENGTH(si)) {
3136 	    if (CheckOSError(DosQuerySysInfo(last, last, /* One entry only */
3137 					     (PVOID)(si+last-1),
3138 					     sizeof(*si)))) {
3139 		if (Perl_rc != ERROR_INVALID_PARAMETER)
3140 		    croak_with_os2error("DosQuerySysInfo() failed");
3141 		break;
3142 	    }
3143 	}
3144 	last--;
3145 	EXTEND(SP,2*last);
3146 	while (i < last) {
3147 	    ST(j) = sv_newmortal();
3148 	    sv_setpv(ST(j++), si_fields[i]);
3149 	    ST(j) = sv_newmortal();
3150 	    sv_setiv(ST(j++), si[i]);
3151 	    i++;
3152 	}
3153 	XSRETURN(2 * last);
3154     }
3155 }
3156 
3157 XS(XS_OS2_SysInfoFor)
3158 {
3159     dXSARGS;
3160     int count = (items == 2 ? (int)SvIV(ST(1)) : 1);
3161 
3162     if (items < 1 || items > 2)
3163 	Perl_croak_nocontext("Usage: OS2::SysInfoFor(id[,count])");
3164     {
3165 	/* System Information Data Buffer (10 extra w.r.t. Warp 4.5) */
3166 	ULONG   si[C_ARRAY_LENGTH(si_fields) + 10];
3167 	APIRET  rc	= NO_ERROR;	/* Return code            */
3168 	int i = 0;
3169 	int start = (int)SvIV(ST(0));
3170 
3171 	if (count > C_ARRAY_LENGTH(si) || count <= 0)
3172 	    Perl_croak(aTHX_ "unexpected count %d for OS2::SysInfoFor()", count);
3173 	if (CheckOSError(DosQuerySysInfo(start,
3174 					 start + count - 1,
3175 					 (PVOID)si,
3176 					 sizeof(si))))
3177 	    croak_with_os2error("DosQuerySysInfo() failed");
3178 	EXTEND(SP,count);
3179 	while (i < count) {
3180 	    ST(i) = sv_newmortal();
3181 	    sv_setiv(ST(i), si[i]);
3182 	    i++;
3183 	}
3184     }
3185     XSRETURN(count);
3186 }
3187 
3188 XS(XS_OS2_BootDrive)
3189 {
3190     dXSARGS;
3191     if (items != 0)
3192 	Perl_croak_nocontext("Usage: OS2::BootDrive()");
3193     {
3194 	ULONG   si[1] = {0};	/* System Information Data Buffer */
3195 	APIRET  rc    = NO_ERROR;	/* Return code            */
3196 	char c;
3197 	dXSTARG;
3198 
3199 	if (CheckOSError(DosQuerySysInfo(QSV_BOOT_DRIVE, QSV_BOOT_DRIVE,
3200 					 (PVOID)si, sizeof(si))))
3201 	    croak_with_os2error("DosQuerySysInfo() failed");
3202 	c = 'a' - 1 + si[0];
3203 	sv_setpvn(TARG, &c, 1);
3204 	XSprePUSH; PUSHTARG;
3205     }
3206     XSRETURN(1);
3207 }
3208 
3209 XS(XS_OS2_Beep)
3210 {
3211     dXSARGS;
3212     if (items > 2)			/* Defaults as for WinAlarm(ERROR) */
3213 	Perl_croak_nocontext("Usage: OS2::Beep(freq = 440, ms = 100)");
3214     {
3215 	ULONG freq	= (items > 0 ? (ULONG)SvUV(ST(0)) : 440);
3216 	ULONG ms	= (items > 1 ? (ULONG)SvUV(ST(1)) : 100);
3217 	ULONG rc;
3218 
3219 	if (CheckOSError(DosBeep(freq, ms)))
3220 	    croak_with_os2error("SysValues_set()");
3221     }
3222     XSRETURN_EMPTY;
3223 }
3224 
3225 
3226 
3227 XS(XS_OS2_MorphPM)
3228 {
3229     dXSARGS;
3230     if (items != 1)
3231 	Perl_croak_nocontext("Usage: OS2::MorphPM(serve)");
3232     {
3233 	bool  serve = SvOK(ST(0));
3234 	unsigned long   pmq = perl_hmq_GET(serve);
3235 	dXSTARG;
3236 
3237 	XSprePUSH; PUSHi((IV)pmq);
3238     }
3239     XSRETURN(1);
3240 }
3241 
3242 XS(XS_OS2_UnMorphPM)
3243 {
3244     dXSARGS;
3245     if (items != 1)
3246 	Perl_croak_nocontext("Usage: OS2::UnMorphPM(serve)");
3247     {
3248 	bool  serve = SvOK(ST(0));
3249 
3250 	perl_hmq_UNSET(serve);
3251     }
3252     XSRETURN(0);
3253 }
3254 
3255 XS(XS_OS2_Serve_Messages)
3256 {
3257     dXSARGS;
3258     if (items != 1)
3259 	Perl_croak_nocontext("Usage: OS2::Serve_Messages(force)");
3260     {
3261 	bool  force = SvOK(ST(0));
3262 	unsigned long   cnt = Perl_Serve_Messages(force);
3263 	dXSTARG;
3264 
3265 	XSprePUSH; PUSHi((IV)cnt);
3266     }
3267     XSRETURN(1);
3268 }
3269 
3270 XS(XS_OS2_Process_Messages)
3271 {
3272     dXSARGS;
3273     if (items < 1 || items > 2)
3274 	Perl_croak_nocontext("Usage: OS2::Process_Messages(force [, cnt])");
3275     {
3276 	bool  force = SvOK(ST(0));
3277 	unsigned long   cnt;
3278 	dXSTARG;
3279 
3280 	if (items == 2) {
3281 	    I32 cntr;
3282 	    SV *sv = ST(1);
3283 
3284 	    (void)SvIV(sv);		/* Force SvIVX */
3285 	    if (!SvIOK(sv))
3286 		Perl_croak_nocontext("Can't upgrade count to IV");
3287 	    cntr = SvIVX(sv);
3288 	    cnt =  Perl_Process_Messages(force, &cntr);
3289 	    SvIVX(sv) = cntr;
3290 	} else {
3291 	    cnt =  Perl_Process_Messages(force, NULL);
3292         }
3293 	XSprePUSH; PUSHi((IV)cnt);
3294     }
3295     XSRETURN(1);
3296 }
3297 
3298 XS(XS_Cwd_current_drive)
3299 {
3300     dXSARGS;
3301     if (items != 0)
3302 	Perl_croak_nocontext("Usage: Cwd::current_drive()");
3303     {
3304 	char	RETVAL;
3305 	dXSTARG;
3306 
3307 	RETVAL = current_drive();
3308 	sv_setpvn(TARG, (char *)&RETVAL, 1);
3309 	XSprePUSH; PUSHTARG;
3310     }
3311     XSRETURN(1);
3312 }
3313 
3314 XS(XS_Cwd_sys_chdir)
3315 {
3316     dXSARGS;
3317     if (items != 1)
3318 	Perl_croak_nocontext("Usage: Cwd::sys_chdir(path)");
3319     {
3320 	STRLEN n_a;
3321 	char *	path = (char *)SvPV(ST(0),n_a);
3322 	bool	RETVAL;
3323 
3324 	RETVAL = sys_chdir(path);
3325 	ST(0) = boolSV(RETVAL);
3326 	if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
3327     }
3328     XSRETURN(1);
3329 }
3330 
3331 XS(XS_Cwd_change_drive)
3332 {
3333     dXSARGS;
3334     if (items != 1)
3335 	Perl_croak_nocontext("Usage: Cwd::change_drive(d)");
3336     {
3337 	STRLEN n_a;
3338 	char	d = (char)*SvPV(ST(0),n_a);
3339 	bool	RETVAL;
3340 
3341 	RETVAL = change_drive(d);
3342 	ST(0) = boolSV(RETVAL);
3343 	if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
3344     }
3345     XSRETURN(1);
3346 }
3347 
3348 XS(XS_Cwd_sys_is_absolute)
3349 {
3350     dXSARGS;
3351     if (items != 1)
3352 	Perl_croak_nocontext("Usage: Cwd::sys_is_absolute(path)");
3353     {
3354 	STRLEN n_a;
3355 	char *	path = (char *)SvPV(ST(0),n_a);
3356 	bool	RETVAL;
3357 
3358 	RETVAL = sys_is_absolute(path);
3359 	ST(0) = boolSV(RETVAL);
3360 	if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
3361     }
3362     XSRETURN(1);
3363 }
3364 
3365 XS(XS_Cwd_sys_is_rooted)
3366 {
3367     dXSARGS;
3368     if (items != 1)
3369 	Perl_croak_nocontext("Usage: Cwd::sys_is_rooted(path)");
3370     {
3371 	STRLEN n_a;
3372 	char *	path = (char *)SvPV(ST(0),n_a);
3373 	bool	RETVAL;
3374 
3375 	RETVAL = sys_is_rooted(path);
3376 	ST(0) = boolSV(RETVAL);
3377 	if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
3378     }
3379     XSRETURN(1);
3380 }
3381 
3382 XS(XS_Cwd_sys_is_relative)
3383 {
3384     dXSARGS;
3385     if (items != 1)
3386 	Perl_croak_nocontext("Usage: Cwd::sys_is_relative(path)");
3387     {
3388 	STRLEN n_a;
3389 	char *	path = (char *)SvPV(ST(0),n_a);
3390 	bool	RETVAL;
3391 
3392 	RETVAL = sys_is_relative(path);
3393 	ST(0) = boolSV(RETVAL);
3394 	if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
3395     }
3396     XSRETURN(1);
3397 }
3398 
3399 XS(XS_Cwd_sys_cwd)
3400 {
3401     dXSARGS;
3402     if (items != 0)
3403 	Perl_croak_nocontext("Usage: Cwd::sys_cwd()");
3404     {
3405 	char p[MAXPATHLEN];
3406 	char *	RETVAL;
3407 
3408 	/* Can't use TARG, since tainting behaves differently */
3409 	RETVAL = _getcwd2(p, MAXPATHLEN);
3410 	ST(0) = sv_newmortal();
3411 	sv_setpv(ST(0), RETVAL);
3412 #ifndef INCOMPLETE_TAINTS
3413 	SvTAINTED_on(ST(0));
3414 #endif
3415     }
3416     XSRETURN(1);
3417 }
3418 
3419 XS(XS_Cwd_sys_abspath)
3420 {
3421     dXSARGS;
3422     if (items > 2)
3423 	Perl_croak_nocontext("Usage: Cwd::sys_abspath(path = '.', dir = NULL)");
3424     {
3425 	STRLEN n_a;
3426 	char *	path = items ? (char *)SvPV(ST(0),n_a) : ".";
3427 	char *	dir, *s, *t, *e;
3428 	char p[MAXPATHLEN];
3429 	char *	RETVAL;
3430 	int l;
3431 	SV *sv;
3432 
3433 	if (items < 2)
3434 	    dir = NULL;
3435 	else {
3436 	    dir = (char *)SvPV(ST(1),n_a);
3437 	}
3438 	if (path[0] == '.' && (path[1] == '/' || path[1] == '\\')) {
3439 	    path += 2;
3440 	}
3441 	if (dir == NULL) {
3442 	    if (_abspath(p, path, MAXPATHLEN) == 0) {
3443 		RETVAL = p;
3444 	    } else {
3445 		RETVAL = NULL;
3446 	    }
3447 	} else {
3448 	    /* Absolute with drive: */
3449 	    if ( sys_is_absolute(path) ) {
3450 		if (_abspath(p, path, MAXPATHLEN) == 0) {
3451 		    RETVAL = p;
3452 		} else {
3453 		    RETVAL = NULL;
3454 		}
3455 	    } else if (path[0] == '/' || path[0] == '\\') {
3456 		/* Rooted, but maybe on different drive. */
3457 		if (isALPHA(dir[0]) && dir[1] == ':' ) {
3458 		    char p1[MAXPATHLEN];
3459 
3460 		    /* Need to prepend the drive. */
3461 		    p1[0] = dir[0];
3462 		    p1[1] = dir[1];
3463 		    Copy(path, p1 + 2, strlen(path) + 1, char);
3464 		    RETVAL = p;
3465 		    if (_abspath(p, p1, MAXPATHLEN) == 0) {
3466 			RETVAL = p;
3467 		    } else {
3468 			RETVAL = NULL;
3469 		    }
3470 		} else if (_abspath(p, path, MAXPATHLEN) == 0) {
3471 		    RETVAL = p;
3472 		} else {
3473 		    RETVAL = NULL;
3474 		}
3475 	    } else {
3476 		/* Either path is relative, or starts with a drive letter. */
3477 		/* If the path starts with a drive letter, then dir is
3478 		   relevant only if
3479 		   a/b)	it is absolute/x:relative on the same drive.
3480 		   c)	path is on current drive, and dir is rooted
3481 		   In all the cases it is safe to drop the drive part
3482 		   of the path. */
3483 		if ( !sys_is_relative(path) ) {
3484 		    if ( ( ( sys_is_absolute(dir)
3485 			     || (isALPHA(dir[0]) && dir[1] == ':'
3486 				 && strnicmp(dir, path,1) == 0))
3487 			   && strnicmp(dir, path,1) == 0)
3488 			 || ( !(isALPHA(dir[0]) && dir[1] == ':')
3489 			      && toupper(path[0]) == current_drive())) {
3490 			path += 2;
3491 		    } else if (_abspath(p, path, MAXPATHLEN) == 0) {
3492 			RETVAL = p; goto done;
3493 		    } else {
3494 			RETVAL = NULL; goto done;
3495 		    }
3496 		}
3497 		{
3498 		    /* Need to prepend the absolute path of dir. */
3499 		    char p1[MAXPATHLEN];
3500 
3501 		    if (_abspath(p1, dir, MAXPATHLEN) == 0) {
3502 			int l = strlen(p1);
3503 
3504 			if (p1[ l - 1 ] != '/') {
3505 			    p1[ l ] = '/';
3506 			    l++;
3507 			}
3508 			Copy(path, p1 + l, strlen(path) + 1, char);
3509 			if (_abspath(p, p1, MAXPATHLEN) == 0) {
3510 			    RETVAL = p;
3511 			} else {
3512 			    RETVAL = NULL;
3513 			}
3514 		    } else {
3515 			RETVAL = NULL;
3516 		    }
3517 		}
3518 	      done:
3519 	    }
3520 	}
3521 	if (!RETVAL)
3522 	    XSRETURN_EMPTY;
3523 	/* Backslashes are already converted to slashes. */
3524 	/* Remove trailing slashes */
3525 	l = strlen(RETVAL);
3526 	while (l > 0 && RETVAL[l-1] == '/')
3527 	    l--;
3528 	ST(0) = sv_newmortal();
3529 	sv_setpvn( sv = (SV*)ST(0), RETVAL, l);
3530 	/* Remove duplicate slashes, skipping the first three, which
3531 	   may be parts of a server-based path */
3532 	s = t = 3 + SvPV_force(sv, n_a);
3533 	e = SvEND(sv);
3534 	/* Do not worry about multibyte chars here, this would contradict the
3535 	   eventual UTFization, and currently most other places break too... */
3536 	while (s < e) {
3537 	    if (s[0] == t[-1] && s[0] == '/')
3538 		s++;				/* Skip duplicate / */
3539 	    else
3540 		*t++ = *s++;
3541 	}
3542 	if (t < e) {
3543 	    *t = 0;
3544 	    SvCUR_set(sv, t - SvPVX(sv));
3545 	}
3546 #ifndef INCOMPLETE_TAINTS
3547 	if (!items)
3548 	    SvTAINTED_on(ST(0));
3549 #endif
3550     }
3551     XSRETURN(1);
3552 }
3553 typedef APIRET (*PELP)(PSZ path, ULONG type);
3554 
3555 /* Kernels after 2000/09/15 understand this too: */
3556 #ifndef LIBPATHSTRICT
3557 #  define LIBPATHSTRICT 3
3558 #endif
3559 
3560 APIRET
3561 ExtLIBPATH(ULONG ord, PSZ path, IV type, int fatal)
3562 {
3563     ULONG what;
3564     PFN f = loadByOrdinal(ord, fatal);	/* if fatal: load or die! */
3565 
3566     if (!f)				/* Impossible with fatal */
3567 	return Perl_rc;
3568     if (type > 0)
3569 	what = END_LIBPATH;
3570     else if (type == 0)
3571 	what = BEGIN_LIBPATH;
3572     else
3573 	what = LIBPATHSTRICT;
3574     return (*(PELP)f)(path, what);
3575 }
3576 
3577 #define extLibpath(to,type, fatal) 					\
3578     (CheckOSError(ExtLIBPATH(ORD_DosQueryExtLibpath, (to), (type), fatal)) ? NULL : (to) )
3579 
3580 #define extLibpath_set(p,type, fatal) 					\
3581     (!CheckOSError(ExtLIBPATH(ORD_DosSetExtLibpath, (p), (type), fatal)))
3582 
3583 static void
3584 early_error(char *msg1, char *msg2, char *msg3)
3585 {	/* Buffer overflow detected; there is very little we can do... */
3586     ULONG rc;
3587 
3588     DosWrite(2, msg1, strlen(msg1), &rc);
3589     DosWrite(2, msg2, strlen(msg2), &rc);
3590     DosWrite(2, msg3, strlen(msg3), &rc);
3591     DosExit(EXIT_PROCESS, 2);
3592 }
3593 
3594 XS(XS_Cwd_extLibpath)
3595 {
3596     dXSARGS;
3597     if (items < 0 || items > 1)
3598 	Perl_croak_nocontext("Usage: OS2::extLibpath(type = 0)");
3599     {
3600 	IV	type;
3601 	char	to[1024];
3602 	U32	rc;
3603 	char *	RETVAL;
3604 	dXSTARG;
3605 	STRLEN l;
3606 
3607 	if (items < 1)
3608 	    type = 0;
3609 	else {
3610 	    type = SvIV(ST(0));
3611 	}
3612 
3613 	to[0] = 1; to[1] = 0;		/* Sometimes no error reported */
3614 	RETVAL = extLibpath(to, type, 1);	/* Make errors fatal */
3615 	if (RETVAL && RETVAL[0] == 1 && RETVAL[1] == 0)
3616 	    Perl_croak_nocontext("panic OS2::extLibpath parameter");
3617 	l = strlen(to);
3618 	if (l >= sizeof(to))
3619 	    early_error("Buffer overflow while getting BEGIN/ENDLIBPATH: `",
3620 			to, "'\r\n");		/* Will not return */
3621 	sv_setpv(TARG, RETVAL);
3622 	XSprePUSH; PUSHTARG;
3623     }
3624     XSRETURN(1);
3625 }
3626 
3627 XS(XS_Cwd_extLibpath_set)
3628 {
3629     dXSARGS;
3630     if (items < 1 || items > 2)
3631 	Perl_croak_nocontext("Usage: OS2::extLibpath_set(s, type = 0)");
3632     {
3633 	STRLEN n_a;
3634 	char *	s = (char *)SvPV(ST(0),n_a);
3635 	IV	type;
3636 	U32	rc;
3637 	bool	RETVAL;
3638 
3639 	if (items < 2)
3640 	    type = 0;
3641 	else {
3642 	    type = SvIV(ST(1));
3643 	}
3644 
3645 	RETVAL = extLibpath_set(s, type, 1);	/* Make errors fatal */
3646 	ST(0) = boolSV(RETVAL);
3647 	if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
3648     }
3649     XSRETURN(1);
3650 }
3651 
3652 ULONG
3653 fill_extLibpath(int type, char *pre, char *post, int replace, char *msg)
3654 {
3655     char buf[2048], *to = buf, buf1[300], *s;
3656     STRLEN l;
3657     ULONG rc;
3658 
3659     if (!pre && !post)
3660 	return 0;
3661     if (pre) {
3662 	pre = dir_subst(pre, strlen(pre), buf1, sizeof buf1, dir_subst_pathlike, msg);
3663 	if (!pre)
3664 	    return ERROR_INVALID_PARAMETER;
3665 	l = strlen(pre);
3666 	if (l >= sizeof(buf)/2)
3667 	    return ERROR_BUFFER_OVERFLOW;
3668 	s = pre - 1;
3669 	while (*++s)
3670 	    if (*s == '/')
3671 		*s = '\\';			/* Be extra causious */
3672 	memcpy(to, pre, l);
3673 	if (!l || to[l-1] != ';')
3674 	    to[l++] = ';';
3675 	to += l;
3676     }
3677 
3678     if (!replace) {
3679       to[0] = 1; to[1] = 0;		/* Sometimes no error reported */
3680       rc = ExtLIBPATH(ORD_DosQueryExtLibpath, to, type, 0);	/* Do not croak */
3681       if (rc)
3682 	return rc;
3683       if (to[0] == 1 && to[1] == 0)
3684 	return ERROR_INVALID_PARAMETER;
3685       to += strlen(to);
3686       if (buf + sizeof(buf) - 1 <= to)	/* Buffer overflow */
3687 	early_error("Buffer overflow while getting BEGIN/ENDLIBPATH: `",
3688 		    buf, "'\r\n");		/* Will not return */
3689       if (to > buf && to[-1] != ';')
3690 	*to++ = ';';
3691     }
3692     if (post) {
3693 	post = dir_subst(post, strlen(post), buf1, sizeof buf1, dir_subst_pathlike, msg);
3694 	if (!post)
3695 	    return ERROR_INVALID_PARAMETER;
3696 	l = strlen(post);
3697 	if (l + to - buf >= sizeof(buf) - 1)
3698 	    return ERROR_BUFFER_OVERFLOW;
3699 	s = post - 1;
3700 	while (*++s)
3701 	    if (*s == '/')
3702 		*s = '\\';			/* Be extra causious */
3703 	memcpy(to, post, l);
3704 	if (!l || to[l-1] != ';')
3705 	    to[l++] = ';';
3706 	to += l;
3707     }
3708     *to = 0;
3709     rc = ExtLIBPATH(ORD_DosSetExtLibpath, buf, type, 0); /* Do not croak */
3710     return rc;
3711 }
3712 
3713 /* Input: Address, BufLen
3714 APIRET APIENTRY
3715 DosQueryModFromEIP (HMODULE * hmod, ULONG * obj, ULONG BufLen, PCHAR Buf,
3716 		    ULONG * Offset, ULONG Address);
3717 */
3718 
3719 DeclOSFuncByORD(APIRET, _DosQueryModFromEIP,ORD_DosQueryModFromEIP,
3720 			(HMODULE * hmod, ULONG * obj, ULONG BufLen, PCHAR Buf,
3721 			ULONG * Offset, ULONG Address),
3722 			(hmod, obj, BufLen, Buf, Offset, Address))
3723 
3724 static SV*
3725 module_name_at(void *pp, enum module_name_how how)
3726 {
3727     dTHX;
3728     char buf[MAXPATHLEN];
3729     char *p = buf;
3730     HMODULE mod;
3731     ULONG obj, offset, rc, addr = (ULONG)pp;
3732 
3733     if (how & mod_name_HMODULE) {
3734 	if ((how & ~mod_name_HMODULE) == mod_name_shortname)
3735 	    Perl_croak(aTHX_ "Can't get short module name from a handle");
3736 	mod = (HMODULE)pp;
3737 	how &= ~mod_name_HMODULE;
3738     } else if (!_DosQueryModFromEIP(&mod, &obj, sizeof(buf), buf, &offset, addr))
3739 	return &PL_sv_undef;
3740     if (how == mod_name_handle)
3741 	return newSVuv(mod);
3742     /* Full name... */
3743     if ( how != mod_name_shortname
3744 	 && CheckOSError(DosQueryModuleName(mod, sizeof(buf), buf)) )
3745 	return &PL_sv_undef;
3746     while (*p) {
3747 	if (*p == '\\')
3748 	    *p = '/';
3749 	p++;
3750     }
3751     return newSVpv(buf, 0);
3752 }
3753 
3754 static SV*
3755 module_name_of_cv(SV *cv, enum module_name_how how)
3756 {
3757     if (!cv || !SvROK(cv) || SvTYPE(SvRV(cv)) != SVt_PVCV || !CvXSUB(SvRV(cv))) {
3758 	dTHX;
3759 
3760 	if (how & mod_name_C_function)
3761 	    return module_name_at((void*)SvIV(cv), how & ~mod_name_C_function);
3762 	else if (how & mod_name_HMODULE)
3763 	    return module_name_at((void*)SvIV(cv), how);
3764 	Perl_croak(aTHX_ "Not an XSUB reference");
3765     }
3766     return module_name_at(CvXSUB(SvRV(cv)), how);
3767 }
3768 
3769 XS(XS_OS2_DLLname)
3770 {
3771     dXSARGS;
3772     if (items > 2)
3773 	Perl_croak(aTHX_ "Usage: OS2::DLLname( [ how, [\\&xsub] ] )");
3774     {
3775 	SV *	RETVAL;
3776 	int	how;
3777 
3778 	if (items < 1)
3779 	    how = mod_name_full;
3780 	else {
3781 	    how = (int)SvIV(ST(0));
3782 	}
3783 	if (items < 2)
3784 	    RETVAL = module_name(how);
3785 	else
3786 	    RETVAL = module_name_of_cv(ST(1), how);
3787 	ST(0) = RETVAL;
3788 	sv_2mortal(ST(0));
3789     }
3790     XSRETURN(1);
3791 }
3792 
3793 DeclOSFuncByORD(INT, _Dos32QueryHeaderInfo, ORD_Dos32QueryHeaderInfo,
3794 			(ULONG r1, ULONG r2, PVOID buf, ULONG szbuf, ULONG fnum),
3795 			(r1, r2, buf, szbuf, fnum))
3796 
3797 XS(XS_OS2__headerInfo)
3798 {
3799     dXSARGS;
3800     if (items > 4 || items < 2)
3801 	Perl_croak(aTHX_ "Usage: OS2::_headerInfo(req,size[,handle,[offset]])");
3802     {
3803 	ULONG	req = (ULONG)SvIV(ST(0));
3804 	STRLEN	size = (STRLEN)SvIV(ST(1)), n_a;
3805 	ULONG	handle = (items >= 3 ? (ULONG)SvIV(ST(2)) : 0);
3806 	ULONG	offset = (items >= 4 ? (ULONG)SvIV(ST(3)) : 0);
3807 
3808 	if (size <= 0)
3809 	    Perl_croak(aTHX_ "OS2::_headerInfo(): unexpected size: %d", (int)size);
3810 	ST(0) = newSVpvn("",0);
3811 	SvGROW(ST(0), size + 1);
3812 	sv_2mortal(ST(0));
3813 
3814 	if (!_Dos32QueryHeaderInfo(handle, offset, SvPV(ST(0), n_a), size, req))
3815 	    Perl_croak(aTHX_ "OS2::_headerInfo(%ld,%ld,%ld,%ld) error: %s",
3816 		       req, size, handle, offset, os2error(Perl_rc));
3817 	SvCUR_set(ST(0), size);
3818 	*SvEND(ST(0)) = 0;
3819     }
3820     XSRETURN(1);
3821 }
3822 
3823 #define DQHI_QUERYLIBPATHSIZE      4
3824 #define DQHI_QUERYLIBPATH          5
3825 
3826 XS(XS_OS2_libPath)
3827 {
3828     dXSARGS;
3829     if (items != 0)
3830 	Perl_croak(aTHX_ "Usage: OS2::libPath()");
3831     {
3832 	ULONG	size;
3833 	STRLEN	n_a;
3834 
3835 	if (!_Dos32QueryHeaderInfo(0, 0, &size, sizeof(size),
3836 				   DQHI_QUERYLIBPATHSIZE))
3837 	    Perl_croak(aTHX_ "OS2::_headerInfo(%ld,%ld,%ld,%ld) error: %s",
3838 		       DQHI_QUERYLIBPATHSIZE, sizeof(size), 0, 0,
3839 		       os2error(Perl_rc));
3840 	ST(0) = newSVpvn("",0);
3841 	SvGROW(ST(0), size + 1);
3842 	sv_2mortal(ST(0));
3843 
3844 	/* We should be careful: apparently, this entry point does not
3845 	   pay attention to the size argument, so may overwrite
3846 	   unrelated data! */
3847 	if (!_Dos32QueryHeaderInfo(0, 0, SvPV(ST(0), n_a), size,
3848 				   DQHI_QUERYLIBPATH))
3849 	    Perl_croak(aTHX_ "OS2::_headerInfo(%ld,%ld,%ld,%ld) error: %s",
3850 		       DQHI_QUERYLIBPATH, size, 0, 0, os2error(Perl_rc));
3851 	SvCUR_set(ST(0), size);
3852 	*SvEND(ST(0)) = 0;
3853     }
3854     XSRETURN(1);
3855 }
3856 
3857 #define get_control87()		_control87(0,0)
3858 #define set_control87		_control87
3859 
3860 XS(XS_OS2__control87)
3861 {
3862     dXSARGS;
3863     if (items != 2)
3864 	Perl_croak(aTHX_ "Usage: OS2::_control87(new,mask)");
3865     {
3866 	unsigned	new = (unsigned)SvIV(ST(0));
3867 	unsigned	mask = (unsigned)SvIV(ST(1));
3868 	unsigned	RETVAL;
3869 	dXSTARG;
3870 
3871 	RETVAL = _control87(new, mask);
3872 	XSprePUSH; PUSHi((IV)RETVAL);
3873     }
3874     XSRETURN(1);
3875 }
3876 
3877 XS(XS_OS2_mytype)
3878 {
3879     dXSARGS;
3880     int which = 0;
3881 
3882     if (items < 0 || items > 1)
3883 	Perl_croak(aTHX_ "Usage: OS2::mytype([which])");
3884     if (items == 1)
3885 	which = (int)SvIV(ST(0));
3886     {
3887 	unsigned	RETVAL;
3888 	dXSTARG;
3889 
3890 	switch (which) {
3891 	case 0:
3892 	    RETVAL = os2_mytype;	/* Reset after fork */
3893 	    break;
3894 	case 1:
3895 	    RETVAL = os2_mytype_ini;	/* Before any fork */
3896 	    break;
3897 	case 2:
3898 	    RETVAL = Perl_os2_initial_mode;	/* Before first morphing */
3899 	    break;
3900 	case 3:
3901 	    RETVAL = my_type();		/* Morphed type */
3902 	    break;
3903 	default:
3904 	    Perl_croak(aTHX_ "OS2::mytype(which): unknown which=%d", which);
3905 	}
3906 	XSprePUSH; PUSHi((IV)RETVAL);
3907     }
3908     XSRETURN(1);
3909 }
3910 
3911 
3912 XS(XS_OS2_mytype_set)
3913 {
3914     dXSARGS;
3915     int type;
3916 
3917     if (items == 1)
3918 	type = (int)SvIV(ST(0));
3919     else
3920 	Perl_croak(aTHX_ "Usage: OS2::mytype_set(type)");
3921     my_type_set(type);
3922     XSRETURN_EMPTY;
3923 }
3924 
3925 
3926 XS(XS_OS2_get_control87)
3927 {
3928     dXSARGS;
3929     if (items != 0)
3930 	Perl_croak(aTHX_ "Usage: OS2::get_control87()");
3931     {
3932 	unsigned	RETVAL;
3933 	dXSTARG;
3934 
3935 	RETVAL = get_control87();
3936 	XSprePUSH; PUSHi((IV)RETVAL);
3937     }
3938     XSRETURN(1);
3939 }
3940 
3941 
3942 XS(XS_OS2_set_control87)
3943 {
3944     dXSARGS;
3945     if (items < 0 || items > 2)
3946 	Perl_croak(aTHX_ "Usage: OS2::set_control87(new=MCW_EM, mask=MCW_EM)");
3947     {
3948 	unsigned	new;
3949 	unsigned	mask;
3950 	unsigned	RETVAL;
3951 	dXSTARG;
3952 
3953 	if (items < 1)
3954 	    new = MCW_EM;
3955 	else {
3956 	    new = (unsigned)SvIV(ST(0));
3957 	}
3958 
3959 	if (items < 2)
3960 	    mask = MCW_EM;
3961 	else {
3962 	    mask = (unsigned)SvIV(ST(1));
3963 	}
3964 
3965 	RETVAL = set_control87(new, mask);
3966 	XSprePUSH; PUSHi((IV)RETVAL);
3967     }
3968     XSRETURN(1);
3969 }
3970 
3971 XS(XS_OS2_incrMaxFHandles)		/* DosSetRelMaxFH */
3972 {
3973     dXSARGS;
3974     if (items < 0 || items > 1)
3975 	Perl_croak(aTHX_ "Usage: OS2::incrMaxFHandles(delta = 0)");
3976     {
3977 	LONG	delta;
3978 	ULONG	RETVAL, rc;
3979 	dXSTARG;
3980 
3981 	if (items < 1)
3982 	    delta = 0;
3983 	else
3984 	    delta = (LONG)SvIV(ST(0));
3985 
3986 	if (CheckOSError(DosSetRelMaxFH(&delta, &RETVAL)))
3987 	    croak_with_os2error("OS2::incrMaxFHandles(): DosSetRelMaxFH() error");
3988 	XSprePUSH; PUSHu((UV)RETVAL);
3989     }
3990     XSRETURN(1);
3991 }
3992 
3993 int
3994 Xs_OS2_init(pTHX)
3995 {
3996     char *file = __FILE__;
3997     {
3998 	GV *gv;
3999 
4000 	if (_emx_env & 0x200) {	/* OS/2 */
4001             newXS("File::Copy::syscopy", XS_File__Copy_syscopy, file);
4002             newXS("Cwd::extLibpath", XS_Cwd_extLibpath, file);
4003             newXS("Cwd::extLibpath_set", XS_Cwd_extLibpath_set, file);
4004             newXS("OS2::extLibpath", XS_Cwd_extLibpath, file);
4005             newXS("OS2::extLibpath_set", XS_Cwd_extLibpath_set, file);
4006 	}
4007         newXS("OS2::Error", XS_OS2_Error, file);
4008         newXS("OS2::Errors2Drive", XS_OS2_Errors2Drive, file);
4009         newXS("OS2::SysInfo", XS_OS2_SysInfo, file);
4010         newXSproto("OS2::DevCap", XS_OS2_DevCap, file, ";$$");
4011         newXSproto("OS2::SysInfoFor", XS_OS2_SysInfoFor, file, "$;$");
4012         newXS("OS2::BootDrive", XS_OS2_BootDrive, file);
4013         newXS("OS2::MorphPM", XS_OS2_MorphPM, file);
4014         newXS("OS2::UnMorphPM", XS_OS2_UnMorphPM, file);
4015         newXS("OS2::Serve_Messages", XS_OS2_Serve_Messages, file);
4016         newXS("OS2::Process_Messages", XS_OS2_Process_Messages, file);
4017         newXS("DynaLoader::mod2fname", XS_DynaLoader_mod2fname, file);
4018         newXS("Cwd::current_drive", XS_Cwd_current_drive, file);
4019         newXS("Cwd::sys_chdir", XS_Cwd_sys_chdir, file);
4020         newXS("Cwd::change_drive", XS_Cwd_change_drive, file);
4021         newXS("Cwd::sys_is_absolute", XS_Cwd_sys_is_absolute, file);
4022         newXS("Cwd::sys_is_rooted", XS_Cwd_sys_is_rooted, file);
4023         newXS("Cwd::sys_is_relative", XS_Cwd_sys_is_relative, file);
4024         newXS("Cwd::sys_cwd", XS_Cwd_sys_cwd, file);
4025         newXS("Cwd::sys_abspath", XS_Cwd_sys_abspath, file);
4026         newXS("OS2::replaceModule", XS_OS2_replaceModule, file);
4027         newXS("OS2::perfSysCall", XS_OS2_perfSysCall, file);
4028         newXSproto("OS2::_control87", XS_OS2__control87, file, "$$");
4029         newXSproto("OS2::get_control87", XS_OS2_get_control87, file, "");
4030         newXSproto("OS2::set_control87", XS_OS2_set_control87, file, ";$$");
4031         newXSproto("OS2::DLLname", XS_OS2_DLLname, file, ";$$");
4032         newXSproto("OS2::mytype", XS_OS2_mytype, file, ";$");
4033         newXSproto("OS2::mytype_set", XS_OS2_mytype_set, file, "$");
4034         newXSproto("OS2::_headerInfo", XS_OS2__headerInfo, file, "$$;$$");
4035         newXSproto("OS2::libPath", XS_OS2_libPath, file, "");
4036         newXSproto("OS2::Timer", XS_OS2_Timer, file, "");
4037         newXSproto("OS2::msCounter", XS_OS2_msCounter, file, "");
4038         newXSproto("OS2::ms_sleep", XS_OS2_ms_sleep, file, "$;$");
4039         newXSproto("OS2::_InfoTable", XS_OS2__InfoTable, file, ";$");
4040         newXSproto("OS2::incrMaxFHandles", XS_OS2_incrMaxFHandles, file, ";$");
4041         newXSproto("OS2::SysValues", XS_OS2_SysValues, file, ";$$");
4042         newXSproto("OS2::SysValues_set", XS_OS2_SysValues_set, file, "$$;$");
4043         newXSproto("OS2::Beep", XS_OS2_Beep, file, ";$$");
4044 	gv = gv_fetchpv("OS2::is_aout", TRUE, SVt_PV);
4045 	GvMULTI_on(gv);
4046 #ifdef PERL_IS_AOUT
4047 	sv_setiv(GvSV(gv), 1);
4048 #endif
4049 	gv = gv_fetchpv("OS2::is_static", TRUE, SVt_PV);
4050 	GvMULTI_on(gv);
4051 #ifdef PERL_IS_AOUT
4052 	sv_setiv(GvSV(gv), 1);
4053 #endif
4054 	gv = gv_fetchpv("OS2::can_fork", TRUE, SVt_PV);
4055 	GvMULTI_on(gv);
4056 	sv_setiv(GvSV(gv), exe_is_aout());
4057 	gv = gv_fetchpv("OS2::emx_rev", TRUE, SVt_PV);
4058 	GvMULTI_on(gv);
4059 	sv_setiv(GvSV(gv), _emx_rev);
4060 	sv_setpv(GvSV(gv), _emx_vprt);
4061 	SvIOK_on(GvSV(gv));
4062 	gv = gv_fetchpv("OS2::emx_env", TRUE, SVt_PV);
4063 	GvMULTI_on(gv);
4064 	sv_setiv(GvSV(gv), _emx_env);
4065 	gv = gv_fetchpv("OS2::os_ver", TRUE, SVt_PV);
4066 	GvMULTI_on(gv);
4067 	sv_setnv(GvSV(gv), _osmajor + 0.001 * _osminor);
4068 	gv = gv_fetchpv("OS2::nsyserror", TRUE, SVt_PV);
4069 	GvMULTI_on(gv);
4070 	sv_setiv(GvSV(gv), 1);		/* DEFAULT: Show number on syserror */
4071     }
4072     return 0;
4073 }
4074 
4075 extern void _emx_init(void*);
4076 
4077 static void jmp_out_of_atexit(void);
4078 
4079 #define FORCE_EMX_INIT_CONTRACT_ARGV	1
4080 #define FORCE_EMX_INIT_INSTALL_ATEXIT	2
4081 
4082 static void
4083 my_emx_init(void *layout) {
4084     static volatile void *old_esp = 0;	/* Cannot be on stack! */
4085 
4086     /* Can't just call emx_init(), since it moves the stack pointer */
4087     /* It also busts a lot of registers, so be extra careful */
4088     __asm__(	"pushf\n"
4089 		"pusha\n"
4090 		"movl %%esp, %1\n"
4091 		"push %0\n"
4092 		"call __emx_init\n"
4093 		"movl %1, %%esp\n"
4094 		"popa\n"
4095 		"popf\n" : : "r" (layout), "m" (old_esp)	);
4096 }
4097 
4098 struct layout_table_t {
4099     ULONG text_base;
4100     ULONG text_end;
4101     ULONG data_base;
4102     ULONG data_end;
4103     ULONG bss_base;
4104     ULONG bss_end;
4105     ULONG heap_base;
4106     ULONG heap_end;
4107     ULONG heap_brk;
4108     ULONG heap_off;
4109     ULONG os2_dll;
4110     ULONG stack_base;
4111     ULONG stack_end;
4112     ULONG flags;
4113     ULONG reserved[2];
4114     char options[64];
4115 };
4116 
4117 static ULONG
4118 my_os_version() {
4119     static ULONG osv_res;		/* Cannot be on stack! */
4120 
4121     /* Can't just call __os_version(), since it does not follow C
4122        calling convention: it busts a lot of registers, so be extra careful */
4123     __asm__(	"pushf\n"
4124 		"pusha\n"
4125 		"call ___os_version\n"
4126 		"movl %%eax, %0\n"
4127 		"popa\n"
4128 		"popf\n" : "=m" (osv_res)	);
4129 
4130     return osv_res;
4131 }
4132 
4133 static void
4134 force_init_emx_runtime(EXCEPTIONREGISTRATIONRECORD *preg, ULONG flags)
4135 {
4136     /* Calling emx_init() will bust the top of stack: it installs an
4137        exception handler and puts argv data there. */
4138     char *oldarg, *oldenv;
4139     void *oldstackend, *oldstack;
4140     PPIB pib;
4141     PTIB tib;
4142     ULONG rc, error = 0, out;
4143     char buf[512];
4144     static struct layout_table_t layout_table;
4145     struct {
4146 	char buf[48*1024]; /* _emx_init() requires 32K, cmd.exe has 64K only */
4147 	double alignment1;
4148 	EXCEPTIONREGISTRATIONRECORD xreg;
4149     } *newstack;
4150     char *s;
4151 
4152     layout_table.os2_dll = (ULONG)&os2_dll_fake;
4153     layout_table.flags   = 0x02000002;	/* flags: application, OMF */
4154 
4155     DosGetInfoBlocks(&tib, &pib);
4156     oldarg = pib->pib_pchcmd;
4157     oldenv = pib->pib_pchenv;
4158     oldstack = tib->tib_pstack;
4159     oldstackend = tib->tib_pstacklimit;
4160 
4161     if ( (char*)&s < (char*)oldstack + 4*1024
4162 	 || (char *)oldstackend < (char*)oldstack + 52*1024 )
4163 	early_error("It is a lunacy to try to run EMX Perl ",
4164 		    "with less than 64K of stack;\r\n",
4165 		    "  at least with non-EMX starter...\r\n");
4166 
4167     /* Minimize the damage to the stack via reducing the size of argv. */
4168     if (flags & FORCE_EMX_INIT_CONTRACT_ARGV) {
4169 	pib->pib_pchcmd = "\0\0";	/* Need 3 concatenated strings */
4170 	pib->pib_pchcmd = "\0";		/* Ended by an extra \0. */
4171     }
4172 
4173     newstack = alloca(sizeof(*newstack));
4174     /* Emulate the stack probe */
4175     s = ((char*)newstack) + sizeof(*newstack);
4176     while (s > (char*)newstack) {
4177 	s[-1] = 0;
4178 	s -= 4096;
4179     }
4180 
4181     /* Reassigning stack is documented to work */
4182     tib->tib_pstack = (void*)newstack;
4183     tib->tib_pstacklimit = (void*)((char*)newstack + sizeof(*newstack));
4184 
4185     /* Can't just call emx_init(), since it moves the stack pointer */
4186     my_emx_init((void*)&layout_table);
4187 
4188     /* Remove the exception handler, cannot use it - too low on the stack.
4189        Check whether it is inside the new stack.  */
4190     buf[0] = 0;
4191     if (tib->tib_pexchain >= tib->tib_pstacklimit
4192 	|| tib->tib_pexchain < tib->tib_pstack) {
4193 	error = 1;
4194 	sprintf(buf,
4195 		"panic: ExceptionHandler misplaced: not %#lx <= %#lx < %#lx\n",
4196 		(unsigned long)tib->tib_pstack,
4197 		(unsigned long)tib->tib_pexchain,
4198 		(unsigned long)tib->tib_pstacklimit);
4199 	goto finish;
4200     }
4201     if (tib->tib_pexchain != &(newstack->xreg)) {
4202 	sprintf(buf, "ExceptionHandler misplaced: %#lx != %#lx\n",
4203 		(unsigned long)tib->tib_pexchain,
4204 		(unsigned long)&(newstack->xreg));
4205     }
4206     rc = DosUnsetExceptionHandler((EXCEPTIONREGISTRATIONRECORD *)tib->tib_pexchain);
4207     if (rc)
4208 	sprintf(buf + strlen(buf),
4209 		"warning: DosUnsetExceptionHandler rc=%#lx=%lu\n", rc, rc);
4210 
4211     if (preg) {
4212 	/* ExceptionRecords should be on stack, in a correct order.  Sigh... */
4213 	preg->prev_structure = 0;
4214 	preg->ExceptionHandler = _emx_exception;
4215 	rc = DosSetExceptionHandler(preg);
4216 	if (rc) {
4217 	    sprintf(buf + strlen(buf),
4218 		    "warning: DosSetExceptionHandler rc=%#lx=%lu\n", rc, rc);
4219 	    DosWrite(2, buf, strlen(buf), &out);
4220 	    emx_exception_init = 1;	/* Do it around spawn*() calls */
4221 	}
4222     } else
4223 	emx_exception_init = 1;		/* Do it around spawn*() calls */
4224 
4225   finish:
4226     /* Restore the damage */
4227     pib->pib_pchcmd = oldarg;
4228     pib->pib_pchcmd = oldenv;
4229     tib->tib_pstacklimit = oldstackend;
4230     tib->tib_pstack = oldstack;
4231     emx_runtime_init = 1;
4232     if (buf[0])
4233 	DosWrite(2, buf, strlen(buf), &out);
4234     if (error)
4235 	exit(56);
4236 }
4237 
4238 static void
4239 jmp_out_of_atexit(void)
4240 {
4241     if (longjmp_at_exit)
4242 	longjmp(at_exit_buf, 1);
4243 }
4244 
4245 extern void _CRT_term(void);
4246 
4247 void
4248 Perl_OS2_term(void **p, int exitstatus, int flags)
4249 {
4250     if (!emx_runtime_secondary)
4251 	return;
4252 
4253     /* The principal executable is not running the same CRTL, so there
4254        is nobody to shutdown *this* CRTL except us... */
4255     if (flags & FORCE_EMX_DEINIT_EXIT) {
4256 	if (p && !emx_exception_init)
4257 	    DosUnsetExceptionHandler((EXCEPTIONREGISTRATIONRECORD *)p);
4258 	/* Do not run the executable's CRTL's termination routines */
4259 	exit(exitstatus);		/* Run at-exit, flush buffers, etc */
4260     }
4261     /* Run at-exit list, and jump out at the end */
4262     if ((flags & FORCE_EMX_DEINIT_RUN_ATEXIT) && !setjmp(at_exit_buf)) {
4263 	longjmp_at_exit = 1;
4264 	exit(exitstatus);		/* The first pass through "if" */
4265     }
4266 
4267     /* Get here if we managed to jump out of exit(), or did not run atexit. */
4268     longjmp_at_exit = 0;		/* Maybe exit() is called again? */
4269 #if 0 /* _atexit_n is not exported */
4270     if (flags & FORCE_EMX_DEINIT_RUN_ATEXIT)
4271 	_atexit_n = 0;			/* Remove the atexit() handlers */
4272 #endif
4273     /* Will segfault on program termination if we leave this dangling... */
4274     if (p && !emx_exception_init)
4275 	DosUnsetExceptionHandler((EXCEPTIONREGISTRATIONRECORD *)p);
4276     /* Typically there is no need to do this, done from _DLL_InitTerm() */
4277     if (flags & FORCE_EMX_DEINIT_CRT_TERM)
4278 	_CRT_term();			/* Flush buffers, etc. */
4279     /* Now it is a good time to call exit() in the caller's CRTL... */
4280 }
4281 
4282 #include <emx/startup.h>
4283 
4284 extern ULONG __os_version();		/* See system.doc */
4285 
4286 void
4287 check_emx_runtime(char **env, EXCEPTIONREGISTRATIONRECORD *preg)
4288 {
4289     ULONG v_crt, v_emx, count = 0, rc = NO_ERROR, rc1, maybe_inited = 0;
4290     static HMTX hmtx_emx_init = NULLHANDLE;
4291     static int emx_init_done = 0;
4292 
4293     /*  If _environ is not set, this code sits in a DLL which
4294 	uses a CRT DLL which not compatible with the executable's
4295 	CRT library.  Some parts of the DLL are not initialized.
4296      */
4297     if (_environ != NULL)
4298 	return;				/* Properly initialized */
4299 
4300     /* It is not DOS, so we may use OS/2 API now */
4301     /* Some data we manipulate is static; protect ourselves from
4302        calling the same API from a different thread. */
4303     DosEnterMustComplete(&count);
4304 
4305     rc1 = DosEnterCritSec();
4306     if (!hmtx_emx_init)
4307 	rc = DosCreateMutexSem(NULL, &hmtx_emx_init, 0, TRUE); /*Create owned*/
4308     else
4309 	maybe_inited = 1;
4310 
4311     if (rc != NO_ERROR)
4312 	hmtx_emx_init = NULLHANDLE;
4313 
4314     if (rc1 == NO_ERROR)
4315 	DosExitCritSec();
4316     DosExitMustComplete(&count);
4317 
4318     while (maybe_inited) { /* Other thread did or is doing the same now */
4319 	if (emx_init_done)
4320 	    return;
4321 	rc = DosRequestMutexSem(hmtx_emx_init,
4322 				(ULONG) SEM_INDEFINITE_WAIT);  /* Timeout (none) */
4323 	if (rc == ERROR_INTERRUPT)
4324 	    continue;
4325 	if (rc != NO_ERROR) {
4326 	    char buf[80];
4327 	    ULONG out;
4328 
4329 	    sprintf(buf,
4330 		    "panic: EMX backdoor init: DosRequestMutexSem error: %lu=%#lx\n", rc, rc);
4331 	    DosWrite(2, buf, strlen(buf), &out);
4332 	    return;
4333 	}
4334 	DosReleaseMutexSem(hmtx_emx_init);
4335 	return;
4336     }
4337 
4338     /*  If the executable does not use EMX.DLL, EMX.DLL is not completely
4339 	initialized either.  Uninitialized EMX.DLL returns 0 in the low
4340 	nibble of __os_version().  */
4341     v_emx = my_os_version();
4342 
4343     /*	_osmajor and _osminor are normally set in _DLL_InitTerm of CRT DLL
4344 	(=>_CRT_init=>_entry2) via a call to __os_version(), then
4345 	reset when the EXE initialization code calls _text=>_init=>_entry2.
4346 	The first time they are wrongly set to 0; the second time the
4347 	EXE initialization code had already called emx_init=>initialize1
4348 	which correctly set version_major, version_minor used by
4349 	__os_version().  */
4350     v_crt = (_osmajor | _osminor);
4351 
4352     if ((_emx_env & 0x200) && !(v_emx & 0xFFFF)) {	/* OS/2, EMX uninit. */
4353 	force_init_emx_runtime( preg,
4354 				FORCE_EMX_INIT_CONTRACT_ARGV
4355 				| FORCE_EMX_INIT_INSTALL_ATEXIT );
4356 	emx_wasnt_initialized = 1;
4357 	/* Update CRTL data basing on now-valid EMX runtime data */
4358 	if (!v_crt) {		/* The only wrong data are the versions. */
4359 	    v_emx = my_os_version();			/* *Now* it works */
4360 	    *(unsigned char *)&_osmajor = v_emx & 0xFF;	/* Cast out const */
4361 	    *(unsigned char *)&_osminor = (v_emx>>8) & 0xFF;
4362 	}
4363     }
4364     emx_runtime_secondary = 1;
4365     /* if (flags & FORCE_EMX_INIT_INSTALL_ATEXIT) */
4366     atexit(jmp_out_of_atexit);		/* Allow run of atexit() w/o exit()  */
4367 
4368     if (env == NULL) {			/* Fetch from the process info block */
4369 	int c = 0;
4370 	PPIB pib;
4371 	PTIB tib;
4372 	char *e, **ep;
4373 
4374 	DosGetInfoBlocks(&tib, &pib);
4375 	e = pib->pib_pchenv;
4376 	while (*e) {			/* Get count */
4377 	    c++;
4378 	    e = e + strlen(e) + 1;
4379 	}
4380 	New(1307, env, c + 1, char*);
4381 	ep = env;
4382 	e = pib->pib_pchenv;
4383 	while (c--) {
4384 	    *ep++ = e;
4385 	    e = e + strlen(e) + 1;
4386 	}
4387 	*ep = NULL;
4388     }
4389     _environ = _org_environ = env;
4390     emx_init_done = 1;
4391     if (hmtx_emx_init)
4392 	DosReleaseMutexSem(hmtx_emx_init);
4393 }
4394 
4395 #define ENTRY_POINT 0x10000
4396 
4397 static int
4398 exe_is_aout(void)
4399 {
4400     struct layout_table_t *layout;
4401     if (emx_wasnt_initialized)
4402 	return 0;
4403     /* Now we know that the principal executable is an EMX application
4404        - unless somebody did already play with delayed initialization... */
4405     /* With EMX applications to determine whether it is AOUT one needs
4406        to examine the start of the executable to find "layout" */
4407     if ( *(unsigned char*)ENTRY_POINT != 0x68		/* PUSH n */
4408 	 || *(unsigned char*)(ENTRY_POINT+5) != 0xe8	/* CALL */
4409 	 || *(unsigned char*)(ENTRY_POINT+10) != 0xeb	/* JMP */
4410 	 || *(unsigned char*)(ENTRY_POINT+12) != 0xe8)	/* CALL */
4411 	return 0;					/* ! EMX executable */
4412     /* Fix alignment */
4413     Copy((char*)(ENTRY_POINT+1), &layout, 1, struct layout_table_t*);
4414     return !(layout->flags & 2);
4415 }
4416 
4417 void
4418 Perl_OS2_init(char **env)
4419 {
4420     Perl_OS2_init3(env, 0, 0);
4421 }
4422 
4423 void
4424 Perl_OS2_init3(char **env, void **preg, int flags)
4425 {
4426     char *shell, *s;
4427     ULONG rc;
4428 
4429     _uflags (_UF_SBRK_MODEL, _UF_SBRK_ARBITRARY);
4430     MALLOC_INIT;
4431 
4432     check_emx_runtime(env, (EXCEPTIONREGISTRATIONRECORD *)preg);
4433 
4434     settmppath();
4435     OS2_Perl_data.xs_init = &Xs_OS2_init;
4436     if (perl_sh_installed) {
4437 	int l = strlen(perl_sh_installed);
4438 
4439 	New(1304, PL_sh_path, l + 1, char);
4440 	memcpy(PL_sh_path, perl_sh_installed, l + 1);
4441     } else if ( (shell = getenv("PERL_SH_DRIVE")) ) {
4442 	New(1304, PL_sh_path, strlen(SH_PATH) + 1, char);
4443 	strcpy(PL_sh_path, SH_PATH);
4444 	PL_sh_path[0] = shell[0];
4445     } else if ( (shell = getenv("PERL_SH_DIR")) ) {
4446 	int l = strlen(shell), i;
4447 
4448 	while (l && (shell[l-1] == '/' || shell[l-1] == '\\'))
4449 	    l--;
4450 	New(1304, PL_sh_path, l + 8, char);
4451 	strncpy(PL_sh_path, shell, l);
4452 	strcpy(PL_sh_path + l, "/sh.exe");
4453 	for (i = 0; i < l; i++) {
4454 	    if (PL_sh_path[i] == '\\') PL_sh_path[i] = '/';
4455 	}
4456     }
4457 #if defined(USE_5005THREADS) || defined(USE_ITHREADS)
4458     MUTEX_INIT(&start_thread_mutex);
4459     MUTEX_INIT(&perlos2_state_mutex);
4460 #endif
4461     os2_mytype = my_type();		/* Do it before morphing.  Needed? */
4462     os2_mytype_ini = os2_mytype;
4463     Perl_os2_initial_mode = -1;		/* Uninit */
4464 
4465     s = getenv("PERL_BEGINLIBPATH");
4466     if (s)
4467       rc = fill_extLibpath(0, s, NULL, 1, "PERL_BEGINLIBPATH");
4468     else
4469       rc = fill_extLibpath(0, getenv("PERL_PRE_BEGINLIBPATH"), getenv("PERL_POST_BEGINLIBPATH"), 0, "PERL_(PRE/POST)_BEGINLIBPATH");
4470     if (!rc) {
4471 	s = getenv("PERL_ENDLIBPATH");
4472 	if (s)
4473 	    rc = fill_extLibpath(1, s, NULL, 1, "PERL_ENDLIBPATH");
4474 	else
4475 	    rc = fill_extLibpath(1, getenv("PERL_PRE_ENDLIBPATH"), getenv("PERL_POST_ENDLIBPATH"), 0, "PERL_(PRE/POST)_ENDLIBPATH");
4476     }
4477     if (rc) {
4478 	char buf[1024];
4479 
4480 	snprintf(buf, sizeof buf, "Error setting BEGIN/ENDLIBPATH: %s\n",
4481 		 os2error(rc));
4482 	DosWrite(2, buf, strlen(buf), &rc);
4483 	exit(2);
4484     }
4485 
4486     _emxload_env("PERL_EMXLOAD_SECS");
4487     /* Some DLLs reset FP flags on load.  We may have been linked with them */
4488     _control87(MCW_EM, MCW_EM);
4489 }
4490 
4491 int
4492 fd_ok(int fd)
4493 {
4494     static ULONG max_fh = 0;
4495 
4496     if (!(_emx_env & 0x200)) return 1;		/* not OS/2. */
4497     if (fd >= max_fh) {				/* Renew */
4498 	LONG delta = 0;
4499 
4500 	if (DosSetRelMaxFH(&delta, &max_fh))	/* Assume it OK??? */
4501 	    return 1;
4502     }
4503     return fd < max_fh;
4504 }
4505 
4506 /* Kernels up to Oct 2003 trap on (invalid) dup(max_fh); [off-by-one + double fault].  */
4507 int
4508 dup2(int from, int to)
4509 {
4510     if (fd_ok(from < to ? to : from))
4511 	return _dup2(from, to);
4512     errno = EBADF;
4513     return -1;
4514 }
4515 
4516 int
4517 dup(int from)
4518 {
4519     if (fd_ok(from))
4520 	return _dup(from);
4521     errno = EBADF;
4522     return -1;
4523 }
4524 
4525 #undef tmpnam
4526 #undef tmpfile
4527 
4528 char *
4529 my_tmpnam (char *str)
4530 {
4531     char *p = getenv("TMP"), *tpath;
4532 
4533     if (!p) p = getenv("TEMP");
4534     tpath = tempnam(p, "pltmp");
4535     if (str && tpath) {
4536 	strcpy(str, tpath);
4537 	return str;
4538     }
4539     return tpath;
4540 }
4541 
4542 FILE *
4543 my_tmpfile ()
4544 {
4545     struct stat s;
4546 
4547     stat(".", &s);
4548     if (s.st_mode & S_IWOTH) {
4549 	return tmpfile();
4550     }
4551     return fopen(my_tmpnam(NULL), "w+b"); /* Race condition, but
4552 					     grants TMP. */
4553 }
4554 
4555 #undef rmdir
4556 
4557 /* EMX flavors do not tolerate trailing slashes.  t/op/mkdir.t has many
4558    trailing slashes, so we need to support this as well. */
4559 
4560 int
4561 my_rmdir (__const__ char *s)
4562 {
4563     char b[MAXPATHLEN];
4564     char *buf = b;
4565     STRLEN l = strlen(s);
4566     int rc;
4567 
4568     if (s[l-1] == '/' || s[l-1] == '\\') {	/* EMX mkdir fails... */
4569 	if (l >= sizeof b)
4570 	    New(1305, buf, l + 1, char);
4571 	strcpy(buf,s);
4572 	while (l > 1 && (s[l-1] == '/' || s[l-1] == '\\'))
4573 	    l--;
4574 	buf[l] = 0;
4575 	s = buf;
4576     }
4577     rc = rmdir(s);
4578     if (b != buf)
4579 	Safefree(buf);
4580     return rc;
4581 }
4582 
4583 #undef mkdir
4584 
4585 int
4586 my_mkdir (__const__ char *s, long perm)
4587 {
4588     char b[MAXPATHLEN];
4589     char *buf = b;
4590     STRLEN l = strlen(s);
4591     int rc;
4592 
4593     if (s[l-1] == '/' || s[l-1] == '\\') {	/* EMX mkdir fails... */
4594 	if (l >= sizeof b)
4595 	    New(1305, buf, l + 1, char);
4596 	strcpy(buf,s);
4597 	while (l > 1 && (s[l-1] == '/' || s[l-1] == '\\'))
4598 	    l--;
4599 	buf[l] = 0;
4600 	s = buf;
4601     }
4602     rc = mkdir(s, perm);
4603     if (b != buf)
4604 	Safefree(buf);
4605     return rc;
4606 }
4607 
4608 #undef flock
4609 
4610 /* This code was contributed by Rocco Caputo. */
4611 int
4612 my_flock(int handle, int o)
4613 {
4614   FILELOCK      rNull, rFull;
4615   ULONG         timeout, handle_type, flag_word;
4616   APIRET        rc;
4617   int           blocking, shared;
4618   static int	use_my_flock = -1;
4619 
4620   if (use_my_flock == -1) {
4621    MUTEX_LOCK(&perlos2_state_mutex);
4622    if (use_my_flock == -1) {
4623     char *s = getenv("USE_PERL_FLOCK");
4624     if (s)
4625 	use_my_flock = atoi(s);
4626     else
4627 	use_my_flock = 1;
4628    }
4629    MUTEX_UNLOCK(&perlos2_state_mutex);
4630   }
4631   if (!(_emx_env & 0x200) || !use_my_flock)
4632     return flock(handle, o);	/* Delegate to EMX. */
4633 
4634                                         /* is this a file? */
4635   if ((DosQueryHType(handle, &handle_type, &flag_word) != 0) ||
4636       (handle_type & 0xFF))
4637   {
4638     errno = EBADF;
4639     return -1;
4640   }
4641                                         /* set lock/unlock ranges */
4642   rNull.lOffset = rNull.lRange = rFull.lOffset = 0;
4643   rFull.lRange = 0x7FFFFFFF;
4644                                         /* set timeout for blocking */
4645   timeout = ((blocking = !(o & LOCK_NB))) ? 100 : 1;
4646                                         /* shared or exclusive? */
4647   shared = (o & LOCK_SH) ? 1 : 0;
4648                                         /* do not block the unlock */
4649   if (o & (LOCK_UN | LOCK_SH | LOCK_EX)) {
4650     rc = DosSetFileLocks(handle, &rFull, &rNull, timeout, shared);
4651     switch (rc) {
4652       case 0:
4653         errno = 0;
4654         return 0;
4655       case ERROR_INVALID_HANDLE:
4656         errno = EBADF;
4657         return -1;
4658       case ERROR_SHARING_BUFFER_EXCEEDED:
4659         errno = ENOLCK;
4660         return -1;
4661       case ERROR_LOCK_VIOLATION:
4662         break;                          /* not an error */
4663       case ERROR_INVALID_PARAMETER:
4664       case ERROR_ATOMIC_LOCK_NOT_SUPPORTED:
4665       case ERROR_READ_LOCKS_NOT_SUPPORTED:
4666         errno = EINVAL;
4667         return -1;
4668       case ERROR_INTERRUPT:
4669         errno = EINTR;
4670         return -1;
4671       default:
4672         errno = EINVAL;
4673         return -1;
4674     }
4675   }
4676                                         /* lock may block */
4677   if (o & (LOCK_SH | LOCK_EX)) {
4678                                         /* for blocking operations */
4679     for (;;) {
4680       rc =
4681         DosSetFileLocks(
4682                 handle,
4683                 &rNull,
4684                 &rFull,
4685                 timeout,
4686                 shared
4687         );
4688       switch (rc) {
4689         case 0:
4690           errno = 0;
4691           return 0;
4692         case ERROR_INVALID_HANDLE:
4693           errno = EBADF;
4694           return -1;
4695         case ERROR_SHARING_BUFFER_EXCEEDED:
4696           errno = ENOLCK;
4697           return -1;
4698         case ERROR_LOCK_VIOLATION:
4699           if (!blocking) {
4700             errno = EWOULDBLOCK;
4701             return -1;
4702           }
4703           break;
4704         case ERROR_INVALID_PARAMETER:
4705         case ERROR_ATOMIC_LOCK_NOT_SUPPORTED:
4706         case ERROR_READ_LOCKS_NOT_SUPPORTED:
4707           errno = EINVAL;
4708           return -1;
4709         case ERROR_INTERRUPT:
4710           errno = EINTR;
4711           return -1;
4712         default:
4713           errno = EINVAL;
4714           return -1;
4715       }
4716                                         /* give away timeslice */
4717       DosSleep(1);
4718     }
4719   }
4720 
4721   errno = 0;
4722   return 0;
4723 }
4724 
4725 static int
4726 use_my_pwent(void)
4727 {
4728   if (_my_pwent == -1) {
4729     char *s = getenv("USE_PERL_PWENT");
4730     if (s)
4731 	_my_pwent = atoi(s);
4732     else
4733 	_my_pwent = 1;
4734   }
4735   return _my_pwent;
4736 }
4737 
4738 #undef setpwent
4739 #undef getpwent
4740 #undef endpwent
4741 
4742 void
4743 my_setpwent(void)
4744 {
4745   if (!use_my_pwent()) {
4746     setpwent();			/* Delegate to EMX. */
4747     return;
4748   }
4749   pwent_cnt = 0;
4750 }
4751 
4752 void
4753 my_endpwent(void)
4754 {
4755   if (!use_my_pwent()) {
4756     endpwent();			/* Delegate to EMX. */
4757     return;
4758   }
4759 }
4760 
4761 struct passwd *
4762 my_getpwent (void)
4763 {
4764   if (!use_my_pwent())
4765     return getpwent();			/* Delegate to EMX. */
4766   if (pwent_cnt++)
4767     return 0;				/* Return one entry only */
4768   return getpwuid(0);
4769 }
4770 
4771 void
4772 setgrent(void)
4773 {
4774   grent_cnt = 0;
4775 }
4776 
4777 void
4778 endgrent(void)
4779 {
4780 }
4781 
4782 struct group *
4783 getgrent (void)
4784 {
4785   if (grent_cnt++)
4786     return 0;				/* Return one entry only */
4787   return getgrgid(0);
4788 }
4789 
4790 #undef getpwuid
4791 #undef getpwnam
4792 
4793 /* Too long to be a crypt() of anything, so it is not-a-valid pw_passwd. */
4794 static const char pw_p[] = "Jf0Wb/BzMFvk7K7lrzK";
4795 
4796 static struct passwd *
4797 passw_wrap(struct passwd *p)
4798 {
4799     char *s;
4800 
4801     if (!p || (p->pw_passwd && *p->pw_passwd)) /* Not a dangerous password */
4802 	return p;
4803     pw = *p;
4804     s = getenv("PW_PASSWD");
4805     if (!s)
4806 	s = (char*)pw_p;		/* Make match impossible */
4807 
4808     pw.pw_passwd = s;
4809     return &pw;
4810 }
4811 
4812 struct passwd *
4813 my_getpwuid (uid_t id)
4814 {
4815     return passw_wrap(getpwuid(id));
4816 }
4817 
4818 struct passwd *
4819 my_getpwnam (__const__ char *n)
4820 {
4821     return passw_wrap(getpwnam(n));
4822 }
4823 
4824 char *
4825 gcvt_os2 (double value, int digits, char *buffer)
4826 {
4827   double absv = value > 0 ? value : -value;
4828   /* EMX implementation is lousy between 0.1 and 0.0001 (uses exponents below
4829      0.1), 1-digit stuff is ok below 0.001; multi-digit below 0.0001. */
4830   int buggy;
4831 
4832   absv *= 10000;
4833   buggy = (absv < 1000 && (absv >= 10 || (absv > 1 && floor(absv) != absv)));
4834 
4835   if (buggy) {
4836     char pat[12];
4837 
4838     sprintf(pat, "%%.%dg", digits);
4839     sprintf(buffer, pat, value);
4840     return buffer;
4841   }
4842   return gcvt (value, digits, buffer);
4843 }
4844 
4845 #undef fork
4846 int fork_with_resources()
4847 {
4848 #if (defined(USE_5005THREADS) || defined(USE_ITHREADS)) && !defined(USE_SLOW_THREAD_SPECIFIC)
4849   dTHX;
4850   void *ctx = PERL_GET_CONTEXT;
4851 #endif
4852   unsigned fpflag = _control87(0,0);
4853   int rc = fork();
4854 
4855   if (rc == 0) {			/* child */
4856 #if (defined(USE_5005THREADS) || defined(USE_ITHREADS)) && !defined(USE_SLOW_THREAD_SPECIFIC)
4857     ALLOC_THREAD_KEY;			/* Acquire the thread-local memory */
4858     PERL_SET_CONTEXT(ctx);		/* Reinit the thread-local memory */
4859 #endif
4860 
4861     {					/* Reload loaded-on-demand DLLs */
4862 	struct dll_handle_t *dlls = dll_handles;
4863 
4864 	while (dlls->modname) {
4865 	    char dllname[260], fail[260];
4866 	    ULONG rc;
4867 
4868 	    if (!dlls->handle) {	/* Was not loaded */
4869 		dlls++;
4870 		continue;
4871 	    }
4872 	    /* It was loaded in the parent.  We need to reload it. */
4873 
4874 	    rc = DosQueryModuleName(dlls->handle, sizeof(dllname), dllname);
4875 	    if (rc) {
4876 		Perl_warn_nocontext("Can't find DLL name for the module `%s' by the handle %d, rc=%lu=%#lx",
4877 				    dlls->modname, (int)dlls->handle, rc, rc);
4878 		dlls++;
4879 		continue;
4880 	    }
4881 	    rc = DosLoadModule(fail, sizeof fail, dllname, &dlls->handle);
4882 	    if (rc)
4883 		Perl_warn_nocontext("Can't load DLL `%s', possible problematic module `%s'",
4884 				    dllname, fail);
4885 	    dlls++;
4886 	}
4887     }
4888 
4889     {					/* Support message queue etc. */
4890 	os2_mytype = my_type();
4891 	/* Apparently, subprocesses (in particular, fork()) do not
4892 	   inherit the morphed state, so os2_mytype is the same as
4893 	   os2_mytype_ini. */
4894 
4895 	if (Perl_os2_initial_mode != -1
4896 	    && Perl_os2_initial_mode != os2_mytype) {
4897 					/* XXXX ??? */
4898 	}
4899     }
4900     if (Perl_HAB_set)
4901 	(void)_obtain_Perl_HAB;
4902     if (Perl_hmq_refcnt) {
4903 	if (my_type() != 3)
4904 	    my_type_set(3);
4905 	Create_HMQ(Perl_hmq_servers != 0,
4906 		   "Cannot create a message queue on fork");
4907     }
4908 
4909     /* We may have loaded some modules */
4910     _control87(fpflag, MCW_EM); /* Some modules reset FP flags on (un)load */
4911   }
4912   return rc;
4913 }
4914 
4915 /* APIRET  APIENTRY DosGetInfoSeg(PSEL pselGlobal, PSEL pselLocal); */
4916 
4917 ULONG _THUNK_FUNCTION(Dos16GetInfoSeg)(USHORT *pGlobal, USHORT *pLocal);
4918 
4919 APIRET  APIENTRY
4920 myDosGetInfoSeg(PGINFOSEG *pGlobal, PLINFOSEG *pLocal)
4921 {
4922     APIRET rc;
4923     USHORT gSel, lSel;		/* Will not cross 64K boundary */
4924 
4925     rc = ((USHORT)
4926           (_THUNK_PROLOG (4+4);
4927            _THUNK_FLAT (&gSel);
4928            _THUNK_FLAT (&lSel);
4929            _THUNK_CALL (Dos16GetInfoSeg)));
4930     if (rc)
4931 	return rc;
4932     *pGlobal = MAKEPGINFOSEG(gSel);
4933     *pLocal  = MAKEPLINFOSEG(lSel);
4934     return rc;
4935 }
4936 
4937 static void
4938 GetInfoTables(void)
4939 {
4940     ULONG rc = 0;
4941 
4942     MUTEX_LOCK(&perlos2_state_mutex);
4943     if (!gTable)
4944       rc = myDosGetInfoSeg(&gTable, &lTable);
4945     MUTEX_UNLOCK(&perlos2_state_mutex);
4946     os2cp_croak(rc, "Dos16GetInfoSeg");
4947 }
4948 
4949 ULONG
4950 msCounter(void)
4951 {				/* XXXX Is not lTable thread-specific? */
4952   if (!gTable)
4953     GetInfoTables();
4954   return gTable->SIS_MsCount;
4955 }
4956 
4957 ULONG
4958 InfoTable(int local)
4959 {
4960   if (!gTable)
4961     GetInfoTables();
4962   return local ? (ULONG)lTable : (ULONG)gTable;
4963 }
4964