xref: /openbsd/gnu/usr.bin/perl/os2/os2.c (revision 09467b48)
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 	    Newxz(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 = CheckOSError(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     Newx(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 
974 extern ULONG _emx_exception (	EXCEPTIONREPORTRECORD *,
975 				EXCEPTIONREGISTRATIONRECORD *,
976                                 CONTEXTRECORD *,
977                                 void *);
978 
979 int
980 do_spawn_ve(pTHX_ SV *really, const char **argv, U32 flag, U32 execf, char *inicmd, U32 addflag)
981 {
982 	int trueflag = flag;
983 	int rc, pass = 1;
984 	char *real_name = NULL;			/* Shut down the warning */
985 	char const * args[4];
986 	static const char * const fargs[4]
987 	    = { "/bin/sh", "-c", "\"$@\"", "spawn-via-shell", };
988 	const char * const *argsp = fargs;
989 	int nargs = 4;
990 	int force_shell;
991  	int new_stderr = -1, nostderr = 0;
992 	int fl_stderr = 0;
993 	STRLEN n_a;
994 	char *buf;
995 	PerlIO *file;
996 
997 	if (flag == P_WAIT)
998 		flag = P_NOWAIT;
999 	if (really) {
1000 	    real_name = SvPV(really, n_a);
1001 	    real_name = savepv(real_name);
1002 	    SAVEFREEPV(real_name);
1003 	    if (!*real_name)
1004 		really = NULL;
1005 	}
1006 
1007       retry:
1008 	if (strEQ(argv[0],"/bin/sh"))
1009 	    argv[0] = PL_sh_path;
1010 
1011 	/* We should check PERL_SH* and PERLLIB_* as well? */
1012 	if (!really || pass >= 2)
1013 	    real_name = argv[0];
1014 	if (real_name[0] != '/' && real_name[0] != '\\'
1015 	    && !(real_name[0] && real_name[1] == ':'
1016 		 && (real_name[2] == '/' || real_name[2] != '\\'))
1017 	    ) /* will spawnvp use PATH? */
1018 	    TAINT_ENV();	/* testing IFS here is overkill, probably */
1019 
1020       reread:
1021 	force_shell = 0;
1022 	if (_emx_env & 0x200) { /* OS/2. */
1023 	    int type = file_type(real_name);
1024 	  type_again:
1025 	    if (type == -1) {		/* Not found */
1026 		errno = ENOENT;
1027 		rc = -1;
1028 		goto do_script;
1029 	    }
1030 	    else if (type == -2) {		/* Not an EXE */
1031 		errno = ENOEXEC;
1032 		rc = -1;
1033 		goto do_script;
1034 	    }
1035 	    else if (type == -3) {		/* Is a directory? */
1036 		/* Special-case this */
1037 		char tbuf[512];
1038 		int l = strlen(real_name);
1039 
1040 		if (l + 5 <= sizeof tbuf) {
1041 		    strcpy(tbuf, real_name);
1042 		    strcpy(tbuf + l, ".exe");
1043 		    type = file_type(tbuf);
1044 		    if (type >= -3)
1045 			goto type_again;
1046 		}
1047 
1048 		errno = ENOEXEC;
1049 		rc = -1;
1050 		goto do_script;
1051 	    }
1052 	    switch (type & 7) {
1053 		/* Ignore WINDOWCOMPAT and FAPI, start them the same type we are. */
1054 	    case FAPPTYP_WINDOWAPI:
1055 	    {	/* Apparently, kids are started basing on startup type, not the morphed type */
1056 		if (os2_mytype != 3) {	/* not PM */
1057 		    if (flag == P_NOWAIT)
1058 			flag = P_PM;
1059 		    else if ((flag & 7) != P_PM && (flag & 7) != P_SESSION && ckWARN(WARN_EXEC))
1060 			Perl_warner(aTHX_ packWARN(WARN_EXEC), "Starting PM process with flag=%d, mytype=%d",
1061 			     flag, os2_mytype);
1062 		}
1063 	    }
1064 	    break;
1065 	    case FAPPTYP_NOTWINDOWCOMPAT:
1066 	    {
1067 		if (os2_mytype != 0) {	/* not full screen */
1068 		    if (flag == P_NOWAIT)
1069 			flag = P_SESSION;
1070 		    else if ((flag & 7) != P_SESSION && ckWARN(WARN_EXEC))
1071 			Perl_warner(aTHX_ packWARN(WARN_EXEC), "Starting Full Screen process with flag=%d, mytype=%d",
1072 			     flag, os2_mytype);
1073 		}
1074 	    }
1075 	    break;
1076 	    case FAPPTYP_NOTSPEC:
1077 		/* Let the shell handle this... */
1078 		force_shell = 1;
1079 		buf = "";		/* Pacify a warning */
1080 		file = 0;		/* Pacify a warning */
1081 		goto doshell_args;
1082 		break;
1083 	    }
1084 	}
1085 
1086 	if (addflag) {
1087 	    addflag = 0;
1088 	    new_stderr = dup(2);		/* Preserve stderr */
1089 	    if (new_stderr == -1) {
1090 		if (errno == EBADF)
1091 		    nostderr = 1;
1092 		else {
1093 		    rc = -1;
1094 		    goto finish;
1095 		}
1096 	    } else
1097 		fl_stderr = fcntl(2, F_GETFD);
1098 	    rc = dup2(1,2);
1099 	    if (rc == -1)
1100 		goto finish;
1101 	    fcntl(new_stderr, F_SETFD, FD_CLOEXEC);
1102 	}
1103 
1104 #if 0
1105 	rc = result(aTHX_ trueflag, spawnvp(flag,real_name,argv));
1106 #else
1107 	if (execf == EXECF_TRUEEXEC)
1108 	    rc = execvp(real_name,argv);
1109 	else if (execf == EXECF_EXEC)
1110 	    rc = spawnvp(trueflag | P_OVERLAY,real_name,argv);
1111 	else if (execf == EXECF_SPAWN_NOWAIT)
1112 	    rc = spawnvp(flag,real_name,argv);
1113         else if (execf == EXECF_SYNC)
1114 	    rc = spawnvp(trueflag,real_name,argv);
1115         else				/* EXECF_SPAWN, EXECF_SPAWN_BYFLAG */
1116 	    rc = result(aTHX_ trueflag,
1117 			spawnvp(flag,real_name,argv));
1118 #endif
1119 	if (rc < 0 && pass == 1) {
1120 	      do_script:
1121 	  if (real_name == argv[0]) {
1122 	    int err = errno;
1123 
1124 	    if (err == ENOENT || err == ENOEXEC) {
1125 		/* No such file, or is a script. */
1126 		/* Try adding script extensions to the file name, and
1127 		   search on PATH. */
1128 		char *scr = find_script(argv[0], TRUE, NULL, 0);
1129 
1130 		if (scr) {
1131 		    char *s = 0, *s1;
1132 		    SV *scrsv = sv_2mortal(newSVpv(scr, 0));
1133 		    SV *bufsv = sv_newmortal();
1134 
1135                     Safefree(scr);
1136 		    scr = SvPV(scrsv, n_a); /* free()ed later */
1137 
1138 		    file = PerlIO_open(scr, "r");
1139 		    argv[0] = scr;
1140 		    if (!file)
1141 			goto panic_file;
1142 
1143 		    buf = sv_gets(bufsv, file, 0 /* No append */);
1144 		    if (!buf)
1145 			buf = "";	/* XXX Needed? */
1146 		    if (!buf[0]) {	/* Empty... */
1147                         struct stat statbuf;
1148 			PerlIO_close(file);
1149 			/* Special case: maybe from -Zexe build, so
1150 			   there is an executable around (contrary to
1151 			   documentation, DosQueryAppType sometimes (?)
1152 			   does not append ".exe", so we could have
1153 			   reached this place). */
1154 			sv_catpvs(scrsv, ".exe");
1155 	                argv[0] = scr = SvPV(scrsv, n_a);	/* Reload */
1156                         if (PerlLIO_stat(scr,&statbuf) >= 0
1157                             && !S_ISDIR(statbuf.st_mode)) {	/* Found */
1158 				real_name = scr;
1159 				pass++;
1160 				goto reread;
1161 			} else {		/* Restore */
1162 				SvCUR_set(scrsv, SvCUR(scrsv) - 4);
1163 				*SvEND(scrsv) = 0;
1164 			}
1165 		    }
1166 		    if (PerlIO_close(file) != 0) { /* Failure */
1167 		      panic_file:
1168 			if (ckWARN(WARN_EXEC))
1169 			   Perl_warner(aTHX_ packWARN(WARN_EXEC), "Error reading \"%s\": %s",
1170 			     scr, Strerror(errno));
1171 			buf = "";	/* Not #! */
1172 			goto doshell_args;
1173 		    }
1174 		    if (buf[0] == '#') {
1175 			if (buf[1] == '!')
1176 			    s = buf + 2;
1177 		    } else if (buf[0] == 'e') {
1178 			if (strBEGINs(buf, "extproc")
1179 			    && isSPACE(buf[7]))
1180 			    s = buf + 8;
1181 		    } else if (buf[0] == 'E') {
1182 			if (strBEGINs(buf, "EXTPROC")
1183 			    && isSPACE(buf[7]))
1184 			    s = buf + 8;
1185 		    }
1186 		    if (!s) {
1187 			buf = "";	/* Not #! */
1188 			goto doshell_args;
1189 		    }
1190 
1191 		    s1 = s;
1192 		    nargs = 0;
1193 		    argsp = args;
1194 		    while (1) {
1195 			/* Do better than pdksh: allow a few args,
1196 			   strip trailing whitespace.  */
1197 			while (isSPACE(*s))
1198 			    s++;
1199 			if (*s == 0)
1200 			    break;
1201 			if (nargs == 4) {
1202 			    nargs = -1;
1203 			    break;
1204 			}
1205 			args[nargs++] = s;
1206 			while (*s && !isSPACE(*s))
1207 			    s++;
1208 			if (*s == 0)
1209 			    break;
1210 			*s++ = 0;
1211 		    }
1212 		    if (nargs == -1) {
1213 			Perl_warner(aTHX_ packWARN(WARN_EXEC), "Too many args on %.*s line of \"%s\"",
1214 			     s1 - buf, buf, scr);
1215 			nargs = 4;
1216 			argsp = fargs;
1217 		    }
1218 		    /* Can jump from far, buf/file invalid if force_shell: */
1219 		  doshell_args:
1220 		    {
1221 			char **a = argv;
1222 			const char *exec_args[2];
1223 
1224 			if (force_shell
1225 			    || (!buf[0] && file)) { /* File without magic */
1226 			    /* In fact we tried all what pdksh would
1227 			       try.  There is no point in calling
1228 			       pdksh, we may just emulate its logic. */
1229 			    char *shell = getenv("EXECSHELL");
1230 			    char *shell_opt = NULL;
1231 
1232 			    if (!shell) {
1233 				char *s;
1234 
1235 				shell_opt = "/c";
1236 				shell = getenv("OS2_SHELL");
1237 				if (inicmd) { /* No spaces at start! */
1238 				    s = inicmd;
1239 				    while (*s && !isSPACE(*s)) {
1240 					if (*s++ == '/') {
1241 					    inicmd = NULL; /* Cannot use */
1242 					    break;
1243 					}
1244 				    }
1245 				}
1246 				if (!inicmd) {
1247 				    s = argv[0];
1248 				    while (*s) {
1249 					/* Dosish shells will choke on slashes
1250 					   in paths, fortunately, this is
1251 					   important for zeroth arg only. */
1252 					if (*s == '/')
1253 					    *s = '\\';
1254 					s++;
1255 				    }
1256 				}
1257 			    }
1258 			    /* If EXECSHELL is set, we do not set */
1259 
1260 			    if (!shell)
1261 				shell = ((_emx_env & 0x200)
1262 					 ? "c:/os2/cmd.exe"
1263 					 : "c:/command.com");
1264 			    nargs = shell_opt ? 2 : 1;	/* shell file args */
1265 			    exec_args[0] = shell;
1266 			    exec_args[1] = shell_opt;
1267 			    argsp = exec_args;
1268 			    if (nargs == 2 && inicmd) {
1269 				/* Use the original cmd line */
1270 				/* XXXX This is good only until we refuse
1271 				        quoted arguments... */
1272 				argv[0] = inicmd;
1273 				argv[1] = NULL;
1274 			    }
1275 			} else if (!buf[0] && inicmd) { /* No file */
1276 			    /* Start with the original cmdline. */
1277 			    /* XXXX This is good only until we refuse
1278 			            quoted arguments... */
1279 
1280 			    argv[0] = inicmd;
1281 			    argv[1] = NULL;
1282 			    nargs = 2;	/* shell -c */
1283 			}
1284 
1285 			while (a[1])		/* Get to the end */
1286 			    a++;
1287 			a++;			/* Copy finil NULL too */
1288 			while (a >= argv) {
1289 			    *(a + nargs) = *a;	/* argv was preallocated to be
1290 						   long enough. */
1291 			    a--;
1292 			}
1293 			while (--nargs >= 0) /* XXXX Discard const... */
1294 			    argv[nargs] = (char*)argsp[nargs];
1295 			/* Enable pathless exec if #! (as pdksh). */
1296 			pass = (buf[0] == '#' ? 2 : 3);
1297 			goto retry;
1298 		    }
1299 		}
1300 		/* Not found: restore errno */
1301 		errno = err;
1302 	    }
1303 	  } else if (errno == ENOEXEC) { /* Cannot transfer `real_name' via shell. */
1304 		if (rc < 0 && ckWARN(WARN_EXEC))
1305 		    Perl_warner(aTHX_ packWARN(WARN_EXEC), "Can't %s script `%s' with ARGV[0] being `%s'",
1306 			 ((execf != EXECF_EXEC && execf != EXECF_TRUEEXEC)
1307 			  ? "spawn" : "exec"),
1308 			 real_name, argv[0]);
1309 		goto warned;
1310 	  } else if (errno == ENOENT) { /* Cannot transfer `real_name' via shell. */
1311 		if (rc < 0 && ckWARN(WARN_EXEC))
1312 		    Perl_warner(aTHX_ packWARN(WARN_EXEC), "Can't %s `%s' with ARGV[0] being `%s' (looking for executables only, not found)",
1313 			 ((execf != EXECF_EXEC && execf != EXECF_TRUEEXEC)
1314 			  ? "spawn" : "exec"),
1315 			 real_name, argv[0]);
1316 		goto warned;
1317 	  }
1318 	} else if (rc < 0 && pass == 2 && errno == ENOENT) { /* File not found */
1319 	    char *no_dir = strrchr(argv[0], '/');
1320 
1321 	    /* Do as pdksh port does: if not found with /, try without
1322 	       path. */
1323 	    if (no_dir) {
1324 		argv[0] = no_dir + 1;
1325 		pass++;
1326 		goto retry;
1327 	    }
1328 	}
1329 	if (rc < 0 && ckWARN(WARN_EXEC))
1330 	    Perl_warner(aTHX_ packWARN(WARN_EXEC), "Can't %s \"%s\": %s\n",
1331 		 ((execf != EXECF_EXEC && execf != EXECF_TRUEEXEC)
1332 		  ? "spawn" : "exec"),
1333 		 real_name, Strerror(errno));
1334       warned:
1335 	if (rc < 0 && (execf != EXECF_SPAWN_NOWAIT)
1336 	    && ((trueflag & 0xFF) == P_WAIT))
1337 	    rc = -1;
1338 
1339   finish:
1340     if (new_stderr != -1) {	/* How can we use error codes? */
1341 	dup2(new_stderr, 2);
1342 	close(new_stderr);
1343 	fcntl(2, F_SETFD, fl_stderr);
1344     } else if (nostderr)
1345        close(2);
1346     return rc;
1347 }
1348 
1349 /* Try converting 1-arg form to (usually shell-less) multi-arg form. */
1350 int
1351 do_spawn3(pTHX_ char *cmd, int execf, int flag)
1352 {
1353     char **argv, **a;
1354     char *s;
1355     char *shell, *copt, *news = NULL;
1356     int rc, seenspace = 0, mergestderr = 0;
1357 
1358     ENTER;
1359 #ifdef TRYSHELL
1360     if ((shell = getenv("EMXSHELL")) != NULL)
1361     	copt = "-c";
1362     else if ((shell = getenv("SHELL")) != NULL)
1363     	copt = "-c";
1364     else if ((shell = getenv("COMSPEC")) != NULL)
1365     	copt = "/C";
1366     else
1367     	shell = "cmd.exe";
1368 #else
1369     /* Consensus on perl5-porters is that it is _very_ important to
1370        have a shell which will not change between computers with the
1371        same architecture, to avoid "action on a distance".
1372        And to have simple build, this shell should be sh. */
1373     shell = PL_sh_path;
1374     copt = "-c";
1375 #endif
1376 
1377     while (*cmd && isSPACE(*cmd))
1378 	cmd++;
1379 
1380     if (strBEGINs(cmd,"/bin/sh") && isSPACE(cmd[7])) {
1381 	STRLEN l = strlen(PL_sh_path);
1382 
1383 	Newx(news, strlen(cmd) - 7 + l + 1, char);
1384 	strcpy(news, PL_sh_path);
1385 	strcpy(news + l, cmd + 7);
1386 	cmd = news;
1387     }
1388 
1389     /* save an extra exec if possible */
1390     /* see if there are shell metacharacters in it */
1391 
1392     if (*cmd == '.' && isSPACE(cmd[1]))
1393 	goto doshell;
1394 
1395     if (strBEGINs(cmd,"exec") && isSPACE(cmd[4]))
1396 	goto doshell;
1397 
1398     for (s = cmd; *s && isALPHA(*s); s++) ;	/* catch VAR=val gizmo */
1399     if (*s == '=')
1400 	goto doshell;
1401 
1402     for (s = cmd; *s; s++) {
1403 	if (*s != ' ' && !isALPHA(*s) && strchr("$&*(){}[]'\";\\|?<>~`\n",*s)) {
1404 	    if (*s == '\n' && s[1] == '\0') {
1405 		*s = '\0';
1406 		break;
1407 	    } else if (*s == '\\' && !seenspace) {
1408 		continue;		/* Allow backslashes in names */
1409 	    } else if (*s == '>' && s >= cmd + 3
1410 			&& s[-1] == '2' && s[1] == '&' && s[2] == '1'
1411 			&& isSPACE(s[-2]) ) {
1412 		char *t = s + 3;
1413 
1414 		while (*t && isSPACE(*t))
1415 		    t++;
1416 		if (!*t) {
1417 		    s[-2] = '\0';
1418 		    mergestderr = 1;
1419 		    break;		/* Allow 2>&1 as the last thing */
1420 		}
1421 	    }
1422 	    /* We do not convert this to do_spawn_ve since shell
1423 	       should be smart enough to start itself gloriously. */
1424 	  doshell:
1425 	    if (execf == EXECF_TRUEEXEC)
1426                 rc = execl(shell,shell,copt,cmd,(char*)0);
1427 	    else if (execf == EXECF_EXEC)
1428                 rc = spawnl(P_OVERLAY,shell,shell,copt,cmd,(char*)0);
1429 	    else if (execf == EXECF_SPAWN_NOWAIT)
1430                 rc = spawnl(P_NOWAIT,shell,shell,copt,cmd,(char*)0);
1431 	    else if (execf == EXECF_SPAWN_BYFLAG)
1432                 rc = spawnl(flag,shell,shell,copt,cmd,(char*)0);
1433 	    else {
1434 		/* In the ak code internal P_NOWAIT is P_WAIT ??? */
1435 		if (execf == EXECF_SYNC)
1436 		   rc = spawnl(P_WAIT,shell,shell,copt,cmd,(char*)0);
1437 		else
1438 		   rc = result(aTHX_ P_WAIT,
1439 			       spawnl(P_NOWAIT,shell,shell,copt,cmd,(char*)0));
1440 		if (rc < 0 && ckWARN(WARN_EXEC))
1441 		    Perl_warner(aTHX_ packWARN(WARN_EXEC), "Can't %s \"%s\": %s",
1442 			 (execf == EXECF_SPAWN ? "spawn" : "exec"),
1443 			 shell, Strerror(errno));
1444 		if (rc < 0)
1445 		    rc = -1;
1446 	    }
1447 	    if (news)
1448 		Safefree(news);
1449 	    goto leave;
1450 	} else if (*s == ' ' || *s == '\t') {
1451 	    seenspace = 1;
1452 	}
1453     }
1454 
1455     /* cmd="a" may lead to "sh", "-c", "\"$@\"", "a", "a.cmd", NULL */
1456     Newx(argv, (s - cmd + 11) / 2, char*);
1457     SAVEFREEPV(argv);
1458     cmd = savepvn(cmd, s-cmd);
1459     SAVEFREEPV(cmd);
1460     a = argv;
1461     for (s = cmd; *s;) {
1462 	while (*s && isSPACE(*s)) s++;
1463 	if (*s)
1464 	    *(a++) = s;
1465 	while (*s && !isSPACE(*s)) s++;
1466 	if (*s)
1467 	    *s++ = '\0';
1468     }
1469     *a = NULL;
1470     if (argv[0])
1471 	rc = do_spawn_ve(aTHX_ NULL, argv, flag, execf, cmd, mergestderr);
1472     else
1473     	rc = -1;
1474     if (news)
1475 	Safefree(news);
1476 leave:
1477     LEAVE;
1478     return rc;
1479 }
1480 
1481 #define ASPAWN_WAIT	0
1482 #define ASPAWN_EXEC	1
1483 #define ASPAWN_NOWAIT	2
1484 
1485 /* Array spawn/exec.  */
1486 int
1487 os2_aspawn_4(pTHX_ SV *really, SV **args, I32 cnt, int execing)
1488 {
1489     SV **argp = (SV **)args;
1490     SV **last = argp + cnt;
1491     char **argv, **a;
1492     int rc;
1493     int flag = P_WAIT, flag_set = 0;
1494     STRLEN n_a;
1495 
1496     ENTER;
1497     if (cnt) {
1498 	Newx(argv, cnt + 3, char*); /* 3 extra to expand #! */
1499 	SAVEFREEPV(argv);
1500 	a = argv;
1501 
1502 	if (cnt > 1 && SvNIOKp(*argp) && !SvPOKp(*argp)) {
1503 	    flag = SvIVx(*argp);
1504 	    flag_set = 1;
1505 	} else
1506 	    --argp;
1507 
1508 	while (++argp < last) {
1509 	    if (*argp) {
1510 		char *arg = SvPVx(*argp, n_a);
1511 		arg = savepv(arg);
1512 		SAVEFREEPV(arg);
1513 		*a++ = arg;
1514 	    } else
1515 		*a++ = "";
1516 	}
1517 	*a = NULL;
1518 
1519 	if ( flag_set && (a == argv + 1)
1520 	     && !really && execing == ASPAWN_WAIT ) { 		/* One arg? */
1521 	    rc = do_spawn3(aTHX_ a[-1], EXECF_SPAWN_BYFLAG, flag);
1522 	} else {
1523 	    const int execf[3] = {EXECF_SPAWN, EXECF_EXEC, EXECF_SPAWN_NOWAIT};
1524 
1525 	    rc = do_spawn_ve(aTHX_ really, argv, flag, execf[execing], NULL, 0);
1526 	}
1527     } else
1528     	rc = -1;
1529     LEAVE;
1530     return rc;
1531 }
1532 
1533 /* Array spawn.  */
1534 int
1535 os2_do_aspawn(pTHX_ SV *really, SV **vmark, SV **vsp)
1536 {
1537     return os2_aspawn_4(aTHX_ really, vmark + 1, vsp - vmark, ASPAWN_WAIT);
1538 }
1539 
1540 /* Array exec.  */
1541 bool
1542 Perl_do_aexec(pTHX_ SV* really, SV** vmark, SV** vsp)
1543 {
1544     return os2_aspawn_4(aTHX_ really, vmark + 1, vsp - vmark, ASPAWN_EXEC);
1545 }
1546 
1547 int
1548 os2_do_spawn(pTHX_ char *cmd)
1549 {
1550     return do_spawn3(aTHX_ cmd, EXECF_SPAWN, 0);
1551 }
1552 
1553 int
1554 do_spawn_nowait(pTHX_ char *cmd)
1555 {
1556     return do_spawn3(aTHX_ cmd, EXECF_SPAWN_NOWAIT,0);
1557 }
1558 
1559 bool
1560 Perl_do_exec(pTHX_ const char *cmd)
1561 {
1562     do_spawn3(aTHX_ cmd, EXECF_EXEC, 0);
1563     return FALSE;
1564 }
1565 
1566 bool
1567 os2exec(pTHX_ char *cmd)
1568 {
1569     return do_spawn3(aTHX_ cmd, EXECF_TRUEEXEC, 0);
1570 }
1571 
1572 PerlIO *
1573 my_syspopen4(pTHX_ char *cmd, char *mode, I32 cnt, SV** args)
1574 {
1575 #ifndef USE_POPEN
1576     int p[2];
1577     I32 this, that, newfd;
1578     I32 pid;
1579     SV *sv;
1580     int fh_fl = 0;			/* Pacify the warning */
1581 
1582     /* `this' is what we use in the parent, `that' in the child. */
1583     this = (*mode == 'w');
1584     that = !this;
1585     if (TAINTING_get) {
1586 	taint_env();
1587 	taint_proper("Insecure %s%s", "EXEC");
1588     }
1589     if (pipe(p) < 0)
1590 	return NULL;
1591     /* Now we need to spawn the child. */
1592     if (p[this] == (*mode == 'r')) {	/* if fh 0/1 was initially closed. */
1593 	int new = dup(p[this]);
1594 
1595 	if (new == -1)
1596 	    goto closepipes;
1597 	close(p[this]);
1598 	p[this] = new;
1599     }
1600     newfd = dup(*mode == 'r');		/* Preserve std* */
1601     if (newfd == -1) {
1602 	/* This cannot happen due to fh being bad after pipe(), since
1603 	   pipe() should have created fh 0 and 1 even if they were
1604 	   initially closed.  But we closed p[this] before.  */
1605 	if (errno != EBADF) {
1606 	  closepipes:
1607 	    close(p[0]);
1608 	    close(p[1]);
1609 	    return NULL;
1610 	}
1611     } else
1612 	fh_fl = fcntl(*mode == 'r', F_GETFD);
1613     if (p[that] != (*mode == 'r')) {	/* if fh 0/1 was initially closed. */
1614 	dup2(p[that], *mode == 'r');
1615 	close(p[that]);
1616     }
1617     /* Where is `this' and newfd now? */
1618     fcntl(p[this], F_SETFD, FD_CLOEXEC);
1619     if (newfd != -1)
1620 	fcntl(newfd, F_SETFD, FD_CLOEXEC);
1621     if (cnt) {	/* Args: "Real cmd", before first arg, the last, execing */
1622 	pid = os2_aspawn_4(aTHX_ NULL, args, cnt, ASPAWN_NOWAIT);
1623     } else
1624 	pid = do_spawn_nowait(aTHX_ cmd);
1625     if (newfd == -1)
1626 	close(*mode == 'r');		/* It was closed initially */
1627     else if (newfd != (*mode == 'r')) {	/* Probably this check is not needed */
1628 	dup2(newfd, *mode == 'r');	/* Return std* back. */
1629 	close(newfd);
1630 	fcntl(*mode == 'r', F_SETFD, fh_fl);
1631     } else
1632 	fcntl(*mode == 'r', F_SETFD, fh_fl);
1633     if (p[that] == (*mode == 'r'))
1634 	close(p[that]);
1635     if (pid == -1) {
1636 	close(p[this]);
1637 	return NULL;
1638     }
1639     if (p[that] < p[this]) {		/* Make fh as small as possible */
1640 	dup2(p[this], p[that]);
1641 	close(p[this]);
1642 	p[this] = p[that];
1643     }
1644     sv = *av_fetch(PL_fdpid,p[this],TRUE);
1645     (void)SvUPGRADE(sv,SVt_IV);
1646     SvIVX(sv) = pid;
1647     PL_forkprocess = pid;
1648     return PerlIO_fdopen(p[this], mode);
1649 
1650 #else  /* USE_POPEN */
1651 
1652     PerlIO *res;
1653     SV *sv;
1654 
1655     if (cnt)
1656 	Perl_croak(aTHX_ "List form of piped open not implemented");
1657 
1658 #  ifdef TRYSHELL
1659     res = popen(cmd, mode);
1660 #  else
1661     char *shell = getenv("EMXSHELL");
1662 
1663     my_setenv("EMXSHELL", PL_sh_path);
1664     res = popen(cmd, mode);
1665     my_setenv("EMXSHELL", shell);
1666 #  endif
1667     sv = *av_fetch(PL_fdpid, PerlIO_fileno(res), TRUE);
1668     (void)SvUPGRADE(sv,SVt_IV);
1669     SvIVX(sv) = -1;			/* A cooky. */
1670     return res;
1671 
1672 #endif /* USE_POPEN */
1673 
1674 }
1675 
1676 PerlIO *
1677 my_syspopen(pTHX_ char *cmd, char *mode)
1678 {
1679     return my_syspopen4(aTHX_ cmd, mode, 0, NULL);
1680 }
1681 
1682 /******************************************************************/
1683 
1684 #ifndef HAS_FORK
1685 int
1686 fork(void)
1687 {
1688     Perl_croak_nocontext(PL_no_func, "Unsupported function fork");
1689     errno = EINVAL;
1690     return -1;
1691 }
1692 #endif
1693 
1694 /*******************************************************************/
1695 /* not implemented in EMX 0.9d */
1696 
1697 char *	ctermid(char *s)	{ return 0; }
1698 
1699 #ifdef MYTTYNAME /* was not in emx0.9a */
1700 void *	ttyname(x)	{ return 0; }
1701 #endif
1702 
1703 /*****************************************************************************/
1704 /* not implemented in C Set++ */
1705 
1706 #ifndef __EMX__
1707 int	setuid(x)	{ errno = EINVAL; return -1; }
1708 int	setgid(x)	{ errno = EINVAL; return -1; }
1709 #endif
1710 
1711 /*****************************************************************************/
1712 /* stat() hack for char/block device */
1713 
1714 #if OS2_STAT_HACK
1715 
1716 enum os2_stat_extra {	/* EMX 0.9d fix 4 defines up to 0100000 */
1717   os2_stat_archived	= 0x1000000,	/* 0100000000 */
1718   os2_stat_hidden	= 0x2000000,	/* 0200000000 */
1719   os2_stat_system	= 0x4000000,	/* 0400000000 */
1720   os2_stat_force	= 0x8000000,	/* Do not ignore flags on chmod */
1721 };
1722 
1723 #define OS2_STAT_SPECIAL (os2_stat_system | os2_stat_archived | os2_stat_hidden)
1724 
1725 static void
1726 massage_os2_attr(struct stat *st)
1727 {
1728     if ( ((st->st_mode & S_IFMT) != S_IFREG
1729 	  && (st->st_mode & S_IFMT) != S_IFDIR)
1730          || !(st->st_attr & (FILE_ARCHIVED | FILE_HIDDEN | FILE_SYSTEM)))
1731 	return;
1732 
1733     if ( st->st_attr & FILE_ARCHIVED )
1734 	st->st_mode |= (os2_stat_archived | os2_stat_force);
1735     if ( st->st_attr & FILE_HIDDEN )
1736 	st->st_mode |= (os2_stat_hidden | os2_stat_force);
1737     if ( st->st_attr & FILE_SYSTEM )
1738 	st->st_mode |= (os2_stat_system | os2_stat_force);
1739 }
1740 
1741     /* First attempt used DosQueryFSAttach which crashed the system when
1742        used with 5.001. Now just look for /dev/. */
1743 int
1744 os2_stat(const char *name, struct stat *st)
1745 {
1746     static int ino = SHRT_MAX;
1747     STRLEN l = strlen(name);
1748 
1749     if ( ( l < 8 || l > 9) || strnicmp(name, "/dev/", 5) != 0
1750          || (    stricmp(name + 5, "con") != 0
1751 	      && stricmp(name + 5, "tty") != 0
1752 	      && stricmp(name + 5, "nul") != 0
1753 	      && stricmp(name + 5, "null") != 0) ) {
1754 	int s = stat(name, st);
1755 
1756 	if (s)
1757 	    return s;
1758 	massage_os2_attr(st);
1759 	return 0;
1760     }
1761 
1762     memset(st, 0, sizeof *st);
1763     st->st_mode = S_IFCHR|0666;
1764     MUTEX_LOCK(&perlos2_state_mutex);
1765     st->st_ino = (ino-- & 0x7FFF);
1766     MUTEX_UNLOCK(&perlos2_state_mutex);
1767     st->st_nlink = 1;
1768     return 0;
1769 }
1770 
1771 int
1772 os2_fstat(int handle, struct stat *st)
1773 {
1774     int s = fstat(handle, st);
1775 
1776     if (s)
1777 	return s;
1778     massage_os2_attr(st);
1779     return 0;
1780 }
1781 
1782 #undef chmod
1783 int
1784 os2_chmod (const char *name, int pmode)	/* Modelled after EMX src/lib/io/chmod.c */
1785 {
1786     int attr, rc;
1787 
1788     if (!(pmode & os2_stat_force))
1789 	return chmod(name, pmode);
1790 
1791     attr = __chmod (name, 0, 0);           /* Get attributes */
1792     if (attr < 0)
1793 	return -1;
1794     if (pmode & S_IWRITE)
1795 	attr &= ~FILE_READONLY;
1796     else
1797 	attr |= FILE_READONLY;
1798     /* New logic */
1799     attr &= ~(FILE_ARCHIVED | FILE_HIDDEN | FILE_SYSTEM);
1800 
1801     if ( pmode & os2_stat_archived )
1802         attr |= FILE_ARCHIVED;
1803     if ( pmode & os2_stat_hidden )
1804         attr |= FILE_HIDDEN;
1805     if ( pmode & os2_stat_system )
1806         attr |= FILE_SYSTEM;
1807 
1808     rc = __chmod (name, 1, attr);
1809     if (rc >= 0) rc = 0;
1810     return rc;
1811 }
1812 
1813 #endif
1814 
1815 #ifdef USE_PERL_SBRK
1816 
1817 /* SBRK() emulation, mostly moved to malloc.c. */
1818 
1819 void *
1820 sys_alloc(int size) {
1821     void *got;
1822     APIRET rc = DosAllocMem(&got, size, PAG_COMMIT | PAG_WRITE);
1823 
1824     if (rc == ERROR_NOT_ENOUGH_MEMORY) {
1825 	return (void *) -1;
1826     } else if ( rc )
1827 	Perl_croak_nocontext("Got an error from DosAllocMem: %li", (long)rc);
1828     return got;
1829 }
1830 
1831 #endif /* USE_PERL_SBRK */
1832 
1833 /* tmp path */
1834 
1835 const char *tmppath = TMPPATH1;
1836 
1837 void
1838 settmppath()
1839 {
1840     char *p = getenv("TMP"), *tpath;
1841     int len;
1842 
1843     if (!p) p = getenv("TEMP");
1844     if (!p) p = getenv("TMPDIR");
1845     if (!p) return;
1846     len = strlen(p);
1847     tpath = (char *)malloc(len + strlen(TMPPATH1) + 2);
1848     if (tpath) {
1849 	strcpy(tpath, p);
1850 	tpath[len] = '/';
1851 	strcpy(tpath + len + 1, TMPPATH1);
1852 	tmppath = tpath;
1853     }
1854 }
1855 
1856 #include "XSUB.h"
1857 
1858 XS(XS_File__Copy_syscopy)
1859 {
1860     dXSARGS;
1861     if (items < 2 || items > 3)
1862 	Perl_croak_nocontext("Usage: File::Copy::syscopy(src,dst,flag=0)");
1863     {
1864 	STRLEN n_a;
1865 	char *	src = (char *)SvPV(ST(0),n_a);
1866 	char *	dst = (char *)SvPV(ST(1),n_a);
1867 	U32	flag;
1868 	int	RETVAL, rc;
1869 	dXSTARG;
1870 
1871 	if (items < 3)
1872 	    flag = 0;
1873 	else {
1874 	    flag = (unsigned long)SvIV(ST(2));
1875 	}
1876 
1877 	RETVAL = !CheckOSError(DosCopy(src, dst, flag));
1878 	XSprePUSH; PUSHi((IV)RETVAL);
1879     }
1880     XSRETURN(1);
1881 }
1882 
1883 /* APIRET APIENTRY DosReplaceModule (PCSZ pszOld, PCSZ pszNew, PCSZ pszBackup); */
1884 
1885 DeclOSFuncByORD(ULONG,replaceModule,ORD_DosReplaceModule,
1886 		(char *old, char *new, char *backup), (old, new, backup))
1887 
1888 XS(XS_OS2_replaceModule); /* prototype to pass -Wmissing-prototypes */
1889 XS(XS_OS2_replaceModule)
1890 {
1891     dXSARGS;
1892     if (items < 1 || items > 3)
1893 	Perl_croak(aTHX_ "Usage: OS2::replaceModule(target [, source [, backup]])");
1894     {
1895 	char *	target = (char *)SvPV_nolen(ST(0));
1896 	char *	source = (items < 2) ? NULL : (char *)SvPV_nolen(ST(1));
1897 	char *	backup = (items < 3) ? NULL : (char *)SvPV_nolen(ST(2));
1898 
1899 	if (!replaceModule(target, source, backup))
1900 	    croak_with_os2error("replaceModule() error");
1901     }
1902     XSRETURN_YES;
1903 }
1904 
1905 /* APIRET APIENTRY DosPerfSysCall(ULONG ulCommand, ULONG ulParm1,
1906                                   ULONG ulParm2, ULONG ulParm3); */
1907 
1908 DeclOSFuncByORD(ULONG,perfSysCall,ORD_DosPerfSysCall,
1909 		(ULONG ulCommand, ULONG ulParm1, ULONG ulParm2, ULONG ulParm3),
1910 		(ulCommand, ulParm1, ulParm2, ulParm3))
1911 
1912 #ifndef CMD_KI_RDCNT
1913 #  define CMD_KI_RDCNT	0x63
1914 #endif
1915 #ifndef CMD_KI_GETQTY
1916 #  define CMD_KI_GETQTY 0x41
1917 #endif
1918 #ifndef QSV_NUMPROCESSORS
1919 #  define QSV_NUMPROCESSORS         26
1920 #endif
1921 
1922 typedef unsigned long long myCPUUTIL[4];	/* time/idle/busy/intr */
1923 
1924 /*
1925 NO_OUTPUT ULONG
1926 perfSysCall(ULONG ulCommand, ULONG ulParm1, ULONG ulParm2, ULONG ulParm3)
1927     PREINIT:
1928 	ULONG rc;
1929     POSTCALL:
1930 	if (!RETVAL)
1931 	    croak_with_os2error("perfSysCall() error");
1932  */
1933 
1934 static int
1935 numprocessors(void)
1936 {
1937     ULONG res;
1938 
1939     if (DosQuerySysInfo(QSV_NUMPROCESSORS, QSV_NUMPROCESSORS, (PVOID)&res, sizeof(res)))
1940 	return 1;			/* Old system? */
1941     return res;
1942 }
1943 
1944 XS(XS_OS2_perfSysCall); /* prototype to pass -Wmissing-prototypes */
1945 XS(XS_OS2_perfSysCall)
1946 {
1947     dXSARGS;
1948     if (items < 0 || items > 4)
1949 	Perl_croak(aTHX_ "Usage: OS2::perfSysCall(ulCommand = CMD_KI_RDCNT, ulParm1= 0, ulParm2= 0, ulParm3= 0)");
1950     SP -= items;
1951     {
1952 	dXSTARG;
1953 	ULONG RETVAL, ulCommand, ulParm1, ulParm2, ulParm3, res;
1954 	myCPUUTIL u[64];
1955 	int total = 0, tot2 = 0;
1956 
1957 	if (items < 1)
1958 	    ulCommand = CMD_KI_RDCNT;
1959 	else {
1960 	    ulCommand = (ULONG)SvUV(ST(0));
1961 	}
1962 
1963 	if (items < 2) {
1964 	    total = (ulCommand == CMD_KI_RDCNT ? numprocessors() : 0);
1965 	    ulParm1 = (total ? (ULONG)u : 0);
1966 
1967 	    if (total > C_ARRAY_LENGTH(u))
1968 		croak("Unexpected number of processors: %d", total);
1969 	} else {
1970 	    ulParm1 = (ULONG)SvUV(ST(1));
1971 	}
1972 
1973 	if (items < 3) {
1974 	    tot2 = (ulCommand == CMD_KI_GETQTY);
1975 	    ulParm2 = (tot2 ? (ULONG)&res : 0);
1976 	} else {
1977 	    ulParm2 = (ULONG)SvUV(ST(2));
1978 	}
1979 
1980 	if (items < 4)
1981 	    ulParm3 = 0;
1982 	else {
1983 	    ulParm3 = (ULONG)SvUV(ST(3));
1984 	}
1985 
1986 	RETVAL = perfSysCall(ulCommand, ulParm1, ulParm2, ulParm3);
1987 	if (!RETVAL)
1988 	    croak_with_os2error("perfSysCall() error");
1989 	XSprePUSH;
1990 	if (total) {
1991 	    int i,j;
1992 
1993 	    if (GIMME_V != G_ARRAY) {
1994 		PUSHn(u[0][0]);		/* Total ticks on the first processor */
1995 		XSRETURN(1);
1996 	    }
1997 	    EXTEND(SP, 4*total);
1998 	    for (i=0; i < total; i++)
1999 		for (j=0; j < 4; j++)
2000 		    PUSHs(sv_2mortal(newSVnv(u[i][j])));
2001 	    XSRETURN(4*total);
2002 	}
2003 	if (tot2) {
2004 	    PUSHu(res);
2005 	    XSRETURN(1);
2006 	}
2007     }
2008     XSRETURN_EMPTY;
2009 }
2010 
2011 #define PERL_PATCHLEVEL_H_IMPLICIT	/* Do not init local_patches. */
2012 #include "patchlevel.h"
2013 #undef PERL_PATCHLEVEL_H_IMPLICIT
2014 
2015 char *
2016 mod2fname(pTHX_ SV *sv)
2017 {
2018     int pos = 6, len, avlen;
2019     unsigned int sum = 0;
2020     char *s;
2021     STRLEN n_a;
2022 
2023     if (!SvROK(sv)) Perl_croak_nocontext("Not a reference given to mod2fname");
2024     sv = SvRV(sv);
2025     if (SvTYPE(sv) != SVt_PVAV)
2026       Perl_croak_nocontext("Not array reference given to mod2fname");
2027 
2028     avlen = av_tindex((AV*)sv);
2029     if (avlen < 0)
2030       Perl_croak_nocontext("Empty array reference given to mod2fname");
2031 
2032     s = SvPV(*av_fetch((AV*)sv, avlen, FALSE), n_a);
2033     strncpy(fname, s, 8);
2034     len = strlen(s);
2035     if (len < 6) pos = len;
2036     while (*s) {
2037 	sum = 33 * sum + *(s++);	/* Checksumming first chars to
2038 					 * get the capitalization into c.s. */
2039     }
2040     avlen --;
2041     while (avlen >= 0) {
2042 	s = SvPV(*av_fetch((AV*)sv, avlen, FALSE), n_a);
2043 	while (*s) {
2044 	    sum = 33 * sum + *(s++);	/* 7 is primitive mod 13. */
2045 	}
2046 	avlen --;
2047     }
2048    /* We always load modules as *specific* DLLs, and with the full name.
2049       When loading a specific DLL by its full name, one cannot get a
2050       different DLL, even if a DLL with the same basename is loaded already.
2051       Thus there is no need to include the version into the mangling scheme. */
2052 #if 0
2053     sum += PERL_VERSION * 200 + PERL_SUBVERSION * 2;  /* Up to 5.6.1 */
2054 #else
2055 #  ifndef COMPATIBLE_VERSION_SUM  /* Binary compatibility with the 5.00553 binary */
2056 #    define COMPATIBLE_VERSION_SUM (5 * 200 + 53 * 2)
2057 #  endif
2058     sum += COMPATIBLE_VERSION_SUM;
2059 #endif
2060     fname[pos] = 'A' + (sum % 26);
2061     fname[pos + 1] = 'A' + (sum / 26 % 26);
2062     fname[pos + 2] = '\0';
2063     return (char *)fname;
2064 }
2065 
2066 XS(XS_DynaLoader_mod2fname)
2067 {
2068     dXSARGS;
2069     if (items != 1)
2070 	Perl_croak_nocontext("Usage: DynaLoader::mod2fname(sv)");
2071     {
2072 	SV *	sv = ST(0);
2073 	char *	RETVAL;
2074 	dXSTARG;
2075 
2076 	RETVAL = mod2fname(aTHX_ sv);
2077 	sv_setpv(TARG, RETVAL);
2078 	XSprePUSH; PUSHTARG;
2079     }
2080     XSRETURN(1);
2081 }
2082 
2083 char *
2084 os2error(int rc)
2085 {
2086 	dTHX;
2087 	ULONG len;
2088 	char *s;
2089 	int number = SvTRUE(get_sv("OS2::nsyserror", GV_ADD));
2090 
2091         if (!(_emx_env & 0x200)) return ""; /* Nop if not OS/2. */
2092 	if (rc == 0)
2093 		return "";
2094 	if (number) {
2095 	    sprintf(os2error_buf, "SYS%04d=%#x: ", rc, rc);
2096 	    s = os2error_buf + strlen(os2error_buf);
2097 	} else
2098 	    s = os2error_buf;
2099 	if (DosGetMessage(NULL, 0, s, sizeof(os2error_buf) - 1 - (s-os2error_buf),
2100 			  rc, "OSO001.MSG", &len)) {
2101 	    char *name = "";
2102 
2103 	    if (!number) {
2104 		sprintf(os2error_buf, "SYS%04d=%#x: ", rc, rc);
2105 		s = os2error_buf + strlen(os2error_buf);
2106 	    }
2107 	    switch (rc) {
2108 	    case PMERR_INVALID_HWND:
2109 		name = "PMERR_INVALID_HWND";
2110 		break;
2111 	    case PMERR_INVALID_HMQ:
2112 		name = "PMERR_INVALID_HMQ";
2113 		break;
2114 	    case PMERR_CALL_FROM_WRONG_THREAD:
2115 		name = "PMERR_CALL_FROM_WRONG_THREAD";
2116 		break;
2117 	    case PMERR_NO_MSG_QUEUE:
2118 		name = "PMERR_NO_MSG_QUEUE";
2119 		break;
2120 	    case PMERR_NOT_IN_A_PM_SESSION:
2121 		name = "PMERR_NOT_IN_A_PM_SESSION";
2122 		break;
2123 	    case PMERR_INVALID_ATOM:
2124 		name = "PMERR_INVALID_ATOM";
2125 		break;
2126 	    case PMERR_INVALID_HATOMTBL:
2127 		name = "PMERR_INVALID_HATOMTMB";
2128 		break;
2129 	    case PMERR_INVALID_INTEGER_ATOM:
2130 		name = "PMERR_INVALID_INTEGER_ATOM";
2131 		break;
2132 	    case PMERR_INVALID_ATOM_NAME:
2133 		name = "PMERR_INVALID_ATOM_NAME";
2134 		break;
2135 	    case PMERR_ATOM_NAME_NOT_FOUND:
2136 		name = "PMERR_ATOM_NAME_NOT_FOUND";
2137 		break;
2138 	    }
2139 	    sprintf(s, "%s%s[No description found in OSO001.MSG]",
2140 		    name, (*name ? "=" : ""));
2141 	} else {
2142 		s[len] = '\0';
2143 		if (len && s[len - 1] == '\n')
2144 			s[--len] = 0;
2145 		if (len && s[len - 1] == '\r')
2146 			s[--len] = 0;
2147 		if (len && s[len - 1] == '.')
2148 			s[--len] = 0;
2149 		if (len >= 10 && number && strnEQ(s, os2error_buf, 7)
2150 		    && s[7] == ':' && s[8] == ' ')
2151 		    /* Some messages start with SYSdddd:, some not */
2152 		    Move(s + 9, s, (len -= 9) + 1, char);
2153 	}
2154 	return os2error_buf;
2155 }
2156 
2157 void
2158 ResetWinError(void)
2159 {
2160   WinError_2_Perl_rc;
2161 }
2162 
2163 void
2164 CroakWinError(int die, char *name)
2165 {
2166   FillWinError;
2167   if (die && Perl_rc)
2168     croak_with_os2error(name ? name : "Win* API call");
2169 }
2170 
2171 static char *
2172 dllname2buffer(pTHX_ char *buf, STRLEN l)
2173 {
2174     char *o;
2175     STRLEN ll;
2176     SV *dll = NULL;
2177 
2178     dll = module_name(mod_name_full);
2179     o = SvPV(dll, ll);
2180     if (ll < l)
2181        memcpy(buf,o,ll);
2182     SvREFCNT_dec(dll);
2183     return (ll >= l ? "???" : buf);
2184 }
2185 
2186 static char *
2187 execname2buffer(char *buf, STRLEN l, char *oname)
2188 {
2189   char *p, *orig = oname, ok = oname != NULL;
2190 
2191   if (_execname(buf, l) != 0) {
2192     if (!oname || strlen(oname) >= l)
2193       return oname;
2194     strcpy(buf, oname);
2195     ok = 0;
2196   }
2197   p = buf;
2198   while (*p) {
2199     if (*p == '\\')
2200 	*p = '/';
2201     if (*p == '/') {
2202 	if (ok && *oname != '/' && *oname != '\\')
2203 	    ok = 0;
2204     } else if (ok && tolower(*oname) != tolower(*p))
2205 	ok = 0;
2206     p++;
2207     oname++;
2208   }
2209   if (ok) { /* orig matches the real name.  Use orig: */
2210      strcpy(buf, orig);		/* _execname() is always uppercased */
2211      p = buf;
2212      while (*p) {
2213        if (*p == '\\')
2214            *p = '/';
2215        p++;
2216      }
2217   }
2218   return buf;
2219 }
2220 
2221 char *
2222 os2_execname(pTHX)
2223 {
2224   char buf[300], *p = execname2buffer(buf, sizeof buf, PL_origargv[0]);
2225 
2226   p = savepv(p);
2227   SAVEFREEPV(p);
2228   return p;
2229 }
2230 
2231 int
2232 Perl_OS2_handler_install(void *handler, enum Perlos2_handler how)
2233 {
2234     char *s, b[300];
2235 
2236     switch (how) {
2237       case Perlos2_handler_mangle:
2238 	perllib_mangle_installed = (char *(*)(char *s, unsigned int l))handler;
2239 	return 1;
2240       case Perlos2_handler_perl_sh:
2241 	s = (char *)handler;
2242 	s = dir_subst(s, strlen(s), b, sizeof b, 0, "handler_perl_sh");
2243 	perl_sh_installed = savepv(s);
2244 	return 1;
2245       case Perlos2_handler_perllib_from:
2246 	s = (char *)handler;
2247 	s = dir_subst(s, strlen(s), b, sizeof b, 0, "handler_perllib_from");
2248 	oldl = strlen(s);
2249 	oldp = savepv(s);
2250 	return 1;
2251       case Perlos2_handler_perllib_to:
2252 	s = (char *)handler;
2253 	s = dir_subst(s, strlen(s), b, sizeof b, 0, "handler_perllib_to");
2254 	newl = strlen(s);
2255 	newp = savepv(s);
2256 	strcpy(mangle_ret, newp);
2257 	s = mangle_ret - 1;
2258 	while (*++s)
2259 	    if (*s == '\\')
2260 		*s = '/';
2261 	return 1;
2262       default:
2263 	return 0;
2264     }
2265 }
2266 
2267 /* Returns a malloc()ed copy */
2268 char *
2269 dir_subst(char *s, unsigned int l, char *b, unsigned int bl, enum dir_subst_e flags, char *msg)
2270 {
2271     char *from, *to = b, *e = b; /* `to' assignment: shut down the warning */
2272     STRLEN froml = 0, tol = 0, rest = 0;	/* froml: likewise */
2273 
2274     if (l >= 2 && s[0] == '~') {
2275 	switch (s[1]) {
2276 	  case 'i': case 'I':
2277 	    from = "installprefix";	break;
2278 	  case 'd': case 'D':
2279 	    from = "dll";		break;
2280 	  case 'e': case 'E':
2281 	    from = "exe";		break;
2282 	  default:
2283 	    from = NULL;
2284 	    froml = l + 1;			/* Will not match */
2285 	    break;
2286 	}
2287 	if (from)
2288 	    froml = strlen(from) + 1;
2289 	if (l >= froml && strnicmp(s + 2, from + 1, froml - 2) == 0) {
2290 	    int strip = 1;
2291 
2292 	    switch (s[1]) {
2293 	      case 'i': case 'I':
2294 		strip = 0;
2295 		tol = strlen(INSTALL_PREFIX);
2296 		if (tol >= bl) {
2297 		    if (flags & dir_subst_fatal)
2298 			Perl_croak_nocontext("INSTALL_PREFIX too long: `%s'", INSTALL_PREFIX);
2299 		    else
2300 			return NULL;
2301 		}
2302 		memcpy(b, INSTALL_PREFIX, tol + 1);
2303 		to = b;
2304 		e = b + tol;
2305 		break;
2306 	      case 'd': case 'D':
2307 		if (flags & dir_subst_fatal) {
2308 		    dTHX;
2309 
2310 		    to = dllname2buffer(aTHX_ b, bl);
2311 		} else {				/* No Perl present yet */
2312 		    HMODULE self = find_myself();
2313 		    APIRET rc = DosQueryModuleName(self, bl, b);
2314 
2315 		    if (rc)
2316 			return 0;
2317 		    to = b - 1;
2318 		    while (*++to)
2319 			if (*to == '\\')
2320 			    *to = '/';
2321 		    to = b;
2322 		}
2323 		break;
2324 	      case 'e': case 'E':
2325 		if (flags & dir_subst_fatal) {
2326 		    dTHX;
2327 
2328 		    to = execname2buffer(b, bl, PL_origargv[0]);
2329 	        } else
2330 		    to = execname2buffer(b, bl, NULL);
2331 		break;
2332 	    }
2333 	    if (!to)
2334 		return NULL;
2335 	    if (strip) {
2336 		e = strrchr(to, '/');
2337 		if (!e && (flags & dir_subst_fatal))
2338 		    Perl_croak_nocontext("%s: Can't parse EXE/DLL name: '%s'", msg, to);
2339 		else if (!e)
2340 		    return NULL;
2341 		*e = 0;
2342 	    }
2343 	    s += froml; l -= froml;
2344 	    if (!l)
2345 		return to;
2346 	    if (!tol)
2347 		tol = strlen(to);
2348 
2349 	    while (l >= 3 && (s[0] == '/' || s[0] == '\\')
2350 		   && s[1] == '.' && s[2] == '.'
2351 		   && (l == 3 || s[3] == '/' || s[3] == '\\' || s[3] == ';')) {
2352 		e = strrchr(b, '/');
2353 		if (!e && (flags & dir_subst_fatal))
2354 			Perl_croak_nocontext("%s: Error stripping dirs from EXE/DLL/INSTALLDIR name", msg);
2355 		else if (!e)
2356 			return NULL;
2357 		*e = 0;
2358 		l -= 3; s += 3;
2359 	    }
2360 	    if (l && s[0] != '/' && s[0] != '\\' && s[0] != ';')
2361 		*e++ = '/';
2362 	}
2363     }						/* Else: copy as is */
2364     if (l && (flags & dir_subst_pathlike)) {
2365 	STRLEN i = 0;
2366 
2367 	while ( i < l - 2 && s[i] != ';')	/* May have ~char after `;' */
2368 	    i++;
2369 	if (i < l - 2) {			/* Found */
2370 	    rest = l - i - 1;
2371 	    l = i + 1;
2372 	}
2373     }
2374     if (e + l >= b + bl) {
2375 	if (flags & dir_subst_fatal)
2376 	    Perl_croak_nocontext("%s: name `%s%s' too long", msg, b, s);
2377 	else
2378 	    return NULL;
2379     }
2380     memcpy(e, s, l);
2381     if (rest) {
2382 	e = dir_subst(s + l, rest, e + l, bl - (e + l - b), flags, msg);
2383 	return e ? b : e;
2384     }
2385     e[l] = 0;
2386     return b;
2387 }
2388 
2389 char *
2390 perllib_mangle_with(char *s, unsigned int l, char *from, unsigned int froml, char *to, unsigned int tol)
2391 {
2392     if (!to)
2393 	return s;
2394     if (l == 0)
2395 	l = strlen(s);
2396     if (l < froml || strnicmp(from, s, froml) != 0)
2397 	return s;
2398     if (l + tol - froml > STATIC_FILE_LENGTH || tol > STATIC_FILE_LENGTH)
2399 	Perl_croak_nocontext("Malformed PERLLIB_PREFIX");
2400     if (to && to != mangle_ret)
2401 	memcpy(mangle_ret, to, tol);
2402     strcpy(mangle_ret + tol, s + froml);
2403     return mangle_ret;
2404 }
2405 
2406 char *
2407 perllib_mangle(char *s, unsigned int l)
2408 {
2409     char *name;
2410 
2411     if (perllib_mangle_installed && (name = perllib_mangle_installed(s,l)))
2412 	return name;
2413     if (!newp && !notfound) {
2414 	newp = getenv(name = "PERLLIB_" STRINGIFY(PERL_REVISION)
2415 		      STRINGIFY(PERL_VERSION) STRINGIFY(PERL_SUBVERSION)
2416 		      "_PREFIX");
2417 	if (!newp)
2418 	    newp = getenv(name = "PERLLIB_" STRINGIFY(PERL_REVISION)
2419 			  STRINGIFY(PERL_VERSION) "_PREFIX");
2420 	if (!newp)
2421 	    newp = getenv(name = "PERLLIB_" STRINGIFY(PERL_REVISION) "_PREFIX");
2422 	if (!newp)
2423 	    newp = getenv(name = "PERLLIB_PREFIX");
2424 	if (newp) {
2425 	    char *s, b[300];
2426 
2427 	    oldp = newp;
2428 	    while (*newp && !isSPACE(*newp) && *newp != ';')
2429 		newp++;			/* Skip old name. */
2430 	    oldl = newp - oldp;
2431 	    s = dir_subst(oldp, oldl, b, sizeof b, dir_subst_fatal, name);
2432 	    oldp = savepv(s);
2433 	    oldl = strlen(s);
2434 	    while (*newp && (isSPACE(*newp) || *newp == ';'))
2435 		newp++;			/* Skip whitespace. */
2436 	    Perl_OS2_handler_install((void *)newp, Perlos2_handler_perllib_to);
2437 	    if (newl == 0 || oldl == 0)
2438 		Perl_croak_nocontext("Malformed %s", name);
2439 	} else
2440 	    notfound = 1;
2441     }
2442     if (!newp)
2443 	return s;
2444     if (l == 0)
2445 	l = strlen(s);
2446     if (l < oldl || strnicmp(oldp, s, oldl) != 0)
2447 	return s;
2448     if (l + newl - oldl > STATIC_FILE_LENGTH || newl > STATIC_FILE_LENGTH)
2449 	Perl_croak_nocontext("Malformed PERLLIB_PREFIX");
2450     strcpy(mangle_ret + newl, s + oldl);
2451     return mangle_ret;
2452 }
2453 
2454 unsigned long
2455 Perl_hab_GET()			/* Needed if perl.h cannot be included */
2456 {
2457     return perl_hab_GET();
2458 }
2459 
2460 static void
2461 Create_HMQ(int serve, char *message)	/* Assumes morphing */
2462 {
2463     unsigned fpflag = _control87(0,0);
2464 
2465     init_PMWIN_entries();
2466     /* 64 messages if before OS/2 3.0, ignored otherwise */
2467     Perl_hmq = (*PMWIN_entries.CreateMsgQueue)(perl_hab_GET(), 64);
2468     if (!Perl_hmq) {
2469 	dTHX;
2470 
2471 	SAVEINT(rmq_cnt);		/* Allow catch()ing. */
2472 	if (rmq_cnt++)
2473 	    _exit(188);		/* Panic can try to create a window. */
2474 	CroakWinError(1, message ? message : "Cannot create a message queue");
2475     }
2476     if (serve != -1)
2477 	(*PMWIN_entries.CancelShutdown)(Perl_hmq, !serve);
2478     /* We may have loaded some modules */
2479     _control87(fpflag, MCW_EM); /* Some modules reset FP flags on (un)load */
2480 }
2481 
2482 #define REGISTERMQ_WILL_SERVE		1
2483 #define REGISTERMQ_IMEDIATE_UNMORPH	2
2484 
2485 HMQ
2486 Perl_Register_MQ(int serve)
2487 {
2488   if (Perl_hmq_refcnt <= 0) {
2489     PPIB pib;
2490     PTIB tib;
2491 
2492     Perl_hmq_refcnt = 0;		/* Be extra safe */
2493     DosGetInfoBlocks(&tib, &pib);
2494     if (!Perl_morph_refcnt) {
2495 	Perl_os2_initial_mode = pib->pib_ultype;
2496 	/* Try morphing into a PM application. */
2497 	if (pib->pib_ultype != 3)		/* 2 is VIO */
2498 	    pib->pib_ultype = 3;		/* 3 is PM */
2499     }
2500     Create_HMQ(-1,			/* We do CancelShutdown ourselves */
2501 	       "Cannot create a message queue, or morph to a PM application");
2502     if ((serve & REGISTERMQ_IMEDIATE_UNMORPH)) {
2503 	if (!Perl_morph_refcnt && Perl_os2_initial_mode != 3)
2504 	    pib->pib_ultype = Perl_os2_initial_mode;
2505     }
2506   }
2507     if (serve & REGISTERMQ_WILL_SERVE) {
2508 	if ( Perl_hmq_servers <= 0	/* Safe to inform us on shutdown, */
2509 	     && Perl_hmq_refcnt > 0 )	/* this was switched off before... */
2510 	    (*PMWIN_entries.CancelShutdown)(Perl_hmq, 0);
2511 	Perl_hmq_servers++;
2512     } else if (!Perl_hmq_servers)	/* Do not inform us on shutdown */
2513 	(*PMWIN_entries.CancelShutdown)(Perl_hmq, 1);
2514     Perl_hmq_refcnt++;
2515     if (!(serve & REGISTERMQ_IMEDIATE_UNMORPH))
2516 	Perl_morph_refcnt++;
2517     return Perl_hmq;
2518 }
2519 
2520 int
2521 Perl_Serve_Messages(int force)
2522 {
2523     int cnt = 0;
2524     QMSG msg;
2525 
2526     if (Perl_hmq_servers > 0 && !force)
2527 	return 0;
2528     if (Perl_hmq_refcnt <= 0)
2529 	Perl_croak_nocontext("No message queue");
2530     while ((*PMWIN_entries.PeekMsg)(Perl_hab, &msg, NULLHANDLE, 0, 0, PM_REMOVE)) {
2531 	cnt++;
2532 	if (msg.msg == WM_QUIT)
2533 	    Perl_croak_nocontext("QUITing...");
2534 	(*PMWIN_entries.DispatchMsg)(Perl_hab, &msg);
2535     }
2536     return cnt;
2537 }
2538 
2539 int
2540 Perl_Process_Messages(int force, I32 *cntp)
2541 {
2542     QMSG msg;
2543 
2544     if (Perl_hmq_servers > 0 && !force)
2545 	return 0;
2546     if (Perl_hmq_refcnt <= 0)
2547 	Perl_croak_nocontext("No message queue");
2548     while ((*PMWIN_entries.GetMsg)(Perl_hab, &msg, NULLHANDLE, 0, 0)) {
2549 	if (cntp)
2550 	    (*cntp)++;
2551 	(*PMWIN_entries.DispatchMsg)(Perl_hab, &msg);
2552 	if (msg.msg == WM_DESTROY)
2553 	    return -1;
2554 	if (msg.msg == WM_CREATE)
2555 	    return +1;
2556     }
2557     Perl_croak_nocontext("QUITing...");
2558 }
2559 
2560 void
2561 Perl_Deregister_MQ(int serve)
2562 {
2563     if (serve & REGISTERMQ_WILL_SERVE)
2564 	Perl_hmq_servers--;
2565 
2566     if (--Perl_hmq_refcnt <= 0) {
2567 	unsigned fpflag = _control87(0,0);
2568 
2569 	init_PMWIN_entries();			/* To be extra safe */
2570 	(*PMWIN_entries.DestroyMsgQueue)(Perl_hmq);
2571 	Perl_hmq = 0;
2572 	/* We may have (un)loaded some modules */
2573 	_control87(fpflag, MCW_EM); /* Some modules reset FP flags on (un)load */
2574     } else if ((serve & REGISTERMQ_WILL_SERVE) && Perl_hmq_servers <= 0)
2575 	(*PMWIN_entries.CancelShutdown)(Perl_hmq, 1); /* Last server exited */
2576     if (!(serve & REGISTERMQ_IMEDIATE_UNMORPH) && (--Perl_morph_refcnt <= 0)) {
2577 	/* Try morphing back from a PM application. */
2578 	PPIB pib;
2579 	PTIB tib;
2580 
2581 	DosGetInfoBlocks(&tib, &pib);
2582 	if (pib->pib_ultype == 3)		/* 3 is PM */
2583 	    pib->pib_ultype = Perl_os2_initial_mode;
2584 	else
2585 	    Perl_warn_nocontext("Unexpected program mode %d when morphing back from PM",
2586 				pib->pib_ultype);
2587     }
2588 }
2589 
2590 #define sys_is_absolute(path) ( isALPHA((path)[0]) && (path)[1] == ':' \
2591 				&& ((path)[2] == '/' || (path)[2] == '\\'))
2592 #define sys_is_rooted _fnisabs
2593 #define sys_is_relative _fnisrel
2594 #define current_drive _getdrive
2595 
2596 #undef chdir				/* Was _chdir2. */
2597 #define sys_chdir(p) (chdir(p) == 0)
2598 #define change_drive(d) (_chdrive(d), (current_drive() == toupper(d)))
2599 
2600 XS(XS_OS2_Error)
2601 {
2602     dXSARGS;
2603     if (items != 2)
2604 	Perl_croak_nocontext("Usage: OS2::Error(harderr, exception)");
2605     {
2606 	int	arg1 = SvIV(ST(0));
2607 	int	arg2 = SvIV(ST(1));
2608 	int	a = ((arg1 ? FERR_ENABLEHARDERR : FERR_DISABLEHARDERR)
2609 		     | (arg2 ? FERR_ENABLEEXCEPTION : FERR_DISABLEEXCEPTION));
2610 	int	RETVAL = ((arg1 ? 1 : 0) | (arg2 ? 2 : 0));
2611 	unsigned long rc;
2612 
2613 	if (CheckOSError(DosError(a)))
2614 	    Perl_croak_nocontext("DosError(%d) failed: %s", a, os2error(Perl_rc));
2615 	ST(0) = sv_newmortal();
2616 	if (DOS_harderr_state >= 0)
2617 	    sv_setiv(ST(0), DOS_harderr_state);
2618 	DOS_harderr_state = RETVAL;
2619     }
2620     XSRETURN(1);
2621 }
2622 
2623 XS(XS_OS2_Errors2Drive)
2624 {
2625     dXSARGS;
2626     if (items != 1)
2627 	Perl_croak_nocontext("Usage: OS2::Errors2Drive(drive)");
2628     {
2629 	STRLEN n_a;
2630 	SV  *sv = ST(0);
2631 	int	suppress = SvOK(sv);
2632 	char	*s = suppress ? SvPV(sv, n_a) : NULL;
2633 	char	drive = (s ? *s : 0);
2634 	unsigned long rc;
2635 
2636 	if (suppress && !isALPHA(drive))
2637 	    Perl_croak_nocontext("Non-char argument '%c' to OS2::Errors2Drive()", drive);
2638 	if (CheckOSError(DosSuppressPopUps((suppress
2639 					    ? SPU_ENABLESUPPRESSION
2640 					    : SPU_DISABLESUPPRESSION),
2641 					   drive)))
2642 	    Perl_croak_nocontext("DosSuppressPopUps(%c) failed: %s", drive,
2643 				 os2error(Perl_rc));
2644 	ST(0) = sv_newmortal();
2645 	if (DOS_suppression_state > 0)
2646 	    sv_setpvn(ST(0), &DOS_suppression_state, 1);
2647 	else if (DOS_suppression_state == 0)
2648             SvPVCLEAR(ST(0));
2649 	DOS_suppression_state = drive;
2650     }
2651     XSRETURN(1);
2652 }
2653 
2654 int
2655 async_mssleep(ULONG ms, int switch_priority) {
2656   /* This is similar to DosSleep(), but has 8ms granularity in time-critical
2657      threads even on Warp3. */
2658   HEV     hevEvent1     = 0;			/* Event semaphore handle    */
2659   HTIMER  htimerEvent1  = 0;			/* Timer handle              */
2660   APIRET  rc            = NO_ERROR;		/* Return code               */
2661   int ret = 1;
2662   ULONG priority = 0, nesting;			/* Shut down the warnings */
2663   PPIB pib;
2664   PTIB tib;
2665   char *e = NULL;
2666   APIRET badrc;
2667 
2668   if (!(_emx_env & 0x200))	/* DOS */
2669     return !_sleep2(ms);
2670 
2671   os2cp_croak(DosCreateEventSem(NULL,	     /* Unnamed */
2672 				&hevEvent1,  /* Handle of semaphore returned */
2673 				DC_SEM_SHARED, /* Shared needed for DosAsyncTimer */
2674 				FALSE),      /* Semaphore is in RESET state  */
2675 	      "DosCreateEventSem");
2676 
2677   if (ms >= switch_priority)
2678     switch_priority = 0;
2679   if (switch_priority) {
2680     if (CheckOSError(DosGetInfoBlocks(&tib, &pib)))
2681 	switch_priority = 0;
2682     else {
2683 	/* In Warp3, to switch scheduling to 8ms step, one needs to do
2684 	   DosAsyncTimer() in time-critical thread.  On laters versions,
2685 	   more and more cases of wait-for-something are covered.
2686 
2687 	   It turns out that on Warp3fp42 it is the priority at the time
2688 	   of DosAsyncTimer() which matters.  Let's hope that this works
2689 	   with later versions too...		XXXX
2690 	 */
2691 	priority = (tib->tib_ptib2->tib2_ulpri);
2692 	if ((priority & 0xFF00) == 0x0300) /* already time-critical */
2693 	    switch_priority = 0;
2694 	/* Make us time-critical.  Just modifying TIB is not enough... */
2695 	/* tib->tib_ptib2->tib2_ulpri = 0x0300;*/
2696 	/* We do not want to run at high priority if a signal causes us
2697 	   to longjmp() out of this section... */
2698 	if (DosEnterMustComplete(&nesting))
2699 	    switch_priority = 0;
2700 	else
2701 	    DosSetPriority(PRTYS_THREAD, PRTYC_TIMECRITICAL, 0, 0);
2702     }
2703   }
2704 
2705   if ((badrc = DosAsyncTimer(ms,
2706 			     (HSEM) hevEvent1,	/* Semaphore to post        */
2707 			     &htimerEvent1)))	/* Timer handler (returned) */
2708      e = "DosAsyncTimer";
2709 
2710   if (switch_priority && tib->tib_ptib2->tib2_ulpri == 0x0300) {
2711 	/* Nobody switched priority while we slept...  Ignore errors... */
2712 	/* tib->tib_ptib2->tib2_ulpri = priority; */	/* Get back... */
2713 	if (!(rc = DosSetPriority(PRTYS_THREAD, (priority>>8) & 0xFF, 0, 0)))
2714 	    rc = DosSetPriority(PRTYS_THREAD, 0, priority & 0xFF, 0);
2715   }
2716   if (switch_priority)
2717       rc = DosExitMustComplete(&nesting);	/* Ignore errors */
2718 
2719   /* The actual blocking call is made with "normal" priority.  This way we
2720      should not bother with DosSleep(0) etc. to compensate for us interrupting
2721      higher-priority threads.  The goal is to prohibit the system spending too
2722      much time halt()ing, not to run us "no matter what". */
2723   if (!e)					/* Wait for AsyncTimer event */
2724       badrc = DosWaitEventSem(hevEvent1, SEM_INDEFINITE_WAIT);
2725 
2726   if (e) ;				/* Do nothing */
2727   else if (badrc == ERROR_INTERRUPT)
2728      ret = 0;
2729   else if (badrc)
2730      e = "DosWaitEventSem";
2731   if ((rc = DosCloseEventSem(hevEvent1)) && !e) { /* Get rid of semaphore */
2732      e = "DosCloseEventSem";
2733      badrc = rc;
2734   }
2735   if (e)
2736      os2cp_croak(badrc, e);
2737   return ret;
2738 }
2739 
2740 XS(XS_OS2_ms_sleep)		/* for testing only... */
2741 {
2742     dXSARGS;
2743     ULONG ms, lim;
2744 
2745     if (items > 2 || items < 1)
2746 	Perl_croak_nocontext("Usage: OS2::ms_sleep(wait_ms [, high_priority_limit])");
2747     ms = SvUV(ST(0));
2748     lim = items > 1 ? SvUV(ST(1)) : ms + 1;
2749     async_mssleep(ms, lim);
2750     XSRETURN_YES;
2751 }
2752 
2753 ULONG (*pDosTmrQueryFreq) (PULONG);
2754 ULONG (*pDosTmrQueryTime) (unsigned long long *);
2755 
2756 XS(XS_OS2_Timer)
2757 {
2758     dXSARGS;
2759     static ULONG freq;
2760     unsigned long long count;
2761     ULONG rc;
2762 
2763     if (items != 0)
2764 	Perl_croak_nocontext("Usage: OS2::Timer()");
2765     if (!freq) {
2766 	*(PFN*)&pDosTmrQueryFreq = loadByOrdinal(ORD_DosTmrQueryFreq, 0);
2767 	*(PFN*)&pDosTmrQueryTime = loadByOrdinal(ORD_DosTmrQueryTime, 0);
2768 	MUTEX_LOCK(&perlos2_state_mutex);
2769 	if (!freq)
2770 	    if (CheckOSError(pDosTmrQueryFreq(&freq)))
2771 		croak_with_os2error("DosTmrQueryFreq");
2772 	MUTEX_UNLOCK(&perlos2_state_mutex);
2773     }
2774     if (CheckOSError(pDosTmrQueryTime(&count)))
2775 	croak_with_os2error("DosTmrQueryTime");
2776     {
2777 	dXSTARG;
2778 
2779 	XSprePUSH; PUSHn(((NV)count)/freq);
2780     }
2781     XSRETURN(1);
2782 }
2783 
2784 XS(XS_OS2_msCounter)
2785 {
2786     dXSARGS;
2787 
2788     if (items != 0)
2789 	Perl_croak_nocontext("Usage: OS2::msCounter()");
2790     {
2791 	dXSTARG;
2792 
2793 	XSprePUSH; PUSHu(msCounter());
2794     }
2795     XSRETURN(1);
2796 }
2797 
2798 XS(XS_OS2__InfoTable)
2799 {
2800     dXSARGS;
2801     int is_local = 0;
2802 
2803     if (items > 1)
2804 	Perl_croak_nocontext("Usage: OS2::_infoTable([isLocal])");
2805     if (items == 1)
2806 	is_local = (int)SvIV(ST(0));
2807     {
2808 	dXSTARG;
2809 
2810 	XSprePUSH; PUSHu(InfoTable(is_local));
2811     }
2812     XSRETURN(1);
2813 }
2814 
2815 static const char * const dc_fields[] = {
2816   "FAMILY",
2817   "IO_CAPS",
2818   "TECHNOLOGY",
2819   "DRIVER_VERSION",
2820   "WIDTH",
2821   "HEIGHT",
2822   "WIDTH_IN_CHARS",
2823   "HEIGHT_IN_CHARS",
2824   "HORIZONTAL_RESOLUTION",
2825   "VERTICAL_RESOLUTION",
2826   "CHAR_WIDTH",
2827   "CHAR_HEIGHT",
2828   "SMALL_CHAR_WIDTH",
2829   "SMALL_CHAR_HEIGHT",
2830   "COLORS",
2831   "COLOR_PLANES",
2832   "COLOR_BITCOUNT",
2833   "COLOR_TABLE_SUPPORT",
2834   "MOUSE_BUTTONS",
2835   "FOREGROUND_MIX_SUPPORT",
2836   "BACKGROUND_MIX_SUPPORT",
2837   "VIO_LOADABLE_FONTS",
2838   "WINDOW_BYTE_ALIGNMENT",
2839   "BITMAP_FORMATS",
2840   "RASTER_CAPS",
2841   "MARKER_HEIGHT",
2842   "MARKER_WIDTH",
2843   "DEVICE_FONTS",
2844   "GRAPHICS_SUBSET",
2845   "GRAPHICS_VERSION",
2846   "GRAPHICS_VECTOR_SUBSET",
2847   "DEVICE_WINDOWING",
2848   "ADDITIONAL_GRAPHICS",
2849   "PHYS_COLORS",
2850   "COLOR_INDEX",
2851   "GRAPHICS_CHAR_WIDTH",
2852   "GRAPHICS_CHAR_HEIGHT",
2853   "HORIZONTAL_FONT_RES",
2854   "VERTICAL_FONT_RES",
2855   "DEVICE_FONT_SIM",
2856   "LINEWIDTH_THICK",
2857   "DEVICE_POLYSET_POINTS",
2858 };
2859 
2860 enum {
2861     DevCap_dc, DevCap_hwnd
2862 };
2863 
2864 HDC (*pWinOpenWindowDC) (HWND hwnd);
2865 HMF (*pDevCloseDC) (HDC hdc);
2866 HDC (*pDevOpenDC) (HAB hab, LONG lType, PCSZ pszToken, LONG lCount,
2867     PDEVOPENDATA pdopData, HDC hdcComp);
2868 BOOL (*pDevQueryCaps) (HDC hdc, LONG lStart, LONG lCount, PLONG alArray);
2869 
2870 
2871 XS(XS_OS2_DevCap)
2872 {
2873     dXSARGS;
2874     if (items > 2)
2875 	Perl_croak_nocontext("Usage: OS2::DevCap()");
2876     {
2877 	/* Device Capabilities Data Buffer (10 extra w.r.t. Warp 4.5) */
2878 	LONG   si[CAPS_DEVICE_POLYSET_POINTS - CAPS_FAMILY + 1];
2879 	int i = 0, j = 0, how = DevCap_dc;
2880 	HDC hScreenDC;
2881 	DEVOPENSTRUC doStruc= {0L, (PSZ)"DISPLAY", NULL, 0L, 0L, 0L, 0L, 0L, 0L};
2882 	ULONG rc1 = NO_ERROR;
2883 	HWND hwnd;
2884 	static volatile int devcap_loaded;
2885 
2886 	if (!devcap_loaded) {
2887 	    *(PFN*)&pWinOpenWindowDC = loadByOrdinal(ORD_WinOpenWindowDC, 0);
2888 	    *(PFN*)&pDevOpenDC = loadByOrdinal(ORD_DevOpenDC, 0);
2889 	    *(PFN*)&pDevCloseDC = loadByOrdinal(ORD_DevCloseDC, 0);
2890 	    *(PFN*)&pDevQueryCaps = loadByOrdinal(ORD_DevQueryCaps, 0);
2891 	    devcap_loaded = 1;
2892 	}
2893 
2894 	if (items >= 2)
2895 	    how = SvIV(ST(1));
2896 	if (!items) {			/* Get device contents from PM */
2897 	    hScreenDC = pDevOpenDC(perl_hab_GET(), OD_MEMORY, (PSZ)"*", 0,
2898 				  (PDEVOPENDATA)&doStruc, NULLHANDLE);
2899 	    if (CheckWinError(hScreenDC))
2900 		croak_with_os2error("DevOpenDC() failed");
2901 	} else if (how == DevCap_dc)
2902 	    hScreenDC = (HDC)SvIV(ST(0));
2903 	else {				/* DevCap_hwnd */
2904 	    if (!Perl_hmq)
2905 		Perl_croak(aTHX_ "Getting a window's device context without a message queue would lock PM");
2906 	    hwnd = (HWND)SvIV(ST(0));
2907 	    hScreenDC = pWinOpenWindowDC(hwnd); /* No need to DevCloseDC() */
2908 	    if (CheckWinError(hScreenDC))
2909 		croak_with_os2error("WinOpenWindowDC() failed");
2910 	}
2911 	if (CheckWinError(pDevQueryCaps(hScreenDC,
2912 					CAPS_FAMILY, /* W3 documented caps */
2913 					CAPS_DEVICE_POLYSET_POINTS
2914 					  - CAPS_FAMILY + 1,
2915 					si)))
2916 	    rc1 = Perl_rc;
2917 	else {
2918 	    EXTEND(SP,2*(CAPS_DEVICE_POLYSET_POINTS - CAPS_FAMILY + 1));
2919 	    while (i < CAPS_DEVICE_POLYSET_POINTS - CAPS_FAMILY + 1) {
2920 		ST(j) = sv_newmortal();
2921 		sv_setpv(ST(j++), dc_fields[i]);
2922 		ST(j) = sv_newmortal();
2923 		sv_setiv(ST(j++), si[i]);
2924 		i++;
2925 	    }
2926 	    i = CAPS_DEVICE_POLYSET_POINTS + 1;
2927 	    while (i < CAPS_DEVICE_POLYSET_POINTS + 11) { /* Just in case... */
2928 		LONG l;
2929 
2930 		if (CheckWinError(pDevQueryCaps(hScreenDC, i, 1, &l)))
2931 		    break;
2932 		EXTEND(SP, j + 2);
2933 		ST(j) = sv_newmortal();
2934 		sv_setiv(ST(j++), i);
2935 		ST(j) = sv_newmortal();
2936 		sv_setiv(ST(j++), l);
2937 		i++;
2938 	    }
2939 	}
2940 	if (!items && CheckWinError(pDevCloseDC(hScreenDC)))
2941 	    Perl_warn_nocontext("DevCloseDC() failed: %s", os2error(Perl_rc));
2942 	if (rc1)
2943 	    Perl_rc = rc1, croak_with_os2error("DevQueryCaps() failed");
2944 	XSRETURN(j);
2945     }
2946 }
2947 
2948 LONG (*pWinQuerySysValue) (HWND hwndDesktop, LONG iSysValue);
2949 BOOL (*pWinSetSysValue) (HWND hwndDesktop, LONG iSysValue, LONG lValue);
2950 
2951 const char * const sv_keys[] = {
2952   "SWAPBUTTON",
2953   "DBLCLKTIME",
2954   "CXDBLCLK",
2955   "CYDBLCLK",
2956   "CXSIZEBORDER",
2957   "CYSIZEBORDER",
2958   "ALARM",
2959   "7",
2960   "8",
2961   "CURSORRATE",
2962   "FIRSTSCROLLRATE",
2963   "SCROLLRATE",
2964   "NUMBEREDLISTS",
2965   "WARNINGFREQ",
2966   "NOTEFREQ",
2967   "ERRORFREQ",
2968   "WARNINGDURATION",
2969   "NOTEDURATION",
2970   "ERRORDURATION",
2971   "19",
2972   "CXSCREEN",
2973   "CYSCREEN",
2974   "CXVSCROLL",
2975   "CYHSCROLL",
2976   "CYVSCROLLARROW",
2977   "CXHSCROLLARROW",
2978   "CXBORDER",
2979   "CYBORDER",
2980   "CXDLGFRAME",
2981   "CYDLGFRAME",
2982   "CYTITLEBAR",
2983   "CYVSLIDER",
2984   "CXHSLIDER",
2985   "CXMINMAXBUTTON",
2986   "CYMINMAXBUTTON",
2987   "CYMENU",
2988   "CXFULLSCREEN",
2989   "CYFULLSCREEN",
2990   "CXICON",
2991   "CYICON",
2992   "CXPOINTER",
2993   "CYPOINTER",
2994   "DEBUG",
2995   "CPOINTERBUTTONS",
2996   "POINTERLEVEL",
2997   "CURSORLEVEL",
2998   "TRACKRECTLEVEL",
2999   "CTIMERS",
3000   "MOUSEPRESENT",
3001   "CXALIGN",
3002   "CYALIGN",
3003   "DESKTOPWORKAREAYTOP",
3004   "DESKTOPWORKAREAYBOTTOM",
3005   "DESKTOPWORKAREAXRIGHT",
3006   "DESKTOPWORKAREAXLEFT",
3007   "55",
3008   "NOTRESERVED",
3009   "EXTRAKEYBEEP",
3010   "SETLIGHTS",
3011   "INSERTMODE",
3012   "60",
3013   "61",
3014   "62",
3015   "63",
3016   "MENUROLLDOWNDELAY",
3017   "MENUROLLUPDELAY",
3018   "ALTMNEMONIC",
3019   "TASKLISTMOUSEACCESS",
3020   "CXICONTEXTWIDTH",
3021   "CICONTEXTLINES",
3022   "CHORDTIME",
3023   "CXCHORD",
3024   "CYCHORD",
3025   "CXMOTIONSTART",
3026   "CYMOTIONSTART",
3027   "BEGINDRAG",
3028   "ENDDRAG",
3029   "SINGLESELECT",
3030   "OPEN",
3031   "CONTEXTMENU",
3032   "CONTEXTHELP",
3033   "TEXTEDIT",
3034   "BEGINSELECT",
3035   "ENDSELECT",
3036   "BEGINDRAGKB",
3037   "ENDDRAGKB",
3038   "SELECTKB",
3039   "OPENKB",
3040   "CONTEXTMENUKB",
3041   "CONTEXTHELPKB",
3042   "TEXTEDITKB",
3043   "BEGINSELECTKB",
3044   "ENDSELECTKB",
3045   "ANIMATION",
3046   "ANIMATIONSPEED",
3047   "MONOICONS",
3048   "KBDALTERED",
3049   "PRINTSCREEN",		/* 97, the last one on one of the DDK header */
3050   "LOCKSTARTINPUT",
3051   "DYNAMICDRAG",
3052   "100",
3053   "101",
3054   "102",
3055   "103",
3056   "104",
3057   "105",
3058   "106",
3059   "107",
3060 /*  "CSYSVALUES",*/
3061 					/* In recent DDK the limit is 108 */
3062 };
3063 
3064 XS(XS_OS2_SysValues)
3065 {
3066     dXSARGS;
3067     if (items > 2)
3068 	Perl_croak_nocontext("Usage: OS2::SysValues(which = -1, hwndDesktop = HWND_DESKTOP)");
3069     {
3070 	int i = 0, j = 0, which = -1;
3071 	HWND hwnd = HWND_DESKTOP;
3072 	static volatile int sv_loaded;
3073 	LONG RETVAL;
3074 
3075 	if (!sv_loaded) {
3076 	    *(PFN*)&pWinQuerySysValue = loadByOrdinal(ORD_WinQuerySysValue, 0);
3077 	    sv_loaded = 1;
3078 	}
3079 
3080 	if (items == 2)
3081 	    hwnd = (HWND)SvIV(ST(1));
3082 	if (items >= 1)
3083 	    which = (int)SvIV(ST(0));
3084 	if (which == -1) {
3085 	    EXTEND(SP,2*C_ARRAY_LENGTH(sv_keys));
3086 	    while (i < C_ARRAY_LENGTH(sv_keys)) {
3087 		ResetWinError();
3088 		RETVAL = pWinQuerySysValue(hwnd, i);
3089 		if ( !RETVAL
3090 		     && !(sv_keys[i][0] >= '0' && sv_keys[i][0] <= '9'
3091 			  && i <= SV_PRINTSCREEN) ) {
3092 		    FillWinError;
3093 		    if (Perl_rc) {
3094 			if (i > SV_PRINTSCREEN)
3095 			    break; /* May be not present on older systems */
3096 			croak_with_os2error("SysValues():");
3097 		    }
3098 
3099 		}
3100 		ST(j) = sv_newmortal();
3101 		sv_setpv(ST(j++), sv_keys[i]);
3102 		ST(j) = sv_newmortal();
3103 		sv_setiv(ST(j++), RETVAL);
3104 		i++;
3105 	    }
3106 	    XSRETURN(2 * i);
3107 	} else {
3108 	    dXSTARG;
3109 
3110 	    ResetWinError();
3111 	    RETVAL = pWinQuerySysValue(hwnd, which);
3112 	    if (!RETVAL) {
3113 		FillWinError;
3114 		if (Perl_rc)
3115 		    croak_with_os2error("SysValues():");
3116 	    }
3117 	    XSprePUSH; PUSHi((IV)RETVAL);
3118 	}
3119     }
3120 }
3121 
3122 XS(XS_OS2_SysValues_set)
3123 {
3124     dXSARGS;
3125     if (items < 2 || items > 3)
3126 	Perl_croak_nocontext("Usage: OS2::SysValues_set(which, val, hwndDesktop = HWND_DESKTOP)");
3127     {
3128 	int which = (int)SvIV(ST(0));
3129 	LONG val = (LONG)SvIV(ST(1));
3130 	HWND hwnd = HWND_DESKTOP;
3131 	static volatile int svs_loaded;
3132 
3133 	if (!svs_loaded) {
3134 	    *(PFN*)&pWinSetSysValue = loadByOrdinal(ORD_WinSetSysValue, 0);
3135 	    svs_loaded = 1;
3136 	}
3137 
3138 	if (items == 3)
3139 	    hwnd = (HWND)SvIV(ST(2));
3140 	if (CheckWinError(pWinSetSysValue(hwnd, which, val)))
3141 	    croak_with_os2error("SysValues_set()");
3142     }
3143     XSRETURN_YES;
3144 }
3145 
3146 #define QSV_MAX_WARP3				QSV_MAX_COMP_LENGTH
3147 
3148 static const char * const si_fields[] = {
3149   "MAX_PATH_LENGTH",
3150   "MAX_TEXT_SESSIONS",
3151   "MAX_PM_SESSIONS",
3152   "MAX_VDM_SESSIONS",
3153   "BOOT_DRIVE",
3154   "DYN_PRI_VARIATION",
3155   "MAX_WAIT",
3156   "MIN_SLICE",
3157   "MAX_SLICE",
3158   "PAGE_SIZE",
3159   "VERSION_MAJOR",
3160   "VERSION_MINOR",
3161   "VERSION_REVISION",
3162   "MS_COUNT",
3163   "TIME_LOW",
3164   "TIME_HIGH",
3165   "TOTPHYSMEM",
3166   "TOTRESMEM",
3167   "TOTAVAILMEM",
3168   "MAXPRMEM",
3169   "MAXSHMEM",
3170   "TIMER_INTERVAL",
3171   "MAX_COMP_LENGTH",
3172   "FOREGROUND_FS_SESSION",
3173   "FOREGROUND_PROCESS",			/* Warp 3 toolkit defines up to this */
3174   "NUMPROCESSORS",
3175   "MAXHPRMEM",
3176   "MAXHSHMEM",
3177   "MAXPROCESSES",
3178   "VIRTUALADDRESSLIMIT",
3179   "INT10ENABLED",			/* From $TOOLKIT-ddk\DDK\video\rel\os2c\include\base\os2\bsedos.h */
3180 };
3181 
3182 XS(XS_OS2_SysInfo)
3183 {
3184     dXSARGS;
3185     if (items != 0)
3186 	Perl_croak_nocontext("Usage: OS2::SysInfo()");
3187     {
3188 	/* System Information Data Buffer (10 extra w.r.t. Warp 4.5) */
3189 	ULONG   si[C_ARRAY_LENGTH(si_fields) + 10];
3190 	APIRET  rc	= NO_ERROR;	/* Return code            */
3191 	int i = 0, j = 0, last = QSV_MAX_WARP3;
3192 
3193 	if (CheckOSError(DosQuerySysInfo(1L, /* Request documented system */
3194 					 last, /* info for Warp 3 */
3195 					 (PVOID)si,
3196 					 sizeof(si))))
3197 	    croak_with_os2error("DosQuerySysInfo() failed");
3198 	while (++last <= C_ARRAY_LENGTH(si)) {
3199 	    if (CheckOSError(DosQuerySysInfo(last, last, /* One entry only */
3200 					     (PVOID)(si+last-1),
3201 					     sizeof(*si)))) {
3202 		if (Perl_rc != ERROR_INVALID_PARAMETER)
3203 		    croak_with_os2error("DosQuerySysInfo() failed");
3204 		break;
3205 	    }
3206 	}
3207 	last--;			/* Count of successfully processed offsets */
3208 	EXTEND(SP,2*last);
3209 	while (i < last) {
3210 	    ST(j) = sv_newmortal();
3211 	    if (i < C_ARRAY_LENGTH(si_fields))
3212 		sv_setpv(ST(j++),  si_fields[i]);
3213 	    else
3214 		sv_setiv(ST(j++),  i + 1);
3215 	    ST(j) = sv_newmortal();
3216 	    sv_setuv(ST(j++), si[i]);
3217 	    i++;
3218 	}
3219 	XSRETURN(2 * last);
3220     }
3221 }
3222 
3223 XS(XS_OS2_SysInfoFor)
3224 {
3225     dXSARGS;
3226     int count = (items == 2 ? (int)SvIV(ST(1)) : 1);
3227 
3228     if (items < 1 || items > 2)
3229 	Perl_croak_nocontext("Usage: OS2::SysInfoFor(id[,count])");
3230     {
3231 	/* System Information Data Buffer (10 extra w.r.t. Warp 4.5) */
3232 	ULONG   si[C_ARRAY_LENGTH(si_fields) + 10];
3233 	APIRET  rc	= NO_ERROR;	/* Return code            */
3234 	int i = 0;
3235 	int start = (int)SvIV(ST(0));
3236 
3237 	if (count > C_ARRAY_LENGTH(si) || count <= 0)
3238 	    Perl_croak(aTHX_ "unexpected count %d for OS2::SysInfoFor()", count);
3239 	if (CheckOSError(DosQuerySysInfo(start,
3240 					 start + count - 1,
3241 					 (PVOID)si,
3242 					 sizeof(si))))
3243 	    croak_with_os2error("DosQuerySysInfo() failed");
3244 	EXTEND(SP,count);
3245 	while (i < count) {
3246 	    ST(i) = sv_newmortal();
3247 	    sv_setiv(ST(i), si[i]);
3248 	    i++;
3249 	}
3250     }
3251     XSRETURN(count);
3252 }
3253 
3254 XS(XS_OS2_BootDrive)
3255 {
3256     dXSARGS;
3257     if (items != 0)
3258 	Perl_croak_nocontext("Usage: OS2::BootDrive()");
3259     {
3260 	ULONG   si[1] = {0};	/* System Information Data Buffer */
3261 	APIRET  rc    = NO_ERROR;	/* Return code            */
3262 	char c;
3263 	dXSTARG;
3264 
3265 	if (CheckOSError(DosQuerySysInfo(QSV_BOOT_DRIVE, QSV_BOOT_DRIVE,
3266 					 (PVOID)si, sizeof(si))))
3267 	    croak_with_os2error("DosQuerySysInfo() failed");
3268 	c = 'a' - 1 + si[0];
3269 	sv_setpvn(TARG, &c, 1);
3270 	XSprePUSH; PUSHTARG;
3271     }
3272     XSRETURN(1);
3273 }
3274 
3275 XS(XS_OS2_Beep)
3276 {
3277     dXSARGS;
3278     if (items > 2)			/* Defaults as for WinAlarm(ERROR) */
3279 	Perl_croak_nocontext("Usage: OS2::Beep(freq = 440, ms = 100)");
3280     {
3281 	ULONG freq	= (items > 0 ? (ULONG)SvUV(ST(0)) : 440);
3282 	ULONG ms	= (items > 1 ? (ULONG)SvUV(ST(1)) : 100);
3283 	ULONG rc;
3284 
3285 	if (CheckOSError(DosBeep(freq, ms)))
3286 	    croak_with_os2error("SysValues_set()");
3287     }
3288     XSRETURN_YES;
3289 }
3290 
3291 
3292 
3293 XS(XS_OS2_MorphPM)
3294 {
3295     dXSARGS;
3296     if (items != 1)
3297 	Perl_croak_nocontext("Usage: OS2::MorphPM(serve)");
3298     {
3299 	bool  serve = SvOK(ST(0));
3300 	unsigned long   pmq = perl_hmq_GET(serve);
3301 	dXSTARG;
3302 
3303 	XSprePUSH; PUSHi((IV)pmq);
3304     }
3305     XSRETURN(1);
3306 }
3307 
3308 XS(XS_OS2_UnMorphPM)
3309 {
3310     dXSARGS;
3311     if (items != 1)
3312 	Perl_croak_nocontext("Usage: OS2::UnMorphPM(serve)");
3313     {
3314 	bool  serve = SvOK(ST(0));
3315 
3316 	perl_hmq_UNSET(serve);
3317     }
3318     XSRETURN(0);
3319 }
3320 
3321 XS(XS_OS2_Serve_Messages)
3322 {
3323     dXSARGS;
3324     if (items != 1)
3325 	Perl_croak_nocontext("Usage: OS2::Serve_Messages(force)");
3326     {
3327 	bool  force = SvOK(ST(0));
3328 	unsigned long   cnt = Perl_Serve_Messages(force);
3329 	dXSTARG;
3330 
3331 	XSprePUSH; PUSHi((IV)cnt);
3332     }
3333     XSRETURN(1);
3334 }
3335 
3336 XS(XS_OS2_Process_Messages)
3337 {
3338     dXSARGS;
3339     if (items < 1 || items > 2)
3340 	Perl_croak_nocontext("Usage: OS2::Process_Messages(force [, cnt])");
3341     {
3342 	bool  force = SvOK(ST(0));
3343 	unsigned long   cnt;
3344 	dXSTARG;
3345 
3346 	if (items == 2) {
3347 	    I32 cntr;
3348 	    SV *sv = ST(1);
3349 
3350 	    (void)SvIV(sv);		/* Force SvIVX */
3351 	    if (!SvIOK(sv))
3352 		Perl_croak_nocontext("Can't upgrade count to IV");
3353 	    cntr = SvIVX(sv);
3354 	    cnt =  Perl_Process_Messages(force, &cntr);
3355 	    SvIVX(sv) = cntr;
3356 	} else {
3357 	    cnt =  Perl_Process_Messages(force, NULL);
3358         }
3359 	XSprePUSH; PUSHi((IV)cnt);
3360     }
3361     XSRETURN(1);
3362 }
3363 
3364 XS(XS_Cwd_current_drive)
3365 {
3366     dXSARGS;
3367     if (items != 0)
3368 	Perl_croak_nocontext("Usage: Cwd::current_drive()");
3369     {
3370 	char	RETVAL;
3371 	dXSTARG;
3372 
3373 	RETVAL = current_drive();
3374 	sv_setpvn(TARG, (char *)&RETVAL, 1);
3375 	XSprePUSH; PUSHTARG;
3376     }
3377     XSRETURN(1);
3378 }
3379 
3380 XS(XS_Cwd_sys_chdir)
3381 {
3382     dXSARGS;
3383     if (items != 1)
3384 	Perl_croak_nocontext("Usage: Cwd::sys_chdir(path)");
3385     {
3386 	STRLEN n_a;
3387 	char *	path = (char *)SvPV(ST(0),n_a);
3388 	bool	RETVAL;
3389 
3390 	RETVAL = sys_chdir(path);
3391 	ST(0) = boolSV(RETVAL);
3392 	if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
3393     }
3394     XSRETURN(1);
3395 }
3396 
3397 XS(XS_Cwd_change_drive)
3398 {
3399     dXSARGS;
3400     if (items != 1)
3401 	Perl_croak_nocontext("Usage: Cwd::change_drive(d)");
3402     {
3403 	STRLEN n_a;
3404 	char	d = (char)*SvPV(ST(0),n_a);
3405 	bool	RETVAL;
3406 
3407 	RETVAL = change_drive(d);
3408 	ST(0) = boolSV(RETVAL);
3409 	if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
3410     }
3411     XSRETURN(1);
3412 }
3413 
3414 XS(XS_Cwd_sys_is_absolute)
3415 {
3416     dXSARGS;
3417     if (items != 1)
3418 	Perl_croak_nocontext("Usage: Cwd::sys_is_absolute(path)");
3419     {
3420 	STRLEN n_a;
3421 	char *	path = (char *)SvPV(ST(0),n_a);
3422 	bool	RETVAL;
3423 
3424 	RETVAL = sys_is_absolute(path);
3425 	ST(0) = boolSV(RETVAL);
3426 	if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
3427     }
3428     XSRETURN(1);
3429 }
3430 
3431 XS(XS_Cwd_sys_is_rooted)
3432 {
3433     dXSARGS;
3434     if (items != 1)
3435 	Perl_croak_nocontext("Usage: Cwd::sys_is_rooted(path)");
3436     {
3437 	STRLEN n_a;
3438 	char *	path = (char *)SvPV(ST(0),n_a);
3439 	bool	RETVAL;
3440 
3441 	RETVAL = sys_is_rooted(path);
3442 	ST(0) = boolSV(RETVAL);
3443 	if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
3444     }
3445     XSRETURN(1);
3446 }
3447 
3448 XS(XS_Cwd_sys_is_relative)
3449 {
3450     dXSARGS;
3451     if (items != 1)
3452 	Perl_croak_nocontext("Usage: Cwd::sys_is_relative(path)");
3453     {
3454 	STRLEN n_a;
3455 	char *	path = (char *)SvPV(ST(0),n_a);
3456 	bool	RETVAL;
3457 
3458 	RETVAL = sys_is_relative(path);
3459 	ST(0) = boolSV(RETVAL);
3460 	if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
3461     }
3462     XSRETURN(1);
3463 }
3464 
3465 XS(XS_Cwd_sys_cwd)
3466 {
3467     dXSARGS;
3468     if (items != 0)
3469 	Perl_croak_nocontext("Usage: Cwd::sys_cwd()");
3470     {
3471 	char p[MAXPATHLEN];
3472 	char *	RETVAL;
3473 
3474 	/* Can't use TARG, since tainting behaves differently */
3475 	RETVAL = _getcwd2(p, MAXPATHLEN);
3476 	ST(0) = sv_newmortal();
3477 	sv_setpv(ST(0), RETVAL);
3478 	SvTAINTED_on(ST(0));
3479     }
3480     XSRETURN(1);
3481 }
3482 
3483 XS(XS_Cwd_sys_abspath)
3484 {
3485     dXSARGS;
3486     if (items > 2)
3487 	Perl_croak_nocontext("Usage: Cwd::sys_abspath(path = '.', dir = NULL)");
3488     {
3489 	STRLEN n_a;
3490 	char *	path = items ? (char *)SvPV(ST(0),n_a) : ".";
3491 	char *	dir, *s, *t, *e;
3492 	char p[MAXPATHLEN];
3493 	char *	RETVAL;
3494 	int l;
3495 	SV *sv;
3496 
3497 	if (items < 2)
3498 	    dir = NULL;
3499 	else {
3500 	    dir = (char *)SvPV(ST(1),n_a);
3501 	}
3502 	if (path[0] == '.' && (path[1] == '/' || path[1] == '\\')) {
3503 	    path += 2;
3504 	}
3505 	if (dir == NULL) {
3506 	    if (_abspath(p, path, MAXPATHLEN) == 0) {
3507 		RETVAL = p;
3508 	    } else {
3509 		RETVAL = NULL;
3510 	    }
3511 	} else {
3512 	    /* Absolute with drive: */
3513 	    if ( sys_is_absolute(path) ) {
3514 		if (_abspath(p, path, MAXPATHLEN) == 0) {
3515 		    RETVAL = p;
3516 		} else {
3517 		    RETVAL = NULL;
3518 		}
3519 	    } else if (path[0] == '/' || path[0] == '\\') {
3520 		/* Rooted, but maybe on different drive. */
3521 		if (isALPHA(dir[0]) && dir[1] == ':' ) {
3522 		    char p1[MAXPATHLEN];
3523 
3524 		    /* Need to prepend the drive. */
3525 		    p1[0] = dir[0];
3526 		    p1[1] = dir[1];
3527 		    Copy(path, p1 + 2, strlen(path) + 1, char);
3528 		    RETVAL = p;
3529 		    if (_abspath(p, p1, MAXPATHLEN) == 0) {
3530 			RETVAL = p;
3531 		    } else {
3532 			RETVAL = NULL;
3533 		    }
3534 		} else if (_abspath(p, path, MAXPATHLEN) == 0) {
3535 		    RETVAL = p;
3536 		} else {
3537 		    RETVAL = NULL;
3538 		}
3539 	    } else {
3540 		/* Either path is relative, or starts with a drive letter. */
3541 		/* If the path starts with a drive letter, then dir is
3542 		   relevant only if
3543 		   a/b)	it is absolute/x:relative on the same drive.
3544 		   c)	path is on current drive, and dir is rooted
3545 		   In all the cases it is safe to drop the drive part
3546 		   of the path. */
3547 		if ( !sys_is_relative(path) ) {
3548 		    if ( ( ( sys_is_absolute(dir)
3549 			     || (isALPHA(dir[0]) && dir[1] == ':'
3550 				 && strnicmp(dir, path,1) == 0))
3551 			   && strnicmp(dir, path,1) == 0)
3552 			 || ( !(isALPHA(dir[0]) && dir[1] == ':')
3553 			      && toupper(path[0]) == current_drive())) {
3554 			path += 2;
3555 		    } else if (_abspath(p, path, MAXPATHLEN) == 0) {
3556 			RETVAL = p; goto done;
3557 		    } else {
3558 			RETVAL = NULL; goto done;
3559 		    }
3560 		}
3561 		{
3562 		    /* Need to prepend the absolute path of dir. */
3563 		    char p1[MAXPATHLEN];
3564 
3565 		    if (_abspath(p1, dir, MAXPATHLEN) == 0) {
3566 			int l = strlen(p1);
3567 
3568 			if (p1[ l - 1 ] != '/') {
3569 			    p1[ l ] = '/';
3570 			    l++;
3571 			}
3572 			Copy(path, p1 + l, strlen(path) + 1, char);
3573 			if (_abspath(p, p1, MAXPATHLEN) == 0) {
3574 			    RETVAL = p;
3575 			} else {
3576 			    RETVAL = NULL;
3577 			}
3578 		    } else {
3579 			RETVAL = NULL;
3580 		    }
3581 		}
3582 	      done:
3583 	    }
3584 	}
3585 	if (!RETVAL)
3586 	    XSRETURN_EMPTY;
3587 	/* Backslashes are already converted to slashes. */
3588 	/* Remove trailing slashes */
3589 	l = strlen(RETVAL);
3590 	while (l > 0 && RETVAL[l-1] == '/')
3591 	    l--;
3592 	ST(0) = sv_newmortal();
3593 	sv_setpvn( sv = (SV*)ST(0), RETVAL, l);
3594 	/* Remove duplicate slashes, skipping the first three, which
3595 	   may be parts of a server-based path */
3596 	s = t = 3 + SvPV_force(sv, n_a);
3597 	e = SvEND(sv);
3598 	/* Do not worry about multibyte chars here, this would contradict the
3599 	   eventual UTFization, and currently most other places break too... */
3600 	while (s < e) {
3601 	    if (s[0] == t[-1] && s[0] == '/')
3602 		s++;				/* Skip duplicate / */
3603 	    else
3604 		*t++ = *s++;
3605 	}
3606 	if (t < e) {
3607 	    *t = 0;
3608 	    SvCUR_set(sv, t - SvPVX(sv));
3609 	}
3610 	if (!items)
3611 	    SvTAINTED_on(ST(0));
3612     }
3613     XSRETURN(1);
3614 }
3615 typedef APIRET (*PELP)(PSZ path, ULONG type);
3616 
3617 /* Kernels after 2000/09/15 understand this too: */
3618 #ifndef LIBPATHSTRICT
3619 #  define LIBPATHSTRICT 3
3620 #endif
3621 
3622 APIRET
3623 ExtLIBPATH(ULONG ord, PSZ path, IV type, int fatal)
3624 {
3625     ULONG what;
3626     PFN f = loadByOrdinal(ord, fatal);	/* if fatal: load or die! */
3627 
3628     if (!f)				/* Impossible with fatal */
3629 	return Perl_rc;
3630     if (type > 0)
3631 	what = END_LIBPATH;
3632     else if (type == 0)
3633 	what = BEGIN_LIBPATH;
3634     else
3635 	what = LIBPATHSTRICT;
3636     return (*(PELP)f)(path, what);
3637 }
3638 
3639 #define extLibpath(to,type, fatal) 					\
3640     (CheckOSError(ExtLIBPATH(ORD_DosQueryExtLibpath, (to), (type), fatal)) ? NULL : (to) )
3641 
3642 #define extLibpath_set(p,type, fatal) 					\
3643     (!CheckOSError(ExtLIBPATH(ORD_DosSetExtLibpath, (p), (type), fatal)))
3644 
3645 static void
3646 early_error(char *msg1, char *msg2, char *msg3)
3647 {	/* Buffer overflow detected; there is very little we can do... */
3648     ULONG rc;
3649 
3650     DosWrite(2, msg1, strlen(msg1), &rc);
3651     DosWrite(2, msg2, strlen(msg2), &rc);
3652     DosWrite(2, msg3, strlen(msg3), &rc);
3653     DosExit(EXIT_PROCESS, 2);
3654 }
3655 
3656 XS(XS_Cwd_extLibpath)
3657 {
3658     dXSARGS;
3659     if (items < 0 || items > 1)
3660 	Perl_croak_nocontext("Usage: OS2::extLibpath(type = 0)");
3661     {
3662 	IV	type;
3663 	char	to[1024];
3664 	U32	rc;
3665 	char *	RETVAL;
3666 	dXSTARG;
3667 	STRLEN l;
3668 
3669 	if (items < 1)
3670 	    type = 0;
3671 	else {
3672 	    type = SvIV(ST(0));
3673 	}
3674 
3675 	to[0] = 1; to[1] = 0;		/* Sometimes no error reported */
3676 	RETVAL = extLibpath(to, type, 1);	/* Make errors fatal */
3677 	if (RETVAL && RETVAL[0] == 1 && RETVAL[1] == 0)
3678 	    Perl_croak_nocontext("panic OS2::extLibpath parameter");
3679 	l = strlen(to);
3680 	if (l >= sizeof(to))
3681 	    early_error("Buffer overflow while getting BEGIN/ENDLIBPATH: `",
3682 			to, "'\r\n");		/* Will not return */
3683 	sv_setpv(TARG, RETVAL);
3684 	XSprePUSH; PUSHTARG;
3685     }
3686     XSRETURN(1);
3687 }
3688 
3689 XS(XS_Cwd_extLibpath_set)
3690 {
3691     dXSARGS;
3692     if (items < 1 || items > 2)
3693 	Perl_croak_nocontext("Usage: OS2::extLibpath_set(s, type = 0)");
3694     {
3695 	STRLEN n_a;
3696 	char *	s = (char *)SvPV(ST(0),n_a);
3697 	IV	type;
3698 	U32	rc;
3699 	bool	RETVAL;
3700 
3701 	if (items < 2)
3702 	    type = 0;
3703 	else {
3704 	    type = SvIV(ST(1));
3705 	}
3706 
3707 	RETVAL = extLibpath_set(s, type, 1);	/* Make errors fatal */
3708 	ST(0) = boolSV(RETVAL);
3709 	if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
3710     }
3711     XSRETURN(1);
3712 }
3713 
3714 ULONG
3715 fill_extLibpath(int type, char *pre, char *post, int replace, char *msg)
3716 {
3717     char buf[2048], *to = buf, buf1[300], *s;
3718     STRLEN l;
3719     ULONG rc;
3720 
3721     if (!pre && !post)
3722 	return 0;
3723     if (pre) {
3724 	pre = dir_subst(pre, strlen(pre), buf1, sizeof buf1, dir_subst_pathlike, msg);
3725 	if (!pre)
3726 	    return ERROR_INVALID_PARAMETER;
3727 	l = strlen(pre);
3728 	if (l >= sizeof(buf)/2)
3729 	    return ERROR_BUFFER_OVERFLOW;
3730 	s = pre - 1;
3731 	while (*++s)
3732 	    if (*s == '/')
3733 		*s = '\\';			/* Be extra cautious */
3734 	memcpy(to, pre, l);
3735 	if (!l || to[l-1] != ';')
3736 	    to[l++] = ';';
3737 	to += l;
3738     }
3739 
3740     if (!replace) {
3741       to[0] = 1; to[1] = 0;		/* Sometimes no error reported */
3742       rc = ExtLIBPATH(ORD_DosQueryExtLibpath, to, type, 0);	/* Do not croak */
3743       if (rc)
3744 	return rc;
3745       if (to[0] == 1 && to[1] == 0)
3746 	return ERROR_INVALID_PARAMETER;
3747       to += strlen(to);
3748       if (buf + sizeof(buf) - 1 <= to)	/* Buffer overflow */
3749 	early_error("Buffer overflow while getting BEGIN/ENDLIBPATH: `",
3750 		    buf, "'\r\n");		/* Will not return */
3751       if (to > buf && to[-1] != ';')
3752 	*to++ = ';';
3753     }
3754     if (post) {
3755 	post = dir_subst(post, strlen(post), buf1, sizeof buf1, dir_subst_pathlike, msg);
3756 	if (!post)
3757 	    return ERROR_INVALID_PARAMETER;
3758 	l = strlen(post);
3759 	if (l + to - buf >= sizeof(buf) - 1)
3760 	    return ERROR_BUFFER_OVERFLOW;
3761 	s = post - 1;
3762 	while (*++s)
3763 	    if (*s == '/')
3764 		*s = '\\';			/* Be extra cautious */
3765 	memcpy(to, post, l);
3766 	if (!l || to[l-1] != ';')
3767 	    to[l++] = ';';
3768 	to += l;
3769     }
3770     *to = 0;
3771     rc = ExtLIBPATH(ORD_DosSetExtLibpath, buf, type, 0); /* Do not croak */
3772     return rc;
3773 }
3774 
3775 /* Input: Address, BufLen
3776 APIRET APIENTRY
3777 DosQueryModFromEIP (HMODULE * hmod, ULONG * obj, ULONG BufLen, PCHAR Buf,
3778 		    ULONG * Offset, ULONG Address);
3779 */
3780 
3781 DeclOSFuncByORD(APIRET, _DosQueryModFromEIP,ORD_DosQueryModFromEIP,
3782 			(HMODULE * hmod, ULONG * obj, ULONG BufLen, PCHAR Buf,
3783 			ULONG * Offset, ULONG Address),
3784 			(hmod, obj, BufLen, Buf, Offset, Address))
3785 
3786 static SV*
3787 module_name_at(void *pp, enum module_name_how how)
3788 {
3789     dTHX;
3790     char buf[MAXPATHLEN];
3791     char *p = buf;
3792     HMODULE mod;
3793     ULONG obj, offset, rc, addr = (ULONG)pp;
3794 
3795     if (how & mod_name_HMODULE) {
3796 	if ((how & ~mod_name_HMODULE) == mod_name_shortname)
3797 	    Perl_croak(aTHX_ "Can't get short module name from a handle");
3798 	mod = (HMODULE)pp;
3799 	how &= ~mod_name_HMODULE;
3800     } else if (!_DosQueryModFromEIP(&mod, &obj, sizeof(buf), buf, &offset, addr))
3801 	return &PL_sv_undef;
3802     if (how == mod_name_handle)
3803 	return newSVuv(mod);
3804     /* Full name... */
3805     if ( how != mod_name_shortname
3806 	 && CheckOSError(DosQueryModuleName(mod, sizeof(buf), buf)) )
3807 	return &PL_sv_undef;
3808     while (*p) {
3809 	if (*p == '\\')
3810 	    *p = '/';
3811 	p++;
3812     }
3813     return newSVpv(buf, 0);
3814 }
3815 
3816 static SV*
3817 module_name_of_cv(SV *cv, enum module_name_how how)
3818 {
3819     if (!cv || !SvROK(cv) || SvTYPE(SvRV(cv)) != SVt_PVCV || !CvXSUB(SvRV(cv))) {
3820 	dTHX;
3821 
3822 	if (how & mod_name_C_function)
3823 	    return module_name_at((void*)SvIV(cv), how & ~mod_name_C_function);
3824 	else if (how & mod_name_HMODULE)
3825 	    return module_name_at((void*)SvIV(cv), how);
3826 	Perl_croak(aTHX_ "Not an XSUB reference");
3827     }
3828     return module_name_at(CvXSUB(SvRV(cv)), how);
3829 }
3830 
3831 XS(XS_OS2_DLLname)
3832 {
3833     dXSARGS;
3834     if (items > 2)
3835 	Perl_croak(aTHX_ "Usage: OS2::DLLname( [ how, [\\&xsub] ] )");
3836     {
3837 	SV *	RETVAL;
3838 	int	how;
3839 
3840 	if (items < 1)
3841 	    how = mod_name_full;
3842 	else {
3843 	    how = (int)SvIV(ST(0));
3844 	}
3845 	if (items < 2)
3846 	    RETVAL = module_name(how);
3847 	else
3848 	    RETVAL = module_name_of_cv(ST(1), how);
3849 	ST(0) = RETVAL;
3850 	sv_2mortal(ST(0));
3851     }
3852     XSRETURN(1);
3853 }
3854 
3855 DeclOSFuncByORD(INT, _Dos32QueryHeaderInfo, ORD_Dos32QueryHeaderInfo,
3856 			(ULONG r1, ULONG r2, PVOID buf, ULONG szbuf, ULONG fnum),
3857 			(r1, r2, buf, szbuf, fnum))
3858 
3859 XS(XS_OS2__headerInfo)
3860 {
3861     dXSARGS;
3862     if (items > 4 || items < 2)
3863 	Perl_croak(aTHX_ "Usage: OS2::_headerInfo(req,size[,handle,[offset]])");
3864     {
3865 	ULONG	req = (ULONG)SvIV(ST(0));
3866 	STRLEN	size = (STRLEN)SvIV(ST(1)), n_a;
3867 	ULONG	handle = (items >= 3 ? (ULONG)SvIV(ST(2)) : 0);
3868 	ULONG	offset = (items >= 4 ? (ULONG)SvIV(ST(3)) : 0);
3869 
3870 	if (size <= 0)
3871 	    Perl_croak(aTHX_ "OS2::_headerInfo(): unexpected size: %d", (int)size);
3872 	ST(0) = newSVpvs("");
3873 	SvGROW(ST(0), size + 1);
3874 	sv_2mortal(ST(0));
3875 
3876 	if (!_Dos32QueryHeaderInfo(handle, offset, SvPV(ST(0), n_a), size, req))
3877 	    Perl_croak(aTHX_ "OS2::_headerInfo(%ld,%ld,%ld,%ld) error: %s",
3878 		       req, size, handle, offset, os2error(Perl_rc));
3879 	SvCUR_set(ST(0), size);
3880 	*SvEND(ST(0)) = 0;
3881     }
3882     XSRETURN(1);
3883 }
3884 
3885 #define DQHI_QUERYLIBPATHSIZE      4
3886 #define DQHI_QUERYLIBPATH          5
3887 
3888 XS(XS_OS2_libPath)
3889 {
3890     dXSARGS;
3891     if (items != 0)
3892 	Perl_croak(aTHX_ "Usage: OS2::libPath()");
3893     {
3894 	ULONG	size;
3895 	STRLEN	n_a;
3896 
3897 	if (!_Dos32QueryHeaderInfo(0, 0, &size, sizeof(size),
3898 				   DQHI_QUERYLIBPATHSIZE))
3899 	    Perl_croak(aTHX_ "OS2::_headerInfo(%ld,%ld,%ld,%ld) error: %s",
3900 		       DQHI_QUERYLIBPATHSIZE, sizeof(size), 0, 0,
3901 		       os2error(Perl_rc));
3902 	ST(0) = newSVpvs("");
3903 	SvGROW(ST(0), size + 1);
3904 	sv_2mortal(ST(0));
3905 
3906 	/* We should be careful: apparently, this entry point does not
3907 	   pay attention to the size argument, so may overwrite
3908 	   unrelated data! */
3909 	if (!_Dos32QueryHeaderInfo(0, 0, SvPV(ST(0), n_a), size,
3910 				   DQHI_QUERYLIBPATH))
3911 	    Perl_croak(aTHX_ "OS2::_headerInfo(%ld,%ld,%ld,%ld) error: %s",
3912 		       DQHI_QUERYLIBPATH, size, 0, 0, os2error(Perl_rc));
3913 	SvCUR_set(ST(0), size);
3914 	*SvEND(ST(0)) = 0;
3915     }
3916     XSRETURN(1);
3917 }
3918 
3919 #define get_control87()		_control87(0,0)
3920 #define set_control87		_control87
3921 
3922 XS(XS_OS2__control87)
3923 {
3924     dXSARGS;
3925     if (items != 2)
3926 	Perl_croak(aTHX_ "Usage: OS2::_control87(new,mask)");
3927     {
3928 	unsigned	new = (unsigned)SvIV(ST(0));
3929 	unsigned	mask = (unsigned)SvIV(ST(1));
3930 	unsigned	RETVAL;
3931 	dXSTARG;
3932 
3933 	RETVAL = _control87(new, mask);
3934 	XSprePUSH; PUSHi((IV)RETVAL);
3935     }
3936     XSRETURN(1);
3937 }
3938 
3939 XS(XS_OS2_mytype)
3940 {
3941     dXSARGS;
3942     int which = 0;
3943 
3944     if (items < 0 || items > 1)
3945 	Perl_croak(aTHX_ "Usage: OS2::mytype([which])");
3946     if (items == 1)
3947 	which = (int)SvIV(ST(0));
3948     {
3949 	unsigned	RETVAL;
3950 	dXSTARG;
3951 
3952 	switch (which) {
3953 	case 0:
3954 	    RETVAL = os2_mytype;	/* Reset after fork */
3955 	    break;
3956 	case 1:
3957 	    RETVAL = os2_mytype_ini;	/* Before any fork */
3958 	    break;
3959 	case 2:
3960 	    RETVAL = Perl_os2_initial_mode;	/* Before first morphing */
3961 	    break;
3962 	case 3:
3963 	    RETVAL = my_type();		/* Morphed type */
3964 	    break;
3965 	default:
3966 	    Perl_croak(aTHX_ "OS2::mytype(which): unknown which=%d", which);
3967 	}
3968 	XSprePUSH; PUSHi((IV)RETVAL);
3969     }
3970     XSRETURN(1);
3971 }
3972 
3973 
3974 XS(XS_OS2_mytype_set)
3975 {
3976     dXSARGS;
3977     int type;
3978 
3979     if (items == 1)
3980 	type = (int)SvIV(ST(0));
3981     else
3982 	Perl_croak(aTHX_ "Usage: OS2::mytype_set(type)");
3983     my_type_set(type);
3984     XSRETURN_YES;
3985 }
3986 
3987 
3988 XS(XS_OS2_get_control87)
3989 {
3990     dXSARGS;
3991     if (items != 0)
3992 	Perl_croak(aTHX_ "Usage: OS2::get_control87()");
3993     {
3994 	unsigned	RETVAL;
3995 	dXSTARG;
3996 
3997 	RETVAL = get_control87();
3998 	XSprePUSH; PUSHi((IV)RETVAL);
3999     }
4000     XSRETURN(1);
4001 }
4002 
4003 
4004 XS(XS_OS2_set_control87)
4005 {
4006     dXSARGS;
4007     if (items < 0 || items > 2)
4008 	Perl_croak(aTHX_ "Usage: OS2::set_control87(new=MCW_EM, mask=MCW_EM)");
4009     {
4010 	unsigned	new;
4011 	unsigned	mask;
4012 	unsigned	RETVAL;
4013 	dXSTARG;
4014 
4015 	if (items < 1)
4016 	    new = MCW_EM;
4017 	else {
4018 	    new = (unsigned)SvIV(ST(0));
4019 	}
4020 
4021 	if (items < 2)
4022 	    mask = MCW_EM;
4023 	else {
4024 	    mask = (unsigned)SvIV(ST(1));
4025 	}
4026 
4027 	RETVAL = set_control87(new, mask);
4028 	XSprePUSH; PUSHi((IV)RETVAL);
4029     }
4030     XSRETURN(1);
4031 }
4032 
4033 XS(XS_OS2_incrMaxFHandles)		/* DosSetRelMaxFH */
4034 {
4035     dXSARGS;
4036     if (items < 0 || items > 1)
4037 	Perl_croak(aTHX_ "Usage: OS2::incrMaxFHandles(delta = 0)");
4038     {
4039 	LONG	delta;
4040 	ULONG	RETVAL, rc;
4041 	dXSTARG;
4042 
4043 	if (items < 1)
4044 	    delta = 0;
4045 	else
4046 	    delta = (LONG)SvIV(ST(0));
4047 
4048 	if (CheckOSError(DosSetRelMaxFH(&delta, &RETVAL)))
4049 	    croak_with_os2error("OS2::incrMaxFHandles(): DosSetRelMaxFH() error");
4050 	XSprePUSH; PUSHu((UV)RETVAL);
4051     }
4052     XSRETURN(1);
4053 }
4054 
4055 /* wait>0: force wait, wait<0: force nowait;
4056    if restore, save/restore flags; otherwise flags are in oflags.
4057 
4058    Returns 1 if connected, 0 if not (due to nowait); croaks on error. */
4059 static ULONG
4060 connectNPipe(ULONG hpipe, int wait, ULONG restore, ULONG oflags)
4061 {
4062     ULONG ret = ERROR_INTERRUPT, rc, flags;
4063 
4064     if (restore && wait)
4065 	os2cp_croak(DosQueryNPHState(hpipe, &oflags), "DosQueryNPHState()");
4066     /* DosSetNPHState fails if more bits than NP_NOWAIT|NP_READMODE_MESSAGE */
4067     oflags &= (NP_NOWAIT | NP_READMODE_MESSAGE);
4068     flags = (oflags & ~NP_NOWAIT) | (wait > 0 ? NP_WAIT : NP_NOWAIT);
4069     /* We know (o)flags unless wait == 0 && restore */
4070     if (wait && (flags != oflags))
4071 	os2cp_croak(DosSetNPHState(hpipe, flags), "DosSetNPHState()");
4072     while (ret == ERROR_INTERRUPT)
4073 	ret = DosConnectNPipe(hpipe);
4074     (void)CheckOSError(ret);
4075     if (restore && wait && (flags != oflags))
4076 	os2cp_croak(DosSetNPHState(hpipe, oflags), "DosSetNPHState() back");
4077     /* We know flags unless wait == 0 && restore */
4078     if ( ((wait || restore) ? (flags & NP_NOWAIT) : 1)
4079 	 && (ret == ERROR_PIPE_NOT_CONNECTED) )
4080 	return 0;			/* normal return value */
4081     if (ret == NO_ERROR)
4082 	return 1;
4083     croak_with_os2error("DosConnectNPipe()");
4084 }
4085 
4086 /* With a lot of manual editing:
4087 NO_OUTPUT ULONG
4088 DosCreateNPipe(PCSZ pszName, OUTLIST HPIPE hpipe, ULONG ulOpenMode, int connect = 1, int count = 1, ULONG ulInbufLength = 8192, ULONG ulOutbufLength = ulInbufLength, ULONG ulPipeMode = count | NP_NOWAIT | NP_TYPE_BYTE | NP_READMODE_BYTE, ULONG ulTimeout = 0)
4089    PREINIT:
4090 	ULONG rc;
4091    C_ARGS:
4092 	pszName, &hpipe, ulOpenMode, ulPipeMode, ulInbufLength, ulOutbufLength, ulTimeout
4093    POSTCALL:
4094 	if (CheckOSError(RETVAL))
4095 	    croak_with_os2error("OS2::mkpipe() error");
4096 */
4097 XS(XS_OS2_pipe); /* prototype to pass -Wmissing-prototypes */
4098 XS(XS_OS2_pipe)
4099 {
4100     dXSARGS;
4101     if (items < 2 || items > 8)
4102 	Perl_croak(aTHX_ "Usage: OS2::pipe(pszName, ulOpenMode, connect= 1, count= 1, ulInbufLength= 8192, ulOutbufLength= ulInbufLength, ulPipeMode= count | NP_NOWAIT | NP_TYPE_BYTE | NP_READMODE_BYTE, ulTimeout= 0)");
4103     {
4104 	ULONG	RETVAL;
4105 	PCSZ	pszName = ( SvOK(ST(0)) ? (PCSZ)SvPV_nolen(ST(0)) : NULL );
4106 	HPIPE	hpipe;
4107 	SV	*OpenMode = ST(1);
4108 	ULONG	ulOpenMode;
4109 	int	connect = 0, count, message_r = 0, message = 0, b = 0;
4110 	ULONG	ulInbufLength,	ulOutbufLength,	ulPipeMode, ulTimeout, rc;
4111 	STRLEN	len;
4112 	char	*s, buf[10], *s1, *perltype = NULL;
4113 	PerlIO	*perlio;
4114 	double	timeout;
4115 
4116 	if (!pszName || !*pszName)
4117 	    Perl_croak(aTHX_ "OS2::pipe(): empty pipe name");
4118 	s = SvPV(OpenMode, len);
4119 	if (memEQs(s, len, "wait")) {	/* DosWaitNPipe() */
4120 	    ULONG ms = 0xFFFFFFFF, ret = ERROR_INTERRUPT; /* Indefinite */
4121 
4122 	    if (items == 3) {
4123 		timeout = (double)SvNV(ST(2));
4124 		ms = timeout * 1000;
4125 		if (timeout < 0)
4126 		    ms = 0xFFFFFFFF; /* Indefinite */
4127 		else if (timeout && !ms)
4128 		    ms = 1;
4129 	    } else if (items > 3)
4130 		Perl_croak(aTHX_ "OS2::pipe(): too many arguments for wait-for-connect: %ld", (long)items);
4131 
4132 	    while (ret == ERROR_INTERRUPT)
4133 		ret = DosWaitNPipe(pszName, ms);	/* XXXX Update ms? */
4134 	    os2cp_croak(ret, "DosWaitNPipe()");
4135 	    XSRETURN_YES;
4136 	}
4137 	if (memEQs(s, len, "call")) {	/* DosCallNPipe() */
4138 	    ULONG ms = 0xFFFFFFFF, got; /* Indefinite */
4139 	    STRLEN l;
4140 	    char *s;
4141 	    char buf[8192];
4142 	    STRLEN ll = sizeof(buf);
4143 	    char *b = buf;
4144 
4145 	    if (items < 3 || items > 5)
4146 		Perl_croak(aTHX_ "usage: OS2::pipe(pszName, 'call', write [, timeout= 0xFFFFFFFF, buffsize = 8192])");
4147 	    s = SvPV(ST(2), l);
4148 	    if (items >= 4) {
4149 		timeout = (double)SvNV(ST(3));
4150 		ms = timeout * 1000;
4151 		if (timeout < 0)
4152 		    ms = 0xFFFFFFFF; /* Indefinite */
4153 		else if (timeout && !ms)
4154 		    ms = 1;
4155 	    }
4156 	    if (items >= 5) {
4157 		STRLEN lll = SvUV(ST(4));
4158 		SV *sv = NEWSV(914, lll);
4159 
4160 		sv_2mortal(sv);
4161 		ll = lll;
4162 		b = SvPVX(sv);
4163 	    }
4164 
4165 	    os2cp_croak(DosCallNPipe(pszName, s, l, b, ll, &got, ms),
4166 			"DosCallNPipe()");
4167 	    XSRETURN_PVN(b, got);
4168 	}
4169 	s1 = buf;
4170 	if (len && len <= 3 && !(*s >= '0' && *s <= '9')) {
4171 	    int r, w, R, W;
4172 
4173 	    r = strchr(s, 'r') != 0;
4174 	    w = strchr(s, 'w') != 0;
4175 	    R = strchr(s, 'R') != 0;
4176 	    W = strchr(s, 'W') != 0;
4177 	    b = strchr(s, 'b') != 0;
4178 	    if (r + w + R + W + b != len || (r && R) || (w && W))
4179 		Perl_croak(aTHX_ "OS2::pipe(): unknown OpenMode argument: `%s'", s);
4180 	    if ((r || R) && (w || W))
4181 		ulOpenMode = NP_INHERIT | NP_NOWRITEBEHIND | NP_ACCESS_DUPLEX;
4182 	    else if (r || R)
4183 		ulOpenMode = NP_INHERIT | NP_NOWRITEBEHIND | NP_ACCESS_INBOUND;
4184 	    else
4185 		ulOpenMode = NP_INHERIT | NP_NOWRITEBEHIND | NP_ACCESS_OUTBOUND;
4186 	    if (R)
4187 		message = message_r = 1;
4188 	    if (W)
4189 		message = 1;
4190 	    else if (w && R)
4191 		Perl_croak(aTHX_ "OS2::pipe(): can't have message read mode for non-message pipes");
4192 	} else
4193 	    ulOpenMode = (ULONG)SvUV(OpenMode);	/* ST(1) */
4194 
4195 	if ( (ulOpenMode & 0x3) == NP_ACCESS_DUPLEX
4196 	     || (ulOpenMode & 0x3) == NP_ACCESS_INBOUND )
4197 	    *s1++ = 'r';
4198 	if ( (ulOpenMode & 0x3) == NP_ACCESS_DUPLEX )
4199 	    *s1++ = '+';
4200 	if ( (ulOpenMode & 0x3) == NP_ACCESS_OUTBOUND )
4201 	    *s1++ = 'w';
4202 	if (b)
4203 	    *s1++ = 'b';
4204 	*s1 = 0;
4205 	if ( (ulOpenMode & 0x3) == NP_ACCESS_DUPLEX )
4206 	    perltype = "+<&";
4207 	else if ( (ulOpenMode & 0x3) == NP_ACCESS_OUTBOUND )
4208 	    perltype = ">&";
4209 	else
4210 	    perltype = "<&";
4211 
4212 	if (items < 3)
4213 	    connect = -1;			/* no wait */
4214 	else if (SvTRUE(ST(2))) {
4215 	    s = SvPV(ST(2), len);
4216 	    if (memEQs(s, len, "nowait"))
4217 		connect = -1;			/* no wait */
4218 	    else if (memEQs(s, len, "wait"))
4219 		connect = 1;			/* wait */
4220 	    else
4221 		Perl_croak(aTHX_ "OS2::pipe(): unknown connect argument: `%s'", s);
4222 	}
4223 
4224 	if (items < 4)
4225 	    count = 1;
4226 	else
4227 	    count = (int)SvIV(ST(3));
4228 
4229 	if (items < 5)
4230 	    ulInbufLength = 8192;
4231 	else
4232 	    ulInbufLength = (ULONG)SvUV(ST(4));
4233 
4234 	if (items < 6)
4235 	    ulOutbufLength = ulInbufLength;
4236 	else
4237 	    ulOutbufLength = (ULONG)SvUV(ST(5));
4238 
4239 	if (count < -1 || count == 0 || count >= 255)
4240 	    Perl_croak(aTHX_ "OS2::pipe(): count should be -1 or between 1 and 254: %ld", (long)count);
4241 	if (count < 0 )
4242 	    count = 255;		/* Unlimited */
4243 
4244 	ulPipeMode = count;
4245 	if (items < 7)
4246 	    ulPipeMode |= (NP_WAIT
4247 			   | (message ? NP_TYPE_MESSAGE : NP_TYPE_BYTE)
4248 			   | (message_r ? NP_READMODE_MESSAGE : NP_READMODE_BYTE));
4249 	else
4250 	    ulPipeMode |= (ULONG)SvUV(ST(6));
4251 
4252 	if (items < 8)
4253 	    timeout = 0;
4254 	else
4255 	    timeout = (double)SvNV(ST(7));
4256 	ulTimeout = timeout * 1000;
4257 	if (timeout < 0)
4258 	    ulTimeout = 0xFFFFFFFF; /* Indefinite */
4259 	else if (timeout && !ulTimeout)
4260 	    ulTimeout = 1;
4261 
4262 	RETVAL = DosCreateNPipe(pszName, &hpipe, ulOpenMode, ulPipeMode, ulInbufLength, ulOutbufLength, ulTimeout);
4263 	if (CheckOSError(RETVAL))
4264 	    croak_with_os2error("OS2::pipe(): DosCreateNPipe() error");
4265 
4266 	if (connect)
4267 	    connectNPipe(hpipe, connect, 1, 0);	/* XXXX wait, retval */
4268 	hpipe = __imphandle(hpipe);
4269 
4270 	perlio = PerlIO_fdopen(hpipe, buf);
4271 	ST(0) = sv_newmortal();
4272 	{
4273 	    GV *gv = (GV *)sv_newmortal();
4274 	    gv_init_pvn(gv, gv_stashpvs("OS2::pipe",1),"__ANONIO__",10,0);
4275 	    if ( do_open6(gv, perltype, strlen(perltype), perlio, NULL, 0) )
4276 		sv_setsv(ST(0), sv_bless(newRV((SV*)gv), gv_stashpv("IO::Handle",1)));
4277 	    else
4278 		ST(0) = &PL_sv_undef;
4279 	}
4280     }
4281     XSRETURN(1);
4282 }
4283 
4284 XS(XS_OS2_pipeCntl); /* prototype to pass -Wmissing-prototypes */
4285 XS(XS_OS2_pipeCntl)
4286 {
4287     dXSARGS;
4288     if (items < 2 || items > 3)
4289 	Perl_croak(aTHX_ "Usage: OS2::pipeCntl(pipe, op [, wait])");
4290     {
4291 	ULONG	rc;
4292 	PerlIO *perlio = IoIFP(sv_2io(ST(0)));
4293 	IV	fn = PerlIO_fileno(perlio);
4294 	HPIPE	hpipe = (HPIPE)fn;
4295 	STRLEN	len;
4296 	char	*s = SvPV(ST(1), len);
4297 	int	wait = 0, disconnect = 0, connect = 0, message = -1, query = 0;
4298 	int	peek = 0, state = 0, info = 0;
4299 
4300 	if (fn < 0)
4301 	    Perl_croak(aTHX_ "OS2::pipeCntl(): not a pipe");
4302 	if (items == 3)
4303 	    wait = (SvTRUE(ST(2)) ? 1 : -1);
4304 
4305 	switch (len) {
4306 	case 4:
4307 	    if (strEQ(s, "byte"))
4308 		message = 0;
4309 	    else if (strEQ(s, "peek"))
4310 		peek = 1;
4311 	    else if (strEQ(s, "info"))
4312 		info = 1;
4313 	    else
4314 		goto unknown;
4315 	    break;
4316 	case 5:
4317 	    if (strEQ(s, "reset"))
4318 		disconnect = connect = 1;
4319 	    else if (strEQ(s, "state"))
4320 		query = 1;
4321 	    else
4322 		goto unknown;
4323 	    break;
4324 	case 7:
4325 	    if (strEQ(s, "connect"))
4326 		connect = 1;
4327 	    else if (strEQ(s, "message"))
4328 		message = 1;
4329 	    else
4330 		goto unknown;
4331 	    break;
4332 	case 9:
4333 	    if (!strEQ(s, "readstate"))
4334 		goto unknown;
4335 	    state = 1;
4336 	    break;
4337 	case 10:
4338 	    if (!strEQ(s, "disconnect"))
4339 		goto unknown;
4340 	    disconnect = 1;
4341 	    break;
4342 	default:
4343 	  unknown:
4344 	    Perl_croak(aTHX_ "OS2::pipeCntl(): unknown argument: `%s'", s);
4345 	    break;
4346 	}
4347 
4348 	if (items == 3 && !connect)
4349 	    Perl_croak(aTHX_ "OS2::pipeCntl(): no wait argument for `%s'", s);
4350 
4351 	XSprePUSH;		/* Do not need arguments any more */
4352 	if (disconnect) {
4353 	    os2cp_croak(DosDisConnectNPipe(hpipe), "OS2::pipeCntl(): DosDisConnectNPipe()");
4354 	    PerlIO_clearerr(perlio);
4355 	}
4356 	if (connect) {
4357 	    if (!connectNPipe(hpipe, wait , 1, 0))
4358 		XSRETURN_IV(-1);
4359 	}
4360 	if (query) {
4361 	    ULONG flags;
4362 
4363 	    os2cp_croak(DosQueryNPHState(hpipe, &flags), "DosQueryNPHState()");
4364 	    XSRETURN_UV(flags);
4365 	}
4366 	if (peek || state || info) {
4367 	    ULONG BytesRead, PipeState;
4368 	    AVAILDATA BytesAvail;
4369 
4370 	    os2cp_croak( DosPeekNPipe(hpipe, NULL, 0, &BytesRead, &BytesAvail,
4371 				      &PipeState), "DosPeekNPipe() for state");
4372 	    if (state) {
4373 		EXTEND(SP, 3);
4374 		mPUSHu(PipeState);
4375 		/*   Bytes (available/in-message) */
4376 		mPUSHi(BytesAvail.cbpipe);
4377 		mPUSHi(BytesAvail.cbmessage);
4378 		XSRETURN(3);
4379 	    } else if (info) {
4380 		/* L S S C C C/Z*
4381 		   ID of the (remote) computer
4382 		   buffers (out/in)
4383 		   instances (max/actual)
4384 		 */
4385 		struct pipe_info_t {
4386 		    ULONG id;			/* char id[4]; */
4387 		    PIPEINFO pInfo;
4388 		    char buf[512];
4389 		} b;
4390 		int size;
4391 
4392 		os2cp_croak( DosQueryNPipeInfo(hpipe, 1, &b.pInfo, sizeof(b) - STRUCT_OFFSET(struct pipe_info_t, pInfo)),
4393 			     "DosQueryNPipeInfo(1)");
4394 		os2cp_croak( DosQueryNPipeInfo(hpipe, 2, &b.id, sizeof(b.id)),
4395 			     "DosQueryNPipeInfo(2)");
4396 		size = b.pInfo.cbName;
4397 		/* Trailing 0 is included in cbName - undocumented; so
4398 		   one should always extract with Z* */
4399 		if (size)		/* name length 254 or less */
4400 		    size--;
4401 		else
4402 		    size = strlen(b.pInfo.szName);
4403 		EXTEND(SP, 6);
4404 		mPUSHp(b.pInfo.szName, size);
4405 		mPUSHu(b.id);
4406 		mPUSHi(b.pInfo.cbOut);
4407 		mPUSHi(b.pInfo.cbIn);
4408 		mPUSHi(b.pInfo.cbMaxInst);
4409 		mPUSHi(b.pInfo.cbCurInst);
4410 		XSRETURN(6);
4411 	    } else if (BytesAvail.cbpipe == 0) {
4412 		XSRETURN_NO;
4413 	    } else {
4414 		SV *tmp = NEWSV(914, BytesAvail.cbpipe);
4415 		char *s = SvPVX(tmp);
4416 
4417 		sv_2mortal(tmp);
4418 		os2cp_croak( DosPeekNPipe(hpipe, s, BytesAvail.cbpipe, &BytesRead,
4419 					  &BytesAvail, &PipeState), "DosPeekNPipe()");
4420 		SvCUR_set(tmp, BytesRead);
4421 		*SvEND(tmp) = 0;
4422 		SvPOK_on(tmp);
4423 		XSprePUSH; PUSHs(tmp);
4424 		XSRETURN(1);
4425 	    }
4426 	}
4427 	if (message > -1) {
4428 	    ULONG oflags, flags;
4429 
4430 	    os2cp_croak(DosQueryNPHState(hpipe, &oflags), "DosQueryNPHState()");
4431 	    /* DosSetNPHState fails if more bits than NP_NOWAIT|NP_READMODE_MESSAGE */
4432 	    oflags &= (NP_NOWAIT | NP_READMODE_MESSAGE);
4433 	    flags = (oflags & NP_NOWAIT)
4434 		| (message ? NP_READMODE_MESSAGE : NP_READMODE_BYTE);
4435 	    if (flags != oflags)
4436 		os2cp_croak(DosSetNPHState(hpipe, flags), "DosSetNPHState()");
4437 	}
4438     }
4439     XSRETURN_YES;
4440 }
4441 
4442 /*
4443 NO_OUTPUT ULONG
4444 DosOpen(PCSZ pszFileName, OUTLIST HFILE hFile, OUTLIST ULONG ulAction, ULONG ulOpenFlags, ULONG ulOpenMode = OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW, ULONG ulAttribute = FILE_NORMAL, ULONG ulFileSize = 0, PEAOP2 pEABuf = NULL);
4445    PREINIT:
4446 	ULONG rc;
4447    C_ARGS:
4448 	pszFileName, &hFile, &ulAction, ulFileSize, ulAttribute, ulOpenFlags, ulOpenMode, pEABuf
4449    POSTCALL:
4450 	if (CheckOSError(RETVAL))
4451 	    croak_with_os2error("OS2::open() error");
4452 */
4453 XS(XS_OS2_open); /* prototype to pass -Wmissing-prototypes */
4454 XS(XS_OS2_open)
4455 {
4456     dXSARGS;
4457     if (items < 2 || items > 6)
4458 	Perl_croak(aTHX_ "Usage: OS2::open(pszFileName, ulOpenMode, ulOpenFlags= OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW, ulAttribute= FILE_NORMAL, ulFileSize= 0, pEABuf= NULL)");
4459     {
4460 #line 39 "pipe.xs"
4461 	ULONG rc;
4462 #line 113 "pipe.c"
4463 	ULONG	RETVAL;
4464 	PCSZ	pszFileName = ( SvOK(ST(0)) ? (PCSZ)SvPV_nolen(ST(0)) : NULL );
4465 	HFILE	hFile;
4466 	ULONG	ulAction;
4467 	ULONG	ulOpenMode = (ULONG)SvUV(ST(1));
4468 	ULONG	ulOpenFlags;
4469 	ULONG	ulAttribute;
4470 	ULONG	ulFileSize;
4471 	PEAOP2	pEABuf;
4472 
4473 	if (items < 3)
4474 	    ulOpenFlags = OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW;
4475 	else {
4476 	    ulOpenFlags = (ULONG)SvUV(ST(2));
4477 	}
4478 
4479 	if (items < 4)
4480 	    ulAttribute = FILE_NORMAL;
4481 	else {
4482 	    ulAttribute = (ULONG)SvUV(ST(3));
4483 	}
4484 
4485 	if (items < 5)
4486 	    ulFileSize = 0;
4487 	else {
4488 	    ulFileSize = (ULONG)SvUV(ST(4));
4489 	}
4490 
4491 	if (items < 6)
4492 	    pEABuf = NULL;
4493 	else {
4494 	    pEABuf = (PEAOP2)SvUV(ST(5));
4495 	}
4496 
4497 	RETVAL = DosOpen(pszFileName, &hFile, &ulAction, ulFileSize, ulAttribute, ulOpenFlags, ulOpenMode, pEABuf);
4498 	if (CheckOSError(RETVAL))
4499 	    croak_with_os2error("OS2::open() error");
4500 	XSprePUSH;	EXTEND(SP,2);
4501 	PUSHs(sv_newmortal());
4502 	sv_setuv(ST(0), (UV)hFile);
4503 	PUSHs(sv_newmortal());
4504 	sv_setuv(ST(1), (UV)ulAction);
4505     }
4506     XSRETURN(2);
4507 }
4508 
4509 int
4510 Xs_OS2_init(pTHX)
4511 {
4512     char *file = __FILE__;
4513     {
4514 	GV *gv;
4515 
4516 	if (_emx_env & 0x200) {	/* OS/2 */
4517             newXS("File::Copy::syscopy", XS_File__Copy_syscopy, file);
4518             newXS("Cwd::extLibpath", XS_Cwd_extLibpath, file);
4519             newXS("Cwd::extLibpath_set", XS_Cwd_extLibpath_set, file);
4520             newXS("OS2::extLibpath", XS_Cwd_extLibpath, file);
4521             newXS("OS2::extLibpath_set", XS_Cwd_extLibpath_set, file);
4522 	}
4523         newXS("OS2::Error", XS_OS2_Error, file);
4524         newXS("OS2::Errors2Drive", XS_OS2_Errors2Drive, file);
4525         newXS("OS2::SysInfo", XS_OS2_SysInfo, file);
4526         newXSproto("OS2::DevCap", XS_OS2_DevCap, file, ";$$");
4527         newXSproto("OS2::SysInfoFor", XS_OS2_SysInfoFor, file, "$;$");
4528         newXS("OS2::BootDrive", XS_OS2_BootDrive, file);
4529         newXS("OS2::MorphPM", XS_OS2_MorphPM, file);
4530         newXS("OS2::UnMorphPM", XS_OS2_UnMorphPM, file);
4531         newXS("OS2::Serve_Messages", XS_OS2_Serve_Messages, file);
4532         newXS("OS2::Process_Messages", XS_OS2_Process_Messages, file);
4533         newXS("DynaLoader::mod2fname", XS_DynaLoader_mod2fname, file);
4534         newXS("Cwd::current_drive", XS_Cwd_current_drive, file);
4535         newXS("Cwd::sys_chdir", XS_Cwd_sys_chdir, file);
4536         newXS("Cwd::change_drive", XS_Cwd_change_drive, file);
4537         newXS("Cwd::sys_is_absolute", XS_Cwd_sys_is_absolute, file);
4538         newXS("Cwd::sys_is_rooted", XS_Cwd_sys_is_rooted, file);
4539         newXS("Cwd::sys_is_relative", XS_Cwd_sys_is_relative, file);
4540         newXS("Cwd::sys_cwd", XS_Cwd_sys_cwd, file);
4541         newXS("Cwd::sys_abspath", XS_Cwd_sys_abspath, file);
4542         newXS("OS2::replaceModule", XS_OS2_replaceModule, file);
4543         newXS("OS2::perfSysCall", XS_OS2_perfSysCall, file);
4544         newXSproto("OS2::_control87", XS_OS2__control87, file, "$$");
4545         newXSproto("OS2::get_control87", XS_OS2_get_control87, file, "");
4546         newXSproto("OS2::set_control87", XS_OS2_set_control87, file, ";$$");
4547         newXSproto("OS2::DLLname", XS_OS2_DLLname, file, ";$$");
4548         newXSproto("OS2::mytype", XS_OS2_mytype, file, ";$");
4549         newXSproto("OS2::mytype_set", XS_OS2_mytype_set, file, "$");
4550         newXSproto("OS2::_headerInfo", XS_OS2__headerInfo, file, "$$;$$");
4551         newXSproto("OS2::libPath", XS_OS2_libPath, file, "");
4552         newXSproto("OS2::Timer", XS_OS2_Timer, file, "");
4553         newXSproto("OS2::msCounter", XS_OS2_msCounter, file, "");
4554         newXSproto("OS2::ms_sleep", XS_OS2_ms_sleep, file, "$;$");
4555         newXSproto("OS2::_InfoTable", XS_OS2__InfoTable, file, ";$");
4556         newXSproto("OS2::incrMaxFHandles", XS_OS2_incrMaxFHandles, file, ";$");
4557         newXSproto("OS2::SysValues", XS_OS2_SysValues, file, ";$$");
4558         newXSproto("OS2::SysValues_set", XS_OS2_SysValues_set, file, "$$;$");
4559         newXSproto("OS2::Beep", XS_OS2_Beep, file, ";$$");
4560         newXSproto("OS2::pipe", XS_OS2_pipe, file, "$$;$$$$$$");
4561         newXSproto("OS2::pipeCntl", XS_OS2_pipeCntl, file, "$$;$");
4562         newXSproto("OS2::open", XS_OS2_open, file, "$$;$$$$");
4563 	gv = gv_fetchpv("OS2::is_aout", TRUE, SVt_PV);
4564 	GvMULTI_on(gv);
4565 #ifdef PERL_IS_AOUT
4566 	sv_setiv(GvSV(gv), 1);
4567 #endif
4568 	gv = gv_fetchpv("OS2::is_static", TRUE, SVt_PV);
4569 	GvMULTI_on(gv);
4570 #ifdef PERL_IS_AOUT
4571 	sv_setiv(GvSV(gv), 1);
4572 #endif
4573 	gv = gv_fetchpv("OS2::can_fork", TRUE, SVt_PV);
4574 	GvMULTI_on(gv);
4575 	sv_setiv(GvSV(gv), exe_is_aout());
4576 	gv = gv_fetchpv("OS2::emx_rev", TRUE, SVt_PV);
4577 	GvMULTI_on(gv);
4578 	sv_setiv(GvSV(gv), _emx_rev);
4579 	sv_setpv(GvSV(gv), _emx_vprt);
4580 	SvIOK_on(GvSV(gv));
4581 	gv = gv_fetchpv("OS2::emx_env", TRUE, SVt_PV);
4582 	GvMULTI_on(gv);
4583 	sv_setiv(GvSV(gv), _emx_env);
4584 	gv = gv_fetchpv("OS2::os_ver", TRUE, SVt_PV);
4585 	GvMULTI_on(gv);
4586 	sv_setnv(GvSV(gv), _osmajor + 0.001 * _osminor);
4587 	gv = gv_fetchpv("OS2::nsyserror", TRUE, SVt_PV);
4588 	GvMULTI_on(gv);
4589 	sv_setiv(GvSV(gv), 1);		/* DEFAULT: Show number on syserror */
4590     }
4591     return 0;
4592 }
4593 
4594 extern void _emx_init(void*);
4595 
4596 static void jmp_out_of_atexit(void);
4597 
4598 #define FORCE_EMX_INIT_CONTRACT_ARGV	1
4599 #define FORCE_EMX_INIT_INSTALL_ATEXIT	2
4600 
4601 static void
4602 my_emx_init(void *layout) {
4603     static volatile void *old_esp = 0;	/* Cannot be on stack! */
4604 
4605     /* Can't just call emx_init(), since it moves the stack pointer */
4606     /* It also busts a lot of registers, so be extra careful */
4607     __asm__(	"pushf\n"
4608 		"pusha\n"
4609 		"movl %%esp, %1\n"
4610 		"push %0\n"
4611 		"call __emx_init\n"
4612 		"movl %1, %%esp\n"
4613 		"popa\n"
4614 		"popf\n" : : "r" (layout), "m" (old_esp)	);
4615 }
4616 
4617 struct layout_table_t {
4618     ULONG text_base;
4619     ULONG text_end;
4620     ULONG data_base;
4621     ULONG data_end;
4622     ULONG bss_base;
4623     ULONG bss_end;
4624     ULONG heap_base;
4625     ULONG heap_end;
4626     ULONG heap_brk;
4627     ULONG heap_off;
4628     ULONG os2_dll;
4629     ULONG stack_base;
4630     ULONG stack_end;
4631     ULONG flags;
4632     ULONG reserved[2];
4633     char options[64];
4634 };
4635 
4636 static ULONG
4637 my_os_version() {
4638     static ULONG osv_res;		/* Cannot be on stack! */
4639 
4640     /* Can't just call __os_version(), since it does not follow C
4641        calling convention: it busts a lot of registers, so be extra careful */
4642     __asm__(	"pushf\n"
4643 		"pusha\n"
4644 		"call ___os_version\n"
4645 		"movl %%eax, %0\n"
4646 		"popa\n"
4647 		"popf\n" : "=m" (osv_res)	);
4648 
4649     return osv_res;
4650 }
4651 
4652 static void
4653 force_init_emx_runtime(EXCEPTIONREGISTRATIONRECORD *preg, ULONG flags)
4654 {
4655     /* Calling emx_init() will bust the top of stack: it installs an
4656        exception handler and puts argv data there. */
4657     char *oldarg, *oldenv;
4658     void *oldstackend, *oldstack;
4659     PPIB pib;
4660     PTIB tib;
4661     ULONG rc, error = 0, out;
4662     char buf[512];
4663     static struct layout_table_t layout_table;
4664     struct {
4665 	char buf[48*1024]; /* _emx_init() requires 32K, cmd.exe has 64K only */
4666 	double alignment1;
4667 	EXCEPTIONREGISTRATIONRECORD xreg;
4668     } *newstack;
4669     char *s;
4670 
4671     layout_table.os2_dll = (ULONG)&os2_dll_fake;
4672     layout_table.flags   = 0x02000002;	/* flags: application, OMF */
4673 
4674     DosGetInfoBlocks(&tib, &pib);
4675     oldarg = pib->pib_pchcmd;
4676     oldenv = pib->pib_pchenv;
4677     oldstack = tib->tib_pstack;
4678     oldstackend = tib->tib_pstacklimit;
4679 
4680     if ( (char*)&s < (char*)oldstack + 4*1024
4681 	 || (char *)oldstackend < (char*)oldstack + 52*1024 )
4682 	early_error("It is a lunacy to try to run EMX Perl ",
4683 		    "with less than 64K of stack;\r\n",
4684 		    "  at least with non-EMX starter...\r\n");
4685 
4686     /* Minimize the damage to the stack via reducing the size of argv. */
4687     if (flags & FORCE_EMX_INIT_CONTRACT_ARGV) {
4688 	pib->pib_pchcmd = "\0\0";	/* Need 3 concatenated strings */
4689 	pib->pib_pchcmd = "\0";		/* Ended by an extra \0. */
4690     }
4691 
4692     newstack = alloca(sizeof(*newstack));
4693     /* Emulate the stack probe */
4694     s = ((char*)newstack) + sizeof(*newstack);
4695     while (s > (char*)newstack) {
4696 	s[-1] = 0;
4697 	s -= 4096;
4698     }
4699 
4700     /* Reassigning stack is documented to work */
4701     tib->tib_pstack = (void*)newstack;
4702     tib->tib_pstacklimit = (void*)((char*)newstack + sizeof(*newstack));
4703 
4704     /* Can't just call emx_init(), since it moves the stack pointer */
4705     my_emx_init((void*)&layout_table);
4706 
4707     /* Remove the exception handler, cannot use it - too low on the stack.
4708        Check whether it is inside the new stack.  */
4709     buf[0] = 0;
4710     if (tib->tib_pexchain >= tib->tib_pstacklimit
4711 	|| tib->tib_pexchain < tib->tib_pstack) {
4712 	error = 1;
4713 	sprintf(buf,
4714 		"panic: ExceptionHandler misplaced: not %#lx <= %#lx < %#lx\n",
4715 		(unsigned long)tib->tib_pstack,
4716 		(unsigned long)tib->tib_pexchain,
4717 		(unsigned long)tib->tib_pstacklimit);
4718 	goto finish;
4719     }
4720     if (tib->tib_pexchain != &(newstack->xreg)) {
4721 	sprintf(buf, "ExceptionHandler misplaced: %#lx != %#lx\n",
4722 		(unsigned long)tib->tib_pexchain,
4723 		(unsigned long)&(newstack->xreg));
4724     }
4725     rc = DosUnsetExceptionHandler((EXCEPTIONREGISTRATIONRECORD *)tib->tib_pexchain);
4726     if (rc)
4727 	sprintf(buf + strlen(buf),
4728 		"warning: DosUnsetExceptionHandler rc=%#lx=%lu\n", rc, rc);
4729 
4730     if (preg) {
4731 	/* ExceptionRecords should be on stack, in a correct order.  Sigh... */
4732 	preg->prev_structure = 0;
4733 	preg->ExceptionHandler = _emx_exception;
4734 	rc = DosSetExceptionHandler(preg);
4735 	if (rc) {
4736 	    sprintf(buf + strlen(buf),
4737 		    "warning: DosSetExceptionHandler rc=%#lx=%lu\n", rc, rc);
4738 	    DosWrite(2, buf, strlen(buf), &out);
4739 	    emx_exception_init = 1;	/* Do it around spawn*() calls */
4740 	}
4741     } else
4742 	emx_exception_init = 1;		/* Do it around spawn*() calls */
4743 
4744   finish:
4745     /* Restore the damage */
4746     pib->pib_pchcmd = oldarg;
4747     pib->pib_pchcmd = oldenv;
4748     tib->tib_pstacklimit = oldstackend;
4749     tib->tib_pstack = oldstack;
4750     emx_runtime_init = 1;
4751     if (buf[0])
4752 	DosWrite(2, buf, strlen(buf), &out);
4753     if (error)
4754 	exit(56);
4755 }
4756 
4757 static void
4758 jmp_out_of_atexit(void)
4759 {
4760     if (longjmp_at_exit)
4761 	longjmp(at_exit_buf, 1);
4762 }
4763 
4764 extern void _CRT_term(void);
4765 
4766 void
4767 Perl_OS2_term(void **p, int exitstatus, int flags)
4768 {
4769     if (!emx_runtime_secondary)
4770 	return;
4771 
4772     /* The principal executable is not running the same CRTL, so there
4773        is nobody to shutdown *this* CRTL except us... */
4774     if (flags & FORCE_EMX_DEINIT_EXIT) {
4775 	if (p && !emx_exception_init)
4776 	    DosUnsetExceptionHandler((EXCEPTIONREGISTRATIONRECORD *)p);
4777 	/* Do not run the executable's CRTL's termination routines */
4778 	exit(exitstatus);		/* Run at-exit, flush buffers, etc */
4779     }
4780     /* Run at-exit list, and jump out at the end */
4781     if ((flags & FORCE_EMX_DEINIT_RUN_ATEXIT) && !setjmp(at_exit_buf)) {
4782 	longjmp_at_exit = 1;
4783 	exit(exitstatus);		/* The first pass through "if" */
4784     }
4785 
4786     /* Get here if we managed to jump out of exit(), or did not run atexit. */
4787     longjmp_at_exit = 0;		/* Maybe exit() is called again? */
4788 #if 0 /* _atexit_n is not exported */
4789     if (flags & FORCE_EMX_DEINIT_RUN_ATEXIT)
4790 	_atexit_n = 0;			/* Remove the atexit() handlers */
4791 #endif
4792     /* Will segfault on program termination if we leave this dangling... */
4793     if (p && !emx_exception_init)
4794 	DosUnsetExceptionHandler((EXCEPTIONREGISTRATIONRECORD *)p);
4795     /* Typically there is no need to do this, done from _DLL_InitTerm() */
4796     if (flags & FORCE_EMX_DEINIT_CRT_TERM)
4797 	_CRT_term();			/* Flush buffers, etc. */
4798     /* Now it is a good time to call exit() in the caller's CRTL... */
4799 }
4800 
4801 #include <emx/startup.h>
4802 
4803 extern ULONG __os_version();		/* See system.doc */
4804 
4805 void
4806 check_emx_runtime(char **env, EXCEPTIONREGISTRATIONRECORD *preg)
4807 {
4808     ULONG v_crt, v_emx, count = 0, rc = NO_ERROR, rc1, maybe_inited = 0;
4809     static HMTX hmtx_emx_init = NULLHANDLE;
4810     static int emx_init_done = 0;
4811 
4812     /*  If _environ is not set, this code sits in a DLL which
4813 	uses a CRT DLL which not compatible with the executable's
4814 	CRT library.  Some parts of the DLL are not initialized.
4815      */
4816     if (_environ != NULL)
4817 	return;				/* Properly initialized */
4818 
4819     /* It is not DOS, so we may use OS/2 API now */
4820     /* Some data we manipulate is static; protect ourselves from
4821        calling the same API from a different thread. */
4822     DosEnterMustComplete(&count);
4823 
4824     rc1 = DosEnterCritSec();
4825     if (!hmtx_emx_init)
4826 	rc = DosCreateMutexSem(NULL, &hmtx_emx_init, 0, TRUE); /*Create owned*/
4827     else
4828 	maybe_inited = 1;
4829 
4830     if (rc != NO_ERROR)
4831 	hmtx_emx_init = NULLHANDLE;
4832 
4833     if (rc1 == NO_ERROR)
4834 	DosExitCritSec();
4835     DosExitMustComplete(&count);
4836 
4837     while (maybe_inited) { /* Other thread did or is doing the same now */
4838 	if (emx_init_done)
4839 	    return;
4840 	rc = DosRequestMutexSem(hmtx_emx_init,
4841 				(ULONG) SEM_INDEFINITE_WAIT);  /* Timeout (none) */
4842 	if (rc == ERROR_INTERRUPT)
4843 	    continue;
4844 	if (rc != NO_ERROR) {
4845 	    char buf[80];
4846 	    ULONG out;
4847 
4848 	    sprintf(buf,
4849 		    "panic: EMX backdoor init: DosRequestMutexSem error: %lu=%#lx\n", rc, rc);
4850 	    DosWrite(2, buf, strlen(buf), &out);
4851 	    return;
4852 	}
4853 	DosReleaseMutexSem(hmtx_emx_init);
4854 	return;
4855     }
4856 
4857     /*  If the executable does not use EMX.DLL, EMX.DLL is not completely
4858 	initialized either.  Uninitialized EMX.DLL returns 0 in the low
4859 	nibble of __os_version().  */
4860     v_emx = my_os_version();
4861 
4862     /*	_osmajor and _osminor are normally set in _DLL_InitTerm of CRT DLL
4863 	(=>_CRT_init=>_entry2) via a call to __os_version(), then
4864 	reset when the EXE initialization code calls _text=>_init=>_entry2.
4865 	The first time they are wrongly set to 0; the second time the
4866 	EXE initialization code had already called emx_init=>initialize1
4867 	which correctly set version_major, version_minor used by
4868 	__os_version().  */
4869     v_crt = (_osmajor | _osminor);
4870 
4871     if ((_emx_env & 0x200) && !(v_emx & 0xFFFF)) {	/* OS/2, EMX uninit. */
4872 	force_init_emx_runtime( preg,
4873 				FORCE_EMX_INIT_CONTRACT_ARGV
4874 				| FORCE_EMX_INIT_INSTALL_ATEXIT );
4875 	emx_wasnt_initialized = 1;
4876 	/* Update CRTL data basing on now-valid EMX runtime data */
4877 	if (!v_crt) {		/* The only wrong data are the versions. */
4878 	    v_emx = my_os_version();			/* *Now* it works */
4879 	    *(unsigned char *)&_osmajor = v_emx & 0xFF;	/* Cast out const */
4880 	    *(unsigned char *)&_osminor = (v_emx>>8) & 0xFF;
4881 	}
4882     }
4883     emx_runtime_secondary = 1;
4884     /* if (flags & FORCE_EMX_INIT_INSTALL_ATEXIT) */
4885     atexit(jmp_out_of_atexit);		/* Allow run of atexit() w/o exit()  */
4886 
4887     if (env == NULL) {			/* Fetch from the process info block */
4888 	int c = 0;
4889 	PPIB pib;
4890 	PTIB tib;
4891 	char *e, **ep;
4892 
4893 	DosGetInfoBlocks(&tib, &pib);
4894 	e = pib->pib_pchenv;
4895 	while (*e) {			/* Get count */
4896 	    c++;
4897 	    e = e + strlen(e) + 1;
4898 	}
4899 	Newx(env, c + 1, char*);
4900 	ep = env;
4901 	e = pib->pib_pchenv;
4902 	while (c--) {
4903 	    *ep++ = e;
4904 	    e = e + strlen(e) + 1;
4905 	}
4906 	*ep = NULL;
4907     }
4908     _environ = _org_environ = env;
4909     emx_init_done = 1;
4910     if (hmtx_emx_init)
4911 	DosReleaseMutexSem(hmtx_emx_init);
4912 }
4913 
4914 #define ENTRY_POINT 0x10000
4915 
4916 static int
4917 exe_is_aout(void)
4918 {
4919     struct layout_table_t *layout;
4920     if (emx_wasnt_initialized)
4921 	return 0;
4922     /* Now we know that the principal executable is an EMX application
4923        - unless somebody did already play with delayed initialization... */
4924     /* With EMX applications to determine whether it is AOUT one needs
4925        to examine the start of the executable to find "layout" */
4926     if ( *(unsigned char*)ENTRY_POINT != 0x68		/* PUSH n */
4927 	 || *(unsigned char*)(ENTRY_POINT+5) != 0xe8	/* CALL */
4928 	 || *(unsigned char*)(ENTRY_POINT+10) != 0xeb	/* JMP */
4929 	 || *(unsigned char*)(ENTRY_POINT+12) != 0xe8)	/* CALL */
4930 	return 0;					/* ! EMX executable */
4931     /* Fix alignment */
4932     Copy((char*)(ENTRY_POINT+1), &layout, 1, struct layout_table_t*);
4933     return !(layout->flags & 2);
4934 }
4935 
4936 void
4937 Perl_OS2_init(char **env)
4938 {
4939     Perl_OS2_init3(env, 0, 0);
4940 }
4941 
4942 void
4943 Perl_OS2_init3(char **env, void **preg, int flags)
4944 {
4945     char *shell, *s;
4946     ULONG rc;
4947 
4948     _uflags (_UF_SBRK_MODEL, _UF_SBRK_ARBITRARY);
4949     MALLOC_INIT;
4950 
4951     check_emx_runtime(env, (EXCEPTIONREGISTRATIONRECORD *)preg);
4952 
4953     settmppath();
4954     OS2_Perl_data.xs_init = &Xs_OS2_init;
4955     if (perl_sh_installed) {
4956 	int l = strlen(perl_sh_installed);
4957 
4958 	Newx(PL_sh_path, l + 1, char);
4959 	memcpy(PL_sh_path, perl_sh_installed, l + 1);
4960     } else if ( (shell = getenv("PERL_SH_DRIVE")) ) {
4961 	Newx(PL_sh_path, strlen(SH_PATH) + 1, char);
4962 	strcpy(PL_sh_path, SH_PATH);
4963 	PL_sh_path[0] = shell[0];
4964     } else if ( (shell = getenv("PERL_SH_DIR")) ) {
4965 	int l = strlen(shell), i;
4966 
4967 	while (l && (shell[l-1] == '/' || shell[l-1] == '\\'))
4968 	    l--;
4969 	Newx(PL_sh_path, l + 8, char);
4970 	strncpy(PL_sh_path, shell, l);
4971 	strcpy(PL_sh_path + l, "/sh.exe");
4972 	for (i = 0; i < l; i++) {
4973 	    if (PL_sh_path[i] == '\\') PL_sh_path[i] = '/';
4974 	}
4975     }
4976     MUTEX_INIT(&start_thread_mutex);
4977     MUTEX_INIT(&perlos2_state_mutex);
4978     os2_mytype = my_type();		/* Do it before morphing.  Needed? */
4979     os2_mytype_ini = os2_mytype;
4980     Perl_os2_initial_mode = -1;		/* Uninit */
4981 
4982     s = getenv("PERL_BEGINLIBPATH");
4983     if (s)
4984       rc = fill_extLibpath(0, s, NULL, 1, "PERL_BEGINLIBPATH");
4985     else
4986       rc = fill_extLibpath(0, getenv("PERL_PRE_BEGINLIBPATH"), getenv("PERL_POST_BEGINLIBPATH"), 0, "PERL_(PRE/POST)_BEGINLIBPATH");
4987     if (!rc) {
4988 	s = getenv("PERL_ENDLIBPATH");
4989 	if (s)
4990 	    rc = fill_extLibpath(1, s, NULL, 1, "PERL_ENDLIBPATH");
4991 	else
4992 	    rc = fill_extLibpath(1, getenv("PERL_PRE_ENDLIBPATH"), getenv("PERL_POST_ENDLIBPATH"), 0, "PERL_(PRE/POST)_ENDLIBPATH");
4993     }
4994     if (rc) {
4995 	char buf[1024];
4996 
4997 	snprintf(buf, sizeof buf, "Error setting BEGIN/ENDLIBPATH: %s\n",
4998 		 os2error(rc));
4999 	DosWrite(2, buf, strlen(buf), &rc);
5000 	exit(2);
5001     }
5002 
5003     _emxload_env("PERL_EMXLOAD_SECS");
5004     /* Some DLLs reset FP flags on load.  We may have been linked with them */
5005     _control87(MCW_EM, MCW_EM);
5006 }
5007 
5008 int
5009 fd_ok(int fd)
5010 {
5011     static ULONG max_fh = 0;
5012 
5013     if (!(_emx_env & 0x200)) return 1;		/* not OS/2. */
5014     if (fd >= max_fh) {				/* Renew */
5015 	LONG delta = 0;
5016 
5017 	if (DosSetRelMaxFH(&delta, &max_fh))	/* Assume it OK??? */
5018 	    return 1;
5019     }
5020     return fd < max_fh;
5021 }
5022 
5023 /* Kernels up to Oct 2003 trap on (invalid) dup(max_fh); [off-by-one + double fault].  */
5024 int
5025 dup2(int from, int to)
5026 {
5027     if (fd_ok(from < to ? to : from))
5028 	return _dup2(from, to);
5029     errno = EBADF;
5030     return -1;
5031 }
5032 
5033 int
5034 dup(int from)
5035 {
5036     if (fd_ok(from))
5037 	return _dup(from);
5038     errno = EBADF;
5039     return -1;
5040 }
5041 
5042 #undef tmpnam
5043 #undef tmpfile
5044 
5045 char *
5046 my_tmpnam (char *str)
5047 {
5048     char *p = getenv("TMP"), *tpath;
5049 
5050     if (!p) p = getenv("TEMP");
5051     tpath = tempnam(p, "pltmp");
5052     if (str && tpath) {
5053 	strcpy(str, tpath);
5054 	return str;
5055     }
5056     return tpath;
5057 }
5058 
5059 FILE *
5060 my_tmpfile ()
5061 {
5062     struct stat s;
5063 
5064     stat(".", &s);
5065     if (s.st_mode & S_IWOTH) {
5066 	return tmpfile();
5067     }
5068     return fopen(my_tmpnam(NULL), "w+b"); /* Race condition, but
5069 					     grants TMP. */
5070 }
5071 
5072 #undef rmdir
5073 
5074 /* EMX flavors do not tolerate trailing slashes.  t/op/mkdir.t has many
5075    trailing slashes, so we need to support this as well. */
5076 
5077 int
5078 my_rmdir (__const__ char *s)
5079 {
5080     char b[MAXPATHLEN];
5081     char *buf = b;
5082     STRLEN l = strlen(s);
5083     int rc;
5084 
5085     if (s[l-1] == '/' || s[l-1] == '\\') {	/* EMX mkdir fails... */
5086 	if (l >= sizeof b)
5087 	    Newx(buf, l + 1, char);
5088 	strcpy(buf,s);
5089 	while (l > 1 && (s[l-1] == '/' || s[l-1] == '\\'))
5090 	    l--;
5091 	buf[l] = 0;
5092 	s = buf;
5093     }
5094     rc = rmdir(s);
5095     if (b != buf)
5096 	Safefree(buf);
5097     return rc;
5098 }
5099 
5100 #undef mkdir
5101 
5102 int
5103 my_mkdir (__const__ char *s, long perm)
5104 {
5105     char b[MAXPATHLEN];
5106     char *buf = b;
5107     STRLEN l = strlen(s);
5108     int rc;
5109 
5110     if (s[l-1] == '/' || s[l-1] == '\\') {	/* EMX mkdir fails... */
5111 	if (l >= sizeof b)
5112 	    Newx(buf, l + 1, char);
5113 	strcpy(buf,s);
5114 	while (l > 1 && (s[l-1] == '/' || s[l-1] == '\\'))
5115 	    l--;
5116 	buf[l] = 0;
5117 	s = buf;
5118     }
5119     rc = mkdir(s, perm);
5120     if (b != buf)
5121 	Safefree(buf);
5122     return rc;
5123 }
5124 
5125 #undef flock
5126 
5127 /* This code was contributed by Rocco Caputo. */
5128 int
5129 my_flock(int handle, int o)
5130 {
5131   FILELOCK      rNull, rFull;
5132   ULONG         timeout, handle_type, flag_word;
5133   APIRET        rc;
5134   int           blocking, shared;
5135   static int	use_my_flock = -1;
5136 
5137   if (use_my_flock == -1) {
5138    MUTEX_LOCK(&perlos2_state_mutex);
5139    if (use_my_flock == -1) {
5140     char *s = getenv("USE_PERL_FLOCK");
5141     if (s)
5142 	use_my_flock = atoi(s);
5143     else
5144 	use_my_flock = 1;
5145    }
5146    MUTEX_UNLOCK(&perlos2_state_mutex);
5147   }
5148   if (!(_emx_env & 0x200) || !use_my_flock)
5149     return flock(handle, o);	/* Delegate to EMX. */
5150 
5151                                         /* is this a file? */
5152   if ((DosQueryHType(handle, &handle_type, &flag_word) != 0) ||
5153       (handle_type & 0xFF))
5154   {
5155     errno = EBADF;
5156     return -1;
5157   }
5158                                         /* set lock/unlock ranges */
5159   rNull.lOffset = rNull.lRange = rFull.lOffset = 0;
5160   rFull.lRange = 0x7FFFFFFF;
5161                                         /* set timeout for blocking */
5162   timeout = ((blocking = !(o & LOCK_NB))) ? 100 : 1;
5163                                         /* shared or exclusive? */
5164   shared = (o & LOCK_SH) ? 1 : 0;
5165                                         /* do not block the unlock */
5166   if (o & (LOCK_UN | LOCK_SH | LOCK_EX)) {
5167     rc = DosSetFileLocks(handle, &rFull, &rNull, timeout, shared);
5168     switch (rc) {
5169       case 0:
5170         errno = 0;
5171         return 0;
5172       case ERROR_INVALID_HANDLE:
5173         errno = EBADF;
5174         return -1;
5175       case ERROR_SHARING_BUFFER_EXCEEDED:
5176         errno = ENOLCK;
5177         return -1;
5178       case ERROR_LOCK_VIOLATION:
5179         break;                          /* not an error */
5180       case ERROR_INVALID_PARAMETER:
5181       case ERROR_ATOMIC_LOCK_NOT_SUPPORTED:
5182       case ERROR_READ_LOCKS_NOT_SUPPORTED:
5183         errno = EINVAL;
5184         return -1;
5185       case ERROR_INTERRUPT:
5186         errno = EINTR;
5187         return -1;
5188       default:
5189         errno = EINVAL;
5190         return -1;
5191     }
5192   }
5193                                         /* lock may block */
5194   if (o & (LOCK_SH | LOCK_EX)) {
5195                                         /* for blocking operations */
5196     for (;;) {
5197       rc =
5198         DosSetFileLocks(
5199                 handle,
5200                 &rNull,
5201                 &rFull,
5202                 timeout,
5203                 shared
5204         );
5205       switch (rc) {
5206         case 0:
5207           errno = 0;
5208           return 0;
5209         case ERROR_INVALID_HANDLE:
5210           errno = EBADF;
5211           return -1;
5212         case ERROR_SHARING_BUFFER_EXCEEDED:
5213           errno = ENOLCK;
5214           return -1;
5215         case ERROR_LOCK_VIOLATION:
5216           if (!blocking) {
5217             errno = EWOULDBLOCK;
5218             return -1;
5219           }
5220           break;
5221         case ERROR_INVALID_PARAMETER:
5222         case ERROR_ATOMIC_LOCK_NOT_SUPPORTED:
5223         case ERROR_READ_LOCKS_NOT_SUPPORTED:
5224           errno = EINVAL;
5225           return -1;
5226         case ERROR_INTERRUPT:
5227           errno = EINTR;
5228           return -1;
5229         default:
5230           errno = EINVAL;
5231           return -1;
5232       }
5233                                         /* give away timeslice */
5234       DosSleep(1);
5235     }
5236   }
5237 
5238   errno = 0;
5239   return 0;
5240 }
5241 
5242 static int
5243 use_my_pwent(void)
5244 {
5245   if (_my_pwent == -1) {
5246     char *s = getenv("USE_PERL_PWENT");
5247     if (s)
5248 	_my_pwent = atoi(s);
5249     else
5250 	_my_pwent = 1;
5251   }
5252   return _my_pwent;
5253 }
5254 
5255 #undef setpwent
5256 #undef getpwent
5257 #undef endpwent
5258 
5259 void
5260 my_setpwent(void)
5261 {
5262   if (!use_my_pwent()) {
5263     setpwent();			/* Delegate to EMX. */
5264     return;
5265   }
5266   pwent_cnt = 0;
5267 }
5268 
5269 void
5270 my_endpwent(void)
5271 {
5272   if (!use_my_pwent()) {
5273     endpwent();			/* Delegate to EMX. */
5274     return;
5275   }
5276 }
5277 
5278 struct passwd *
5279 my_getpwent (void)
5280 {
5281   if (!use_my_pwent())
5282     return getpwent();			/* Delegate to EMX. */
5283   if (pwent_cnt++)
5284     return 0;				/* Return one entry only */
5285   return getpwuid(0);
5286 }
5287 
5288 void
5289 setgrent(void)
5290 {
5291   grent_cnt = 0;
5292 }
5293 
5294 void
5295 endgrent(void)
5296 {
5297 }
5298 
5299 struct group *
5300 getgrent (void)
5301 {
5302   if (grent_cnt++)
5303     return 0;				/* Return one entry only */
5304   return getgrgid(0);
5305 }
5306 
5307 #undef getpwuid
5308 #undef getpwnam
5309 
5310 /* Too long to be a crypt() of anything, so it is not-a-valid pw_passwd. */
5311 static const char pw_p[] = "Jf0Wb/BzMFvk7K7lrzK";
5312 
5313 static struct passwd *
5314 passw_wrap(struct passwd *p)
5315 {
5316     char *s;
5317 
5318     if (!p || (p->pw_passwd && *p->pw_passwd)) /* Not a dangerous password */
5319 	return p;
5320     pw = *p;
5321     s = getenv("PW_PASSWD");
5322     if (!s)
5323 	s = (char*)pw_p;		/* Make match impossible */
5324 
5325     pw.pw_passwd = s;
5326     return &pw;
5327 }
5328 
5329 struct passwd *
5330 my_getpwuid (uid_t id)
5331 {
5332     return passw_wrap(getpwuid(id));
5333 }
5334 
5335 struct passwd *
5336 my_getpwnam (__const__ char *n)
5337 {
5338     return passw_wrap(getpwnam(n));
5339 }
5340 
5341 char *
5342 gcvt_os2 (double value, int digits, char *buffer)
5343 {
5344   double absv = value > 0 ? value : -value;
5345   /* EMX implementation is lousy between 0.1 and 0.0001 (uses exponents below
5346      0.1), 1-digit stuff is ok below 0.001; multi-digit below 0.0001. */
5347   int buggy;
5348 
5349   absv *= 10000;
5350   buggy = (absv < 1000 && (absv >= 10 || (absv > 1 && floor(absv) != absv)));
5351 
5352   if (buggy) {
5353     char pat[12];
5354 
5355     sprintf(pat, "%%.%dg", digits);
5356     sprintf(buffer, pat, value);
5357     return buffer;
5358   }
5359   return gcvt (value, digits, buffer);
5360 }
5361 
5362 #undef fork
5363 int fork_with_resources()
5364 {
5365 #if (defined(USE_5005THREADS) || defined(USE_ITHREADS)) && !defined(USE_SLOW_THREAD_SPECIFIC)
5366   dTHX;
5367   void *ctx = PERL_GET_CONTEXT;
5368 #endif
5369   unsigned fpflag = _control87(0,0);
5370   int rc = fork();
5371 
5372   if (rc == 0) {			/* child */
5373 #if (defined(USE_5005THREADS) || defined(USE_ITHREADS)) && !defined(USE_SLOW_THREAD_SPECIFIC)
5374     ALLOC_THREAD_KEY;			/* Acquire the thread-local memory */
5375     PERL_SET_CONTEXT(ctx);		/* Reinit the thread-local memory */
5376 #endif
5377 
5378     {					/* Reload loaded-on-demand DLLs */
5379 	struct dll_handle_t *dlls = dll_handles;
5380 
5381 	while (dlls->modname) {
5382 	    char dllname[260], fail[260];
5383 	    ULONG rc;
5384 
5385 	    if (!dlls->handle) {	/* Was not loaded */
5386 		dlls++;
5387 		continue;
5388 	    }
5389 	    /* It was loaded in the parent.  We need to reload it. */
5390 
5391 	    rc = DosQueryModuleName(dlls->handle, sizeof(dllname), dllname);
5392 	    if (rc) {
5393 		Perl_warn_nocontext("Can't find DLL name for the module `%s' by the handle %d, rc=%lu=%#lx",
5394 				    dlls->modname, (int)dlls->handle, rc, rc);
5395 		dlls++;
5396 		continue;
5397 	    }
5398 	    rc = DosLoadModule(fail, sizeof fail, dllname, &dlls->handle);
5399 	    if (rc)
5400 		Perl_warn_nocontext("Can't load DLL `%s', possible problematic module `%s'",
5401 				    dllname, fail);
5402 	    dlls++;
5403 	}
5404     }
5405 
5406     {					/* Support message queue etc. */
5407 	os2_mytype = my_type();
5408 	/* Apparently, subprocesses (in particular, fork()) do not
5409 	   inherit the morphed state, so os2_mytype is the same as
5410 	   os2_mytype_ini. */
5411 
5412 	if (Perl_os2_initial_mode != -1
5413 	    && Perl_os2_initial_mode != os2_mytype) {
5414 					/* XXXX ??? */
5415 	}
5416     }
5417     if (Perl_HAB_set)
5418 	(void)_obtain_Perl_HAB;
5419     if (Perl_hmq_refcnt) {
5420 	if (my_type() != 3)
5421 	    my_type_set(3);
5422 	Create_HMQ(Perl_hmq_servers != 0,
5423 		   "Cannot create a message queue on fork");
5424     }
5425 
5426     /* We may have loaded some modules */
5427     _control87(fpflag, MCW_EM); /* Some modules reset FP flags on (un)load */
5428   }
5429   return rc;
5430 }
5431 
5432 /* APIRET  APIENTRY DosGetInfoSeg(PSEL pselGlobal, PSEL pselLocal); */
5433 
5434 ULONG _THUNK_FUNCTION(Dos16GetInfoSeg)(USHORT *pGlobal, USHORT *pLocal);
5435 
5436 APIRET  APIENTRY
5437 myDosGetInfoSeg(PGINFOSEG *pGlobal, PLINFOSEG *pLocal)
5438 {
5439     APIRET rc;
5440     USHORT gSel, lSel;		/* Will not cross 64K boundary */
5441 
5442     rc = ((USHORT)
5443           (_THUNK_PROLOG (4+4);
5444            _THUNK_FLAT (&gSel);
5445            _THUNK_FLAT (&lSel);
5446            _THUNK_CALL (Dos16GetInfoSeg)));
5447     if (rc)
5448 	return rc;
5449     *pGlobal = MAKEPGINFOSEG(gSel);
5450     *pLocal  = MAKEPLINFOSEG(lSel);
5451     return rc;
5452 }
5453 
5454 static void
5455 GetInfoTables(void)
5456 {
5457     ULONG rc = 0;
5458 
5459     MUTEX_LOCK(&perlos2_state_mutex);
5460     if (!gTable)
5461       rc = myDosGetInfoSeg(&gTable, &lTable);
5462     MUTEX_UNLOCK(&perlos2_state_mutex);
5463     os2cp_croak(rc, "Dos16GetInfoSeg");
5464 }
5465 
5466 ULONG
5467 msCounter(void)
5468 {				/* XXXX Is not lTable thread-specific? */
5469   if (!gTable)
5470     GetInfoTables();
5471   return gTable->SIS_MsCount;
5472 }
5473 
5474 ULONG
5475 InfoTable(int local)
5476 {
5477   if (!gTable)
5478     GetInfoTables();
5479   return local ? (ULONG)lTable : (ULONG)gTable;
5480 }
5481