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