xref: /openbsd-src/gnu/usr.bin/perl/os2/os2.c (revision 47911bd667ac77dc523b8a13ef40b012dbffa741)
1 #define INCL_DOS
2 #define INCL_NOPM
3 #define INCL_DOSFILEMGR
4 #define INCL_DOSMEMMGR
5 #define INCL_DOSERRORS
6 /* These 3 are needed for compile if os2.h includes os2tk.h, not os2emx.h */
7 #define INCL_DOSPROCESS
8 #define SPU_DISABLESUPPRESSION          0
9 #define SPU_ENABLESUPPRESSION           1
10 #include <os2.h>
11 #include "dlfcn.h"
12 
13 #include <sys/uflags.h>
14 
15 /*
16  * Various Unix compatibility functions for OS/2
17  */
18 
19 #include <stdio.h>
20 #include <errno.h>
21 #include <limits.h>
22 #include <process.h>
23 #include <fcntl.h>
24 #include <pwd.h>
25 #include <grp.h>
26 
27 #define PERLIO_NOT_STDIO 0
28 
29 #include "EXTERN.h"
30 #include "perl.h"
31 
32 #ifdef USE_5005THREADS
33 
34 typedef void (*emx_startroutine)(void *);
35 typedef void* (*pthreads_startroutine)(void *);
36 
37 enum pthreads_state {
38     pthreads_st_none = 0,
39     pthreads_st_run,
40     pthreads_st_exited,
41     pthreads_st_detached,
42     pthreads_st_waited,
43 };
44 const char *pthreads_states[] = {
45     "uninit",
46     "running",
47     "exited",
48     "detached",
49     "waited for",
50 };
51 
52 typedef struct {
53     void *status;
54     perl_cond cond;
55     enum pthreads_state state;
56 } thread_join_t;
57 
58 thread_join_t *thread_join_data;
59 int thread_join_count;
60 perl_mutex start_thread_mutex;
61 
62 int
63 pthread_join(perl_os_thread tid, void **status)
64 {
65     MUTEX_LOCK(&start_thread_mutex);
66     switch (thread_join_data[tid].state) {
67     case pthreads_st_exited:
68 	thread_join_data[tid].state = pthreads_st_none;	/* Ready to reuse */
69 	MUTEX_UNLOCK(&start_thread_mutex);
70 	*status = thread_join_data[tid].status;
71 	break;
72     case pthreads_st_waited:
73 	MUTEX_UNLOCK(&start_thread_mutex);
74 	Perl_croak_nocontext("join with a thread with a waiter");
75 	break;
76     case pthreads_st_run:
77 	thread_join_data[tid].state = pthreads_st_waited;
78 	COND_INIT(&thread_join_data[tid].cond);
79 	MUTEX_UNLOCK(&start_thread_mutex);
80 	COND_WAIT(&thread_join_data[tid].cond, NULL);
81 	COND_DESTROY(&thread_join_data[tid].cond);
82 	thread_join_data[tid].state = pthreads_st_none;	/* Ready to reuse */
83 	*status = thread_join_data[tid].status;
84 	break;
85     default:
86 	MUTEX_UNLOCK(&start_thread_mutex);
87 	Perl_croak_nocontext("join: unknown thread state: '%s'",
88 	      pthreads_states[thread_join_data[tid].state]);
89 	break;
90     }
91     return 0;
92 }
93 
94 void
95 pthread_startit(void *arg)
96 {
97     /* Thread is already started, we need to transfer control only */
98     pthreads_startroutine start_routine = *((pthreads_startroutine*)arg);
99     int tid = pthread_self();
100     void *retval;
101 
102     arg = ((void**)arg)[1];
103     if (tid >= thread_join_count) {
104 	int oc = thread_join_count;
105 
106 	thread_join_count = tid + 5 + tid/5;
107 	if (thread_join_data) {
108 	    Renew(thread_join_data, thread_join_count, thread_join_t);
109 	    Zero(thread_join_data + oc, thread_join_count - oc, thread_join_t);
110 	} else {
111 	    Newz(1323, thread_join_data, thread_join_count, thread_join_t);
112 	}
113     }
114     if (thread_join_data[tid].state != pthreads_st_none)
115 	Perl_croak_nocontext("attempt to reuse thread id %i", tid);
116     thread_join_data[tid].state = pthreads_st_run;
117     /* Now that we copied/updated the guys, we may release the caller... */
118     MUTEX_UNLOCK(&start_thread_mutex);
119     thread_join_data[tid].status = (*start_routine)(arg);
120     switch (thread_join_data[tid].state) {
121     case pthreads_st_waited:
122 	COND_SIGNAL(&thread_join_data[tid].cond);
123 	break;
124     default:
125 	thread_join_data[tid].state = pthreads_st_exited;
126 	break;
127     }
128 }
129 
130 int
131 pthread_create(perl_os_thread *tid, const pthread_attr_t *attr,
132 	       void *(*start_routine)(void*), void *arg)
133 {
134     void *args[2];
135 
136     args[0] = (void*)start_routine;
137     args[1] = arg;
138 
139     MUTEX_LOCK(&start_thread_mutex);
140     *tid = _beginthread(pthread_startit, /*stack*/ NULL,
141 			/*stacksize*/ 10*1024*1024, (void*)args);
142     MUTEX_LOCK(&start_thread_mutex);
143     MUTEX_UNLOCK(&start_thread_mutex);
144     return *tid ? 0 : EINVAL;
145 }
146 
147 int
148 pthread_detach(perl_os_thread tid)
149 {
150     MUTEX_LOCK(&start_thread_mutex);
151     switch (thread_join_data[tid].state) {
152     case pthreads_st_waited:
153 	MUTEX_UNLOCK(&start_thread_mutex);
154 	Perl_croak_nocontext("detach on a thread with a waiter");
155 	break;
156     case pthreads_st_run:
157 	thread_join_data[tid].state = pthreads_st_detached;
158 	MUTEX_UNLOCK(&start_thread_mutex);
159 	break;
160     default:
161 	MUTEX_UNLOCK(&start_thread_mutex);
162 	Perl_croak_nocontext("detach: unknown thread state: '%s'",
163 	      pthreads_states[thread_join_data[tid].state]);
164 	break;
165     }
166     return 0;
167 }
168 
169 /* This is a very bastardized version: */
170 int
171 os2_cond_wait(perl_cond *c, perl_mutex *m)
172 {
173     int rc;
174     STRLEN n_a;
175     if ((rc = DosResetEventSem(*c,&n_a)) && (rc != ERROR_ALREADY_RESET))
176 	Perl_croak_nocontext("panic: COND_WAIT-reset: rc=%i", rc);
177     if (m) MUTEX_UNLOCK(m);
178     if (CheckOSError(DosWaitEventSem(*c,SEM_INDEFINITE_WAIT))
179 	&& (rc != ERROR_INTERRUPT))
180 	Perl_croak_nocontext("panic: COND_WAIT: rc=%i", rc);
181     if (rc == ERROR_INTERRUPT)
182 	errno = EINTR;
183     if (m) MUTEX_LOCK(m);
184 }
185 #endif
186 
187 static int exe_is_aout(void);
188 
189 /*****************************************************************************/
190 /* 2.1 would not resolve symbols on demand, and has no ExtLIBPATH. */
191 #define C_ARR_LEN(sym)	(sizeof(sym)/sizeof(*sym))
192 
193 struct dll_handle {
194     const char *modname;
195     HMODULE handle;
196 };
197 static struct dll_handle doscalls_handle = {"doscalls", 0};
198 static struct dll_handle tcp_handle = {"tcp32dll", 0};
199 static struct dll_handle pmwin_handle = {"pmwin", 0};
200 static struct dll_handle rexx_handle = {"rexx", 0};
201 static struct dll_handle rexxapi_handle = {"rexxapi", 0};
202 static struct dll_handle sesmgr_handle = {"sesmgr", 0};
203 static struct dll_handle pmshapi_handle = {"pmshapi", 0};
204 
205 /* This should match enum entries_ordinals defined in os2ish.h. */
206 static const struct {
207     struct dll_handle *dll;
208     const char *entryname;
209     int entrypoint;
210 } loadOrdinals[ORD_NENTRIES] = {
211   {&doscalls_handle, NULL, 874},	/* DosQueryExtLibpath */
212   {&doscalls_handle, NULL, 873},	/* DosSetExtLibpath */
213   {&doscalls_handle, NULL, 460},	/* DosVerifyPidTid */
214   {&tcp_handle, "SETHOSTENT", 0},
215   {&tcp_handle, "SETNETENT" , 0},
216   {&tcp_handle, "SETPROTOENT", 0},
217   {&tcp_handle, "SETSERVENT", 0},
218   {&tcp_handle, "GETHOSTENT", 0},
219   {&tcp_handle, "GETNETENT" , 0},
220   {&tcp_handle, "GETPROTOENT", 0},
221   {&tcp_handle, "GETSERVENT", 0},
222   {&tcp_handle, "ENDHOSTENT", 0},
223   {&tcp_handle, "ENDNETENT", 0},
224   {&tcp_handle, "ENDPROTOENT", 0},
225   {&tcp_handle, "ENDSERVENT", 0},
226   {&pmwin_handle, NULL, 763},		/* WinInitialize */
227   {&pmwin_handle, NULL, 716},		/* WinCreateMsgQueue */
228   {&pmwin_handle, NULL, 726},		/* WinDestroyMsgQueue */
229   {&pmwin_handle, NULL, 918},		/* WinPeekMsg */
230   {&pmwin_handle, NULL, 915},		/* WinGetMsg */
231   {&pmwin_handle, NULL, 912},		/* WinDispatchMsg */
232   {&pmwin_handle, NULL, 753},		/* WinGetLastError */
233   {&pmwin_handle, NULL, 705},		/* WinCancelShutdown */
234 	/* These are needed in extensions.
235 	   How to protect PMSHAPI: it comes through EMX functions? */
236   {&rexx_handle,    "RexxStart", 0},
237   {&rexx_handle,    "RexxVariablePool", 0},
238   {&rexxapi_handle, "RexxRegisterFunctionExe", 0},
239   {&rexxapi_handle, "RexxDeregisterFunction", 0},
240   {&sesmgr_handle,  "DOSSMSETTITLE", 0}, /* Would not work runtime-loaded */
241   {&pmshapi_handle, "PRF32QUERYPROFILESIZE", 0},
242   {&pmshapi_handle, "PRF32OPENPROFILE", 0},
243   {&pmshapi_handle, "PRF32CLOSEPROFILE", 0},
244   {&pmshapi_handle, "PRF32QUERYPROFILE", 0},
245   {&pmshapi_handle, "PRF32RESET", 0},
246   {&pmshapi_handle, "PRF32QUERYPROFILEDATA", 0},
247   {&pmshapi_handle, "PRF32WRITEPROFILEDATA", 0},
248 
249   /* At least some of these do not work by name, since they need
250 	WIN32 instead of WIN... */
251 #if 0
252   These were generated with
253     nm I:\emx\lib\os2.a  | fgrep -f API-list | grep = > API-list-entries
254     perl -wnle "next unless /^0+\s+E\s+_(\w+)=(\w+).(\d+)/; print qq(    ORD_$1,)" API-list-entries > API-list-ORD_
255     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
256 #endif
257   {&pmshapi_handle, NULL, 123},		/* WinChangeSwitchEntry */
258   {&pmshapi_handle, NULL, 124},		/* WinQuerySwitchEntry */
259   {&pmshapi_handle, NULL, 125},		/* WinQuerySwitchHandle */
260   {&pmshapi_handle, NULL, 126},		/* WinQuerySwitchList */
261   {&pmshapi_handle, NULL, 131},		/* WinSwitchToProgram */
262   {&pmwin_handle, NULL, 702},		/* WinBeginEnumWindows */
263   {&pmwin_handle, NULL, 737},		/* WinEndEnumWindows */
264   {&pmwin_handle, NULL, 740},		/* WinEnumDlgItem */
265   {&pmwin_handle, NULL, 756},		/* WinGetNextWindow */
266   {&pmwin_handle, NULL, 768},		/* WinIsChild */
267   {&pmwin_handle, NULL, 799},		/* WinQueryActiveWindow */
268   {&pmwin_handle, NULL, 805},		/* WinQueryClassName */
269   {&pmwin_handle, NULL, 817},		/* WinQueryFocus */
270   {&pmwin_handle, NULL, 834},		/* WinQueryWindow */
271   {&pmwin_handle, NULL, 837},		/* WinQueryWindowPos */
272   {&pmwin_handle, NULL, 838},		/* WinQueryWindowProcess */
273   {&pmwin_handle, NULL, 841},		/* WinQueryWindowText */
274   {&pmwin_handle, NULL, 842},		/* WinQueryWindowTextLength */
275   {&pmwin_handle, NULL, 860},		/* WinSetFocus */
276   {&pmwin_handle, NULL, 875},		/* WinSetWindowPos */
277   {&pmwin_handle, NULL, 877},		/* WinSetWindowText */
278   {&pmwin_handle, NULL, 883},		/* WinShowWindow */
279   {&pmwin_handle, NULL, 772},		/* WinIsWindow */
280   {&pmwin_handle, NULL, 899},		/* WinWindowFromId */
281   {&pmwin_handle, NULL, 900},		/* WinWindowFromPoint */
282   {&pmwin_handle, NULL, 919},		/* WinPostMsg */
283   {&pmwin_handle, NULL, 735},		/* WinEnableWindow */
284   {&pmwin_handle, NULL, 736},		/* WinEnableWindowUpdate */
285   {&pmwin_handle, NULL, 773},		/* WinIsWindowEnabled */
286   {&pmwin_handle, NULL, 774},		/* WinIsWindowShowing */
287   {&pmwin_handle, NULL, 775},		/* WinIsWindowVisible */
288   {&pmwin_handle, NULL, 839},		/* WinQueryWindowPtr */
289   {&pmwin_handle, NULL, 843},		/* WinQueryWindowULong */
290   {&pmwin_handle, NULL, 844},		/* WinQueryWindowUShort */
291   {&pmwin_handle, NULL, 874},		/* WinSetWindowBits */
292   {&pmwin_handle, NULL, 876},		/* WinSetWindowPtr */
293   {&pmwin_handle, NULL, 878},		/* WinSetWindowULong */
294   {&pmwin_handle, NULL, 879},		/* WinSetWindowUShort */
295   {&pmwin_handle, NULL, 813},		/* WinQueryDesktopWindow */
296   {&pmwin_handle, NULL, 851},		/* WinSetActiveWindow */
297   {&doscalls_handle, NULL, 360},	/* DosQueryModFromEIP */
298 };
299 
300 static PFN ExtFCN[C_ARR_LEN(loadOrdinals)];	/* Labeled by ord ORD_*. */
301 const Perl_PFN * const pExtFCN = ExtFCN;
302 struct PMWIN_entries_t PMWIN_entries;
303 
304 HMODULE
305 loadModule(const char *modname, int fail)
306 {
307     HMODULE h = (HMODULE)dlopen(modname, 0);
308 
309     if (!h && fail)
310 	Perl_croak_nocontext("Error loading module '%s': %s",
311 			     modname, dlerror());
312     return h;
313 }
314 
315 PFN
316 loadByOrdinal(enum entries_ordinals ord, int fail)
317 {
318     if (ExtFCN[ord] == NULL) {
319 	PFN fcn = (PFN)-1;
320 	APIRET rc;
321 
322 	if (!loadOrdinals[ord].dll->handle)
323 	    loadOrdinals[ord].dll->handle
324 		= loadModule(loadOrdinals[ord].dll->modname, fail);
325 	if (!loadOrdinals[ord].dll->handle)
326 	    return 0;			/* Possible with FAIL==0 only */
327 	if (CheckOSError(DosQueryProcAddr(loadOrdinals[ord].dll->handle,
328 					  loadOrdinals[ord].entrypoint,
329 					  loadOrdinals[ord].entryname,&fcn))) {
330 	    char buf[20], *s = (char*)loadOrdinals[ord].entryname;
331 
332 	    if (!fail)
333 		return 0;
334 	    if (!s)
335 		sprintf(s = buf, "%d", loadOrdinals[ord].entrypoint);
336 	    Perl_croak_nocontext(
337 		 "This version of OS/2 does not support %s.%s",
338 		 loadOrdinals[ord].dll->modname, s);
339 	}
340 	ExtFCN[ord] = fcn;
341     }
342     if ((long)ExtFCN[ord] == -1)
343 	Perl_croak_nocontext("panic queryaddr");
344     return ExtFCN[ord];
345 }
346 
347 void
348 init_PMWIN_entries(void)
349 {
350     int i;
351 
352     for (i = ORD_WinInitialize; i <= ORD_WinCancelShutdown; i++)
353 	((PFN*)&PMWIN_entries)[i - ORD_WinInitialize] = loadByOrdinal(i, 1);
354 }
355 
356 /*****************************************************/
357 /* socket forwarders without linking with tcpip DLLs */
358 
359 DeclFuncByORD(struct hostent *,  gethostent,  ORD_GETHOSTENT,  (void), ())
360 DeclFuncByORD(struct netent  *,  getnetent,   ORD_GETNETENT,   (void), ())
361 DeclFuncByORD(struct protoent *, getprotoent, ORD_GETPROTOENT, (void), ())
362 DeclFuncByORD(struct servent *,  getservent,  ORD_GETSERVENT,  (void), ())
363 
364 DeclVoidFuncByORD(sethostent,  ORD_SETHOSTENT,  (int x), (x))
365 DeclVoidFuncByORD(setnetent,   ORD_SETNETENT,   (int x), (x))
366 DeclVoidFuncByORD(setprotoent, ORD_SETPROTOENT, (int x), (x))
367 DeclVoidFuncByORD(setservent,  ORD_SETSERVENT,  (int x), (x))
368 
369 DeclVoidFuncByORD(endhostent,  ORD_ENDHOSTENT,  (void), ())
370 DeclVoidFuncByORD(endnetent,   ORD_ENDNETENT,   (void), ())
371 DeclVoidFuncByORD(endprotoent, ORD_ENDPROTOENT, (void), ())
372 DeclVoidFuncByORD(endservent,  ORD_ENDSERVENT,  (void), ())
373 
374 /* priorities */
375 static signed char priors[] = {0, 1, 3, 2}; /* Last two interchanged,
376 					       self inverse. */
377 #define QSS_INI_BUFFER 1024
378 
379 ULONG (*pDosVerifyPidTid) (PID pid, TID tid);
380 static int pidtid_lookup;
381 
382 PQTOPLEVEL
383 get_sysinfo(ULONG pid, ULONG flags)
384 {
385     char *pbuffer;
386     ULONG rc, buf_len = QSS_INI_BUFFER;
387     PQTOPLEVEL psi;
388 
389     if (!pidtid_lookup) {
390 	pidtid_lookup = 1;
391 	*(PFN*)&pDosVerifyPidTid = loadByOrdinal(ORD_DosVerifyPidTid, 0);
392     }
393     if (pDosVerifyPidTid) {	/* Warp3 or later */
394 	/* Up to some fixpak QuerySysState() kills the system if a non-existent
395 	   pid is used. */
396 	if (CheckOSError(pDosVerifyPidTid(pid, 1)))
397 	    return 0;
398     }
399     New(1322, pbuffer, buf_len, char);
400     /* QSS_PROCESS | QSS_MODULE | QSS_SEMAPHORES | QSS_SHARED */
401     rc = QuerySysState(flags, pid, pbuffer, buf_len);
402     while (rc == ERROR_BUFFER_OVERFLOW) {
403 	Renew(pbuffer, buf_len *= 2, char);
404 	rc = QuerySysState(flags, pid, pbuffer, buf_len);
405     }
406     if (rc) {
407 	FillOSError(rc);
408 	Safefree(pbuffer);
409 	return 0;
410     }
411     psi = (PQTOPLEVEL)pbuffer;
412     if (psi && pid && pid != psi->procdata->pid) {
413       Safefree(psi);
414       Perl_croak_nocontext("panic: wrong pid in sysinfo");
415     }
416     return psi;
417 }
418 
419 #define PRIO_ERR 0x1111
420 
421 static ULONG
422 sys_prio(pid)
423 {
424   ULONG prio;
425   PQTOPLEVEL psi;
426 
427   if (!pid)
428       return PRIO_ERR;
429   psi = get_sysinfo(pid, QSS_PROCESS);
430   if (!psi)
431       return PRIO_ERR;
432   prio = psi->procdata->threads->priority;
433   Safefree(psi);
434   return prio;
435 }
436 
437 int
438 setpriority(int which, int pid, int val)
439 {
440   ULONG rc, prio = sys_prio(pid);
441 
442   if (!(_emx_env & 0x200)) return 0; /* Nop if not OS/2. */
443   if (priors[(32 - val) >> 5] + 1 == (prio >> 8)) {
444       /* Do not change class. */
445       return CheckOSError(DosSetPriority((pid < 0)
446 					 ? PRTYS_PROCESSTREE : PRTYS_PROCESS,
447 					 0,
448 					 (32 - val) % 32 - (prio & 0xFF),
449 					 abs(pid)))
450       ? -1 : 0;
451   } else /* if ((32 - val) % 32 == (prio & 0xFF)) */ {
452       /* Documentation claims one can change both class and basevalue,
453        * but I find it wrong. */
454       /* Change class, but since delta == 0 denotes absolute 0, correct. */
455       if (CheckOSError(DosSetPriority((pid < 0)
456 				      ? PRTYS_PROCESSTREE : PRTYS_PROCESS,
457 				      priors[(32 - val) >> 5] + 1,
458 				      0,
459 				      abs(pid))))
460 	  return -1;
461       if ( ((32 - val) % 32) == 0 ) return 0;
462       return CheckOSError(DosSetPriority((pid < 0)
463 					 ? PRTYS_PROCESSTREE : PRTYS_PROCESS,
464 					 0,
465 					 (32 - val) % 32,
466 					 abs(pid)))
467 	  ? -1 : 0;
468   }
469 }
470 
471 int
472 getpriority(int which /* ignored */, int pid)
473 {
474   ULONG ret;
475 
476   if (!(_emx_env & 0x200)) return 0; /* Nop if not OS/2. */
477   ret = sys_prio(pid);
478   if (ret == PRIO_ERR) {
479       return -1;
480   }
481   return (1 - priors[((ret >> 8) - 1)])*32 - (ret & 0xFF);
482 }
483 
484 /*****************************************************************************/
485 /* spawn */
486 
487 int emx_runtime_init;			/* If 1, we need to manually init it */
488 int emx_exception_init;			/* If 1, we need to manually set it */
489 
490 /* There is no big sense to make it thread-specific, since signals
491    are delivered to thread 1 only.  XXXX Maybe make it into an array? */
492 static int spawn_pid;
493 static int spawn_killed;
494 
495 static Signal_t
496 spawn_sighandler(int sig)
497 {
498     /* Some programs do not arrange for the keyboard signals to be
499        delivered to them.  We need to deliver the signal manually. */
500     /* We may get a signal only if
501        a) kid does not receive keyboard signal: deliver it;
502        b) kid already died, and we get a signal.  We may only hope
503           that the pid number was not reused.
504      */
505 
506     if (spawn_killed)
507 	sig = SIGKILL;			/* Try harder. */
508     kill(spawn_pid, sig);
509     spawn_killed = 1;
510 }
511 
512 static int
513 result(pTHX_ int flag, int pid)
514 {
515 	int r, status;
516 	Signal_t (*ihand)();     /* place to save signal during system() */
517 	Signal_t (*qhand)();     /* place to save signal during system() */
518 #ifndef __EMX__
519 	RESULTCODES res;
520 	int rpid;
521 #endif
522 
523 	if (pid < 0 || flag != 0)
524 		return pid;
525 
526 #ifdef __EMX__
527 	spawn_pid = pid;
528 	spawn_killed = 0;
529 	ihand = rsignal(SIGINT, &spawn_sighandler);
530 	qhand = rsignal(SIGQUIT, &spawn_sighandler);
531 	do {
532 	    r = wait4pid(pid, &status, 0);
533 	} while (r == -1 && errno == EINTR);
534 	rsignal(SIGINT, ihand);
535 	rsignal(SIGQUIT, qhand);
536 
537 	PL_statusvalue = (U16)status;
538 	if (r < 0)
539 		return -1;
540 	return status & 0xFFFF;
541 #else
542 	ihand = rsignal(SIGINT, SIG_IGN);
543 	r = DosWaitChild(DCWA_PROCESS, DCWW_WAIT, &res, &rpid, pid);
544 	rsignal(SIGINT, ihand);
545 	PL_statusvalue = res.codeResult << 8 | res.codeTerminate;
546 	if (r)
547 		return -1;
548 	return PL_statusvalue;
549 #endif
550 }
551 
552 enum execf_t {
553   EXECF_SPAWN,
554   EXECF_EXEC,
555   EXECF_TRUEEXEC,
556   EXECF_SPAWN_NOWAIT,
557   EXECF_SPAWN_BYFLAG,
558   EXECF_SYNC
559 };
560 
561 /* const char* const ptypes[] = { "FS", "DOS", "VIO", "PM", "DETACH" }; */
562 
563 static int
564 my_type()
565 {
566     int rc;
567     TIB *tib;
568     PIB *pib;
569 
570     if (!(_emx_env & 0x200)) return 1; /* not OS/2. */
571     if (CheckOSError(DosGetInfoBlocks(&tib, &pib)))
572 	return -1;
573 
574     return (pib->pib_ultype);
575 }
576 
577 static ULONG
578 file_type(char *path)
579 {
580     int rc;
581     ULONG apptype;
582 
583     if (!(_emx_env & 0x200))
584 	Perl_croak_nocontext("file_type not implemented on DOS"); /* not OS/2. */
585     if (CheckOSError(DosQueryAppType(path, &apptype))) {
586 	switch (rc) {
587 	case ERROR_FILE_NOT_FOUND:
588 	case ERROR_PATH_NOT_FOUND:
589 	    return -1;
590 	case ERROR_ACCESS_DENIED:	/* Directory with this name found? */
591 	    return -3;
592 	default:			/* Found, but not an
593 					   executable, or some other
594 					   read error. */
595 	    return -2;
596 	}
597     }
598     return apptype;
599 }
600 
601 static ULONG os2_mytype;
602 
603 /* Spawn/exec a program, revert to shell if needed. */
604 /* global PL_Argv[] contains arguments. */
605 
606 extern ULONG _emx_exception (	EXCEPTIONREPORTRECORD *,
607 				EXCEPTIONREGISTRATIONRECORD *,
608                                 CONTEXTRECORD *,
609                                 void *);
610 
611 int
612 do_spawn_ve(pTHX_ SV *really, U32 flag, U32 execf, char *inicmd, U32 addflag)
613 {
614 	int trueflag = flag;
615 	int rc, pass = 1;
616 	char *tmps;
617 	char *args[4];
618 	static char * fargs[4]
619 	    = { "/bin/sh", "-c", "\"$@\"", "spawn-via-shell", };
620 	char **argsp = fargs;
621 	int nargs = 4;
622 	int force_shell;
623  	int new_stderr = -1, nostderr = 0;
624 	int fl_stderr = 0;
625 	STRLEN n_a;
626 	char *buf;
627 	PerlIO *file;
628 
629 	if (flag == P_WAIT)
630 		flag = P_NOWAIT;
631 
632       retry:
633 	if (strEQ(PL_Argv[0],"/bin/sh"))
634 	    PL_Argv[0] = PL_sh_path;
635 
636 	/* We should check PERL_SH* and PERLLIB_* as well? */
637 	if (!really || !*(tmps = SvPV(really, n_a)))
638 	    tmps = PL_Argv[0];
639 	if (tmps[0] != '/' && tmps[0] != '\\'
640 	    && !(tmps[0] && tmps[1] == ':'
641 		 && (tmps[2] == '/' || tmps[2] != '\\'))
642 	    ) /* will spawnvp use PATH? */
643 	    TAINT_ENV();	/* testing IFS here is overkill, probably */
644 
645       reread:
646 	force_shell = 0;
647 	if (_emx_env & 0x200) { /* OS/2. */
648 	    int type = file_type(tmps);
649 	  type_again:
650 	    if (type == -1) {		/* Not found */
651 		errno = ENOENT;
652 		rc = -1;
653 		goto do_script;
654 	    }
655 	    else if (type == -2) {		/* Not an EXE */
656 		errno = ENOEXEC;
657 		rc = -1;
658 		goto do_script;
659 	    }
660 	    else if (type == -3) {		/* Is a directory? */
661 		/* Special-case this */
662 		char tbuf[512];
663 		int l = strlen(tmps);
664 
665 		if (l + 5 <= sizeof tbuf) {
666 		    strcpy(tbuf, tmps);
667 		    strcpy(tbuf + l, ".exe");
668 		    type = file_type(tbuf);
669 		    if (type >= -3)
670 			goto type_again;
671 		}
672 
673 		errno = ENOEXEC;
674 		rc = -1;
675 		goto do_script;
676 	    }
677 	    switch (type & 7) {
678 		/* Ignore WINDOWCOMPAT and FAPI, start them the same type we are. */
679 	    case FAPPTYP_WINDOWAPI:
680 	    {
681 		if (os2_mytype != 3) {	/* not PM */
682 		    if (flag == P_NOWAIT)
683 			flag = P_PM;
684 		    else if ((flag & 7) != P_PM && (flag & 7) != P_SESSION)
685 			Perl_warner(aTHX_ packWARN(WARN_EXEC), "Starting PM process with flag=%d, mytype=%d",
686 			     flag, os2_mytype);
687 		}
688 	    }
689 	    break;
690 	    case FAPPTYP_NOTWINDOWCOMPAT:
691 	    {
692 		if (os2_mytype != 0) {	/* not full screen */
693 		    if (flag == P_NOWAIT)
694 			flag = P_SESSION;
695 		    else if ((flag & 7) != P_SESSION)
696 			Perl_warner(aTHX_ packWARN(WARN_EXEC), "Starting Full Screen process with flag=%d, mytype=%d",
697 			     flag, os2_mytype);
698 		}
699 	    }
700 	    break;
701 	    case FAPPTYP_NOTSPEC:
702 		/* Let the shell handle this... */
703 		force_shell = 1;
704 		buf = "";		/* Pacify a warning */
705 		file = 0;		/* Pacify a warning */
706 		goto doshell_args;
707 		break;
708 	    }
709 	}
710 
711 	if (addflag) {
712 	    addflag = 0;
713 	    new_stderr = dup(2);		/* Preserve stderr */
714 	    if (new_stderr == -1) {
715 		if (errno == EBADF)
716 		    nostderr = 1;
717 		else {
718 		    rc = -1;
719 		    goto finish;
720 		}
721 	    } else
722 		fl_stderr = fcntl(2, F_GETFD);
723 	    rc = dup2(1,2);
724 	    if (rc == -1)
725 		goto finish;
726 	    fcntl(new_stderr, F_SETFD, FD_CLOEXEC);
727 	}
728 
729 #if 0
730 	rc = result(aTHX_ trueflag, spawnvp(flag,tmps,PL_Argv));
731 #else
732 	if (execf == EXECF_TRUEEXEC)
733 	    rc = execvp(tmps,PL_Argv);
734 	else if (execf == EXECF_EXEC)
735 	    rc = spawnvp(trueflag | P_OVERLAY,tmps,PL_Argv);
736 	else if (execf == EXECF_SPAWN_NOWAIT)
737 	    rc = spawnvp(flag,tmps,PL_Argv);
738         else if (execf == EXECF_SYNC)
739 	    rc = spawnvp(trueflag,tmps,PL_Argv);
740         else				/* EXECF_SPAWN, EXECF_SPAWN_BYFLAG */
741 	    rc = result(aTHX_ trueflag,
742 			spawnvp(flag,tmps,PL_Argv));
743 #endif
744 	if (rc < 0 && pass == 1
745 	    && (tmps == PL_Argv[0])) { /* Cannot transfer `really' via shell. */
746 	      do_script:
747 	    {
748 	    int err = errno;
749 
750 	    if (err == ENOENT || err == ENOEXEC) {
751 		/* No such file, or is a script. */
752 		/* Try adding script extensions to the file name, and
753 		   search on PATH. */
754 		char *scr = find_script(PL_Argv[0], TRUE, NULL, 0);
755 
756 		if (scr) {
757 		    char *s = 0, *s1;
758 		    SV *scrsv = sv_2mortal(newSVpv(scr, 0));
759 		    SV *bufsv = sv_newmortal();
760 
761                     Safefree(scr);
762 		    scr = SvPV(scrsv, n_a); /* free()ed later */
763 
764 		    file = PerlIO_open(scr, "r");
765 		    PL_Argv[0] = scr;
766 		    if (!file)
767 			goto panic_file;
768 
769 		    buf = sv_gets(bufsv, file, 0 /* No append */);
770 		    if (!buf)
771 			buf = "";	/* XXX Needed? */
772 		    if (!buf[0]) {	/* Empty... */
773 			PerlIO_close(file);
774 			/* Special case: maybe from -Zexe build, so
775 			   there is an executable around (contrary to
776 			   documentation, DosQueryAppType sometimes (?)
777 			   does not append ".exe", so we could have
778 			   reached this place). */
779 			sv_catpv(scrsv, ".exe");
780 	                scr = SvPV(scrsv, n_a);	/* Reload */
781 			if (PerlLIO_stat(scr,&PL_statbuf) >= 0
782 			    && !S_ISDIR(PL_statbuf.st_mode)) {	/* Found */
783 				tmps = scr;
784 				pass++;
785 				goto reread;
786 			} else {		/* Restore */
787 				SvCUR_set(scrsv, SvCUR(scrsv) - 4);
788 				*SvEND(scrsv) = 0;
789 			}
790 		    }
791 		    if (PerlIO_close(file) != 0) { /* Failure */
792 		      panic_file:
793 			Perl_warner(aTHX_ packWARN(WARN_EXEC), "Error reading \"%s\": %s",
794 			     scr, Strerror(errno));
795 			buf = "";	/* Not #! */
796 			goto doshell_args;
797 		    }
798 		    if (buf[0] == '#') {
799 			if (buf[1] == '!')
800 			    s = buf + 2;
801 		    } else if (buf[0] == 'e') {
802 			if (strnEQ(buf, "extproc", 7)
803 			    && isSPACE(buf[7]))
804 			    s = buf + 8;
805 		    } else if (buf[0] == 'E') {
806 			if (strnEQ(buf, "EXTPROC", 7)
807 			    && isSPACE(buf[7]))
808 			    s = buf + 8;
809 		    }
810 		    if (!s) {
811 			buf = "";	/* Not #! */
812 			goto doshell_args;
813 		    }
814 
815 		    s1 = s;
816 		    nargs = 0;
817 		    argsp = args;
818 		    while (1) {
819 			/* Do better than pdksh: allow a few args,
820 			   strip trailing whitespace.  */
821 			while (isSPACE(*s))
822 			    s++;
823 			if (*s == 0)
824 			    break;
825 			if (nargs == 4) {
826 			    nargs = -1;
827 			    break;
828 			}
829 			args[nargs++] = s;
830 			while (*s && !isSPACE(*s))
831 			    s++;
832 			if (*s == 0)
833 			    break;
834 			*s++ = 0;
835 		    }
836 		    if (nargs == -1) {
837 			Perl_warner(aTHX_ packWARN(WARN_EXEC), "Too many args on %.*s line of \"%s\"",
838 			     s1 - buf, buf, scr);
839 			nargs = 4;
840 			argsp = fargs;
841 		    }
842 		    /* Can jump from far, buf/file invalid if force_shell: */
843 		  doshell_args:
844 		    {
845 			char **a = PL_Argv;
846 			char *exec_args[2];
847 
848 			if (force_shell
849 			    || (!buf[0] && file)) { /* File without magic */
850 			    /* In fact we tried all what pdksh would
851 			       try.  There is no point in calling
852 			       pdksh, we may just emulate its logic. */
853 			    char *shell = getenv("EXECSHELL");
854 			    char *shell_opt = NULL;
855 
856 			    if (!shell) {
857 				char *s;
858 
859 				shell_opt = "/c";
860 				shell = getenv("OS2_SHELL");
861 				if (inicmd) { /* No spaces at start! */
862 				    s = inicmd;
863 				    while (*s && !isSPACE(*s)) {
864 					if (*s++ == '/') {
865 					    inicmd = NULL; /* Cannot use */
866 					    break;
867 					}
868 				    }
869 				}
870 				if (!inicmd) {
871 				    s = PL_Argv[0];
872 				    while (*s) {
873 					/* Dosish shells will choke on slashes
874 					   in paths, fortunately, this is
875 					   important for zeroth arg only. */
876 					if (*s == '/')
877 					    *s = '\\';
878 					s++;
879 				    }
880 				}
881 			    }
882 			    /* If EXECSHELL is set, we do not set */
883 
884 			    if (!shell)
885 				shell = ((_emx_env & 0x200)
886 					 ? "c:/os2/cmd.exe"
887 					 : "c:/command.com");
888 			    nargs = shell_opt ? 2 : 1;	/* shell file args */
889 			    exec_args[0] = shell;
890 			    exec_args[1] = shell_opt;
891 			    argsp = exec_args;
892 			    if (nargs == 2 && inicmd) {
893 				/* Use the original cmd line */
894 				/* XXXX This is good only until we refuse
895 				        quoted arguments... */
896 				PL_Argv[0] = inicmd;
897 				PL_Argv[1] = Nullch;
898 			    }
899 			} else if (!buf[0] && inicmd) { /* No file */
900 			    /* Start with the original cmdline. */
901 			    /* XXXX This is good only until we refuse
902 			            quoted arguments... */
903 
904 			    PL_Argv[0] = inicmd;
905 			    PL_Argv[1] = Nullch;
906 			    nargs = 2;	/* shell -c */
907 			}
908 
909 			while (a[1])		/* Get to the end */
910 			    a++;
911 			a++;			/* Copy finil NULL too */
912 			while (a >= PL_Argv) {
913 			    *(a + nargs) = *a;	/* PL_Argv was preallocated to be
914 						   long enough. */
915 			    a--;
916 			}
917 			while (--nargs >= 0)
918 			    PL_Argv[nargs] = argsp[nargs];
919 			/* Enable pathless exec if #! (as pdksh). */
920 			pass = (buf[0] == '#' ? 2 : 3);
921 			goto retry;
922 		    }
923 		}
924 		/* Not found: restore errno */
925 		errno = err;
926 	    }
927 	  }
928 	} else if (rc < 0 && pass == 2 && errno == ENOENT) { /* File not found */
929 	    char *no_dir = strrchr(PL_Argv[0], '/');
930 
931 	    /* Do as pdksh port does: if not found with /, try without
932 	       path. */
933 	    if (no_dir) {
934 		PL_Argv[0] = no_dir + 1;
935 		pass++;
936 		goto retry;
937 	    }
938 	}
939 	if (rc < 0 && ckWARN(WARN_EXEC))
940 	    Perl_warner(aTHX_ packWARN(WARN_EXEC), "Can't %s \"%s\": %s\n",
941 		 ((execf != EXECF_EXEC && execf != EXECF_TRUEEXEC)
942 		  ? "spawn" : "exec"),
943 		 PL_Argv[0], Strerror(errno));
944 	if (rc < 0 && (execf != EXECF_SPAWN_NOWAIT)
945 	    && ((trueflag & 0xFF) == P_WAIT))
946 	    rc = -1;
947 
948   finish:
949     if (new_stderr != -1) {	/* How can we use error codes? */
950 	dup2(new_stderr, 2);
951 	close(new_stderr);
952 	fcntl(2, F_SETFD, fl_stderr);
953     } else if (nostderr)
954        close(2);
955     return rc;
956 }
957 
958 /* Try converting 1-arg form to (usually shell-less) multi-arg form. */
959 int
960 do_spawn3(pTHX_ char *cmd, int execf, int flag)
961 {
962     register char **a;
963     register char *s;
964     char *shell, *copt, *news = NULL;
965     int rc, seenspace = 0, mergestderr = 0;
966 
967 #ifdef TRYSHELL
968     if ((shell = getenv("EMXSHELL")) != NULL)
969     	copt = "-c";
970     else if ((shell = getenv("SHELL")) != NULL)
971     	copt = "-c";
972     else if ((shell = getenv("COMSPEC")) != NULL)
973     	copt = "/C";
974     else
975     	shell = "cmd.exe";
976 #else
977     /* Consensus on perl5-porters is that it is _very_ important to
978        have a shell which will not change between computers with the
979        same architecture, to avoid "action on a distance".
980        And to have simple build, this shell should be sh. */
981     shell = PL_sh_path;
982     copt = "-c";
983 #endif
984 
985     while (*cmd && isSPACE(*cmd))
986 	cmd++;
987 
988     if (strnEQ(cmd,"/bin/sh",7) && isSPACE(cmd[7])) {
989 	STRLEN l = strlen(PL_sh_path);
990 
991 	New(1302, news, strlen(cmd) - 7 + l + 1, char);
992 	strcpy(news, PL_sh_path);
993 	strcpy(news + l, cmd + 7);
994 	cmd = news;
995     }
996 
997     /* save an extra exec if possible */
998     /* see if there are shell metacharacters in it */
999 
1000     if (*cmd == '.' && isSPACE(cmd[1]))
1001 	goto doshell;
1002 
1003     if (strnEQ(cmd,"exec",4) && isSPACE(cmd[4]))
1004 	goto doshell;
1005 
1006     for (s = cmd; *s && isALPHA(*s); s++) ;	/* catch VAR=val gizmo */
1007     if (*s == '=')
1008 	goto doshell;
1009 
1010     for (s = cmd; *s; s++) {
1011 	if (*s != ' ' && !isALPHA(*s) && strchr("$&*(){}[]'\";\\|?<>~`\n",*s)) {
1012 	    if (*s == '\n' && s[1] == '\0') {
1013 		*s = '\0';
1014 		break;
1015 	    } else if (*s == '\\' && !seenspace) {
1016 		continue;		/* Allow backslashes in names */
1017 	    } else if (*s == '>' && s >= cmd + 3
1018 			&& s[-1] == '2' && s[1] == '&' && s[2] == '1'
1019 			&& isSPACE(s[-2]) ) {
1020 		char *t = s + 3;
1021 
1022 		while (*t && isSPACE(*t))
1023 		    t++;
1024 		if (!*t) {
1025 		    s[-2] = '\0';
1026 		    mergestderr = 1;
1027 		    break;		/* Allow 2>&1 as the last thing */
1028 		}
1029 	    }
1030 	    /* We do not convert this to do_spawn_ve since shell
1031 	       should be smart enough to start itself gloriously. */
1032 	  doshell:
1033 	    if (execf == EXECF_TRUEEXEC)
1034                 rc = execl(shell,shell,copt,cmd,(char*)0);
1035 	    else if (execf == EXECF_EXEC)
1036                 rc = spawnl(P_OVERLAY,shell,shell,copt,cmd,(char*)0);
1037 	    else if (execf == EXECF_SPAWN_NOWAIT)
1038                 rc = spawnl(P_NOWAIT,shell,shell,copt,cmd,(char*)0);
1039 	    else if (execf == EXECF_SPAWN_BYFLAG)
1040                 rc = spawnl(flag,shell,shell,copt,cmd,(char*)0);
1041 	    else {
1042 		/* In the ak code internal P_NOWAIT is P_WAIT ??? */
1043 		if (execf == EXECF_SYNC)
1044 		   rc = spawnl(P_WAIT,shell,shell,copt,cmd,(char*)0);
1045 		else
1046 		   rc = result(aTHX_ P_WAIT,
1047 			       spawnl(P_NOWAIT,shell,shell,copt,cmd,(char*)0));
1048 		if (rc < 0 && ckWARN(WARN_EXEC))
1049 		    Perl_warner(aTHX_ packWARN(WARN_EXEC), "Can't %s \"%s\": %s",
1050 			 (execf == EXECF_SPAWN ? "spawn" : "exec"),
1051 			 shell, Strerror(errno));
1052 		if (rc < 0)
1053 		    rc = -1;
1054 	    }
1055 	    if (news)
1056 		Safefree(news);
1057 	    return rc;
1058 	} else if (*s == ' ' || *s == '\t') {
1059 	    seenspace = 1;
1060 	}
1061     }
1062 
1063     /* cmd="a" may lead to "sh", "-c", "\"$@\"", "a", "a.cmd", NULL */
1064     New(1303,PL_Argv, (s - cmd + 11) / 2, char*);
1065     PL_Cmd = savepvn(cmd, s-cmd);
1066     a = PL_Argv;
1067     for (s = PL_Cmd; *s;) {
1068 	while (*s && isSPACE(*s)) s++;
1069 	if (*s)
1070 	    *(a++) = s;
1071 	while (*s && !isSPACE(*s)) s++;
1072 	if (*s)
1073 	    *s++ = '\0';
1074     }
1075     *a = Nullch;
1076     if (PL_Argv[0])
1077 	rc = do_spawn_ve(aTHX_ NULL, flag, execf, cmd, mergestderr);
1078     else
1079     	rc = -1;
1080     if (news)
1081 	Safefree(news);
1082     do_execfree();
1083     return rc;
1084 }
1085 
1086 /* Array spawn.  */
1087 int
1088 os2_do_aspawn(pTHX_ SV *really, register void **vmark, register void **vsp)
1089 {
1090     register SV **mark = (SV **)vmark;
1091     register SV **sp = (SV **)vsp;
1092     register char **a;
1093     int rc;
1094     int flag = P_WAIT, flag_set = 0;
1095     STRLEN n_a;
1096 
1097     if (sp > mark) {
1098 	New(1301,PL_Argv, sp - mark + 3, char*);
1099 	a = PL_Argv;
1100 
1101 	if (mark < sp && SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
1102 		++mark;
1103 		flag = SvIVx(*mark);
1104 		flag_set = 1;
1105 
1106 	}
1107 
1108 	while (++mark <= sp) {
1109 	    if (*mark)
1110 		*a++ = SvPVx(*mark, n_a);
1111 	    else
1112 		*a++ = "";
1113 	}
1114 	*a = Nullch;
1115 
1116 	if (flag_set && (a == PL_Argv + 1)) { /* One arg? */
1117 	    rc = do_spawn3(aTHX_ a[-1], EXECF_SPAWN_BYFLAG, flag);
1118 	} else
1119 	    rc = do_spawn_ve(aTHX_ really, flag, EXECF_SPAWN, NULL, 0);
1120     } else
1121     	rc = -1;
1122     do_execfree();
1123     return rc;
1124 }
1125 
1126 int
1127 os2_do_spawn(pTHX_ char *cmd)
1128 {
1129     return do_spawn3(aTHX_ cmd, EXECF_SPAWN, 0);
1130 }
1131 
1132 int
1133 do_spawn_nowait(pTHX_ char *cmd)
1134 {
1135     return do_spawn3(aTHX_ cmd, EXECF_SPAWN_NOWAIT,0);
1136 }
1137 
1138 bool
1139 Perl_do_exec(pTHX_ char *cmd)
1140 {
1141     do_spawn3(aTHX_ cmd, EXECF_EXEC, 0);
1142     return FALSE;
1143 }
1144 
1145 bool
1146 os2exec(pTHX_ char *cmd)
1147 {
1148     return do_spawn3(aTHX_ cmd, EXECF_TRUEEXEC, 0);
1149 }
1150 
1151 PerlIO *
1152 my_syspopen(pTHX_ char *cmd, char *mode)
1153 {
1154 #ifndef USE_POPEN
1155     int p[2];
1156     register I32 this, that, newfd;
1157     register I32 pid;
1158     SV *sv;
1159     int fh_fl = 0;			/* Pacify the warning */
1160 
1161     /* `this' is what we use in the parent, `that' in the child. */
1162     this = (*mode == 'w');
1163     that = !this;
1164     if (PL_tainting) {
1165 	taint_env();
1166 	taint_proper("Insecure %s%s", "EXEC");
1167     }
1168     if (pipe(p) < 0)
1169 	return Nullfp;
1170     /* Now we need to spawn the child. */
1171     if (p[this] == (*mode == 'r')) {	/* if fh 0/1 was initially closed. */
1172 	int new = dup(p[this]);
1173 
1174 	if (new == -1)
1175 	    goto closepipes;
1176 	close(p[this]);
1177 	p[this] = new;
1178     }
1179     newfd = dup(*mode == 'r');		/* Preserve std* */
1180     if (newfd == -1) {
1181 	/* This cannot happen due to fh being bad after pipe(), since
1182 	   pipe() should have created fh 0 and 1 even if they were
1183 	   initially closed.  But we closed p[this] before.  */
1184 	if (errno != EBADF) {
1185 	  closepipes:
1186 	    close(p[0]);
1187 	    close(p[1]);
1188 	    return Nullfp;
1189 	}
1190     } else
1191 	fh_fl = fcntl(*mode == 'r', F_GETFD);
1192     if (p[that] != (*mode == 'r')) {	/* if fh 0/1 was initially closed. */
1193 	dup2(p[that], *mode == 'r');
1194 	close(p[that]);
1195     }
1196     /* Where is `this' and newfd now? */
1197     fcntl(p[this], F_SETFD, FD_CLOEXEC);
1198     if (newfd != -1)
1199 	fcntl(newfd, F_SETFD, FD_CLOEXEC);
1200     pid = do_spawn_nowait(aTHX_ cmd);
1201     if (newfd == -1)
1202 	close(*mode == 'r');		/* It was closed initially */
1203     else if (newfd != (*mode == 'r')) {	/* Probably this check is not needed */
1204 	dup2(newfd, *mode == 'r');	/* Return std* back. */
1205 	close(newfd);
1206 	fcntl(*mode == 'r', F_SETFD, fh_fl);
1207     } else
1208 	fcntl(*mode == 'r', F_SETFD, fh_fl);
1209     if (p[that] == (*mode == 'r'))
1210 	close(p[that]);
1211     if (pid == -1) {
1212 	close(p[this]);
1213 	return Nullfp;
1214     }
1215     if (p[that] < p[this]) {		/* Make fh as small as possible */
1216 	dup2(p[this], p[that]);
1217 	close(p[this]);
1218 	p[this] = p[that];
1219     }
1220     sv = *av_fetch(PL_fdpid,p[this],TRUE);
1221     (void)SvUPGRADE(sv,SVt_IV);
1222     SvIVX(sv) = pid;
1223     PL_forkprocess = pid;
1224     return PerlIO_fdopen(p[this], mode);
1225 
1226 #else  /* USE_POPEN */
1227 
1228     PerlIO *res;
1229     SV *sv;
1230 
1231 #  ifdef TRYSHELL
1232     res = popen(cmd, mode);
1233 #  else
1234     char *shell = getenv("EMXSHELL");
1235 
1236     my_setenv("EMXSHELL", PL_sh_path);
1237     res = popen(cmd, mode);
1238     my_setenv("EMXSHELL", shell);
1239 #  endif
1240     sv = *av_fetch(PL_fdpid, PerlIO_fileno(res), TRUE);
1241     (void)SvUPGRADE(sv,SVt_IV);
1242     SvIVX(sv) = -1;			/* A cooky. */
1243     return res;
1244 
1245 #endif /* USE_POPEN */
1246 
1247 }
1248 
1249 /******************************************************************/
1250 
1251 #ifndef HAS_FORK
1252 int
1253 fork(void)
1254 {
1255     Perl_croak_nocontext(PL_no_func, "Unsupported function fork");
1256     errno = EINVAL;
1257     return -1;
1258 }
1259 #endif
1260 
1261 /*******************************************************************/
1262 /* not implemented in EMX 0.9d */
1263 
1264 char *	ctermid(char *s)	{ return 0; }
1265 
1266 #ifdef MYTTYNAME /* was not in emx0.9a */
1267 void *	ttyname(x)	{ return 0; }
1268 #endif
1269 
1270 /*****************************************************************************/
1271 /* not implemented in C Set++ */
1272 
1273 #ifndef __EMX__
1274 int	setuid(x)	{ errno = EINVAL; return -1; }
1275 int	setgid(x)	{ errno = EINVAL; return -1; }
1276 #endif
1277 
1278 /*****************************************************************************/
1279 /* stat() hack for char/block device */
1280 
1281 #if OS2_STAT_HACK
1282 
1283     /* First attempt used DosQueryFSAttach which crashed the system when
1284        used with 5.001. Now just look for /dev/. */
1285 
1286 int
1287 os2_stat(const char *name, struct stat *st)
1288 {
1289     static int ino = SHRT_MAX;
1290 
1291     if (stricmp(name, "/dev/con") != 0
1292      && stricmp(name, "/dev/tty") != 0)
1293 	return stat(name, st);
1294 
1295     memset(st, 0, sizeof *st);
1296     st->st_mode = S_IFCHR|0666;
1297     st->st_ino = (ino-- & 0x7FFF);
1298     st->st_nlink = 1;
1299     return 0;
1300 }
1301 
1302 #endif
1303 
1304 #ifdef USE_PERL_SBRK
1305 
1306 /* SBRK() emulation, mostly moved to malloc.c. */
1307 
1308 void *
1309 sys_alloc(int size) {
1310     void *got;
1311     APIRET rc = DosAllocMem(&got, size, PAG_COMMIT | PAG_WRITE);
1312 
1313     if (rc == ERROR_NOT_ENOUGH_MEMORY) {
1314 	return (void *) -1;
1315     } else if ( rc )
1316 	Perl_croak_nocontext("Got an error from DosAllocMem: %li", (long)rc);
1317     return got;
1318 }
1319 
1320 #endif /* USE_PERL_SBRK */
1321 
1322 /* tmp path */
1323 
1324 char *tmppath = TMPPATH1;
1325 
1326 void
1327 settmppath()
1328 {
1329     char *p = getenv("TMP"), *tpath;
1330     int len;
1331 
1332     if (!p) p = getenv("TEMP");
1333     if (!p) return;
1334     len = strlen(p);
1335     tpath = (char *)malloc(len + strlen(TMPPATH1) + 2);
1336     if (tpath) {
1337 	strcpy(tpath, p);
1338 	tpath[len] = '/';
1339 	strcpy(tpath + len + 1, TMPPATH1);
1340 	tmppath = tpath;
1341     }
1342 }
1343 
1344 #include "XSUB.h"
1345 
1346 XS(XS_File__Copy_syscopy)
1347 {
1348     dXSARGS;
1349     if (items < 2 || items > 3)
1350 	Perl_croak_nocontext("Usage: File::Copy::syscopy(src,dst,flag=0)");
1351     {
1352 	STRLEN n_a;
1353 	char *	src = (char *)SvPV(ST(0),n_a);
1354 	char *	dst = (char *)SvPV(ST(1),n_a);
1355 	U32	flag;
1356 	int	RETVAL, rc;
1357 
1358 	if (items < 3)
1359 	    flag = 0;
1360 	else {
1361 	    flag = (unsigned long)SvIV(ST(2));
1362 	}
1363 
1364 	RETVAL = !CheckOSError(DosCopy(src, dst, flag));
1365 	ST(0) = sv_newmortal();
1366 	sv_setiv(ST(0), (IV)RETVAL);
1367     }
1368     XSRETURN(1);
1369 }
1370 
1371 #define PERL_PATCHLEVEL_H_IMPLICIT	/* Do not init local_patches. */
1372 #include "patchlevel.h"
1373 #undef PERL_PATCHLEVEL_H_IMPLICIT
1374 
1375 char *
1376 mod2fname(pTHX_ SV *sv)
1377 {
1378     static char fname[9];
1379     int pos = 6, len, avlen;
1380     unsigned int sum = 0;
1381     char *s;
1382     STRLEN n_a;
1383 
1384     if (!SvROK(sv)) Perl_croak_nocontext("Not a reference given to mod2fname");
1385     sv = SvRV(sv);
1386     if (SvTYPE(sv) != SVt_PVAV)
1387       Perl_croak_nocontext("Not array reference given to mod2fname");
1388 
1389     avlen = av_len((AV*)sv);
1390     if (avlen < 0)
1391       Perl_croak_nocontext("Empty array reference given to mod2fname");
1392 
1393     s = SvPV(*av_fetch((AV*)sv, avlen, FALSE), n_a);
1394     strncpy(fname, s, 8);
1395     len = strlen(s);
1396     if (len < 6) pos = len;
1397     while (*s) {
1398 	sum = 33 * sum + *(s++);	/* Checksumming first chars to
1399 					 * get the capitalization into c.s. */
1400     }
1401     avlen --;
1402     while (avlen >= 0) {
1403 	s = SvPV(*av_fetch((AV*)sv, avlen, FALSE), n_a);
1404 	while (*s) {
1405 	    sum = 33 * sum + *(s++);	/* 7 is primitive mod 13. */
1406 	}
1407 	avlen --;
1408     }
1409 #ifdef USE_5005THREADS
1410     sum++;				/* Avoid conflict of DLLs in memory. */
1411 #endif
1412    /* We always load modules as *specific* DLLs, and with the full name.
1413       When loading a specific DLL by its full name, one cannot get a
1414       different DLL, even if a DLL with the same basename is loaded already.
1415       Thus there is no need to include the version into the mangling scheme. */
1416 #if 0
1417     sum += PERL_VERSION * 200 + PERL_SUBVERSION * 2;  /* Up to 5.6.1 */
1418 #else
1419 #  ifndef COMPATIBLE_VERSION_SUM  /* Binary compatibility with the 5.00553 binary */
1420 #    define COMPATIBLE_VERSION_SUM (5 * 200 + 53 * 2)
1421 #  endif
1422     sum += COMPATIBLE_VERSION_SUM;
1423 #endif
1424     fname[pos] = 'A' + (sum % 26);
1425     fname[pos + 1] = 'A' + (sum / 26 % 26);
1426     fname[pos + 2] = '\0';
1427     return (char *)fname;
1428 }
1429 
1430 XS(XS_DynaLoader_mod2fname)
1431 {
1432     dXSARGS;
1433     if (items != 1)
1434 	Perl_croak_nocontext("Usage: DynaLoader::mod2fname(sv)");
1435     {
1436 	SV *	sv = ST(0);
1437 	char *	RETVAL;
1438 
1439 	RETVAL = mod2fname(aTHX_ sv);
1440 	ST(0) = sv_newmortal();
1441 	sv_setpv((SV*)ST(0), RETVAL);
1442     }
1443     XSRETURN(1);
1444 }
1445 
1446 char *
1447 os2error(int rc)
1448 {
1449 	static char buf[300];
1450 	ULONG len;
1451 	char *s;
1452 	int number = SvTRUE(get_sv("OS2::nsyserror", TRUE));
1453 
1454         if (!(_emx_env & 0x200)) return ""; /* Nop if not OS/2. */
1455 	if (rc == 0)
1456 		return "";
1457 	if (number) {
1458 	    sprintf(buf, "SYS%04d=%#x: ", rc, rc);
1459 	    s = buf + strlen(buf);
1460 	} else
1461 	    s = buf;
1462 	if (DosGetMessage(NULL, 0, s, sizeof(buf) - 1 - (s-buf),
1463 			  rc, "OSO001.MSG", &len)) {
1464 	    if (!number) {
1465 		sprintf(buf, "SYS%04d=%#x: ", rc, rc);
1466 		s = buf + strlen(buf);
1467 	    }
1468 	    sprintf(s, "[No description found in OSO001.MSG]");
1469 	} else {
1470 		s[len] = '\0';
1471 		if (len && s[len - 1] == '\n')
1472 			s[--len] = 0;
1473 		if (len && s[len - 1] == '\r')
1474 			s[--len] = 0;
1475 		if (len && s[len - 1] == '.')
1476 			s[--len] = 0;
1477 		if (len >= 10 && number && strnEQ(s, buf, 7)
1478 		    && s[7] == ':' && s[8] == ' ')
1479 		    /* Some messages start with SYSdddd:, some not */
1480 		    Move(s + 9, s, (len -= 9) + 1, char);
1481 	}
1482 	return buf;
1483 }
1484 
1485 void
1486 ResetWinError(void)
1487 {
1488   WinError_2_Perl_rc;
1489 }
1490 
1491 void
1492 CroakWinError(int die, char *name)
1493 {
1494   FillWinError;
1495   if (die && Perl_rc)
1496     croak("%s: %s", (name ? name : "Win* API call"), os2error(Perl_rc));
1497 }
1498 
1499 char *
1500 os2_execname(pTHX)
1501 {
1502   char buf[300], *p, *o = PL_origargv[0], ok = 1;
1503 
1504   if (_execname(buf, sizeof buf) != 0)
1505 	return o;
1506   p = buf;
1507   while (*p) {
1508     if (*p == '\\')
1509 	*p = '/';
1510     if (*p == '/') {
1511 	if (ok && *o != '/' && *o != '\\')
1512 	    ok = 0;
1513     } else if (ok && tolower(*o) != tolower(*p))
1514 	ok = 0;
1515     p++;
1516     o++;
1517   }
1518   if (ok) { /* PL_origargv[0] matches the real name.  Use PL_origargv[0]: */
1519      strcpy(buf, PL_origargv[0]);	/* _execname() is always uppercased */
1520      p = buf;
1521      while (*p) {
1522        if (*p == '\\')
1523            *p = '/';
1524        p++;
1525      }
1526   }
1527   p = savepv(buf);
1528   SAVEFREEPV(p);
1529   return p;
1530 }
1531 
1532 char *
1533 perllib_mangle(char *s, unsigned int l)
1534 {
1535     static char *newp, *oldp;
1536     static int newl, oldl, notfound;
1537     static char ret[STATIC_FILE_LENGTH+1];
1538 
1539     if (!newp && !notfound) {
1540 	newp = getenv("PERLLIB_PREFIX");
1541 	if (newp) {
1542 	    char *s;
1543 
1544 	    oldp = newp;
1545 	    while (*newp && !isSPACE(*newp) && *newp != ';') {
1546 		newp++; oldl++;		/* Skip digits. */
1547 	    }
1548 	    while (*newp && (isSPACE(*newp) || *newp == ';')) {
1549 		newp++;			/* Skip whitespace. */
1550 	    }
1551 	    newl = strlen(newp);
1552 	    if (newl == 0 || oldl == 0) {
1553 		Perl_croak_nocontext("Malformed PERLLIB_PREFIX");
1554 	    }
1555 	    strcpy(ret, newp);
1556 	    s = ret;
1557 	    while (*s) {
1558 		if (*s == '\\') *s = '/';
1559 		s++;
1560 	    }
1561 	} else {
1562 	    notfound = 1;
1563 	}
1564     }
1565     if (!newp) {
1566 	return s;
1567     }
1568     if (l == 0) {
1569 	l = strlen(s);
1570     }
1571     if (l < oldl || strnicmp(oldp, s, oldl) != 0) {
1572 	return s;
1573     }
1574     if (l + newl - oldl > STATIC_FILE_LENGTH || newl > STATIC_FILE_LENGTH) {
1575 	Perl_croak_nocontext("Malformed PERLLIB_PREFIX");
1576     }
1577     strcpy(ret + newl, s + oldl);
1578     return ret;
1579 }
1580 
1581 unsigned long
1582 Perl_hab_GET()			/* Needed if perl.h cannot be included */
1583 {
1584     return perl_hab_GET();
1585 }
1586 
1587 HMQ
1588 Perl_Register_MQ(int serve)
1589 {
1590     PPIB pib;
1591     PTIB tib;
1592 
1593     if (Perl_hmq_refcnt > 0)
1594 	return Perl_hmq;
1595     Perl_hmq_refcnt = 0;		/* Be extra safe */
1596     DosGetInfoBlocks(&tib, &pib);
1597     Perl_os2_initial_mode = pib->pib_ultype;
1598     /* Try morphing into a PM application. */
1599     if (pib->pib_ultype != 3)		/* 2 is VIO */
1600 	pib->pib_ultype = 3;		/* 3 is PM */
1601     init_PMWIN_entries();
1602     /* 64 messages if before OS/2 3.0, ignored otherwise */
1603     Perl_hmq = (*PMWIN_entries.CreateMsgQueue)(perl_hab_GET(), 64);
1604     if (!Perl_hmq) {
1605 	static int cnt;
1606 
1607 	SAVEINT(cnt);			/* Allow catch()ing. */
1608 	if (cnt++)
1609 	    _exit(188);			/* Panic can try to create a window. */
1610 	Perl_croak_nocontext("Cannot create a message queue, or morph to a PM application");
1611     }
1612     if (serve) {
1613 	if ( Perl_hmq_servers <= 0	/* Safe to inform us on shutdown, */
1614 	     && Perl_hmq_refcnt > 0 )	/* this was switched off before... */
1615 	    (*PMWIN_entries.CancelShutdown)(Perl_hmq, 0);
1616 	Perl_hmq_servers++;
1617     } else if (!Perl_hmq_servers)	/* Do not inform us on shutdown */
1618 	(*PMWIN_entries.CancelShutdown)(Perl_hmq, 1);
1619     Perl_hmq_refcnt++;
1620     return Perl_hmq;
1621 }
1622 
1623 int
1624 Perl_Serve_Messages(int force)
1625 {
1626     int cnt = 0;
1627     QMSG msg;
1628 
1629     if (Perl_hmq_servers > 0 && !force)
1630 	return 0;
1631     if (Perl_hmq_refcnt <= 0)
1632 	Perl_croak_nocontext("No message queue");
1633     while ((*PMWIN_entries.PeekMsg)(Perl_hab, &msg, NULLHANDLE, 0, 0, PM_REMOVE)) {
1634 	cnt++;
1635 	if (msg.msg == WM_QUIT)
1636 	    Perl_croak_nocontext("QUITing...");
1637 	(*PMWIN_entries.DispatchMsg)(Perl_hab, &msg);
1638     }
1639     return cnt;
1640 }
1641 
1642 int
1643 Perl_Process_Messages(int force, I32 *cntp)
1644 {
1645     QMSG msg;
1646 
1647     if (Perl_hmq_servers > 0 && !force)
1648 	return 0;
1649     if (Perl_hmq_refcnt <= 0)
1650 	Perl_croak_nocontext("No message queue");
1651     while ((*PMWIN_entries.GetMsg)(Perl_hab, &msg, NULLHANDLE, 0, 0)) {
1652 	if (cntp)
1653 	    (*cntp)++;
1654 	(*PMWIN_entries.DispatchMsg)(Perl_hab, &msg);
1655 	if (msg.msg == WM_DESTROY)
1656 	    return -1;
1657 	if (msg.msg == WM_CREATE)
1658 	    return +1;
1659     }
1660     Perl_croak_nocontext("QUITing...");
1661 }
1662 
1663 void
1664 Perl_Deregister_MQ(int serve)
1665 {
1666     PPIB pib;
1667     PTIB tib;
1668 
1669     if (serve)
1670 	Perl_hmq_servers--;
1671     if (--Perl_hmq_refcnt <= 0) {
1672 	init_PMWIN_entries();			/* To be extra safe */
1673 	(*PMWIN_entries.DestroyMsgQueue)(Perl_hmq);
1674 	Perl_hmq = 0;
1675 	/* Try morphing back from a PM application. */
1676 	DosGetInfoBlocks(&tib, &pib);
1677 	if (pib->pib_ultype == 3)		/* 3 is PM */
1678 	    pib->pib_ultype = Perl_os2_initial_mode;
1679 	else
1680 	    Perl_warn_nocontext("Unexpected program mode %d when morphing back from PM",
1681 		 pib->pib_ultype);
1682     } else if (serve && Perl_hmq_servers <= 0)	/* Last server exited */
1683 	(*PMWIN_entries.CancelShutdown)(Perl_hmq, 1);
1684 }
1685 
1686 #define sys_is_absolute(path) ( isALPHA((path)[0]) && (path)[1] == ':' \
1687 				&& ((path)[2] == '/' || (path)[2] == '\\'))
1688 #define sys_is_rooted _fnisabs
1689 #define sys_is_relative _fnisrel
1690 #define current_drive _getdrive
1691 
1692 #undef chdir				/* Was _chdir2. */
1693 #define sys_chdir(p) (chdir(p) == 0)
1694 #define change_drive(d) (_chdrive(d), (current_drive() == toupper(d)))
1695 
1696 static int DOS_harderr_state = -1;
1697 
1698 XS(XS_OS2_Error)
1699 {
1700     dXSARGS;
1701     if (items != 2)
1702 	Perl_croak_nocontext("Usage: OS2::Error(harderr, exception)");
1703     {
1704 	int	arg1 = SvIV(ST(0));
1705 	int	arg2 = SvIV(ST(1));
1706 	int	a = ((arg1 ? FERR_ENABLEHARDERR : FERR_DISABLEHARDERR)
1707 		     | (arg2 ? FERR_ENABLEEXCEPTION : FERR_DISABLEEXCEPTION));
1708 	int	RETVAL = ((arg1 ? 1 : 0) | (arg2 ? 2 : 0));
1709 	unsigned long rc;
1710 
1711 	if (CheckOSError(DosError(a)))
1712 	    Perl_croak_nocontext("DosError(%d) failed", a);
1713 	ST(0) = sv_newmortal();
1714 	if (DOS_harderr_state >= 0)
1715 	    sv_setiv(ST(0), DOS_harderr_state);
1716 	DOS_harderr_state = RETVAL;
1717     }
1718     XSRETURN(1);
1719 }
1720 
1721 static signed char DOS_suppression_state = -1;
1722 
1723 XS(XS_OS2_Errors2Drive)
1724 {
1725     dXSARGS;
1726     if (items != 1)
1727 	Perl_croak_nocontext("Usage: OS2::Errors2Drive(drive)");
1728     {
1729 	STRLEN n_a;
1730 	SV  *sv = ST(0);
1731 	int	suppress = SvOK(sv);
1732 	char	*s = suppress ? SvPV(sv, n_a) : NULL;
1733 	char	drive = (s ? *s : 0);
1734 	unsigned long rc;
1735 
1736 	if (suppress && !isALPHA(drive))
1737 	    Perl_croak_nocontext("Non-char argument '%c' to OS2::Errors2Drive()", drive);
1738 	if (CheckOSError(DosSuppressPopUps((suppress
1739 					    ? SPU_ENABLESUPPRESSION
1740 					    : SPU_DISABLESUPPRESSION),
1741 					   drive)))
1742 	    Perl_croak_nocontext("DosSuppressPopUps(%c) failed", drive);
1743 	ST(0) = sv_newmortal();
1744 	if (DOS_suppression_state > 0)
1745 	    sv_setpvn(ST(0), &DOS_suppression_state, 1);
1746 	else if (DOS_suppression_state == 0)
1747 	    sv_setpvn(ST(0), "", 0);
1748 	DOS_suppression_state = drive;
1749     }
1750     XSRETURN(1);
1751 }
1752 
1753 static const char * const si_fields[QSV_MAX] = {
1754   "MAX_PATH_LENGTH",
1755   "MAX_TEXT_SESSIONS",
1756   "MAX_PM_SESSIONS",
1757   "MAX_VDM_SESSIONS",
1758   "BOOT_DRIVE",
1759   "DYN_PRI_VARIATION",
1760   "MAX_WAIT",
1761   "MIN_SLICE",
1762   "MAX_SLICE",
1763   "PAGE_SIZE",
1764   "VERSION_MAJOR",
1765   "VERSION_MINOR",
1766   "VERSION_REVISION",
1767   "MS_COUNT",
1768   "TIME_LOW",
1769   "TIME_HIGH",
1770   "TOTPHYSMEM",
1771   "TOTRESMEM",
1772   "TOTAVAILMEM",
1773   "MAXPRMEM",
1774   "MAXSHMEM",
1775   "TIMER_INTERVAL",
1776   "MAX_COMP_LENGTH",
1777   "FOREGROUND_FS_SESSION",
1778   "FOREGROUND_PROCESS"
1779 };
1780 
1781 XS(XS_OS2_SysInfo)
1782 {
1783     dXSARGS;
1784     if (items != 0)
1785 	Perl_croak_nocontext("Usage: OS2::SysInfo()");
1786     {
1787 	ULONG   si[QSV_MAX] = {0};	/* System Information Data Buffer */
1788 	APIRET  rc	= NO_ERROR;	/* Return code            */
1789 	int i = 0, j = 0;
1790 
1791 	if (CheckOSError(DosQuerySysInfo(1L, /* Request all available system */
1792 					 QSV_MAX, /* information */
1793 					 (PVOID)si,
1794 					 sizeof(si))))
1795 	    Perl_croak_nocontext("DosQuerySysInfo() failed");
1796 	EXTEND(SP,2*QSV_MAX);
1797 	while (i < QSV_MAX) {
1798 	    ST(j) = sv_newmortal();
1799 	    sv_setpv(ST(j++), si_fields[i]);
1800 	    ST(j) = sv_newmortal();
1801 	    sv_setiv(ST(j++), si[i]);
1802 	    i++;
1803 	}
1804     }
1805     XSRETURN(2 * QSV_MAX);
1806 }
1807 
1808 XS(XS_OS2_BootDrive)
1809 {
1810     dXSARGS;
1811     if (items != 0)
1812 	Perl_croak_nocontext("Usage: OS2::BootDrive()");
1813     {
1814 	ULONG   si[1] = {0};	/* System Information Data Buffer */
1815 	APIRET  rc    = NO_ERROR;	/* Return code            */
1816 	char c;
1817 
1818 	if (CheckOSError(DosQuerySysInfo(QSV_BOOT_DRIVE, QSV_BOOT_DRIVE,
1819 					 (PVOID)si, sizeof(si))))
1820 	    Perl_croak_nocontext("DosQuerySysInfo() failed");
1821 	ST(0) = sv_newmortal();
1822 	c = 'a' - 1 + si[0];
1823 	sv_setpvn(ST(0), &c, 1);
1824     }
1825     XSRETURN(1);
1826 }
1827 
1828 XS(XS_OS2_MorphPM)
1829 {
1830     dXSARGS;
1831     if (items != 1)
1832 	Perl_croak_nocontext("Usage: OS2::MorphPM(serve)");
1833     {
1834 	bool  serve = SvOK(ST(0));
1835 	unsigned long   pmq = perl_hmq_GET(serve);
1836 
1837 	ST(0) = sv_newmortal();
1838 	sv_setiv(ST(0), pmq);
1839     }
1840     XSRETURN(1);
1841 }
1842 
1843 XS(XS_OS2_UnMorphPM)
1844 {
1845     dXSARGS;
1846     if (items != 1)
1847 	Perl_croak_nocontext("Usage: OS2::UnMorphPM(serve)");
1848     {
1849 	bool  serve = SvOK(ST(0));
1850 
1851 	perl_hmq_UNSET(serve);
1852     }
1853     XSRETURN(0);
1854 }
1855 
1856 XS(XS_OS2_Serve_Messages)
1857 {
1858     dXSARGS;
1859     if (items != 1)
1860 	Perl_croak_nocontext("Usage: OS2::Serve_Messages(force)");
1861     {
1862 	bool  force = SvOK(ST(0));
1863 	unsigned long   cnt = Perl_Serve_Messages(force);
1864 
1865 	ST(0) = sv_newmortal();
1866 	sv_setiv(ST(0), cnt);
1867     }
1868     XSRETURN(1);
1869 }
1870 
1871 XS(XS_OS2_Process_Messages)
1872 {
1873     dXSARGS;
1874     if (items < 1 || items > 2)
1875 	Perl_croak_nocontext("Usage: OS2::Process_Messages(force [, cnt])");
1876     {
1877 	bool  force = SvOK(ST(0));
1878 	unsigned long   cnt;
1879 
1880 	if (items == 2) {
1881 	    I32 cntr;
1882 	    SV *sv = ST(1);
1883 
1884 	    (void)SvIV(sv);		/* Force SvIVX */
1885 	    if (!SvIOK(sv))
1886 		Perl_croak_nocontext("Can't upgrade count to IV");
1887 	    cntr = SvIVX(sv);
1888 	    cnt =  Perl_Process_Messages(force, &cntr);
1889 	    SvIVX(sv) = cntr;
1890 	} else {
1891 	    cnt =  Perl_Process_Messages(force, NULL);
1892         }
1893 	ST(0) = sv_newmortal();
1894 	sv_setiv(ST(0), cnt);
1895     }
1896     XSRETURN(1);
1897 }
1898 
1899 XS(XS_Cwd_current_drive)
1900 {
1901     dXSARGS;
1902     if (items != 0)
1903 	Perl_croak_nocontext("Usage: Cwd::current_drive()");
1904     {
1905 	char	RETVAL;
1906 
1907 	RETVAL = current_drive();
1908 	ST(0) = sv_newmortal();
1909 	sv_setpvn(ST(0), (char *)&RETVAL, 1);
1910     }
1911     XSRETURN(1);
1912 }
1913 
1914 XS(XS_Cwd_sys_chdir)
1915 {
1916     dXSARGS;
1917     if (items != 1)
1918 	Perl_croak_nocontext("Usage: Cwd::sys_chdir(path)");
1919     {
1920 	STRLEN n_a;
1921 	char *	path = (char *)SvPV(ST(0),n_a);
1922 	bool	RETVAL;
1923 
1924 	RETVAL = sys_chdir(path);
1925 	ST(0) = boolSV(RETVAL);
1926 	if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
1927     }
1928     XSRETURN(1);
1929 }
1930 
1931 XS(XS_Cwd_change_drive)
1932 {
1933     dXSARGS;
1934     if (items != 1)
1935 	Perl_croak_nocontext("Usage: Cwd::change_drive(d)");
1936     {
1937 	STRLEN n_a;
1938 	char	d = (char)*SvPV(ST(0),n_a);
1939 	bool	RETVAL;
1940 
1941 	RETVAL = change_drive(d);
1942 	ST(0) = boolSV(RETVAL);
1943 	if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
1944     }
1945     XSRETURN(1);
1946 }
1947 
1948 XS(XS_Cwd_sys_is_absolute)
1949 {
1950     dXSARGS;
1951     if (items != 1)
1952 	Perl_croak_nocontext("Usage: Cwd::sys_is_absolute(path)");
1953     {
1954 	STRLEN n_a;
1955 	char *	path = (char *)SvPV(ST(0),n_a);
1956 	bool	RETVAL;
1957 
1958 	RETVAL = sys_is_absolute(path);
1959 	ST(0) = boolSV(RETVAL);
1960 	if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
1961     }
1962     XSRETURN(1);
1963 }
1964 
1965 XS(XS_Cwd_sys_is_rooted)
1966 {
1967     dXSARGS;
1968     if (items != 1)
1969 	Perl_croak_nocontext("Usage: Cwd::sys_is_rooted(path)");
1970     {
1971 	STRLEN n_a;
1972 	char *	path = (char *)SvPV(ST(0),n_a);
1973 	bool	RETVAL;
1974 
1975 	RETVAL = sys_is_rooted(path);
1976 	ST(0) = boolSV(RETVAL);
1977 	if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
1978     }
1979     XSRETURN(1);
1980 }
1981 
1982 XS(XS_Cwd_sys_is_relative)
1983 {
1984     dXSARGS;
1985     if (items != 1)
1986 	Perl_croak_nocontext("Usage: Cwd::sys_is_relative(path)");
1987     {
1988 	STRLEN n_a;
1989 	char *	path = (char *)SvPV(ST(0),n_a);
1990 	bool	RETVAL;
1991 
1992 	RETVAL = sys_is_relative(path);
1993 	ST(0) = boolSV(RETVAL);
1994 	if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
1995     }
1996     XSRETURN(1);
1997 }
1998 
1999 XS(XS_Cwd_sys_cwd)
2000 {
2001     dXSARGS;
2002     if (items != 0)
2003 	Perl_croak_nocontext("Usage: Cwd::sys_cwd()");
2004     {
2005 	char p[MAXPATHLEN];
2006 	char *	RETVAL;
2007 	RETVAL = _getcwd2(p, MAXPATHLEN);
2008 	ST(0) = sv_newmortal();
2009 	sv_setpv((SV*)ST(0), RETVAL);
2010 #ifndef INCOMPLETE_TAINTS
2011 	SvTAINTED_on(ST(0));
2012 #endif
2013     }
2014     XSRETURN(1);
2015 }
2016 
2017 XS(XS_Cwd_sys_abspath)
2018 {
2019     dXSARGS;
2020     if (items < 1 || items > 2)
2021 	Perl_croak_nocontext("Usage: Cwd::sys_abspath(path, dir = NULL)");
2022     {
2023 	STRLEN n_a;
2024 	char *	path = (char *)SvPV(ST(0),n_a);
2025 	char *	dir, *s, *t, *e;
2026 	char p[MAXPATHLEN];
2027 	char *	RETVAL;
2028 	int l;
2029 	SV *sv;
2030 
2031 	if (items < 2)
2032 	    dir = NULL;
2033 	else {
2034 	    dir = (char *)SvPV(ST(1),n_a);
2035 	}
2036 	if (path[0] == '.' && (path[1] == '/' || path[1] == '\\')) {
2037 	    path += 2;
2038 	}
2039 	if (dir == NULL) {
2040 	    if (_abspath(p, path, MAXPATHLEN) == 0) {
2041 		RETVAL = p;
2042 	    } else {
2043 		RETVAL = NULL;
2044 	    }
2045 	} else {
2046 	    /* Absolute with drive: */
2047 	    if ( sys_is_absolute(path) ) {
2048 		if (_abspath(p, path, MAXPATHLEN) == 0) {
2049 		    RETVAL = p;
2050 		} else {
2051 		    RETVAL = NULL;
2052 		}
2053 	    } else if (path[0] == '/' || path[0] == '\\') {
2054 		/* Rooted, but maybe on different drive. */
2055 		if (isALPHA(dir[0]) && dir[1] == ':' ) {
2056 		    char p1[MAXPATHLEN];
2057 
2058 		    /* Need to prepend the drive. */
2059 		    p1[0] = dir[0];
2060 		    p1[1] = dir[1];
2061 		    Copy(path, p1 + 2, strlen(path) + 1, char);
2062 		    RETVAL = p;
2063 		    if (_abspath(p, p1, MAXPATHLEN) == 0) {
2064 			RETVAL = p;
2065 		    } else {
2066 			RETVAL = NULL;
2067 		    }
2068 		} else if (_abspath(p, path, MAXPATHLEN) == 0) {
2069 		    RETVAL = p;
2070 		} else {
2071 		    RETVAL = NULL;
2072 		}
2073 	    } else {
2074 		/* Either path is relative, or starts with a drive letter. */
2075 		/* If the path starts with a drive letter, then dir is
2076 		   relevant only if
2077 		   a/b)	it is absolute/x:relative on the same drive.
2078 		   c)	path is on current drive, and dir is rooted
2079 		   In all the cases it is safe to drop the drive part
2080 		   of the path. */
2081 		if ( !sys_is_relative(path) ) {
2082 		    if ( ( ( sys_is_absolute(dir)
2083 			     || (isALPHA(dir[0]) && dir[1] == ':'
2084 				 && strnicmp(dir, path,1) == 0))
2085 			   && strnicmp(dir, path,1) == 0)
2086 			 || ( !(isALPHA(dir[0]) && dir[1] == ':')
2087 			      && toupper(path[0]) == current_drive())) {
2088 			path += 2;
2089 		    } else if (_abspath(p, path, MAXPATHLEN) == 0) {
2090 			RETVAL = p; goto done;
2091 		    } else {
2092 			RETVAL = NULL; goto done;
2093 		    }
2094 		}
2095 		{
2096 		    /* Need to prepend the absolute path of dir. */
2097 		    char p1[MAXPATHLEN];
2098 
2099 		    if (_abspath(p1, dir, MAXPATHLEN) == 0) {
2100 			int l = strlen(p1);
2101 
2102 			if (p1[ l - 1 ] != '/') {
2103 			    p1[ l ] = '/';
2104 			    l++;
2105 			}
2106 			Copy(path, p1 + l, strlen(path) + 1, char);
2107 			if (_abspath(p, p1, MAXPATHLEN) == 0) {
2108 			    RETVAL = p;
2109 			} else {
2110 			    RETVAL = NULL;
2111 			}
2112 		    } else {
2113 			RETVAL = NULL;
2114 		    }
2115 		}
2116 	      done:
2117 	    }
2118 	}
2119 	if (!RETVAL)
2120 	    XSRETURN_EMPTY;
2121 	/* Backslashes are already converted to slashes. */
2122 	/* Remove trailing slashes */
2123 	l = strlen(RETVAL);
2124 	while (l > 0 && RETVAL[l-1] == '/')
2125 	    l--;
2126 	ST(0) = sv_newmortal();
2127 	sv_setpvn( sv = (SV*)ST(0), RETVAL, l);
2128 	/* Remove duplicate slashes, skipping the first three, which
2129 	   may be parts of a server-based path */
2130 	s = t = 3 + SvPV_force(sv, n_a);
2131 	e = SvEND(sv);
2132 	/* Do not worry about multibyte chars here, this would contradict the
2133 	   eventual UTFization, and currently most other places break too... */
2134 	while (s < e) {
2135 	    if (s[0] == t[-1] && s[0] == '/')
2136 		s++;				/* Skip duplicate / */
2137 	    else
2138 		*t++ = *s++;
2139 	}
2140 	if (t < e) {
2141 	    *t = 0;
2142 	    SvCUR_set(sv, t - SvPVX(sv));
2143 	}
2144     }
2145     XSRETURN(1);
2146 }
2147 typedef APIRET (*PELP)(PSZ path, ULONG type);
2148 
2149 /* Kernels after 2000/09/15 understand this too: */
2150 #ifndef LIBPATHSTRICT
2151 #  define LIBPATHSTRICT 3
2152 #endif
2153 
2154 APIRET
2155 ExtLIBPATH(ULONG ord, PSZ path, IV type)
2156 {
2157     ULONG what;
2158     PFN f = loadByOrdinal(ord, 1);	/* Guarantied to load or die! */
2159 
2160     if (type > 0)
2161 	what = END_LIBPATH;
2162     else if (type == 0)
2163 	what = BEGIN_LIBPATH;
2164     else
2165 	what = LIBPATHSTRICT;
2166     return (*(PELP)f)(path, what);
2167 }
2168 
2169 #define extLibpath(to,type) 						\
2170     (CheckOSError(ExtLIBPATH(ORD_DosQueryExtLibpath, (to), (type))) ? NULL : (to) )
2171 
2172 #define extLibpath_set(p,type) 					\
2173     (!CheckOSError(ExtLIBPATH(ORD_DosSetExtLibpath, (p), (type))))
2174 
2175 XS(XS_Cwd_extLibpath)
2176 {
2177     dXSARGS;
2178     if (items < 0 || items > 1)
2179 	Perl_croak_nocontext("Usage: Cwd::extLibpath(type = 0)");
2180     {
2181 	IV	type;
2182 	char	to[1024];
2183 	U32	rc;
2184 	char *	RETVAL;
2185 
2186 	if (items < 1)
2187 	    type = 0;
2188 	else {
2189 	    type = SvIV(ST(0));
2190 	}
2191 
2192 	to[0] = 1; to[1] = 0;		/* Sometimes no error reported */
2193 	RETVAL = extLibpath(to, type);
2194 	if (RETVAL && RETVAL[0] == 1 && RETVAL[1] == 0)
2195 	    Perl_croak_nocontext("panic Cwd::extLibpath parameter");
2196 	ST(0) = sv_newmortal();
2197 	sv_setpv((SV*)ST(0), RETVAL);
2198     }
2199     XSRETURN(1);
2200 }
2201 
2202 XS(XS_Cwd_extLibpath_set)
2203 {
2204     dXSARGS;
2205     if (items < 1 || items > 2)
2206 	Perl_croak_nocontext("Usage: Cwd::extLibpath_set(s, type = 0)");
2207     {
2208 	STRLEN n_a;
2209 	char *	s = (char *)SvPV(ST(0),n_a);
2210 	IV	type;
2211 	U32	rc;
2212 	bool	RETVAL;
2213 
2214 	if (items < 2)
2215 	    type = 0;
2216 	else {
2217 	    type = SvIV(ST(1));
2218 	}
2219 
2220 	RETVAL = extLibpath_set(s, type);
2221 	ST(0) = boolSV(RETVAL);
2222 	if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
2223     }
2224     XSRETURN(1);
2225 }
2226 
2227 /* Input: Address, BufLen
2228 APIRET APIENTRY
2229 DosQueryModFromEIP (HMODULE * hmod, ULONG * obj, ULONG BufLen, PCHAR Buf,
2230 		    ULONG * Offset, ULONG Address);
2231 */
2232 
2233 DeclOSFuncByORD(APIRET, _DosQueryModFromEIP,ORD_DosQueryModFromEIP,
2234 			(HMODULE * hmod, ULONG * obj, ULONG BufLen, PCHAR Buf,
2235 			ULONG * Offset, ULONG Address),
2236 			(hmod, obj, BufLen, Buf, Offset, Address))
2237 
2238 enum module_name_how { mod_name_handle, mod_name_shortname, mod_name_full};
2239 
2240 static SV*
2241 module_name_at(void *pp, enum module_name_how how)
2242 {
2243     char buf[MAXPATHLEN];
2244     char *p = buf;
2245     HMODULE mod;
2246     ULONG obj, offset, rc;
2247 
2248     if (!_DosQueryModFromEIP(&mod, &obj, sizeof(buf), buf, &offset, (ULONG)pp))
2249 	return &PL_sv_undef;
2250     if (how == mod_name_handle)
2251 	return newSVuv(mod);
2252     /* Full name... */
2253     if ( how == mod_name_full
2254 	 && CheckOSError(DosQueryModuleName(mod, sizeof(buf), buf)) )
2255 	return &PL_sv_undef;
2256     while (*p) {
2257 	if (*p == '\\')
2258 	    *p = '/';
2259 	p++;
2260     }
2261     return newSVpv(buf, 0);
2262 }
2263 
2264 static SV*
2265 module_name_of_cv(SV *cv, enum module_name_how how)
2266 {
2267     if (!cv || !SvROK(cv) || SvTYPE(SvRV(cv)) != SVt_PVCV || !CvXSUB(SvRV(cv)))
2268 	croak("Not an XSUB reference");
2269     return module_name_at(CvXSUB(SvRV(cv)), how);
2270 }
2271 
2272 /* Find module name to which *this* subroutine is compiled */
2273 #define module_name(how)	module_name_at(&module_name_at, how)
2274 
2275 XS(XS_OS2_DLLname)
2276 {
2277     dXSARGS;
2278     if (items > 2)
2279 	Perl_croak(aTHX_ "Usage: OS2::DLLname( [ how, [\\&xsub] ] )");
2280     {
2281 	SV *	RETVAL;
2282 	int	how;
2283 
2284 	if (items < 1)
2285 	    how = mod_name_full;
2286 	else {
2287 	    how = (int)SvIV(ST(0));
2288 	}
2289 	if (items < 2)
2290 	    RETVAL = module_name(how);
2291 	else
2292 	    RETVAL = module_name_of_cv(ST(1), how);
2293 	ST(0) = RETVAL;
2294 	sv_2mortal(ST(0));
2295     }
2296     XSRETURN(1);
2297 }
2298 
2299 #define get_control87()		_control87(0,0)
2300 #define set_control87		_control87
2301 
2302 XS(XS_OS2__control87)
2303 {
2304     dXSARGS;
2305     if (items != 2)
2306 	croak("Usage: OS2::_control87(new,mask)");
2307     {
2308 	unsigned	new = (unsigned)SvIV(ST(0));
2309 	unsigned	mask = (unsigned)SvIV(ST(1));
2310 	unsigned	RETVAL;
2311 
2312 	RETVAL = _control87(new, mask);
2313 	ST(0) = sv_newmortal();
2314 	sv_setiv(ST(0), (IV)RETVAL);
2315     }
2316     XSRETURN(1);
2317 }
2318 
2319 XS(XS_OS2_get_control87)
2320 {
2321     dXSARGS;
2322     if (items != 0)
2323 	croak("Usage: OS2::get_control87()");
2324     {
2325 	unsigned	RETVAL;
2326 
2327 	RETVAL = get_control87();
2328 	ST(0) = sv_newmortal();
2329 	sv_setiv(ST(0), (IV)RETVAL);
2330     }
2331     XSRETURN(1);
2332 }
2333 
2334 
2335 XS(XS_OS2_set_control87)
2336 {
2337     dXSARGS;
2338     if (items < 0 || items > 2)
2339 	croak("Usage: OS2::set_control87(new=MCW_EM, mask=MCW_EM)");
2340     {
2341 	unsigned	new;
2342 	unsigned	mask;
2343 	unsigned	RETVAL;
2344 
2345 	if (items < 1)
2346 	    new = MCW_EM;
2347 	else {
2348 	    new = (unsigned)SvIV(ST(0));
2349 	}
2350 
2351 	if (items < 2)
2352 	    mask = MCW_EM;
2353 	else {
2354 	    mask = (unsigned)SvIV(ST(1));
2355 	}
2356 
2357 	RETVAL = set_control87(new, mask);
2358 	ST(0) = sv_newmortal();
2359 	sv_setiv(ST(0), (IV)RETVAL);
2360     }
2361     XSRETURN(1);
2362 }
2363 
2364 int
2365 Xs_OS2_init(pTHX)
2366 {
2367     char *file = __FILE__;
2368     {
2369 	GV *gv;
2370 
2371 	if (_emx_env & 0x200) {	/* OS/2 */
2372             newXS("File::Copy::syscopy", XS_File__Copy_syscopy, file);
2373             newXS("Cwd::extLibpath", XS_Cwd_extLibpath, file);
2374             newXS("Cwd::extLibpath_set", XS_Cwd_extLibpath_set, file);
2375 	}
2376         newXS("OS2::Error", XS_OS2_Error, file);
2377         newXS("OS2::Errors2Drive", XS_OS2_Errors2Drive, file);
2378         newXS("OS2::SysInfo", XS_OS2_SysInfo, file);
2379         newXS("OS2::BootDrive", XS_OS2_BootDrive, file);
2380         newXS("OS2::MorphPM", XS_OS2_MorphPM, file);
2381         newXS("OS2::UnMorphPM", XS_OS2_UnMorphPM, file);
2382         newXS("OS2::Serve_Messages", XS_OS2_Serve_Messages, file);
2383         newXS("OS2::Process_Messages", XS_OS2_Process_Messages, file);
2384         newXS("DynaLoader::mod2fname", XS_DynaLoader_mod2fname, file);
2385         newXS("Cwd::current_drive", XS_Cwd_current_drive, file);
2386         newXS("Cwd::sys_chdir", XS_Cwd_sys_chdir, file);
2387         newXS("Cwd::change_drive", XS_Cwd_change_drive, file);
2388         newXS("Cwd::sys_is_absolute", XS_Cwd_sys_is_absolute, file);
2389         newXS("Cwd::sys_is_rooted", XS_Cwd_sys_is_rooted, file);
2390         newXS("Cwd::sys_is_relative", XS_Cwd_sys_is_relative, file);
2391         newXS("Cwd::sys_cwd", XS_Cwd_sys_cwd, file);
2392         newXS("Cwd::sys_abspath", XS_Cwd_sys_abspath, file);
2393         newXSproto("OS2::_control87", XS_OS2__control87, file, "$$");
2394         newXSproto("OS2::get_control87", XS_OS2_get_control87, file, "");
2395         newXSproto("OS2::set_control87", XS_OS2_set_control87, file, ";$$");
2396         newXSproto("OS2::DLLname", XS_OS2_DLLname, file, ";$$");
2397 	gv = gv_fetchpv("OS2::is_aout", TRUE, SVt_PV);
2398 	GvMULTI_on(gv);
2399 #ifdef PERL_IS_AOUT
2400 	sv_setiv(GvSV(gv), 1);
2401 #endif
2402 	gv = gv_fetchpv("OS2::can_fork", TRUE, SVt_PV);
2403 	GvMULTI_on(gv);
2404 	sv_setiv(GvSV(gv), exe_is_aout());
2405 	gv = gv_fetchpv("OS2::emx_rev", TRUE, SVt_PV);
2406 	GvMULTI_on(gv);
2407 	sv_setiv(GvSV(gv), _emx_rev);
2408 	sv_setpv(GvSV(gv), _emx_vprt);
2409 	SvIOK_on(GvSV(gv));
2410 	gv = gv_fetchpv("OS2::emx_env", TRUE, SVt_PV);
2411 	GvMULTI_on(gv);
2412 	sv_setiv(GvSV(gv), _emx_env);
2413 	gv = gv_fetchpv("OS2::os_ver", TRUE, SVt_PV);
2414 	GvMULTI_on(gv);
2415 	sv_setnv(GvSV(gv), _osmajor + 0.001 * _osminor);
2416 	gv = gv_fetchpv("OS2::nsyserror", TRUE, SVt_PV);
2417 	GvMULTI_on(gv);
2418 	sv_setiv(GvSV(gv), 1);		/* DEFAULT: Show number on syserror */
2419     }
2420     return 0;
2421 }
2422 
2423 OS2_Perl_data_t OS2_Perl_data;
2424 
2425 extern void _emx_init(void*);
2426 
2427 static void jmp_out_of_atexit(void);
2428 
2429 #define FORCE_EMX_INIT_CONTRACT_ARGV	1
2430 #define FORCE_EMX_INIT_INSTALL_ATEXIT	2
2431 
2432 static void
2433 my_emx_init(void *layout) {
2434     static volatile void *p = 0;	/* Cannot be on stack! */
2435 
2436     /* Can't just call emx_init(), since it moves the stack pointer */
2437     /* It also busts a lot of registers, so be extra careful */
2438     __asm__(	"pushf\n"
2439 		"pusha\n"
2440 		"movl %%esp, %1\n"
2441 		"push %0\n"
2442 		"call __emx_init\n"
2443 		"movl %1, %%esp\n"
2444 		"popa\n"
2445 		"popf\n" : : "r" (layout), "m" (p)	);
2446 }
2447 
2448 struct layout_table_t {
2449     ULONG text_base;
2450     ULONG text_end;
2451     ULONG data_base;
2452     ULONG data_end;
2453     ULONG bss_base;
2454     ULONG bss_end;
2455     ULONG heap_base;
2456     ULONG heap_end;
2457     ULONG heap_brk;
2458     ULONG heap_off;
2459     ULONG os2_dll;
2460     ULONG stack_base;
2461     ULONG stack_end;
2462     ULONG flags;
2463     ULONG reserved[2];
2464     char options[64];
2465 };
2466 
2467 static ULONG
2468 my_os_version() {
2469     static ULONG res;			/* Cannot be on stack! */
2470 
2471     /* Can't just call __os_version(), since it does not follow C
2472        calling convention: it busts a lot of registers, so be extra careful */
2473     __asm__(	"pushf\n"
2474 		"pusha\n"
2475 		"call ___os_version\n"
2476 		"movl %%eax, %0\n"
2477 		"popa\n"
2478 		"popf\n" : "=m" (res)	);
2479 
2480     return res;
2481 }
2482 
2483 static void
2484 force_init_emx_runtime(EXCEPTIONREGISTRATIONRECORD *preg, ULONG flags)
2485 {
2486     /* Calling emx_init() will bust the top of stack: it installs an
2487        exception handler and puts argv data there. */
2488     char *oldarg, *oldenv;
2489     void *oldstackend, *oldstack;
2490     PPIB pib;
2491     PTIB tib;
2492     static ULONG os2_dll;
2493     ULONG rc, error = 0, out;
2494     char buf[512];
2495     static struct layout_table_t layout_table;
2496     struct {
2497 	char buf[48*1024]; /* _emx_init() requires 32K, cmd.exe has 64K only */
2498 	double alignment1;
2499 	EXCEPTIONREGISTRATIONRECORD xreg;
2500     } *newstack;
2501     char *s;
2502 
2503     layout_table.os2_dll = (ULONG)&os2_dll;
2504     layout_table.flags   = 0x02000002;	/* flags: application, OMF */
2505 
2506     DosGetInfoBlocks(&tib, &pib);
2507     oldarg = pib->pib_pchcmd;
2508     oldenv = pib->pib_pchenv;
2509     oldstack = tib->tib_pstack;
2510     oldstackend = tib->tib_pstacklimit;
2511 
2512     /* Minimize the damage to the stack via reducing the size of argv. */
2513     if (flags & FORCE_EMX_INIT_CONTRACT_ARGV) {
2514 	pib->pib_pchcmd = "\0\0";	/* Need 3 concatenated strings */
2515 	pib->pib_pchcmd = "\0";		/* Ended by an extra \0. */
2516     }
2517 
2518     newstack = alloca(sizeof(*newstack));
2519     /* Emulate the stack probe */
2520     s = ((char*)newstack) + sizeof(*newstack);
2521     while (s > (char*)newstack) {
2522 	s[-1] = 0;
2523 	s -= 4096;
2524     }
2525 
2526     /* Reassigning stack is documented to work */
2527     tib->tib_pstack = (void*)newstack;
2528     tib->tib_pstacklimit = (void*)((char*)newstack + sizeof(*newstack));
2529 
2530     /* Can't just call emx_init(), since it moves the stack pointer */
2531     my_emx_init((void*)&layout_table);
2532 
2533     /* Remove the exception handler, cannot use it - too low on the stack.
2534        Check whether it is inside the new stack.  */
2535     buf[0] = 0;
2536     if (tib->tib_pexchain >= tib->tib_pstacklimit
2537 	|| tib->tib_pexchain < tib->tib_pstack) {
2538 	error = 1;
2539 	sprintf(buf,
2540 		"panic: ExceptionHandler misplaced: not %#lx <= %#lx < %#lx\n",
2541 		(unsigned long)tib->tib_pstack,
2542 		(unsigned long)tib->tib_pexchain,
2543 		(unsigned long)tib->tib_pstacklimit);
2544 	goto finish;
2545     }
2546     if (tib->tib_pexchain != &(newstack->xreg)) {
2547 	sprintf(buf, "ExceptionHandler misplaced: %#lx != %#lx\n",
2548 		(unsigned long)tib->tib_pexchain,
2549 		(unsigned long)&(newstack->xreg));
2550     }
2551     rc = DosUnsetExceptionHandler((EXCEPTIONREGISTRATIONRECORD *)tib->tib_pexchain);
2552     if (rc)
2553 	sprintf(buf + strlen(buf),
2554 		"warning: DosUnsetExceptionHandler rc=%#lx=%lu\n", rc, rc);
2555 
2556     if (preg) {
2557 	/* ExceptionRecords should be on stack, in a correct order.  Sigh... */
2558 	preg->prev_structure = 0;
2559 	preg->ExceptionHandler = _emx_exception;
2560 	rc = DosSetExceptionHandler(preg);
2561 	if (rc) {
2562 	    sprintf(buf + strlen(buf),
2563 		    "warning: DosSetExceptionHandler rc=%#lx=%lu\n", rc, rc);
2564 	    DosWrite(2, buf, strlen(buf), &out);
2565 	    emx_exception_init = 1;	/* Do it around spawn*() calls */
2566 	}
2567     } else
2568 	emx_exception_init = 1;		/* Do it around spawn*() calls */
2569 
2570   finish:
2571     /* Restore the damage */
2572     pib->pib_pchcmd = oldarg;
2573     pib->pib_pchcmd = oldenv;
2574     tib->tib_pstacklimit = oldstackend;
2575     tib->tib_pstack = oldstack;
2576     emx_runtime_init = 1;
2577     if (buf[0])
2578 	DosWrite(2, buf, strlen(buf), &out);
2579     if (error)
2580 	exit(56);
2581 }
2582 
2583 jmp_buf at_exit_buf;
2584 int longjmp_at_exit;
2585 
2586 static void
2587 jmp_out_of_atexit(void)
2588 {
2589     if (longjmp_at_exit)
2590 	longjmp(at_exit_buf, 1);
2591 }
2592 
2593 extern void _CRT_term(void);
2594 
2595 int emx_runtime_secondary;
2596 
2597 void
2598 Perl_OS2_term(void **p, int exitstatus, int flags)
2599 {
2600     if (!emx_runtime_secondary)
2601 	return;
2602 
2603     /* The principal executable is not running the same CRTL, so there
2604        is nobody to shutdown *this* CRTL except us... */
2605     if (flags & FORCE_EMX_DEINIT_EXIT) {
2606 	if (p && !emx_exception_init)
2607 	    DosUnsetExceptionHandler((EXCEPTIONREGISTRATIONRECORD *)p);
2608 	/* Do not run the executable's CRTL's termination routines */
2609 	exit(exitstatus);		/* Run at-exit, flush buffers, etc */
2610     }
2611     /* Run at-exit list, and jump out at the end */
2612     if ((flags & FORCE_EMX_DEINIT_RUN_ATEXIT) && !setjmp(at_exit_buf)) {
2613 	longjmp_at_exit = 1;
2614 	exit(exitstatus);		/* The first pass through "if" */
2615     }
2616 
2617     /* Get here if we managed to jump out of exit(), or did not run atexit. */
2618     longjmp_at_exit = 0;		/* Maybe exit() is called again? */
2619 #if 0 /* _atexit_n is not exported */
2620     if (flags & FORCE_EMX_DEINIT_RUN_ATEXIT)
2621 	_atexit_n = 0;			/* Remove the atexit() handlers */
2622 #endif
2623     /* Will segfault on program termination if we leave this dangling... */
2624     if (p && !emx_exception_init)
2625 	DosUnsetExceptionHandler((EXCEPTIONREGISTRATIONRECORD *)p);
2626     /* Typically there is no need to do this, done from _DLL_InitTerm() */
2627     if (flags & FORCE_EMX_DEINIT_CRT_TERM)
2628 	_CRT_term();			/* Flush buffers, etc. */
2629     /* Now it is a good time to call exit() in the caller's CRTL... */
2630 }
2631 
2632 #include <emx/startup.h>
2633 
2634 extern ULONG __os_version();		/* See system.doc */
2635 
2636 static int emx_wasnt_initialized;
2637 
2638 void
2639 check_emx_runtime(char **env, EXCEPTIONREGISTRATIONRECORD *preg)
2640 {
2641     ULONG v_crt, v_emx;
2642 
2643     /*  If _environ is not set, this code sits in a DLL which
2644 	uses a CRT DLL which not compatible with the executable's
2645 	CRT library.  Some parts of the DLL are not initialized.
2646      */
2647     if (_environ != NULL)
2648 	return;				/* Properly initialized */
2649 
2650     /*  If the executable does not use EMX.DLL, EMX.DLL is not completely
2651 	initialized either.  Uninitialized EMX.DLL returns 0 in the low
2652 	nibble of __os_version().  */
2653     v_emx = my_os_version();
2654 
2655     /*	_osmajor and _osminor are normally set in _DLL_InitTerm of CRT DLL
2656 	(=>_CRT_init=>_entry2) via a call to __os_version(), then
2657 	reset when the EXE initialization code calls _text=>_init=>_entry2.
2658 	The first time they are wrongly set to 0; the second time the
2659 	EXE initialization code had already called emx_init=>initialize1
2660 	which correctly set version_major, version_minor used by
2661 	__os_version().  */
2662     v_crt = (_osmajor | _osminor);
2663 
2664     if ((_emx_env & 0x200) && !(v_emx & 0xFFFF)) {	/* OS/2, EMX uninit. */
2665 	force_init_emx_runtime( preg,
2666 				FORCE_EMX_INIT_CONTRACT_ARGV
2667 				| FORCE_EMX_INIT_INSTALL_ATEXIT );
2668 	emx_wasnt_initialized = 1;
2669 	/* Update CRTL data basing on now-valid EMX runtime data */
2670 	if (!v_crt) {		/* The only wrong data are the versions. */
2671 	    v_emx = my_os_version();			/* *Now* it works */
2672 	    *(unsigned char *)&_osmajor = v_emx & 0xFF;	/* Cast out const */
2673 	    *(unsigned char *)&_osminor = (v_emx>>8) & 0xFF;
2674 	}
2675     }
2676     emx_runtime_secondary = 1;
2677     /* if (flags & FORCE_EMX_INIT_INSTALL_ATEXIT) */
2678     atexit(jmp_out_of_atexit);		/* Allow run of atexit() w/o exit()  */
2679 
2680     if (env == NULL) {			/* Fetch from the process info block */
2681 	int c = 0;
2682 	PPIB pib;
2683 	PTIB tib;
2684 	char *e, **ep;
2685 
2686 	DosGetInfoBlocks(&tib, &pib);
2687 	e = pib->pib_pchenv;
2688 	while (*e) {			/* Get count */
2689 	    c++;
2690 	    e = e + strlen(e) + 1;
2691 	}
2692 	New(1307, env, c + 1, char*);
2693 	ep = env;
2694 	e = pib->pib_pchenv;
2695 	while (c--) {
2696 	    *ep++ = e;
2697 	    e = e + strlen(e) + 1;
2698 	}
2699 	*ep = NULL;
2700     }
2701     _environ = _org_environ = env;
2702 }
2703 
2704 #define ENTRY_POINT 0x10000
2705 
2706 static int
2707 exe_is_aout(void)
2708 {
2709     struct layout_table_t *layout;
2710     if (emx_wasnt_initialized)
2711 	return 0;
2712     /* Now we know that the principal executable is an EMX application
2713        - unless somebody did already play with delayed initialization... */
2714     /* With EMX applications to determine whether it is AOUT one needs
2715        to examine the start of the executable to find "layout" */
2716     if ( *(unsigned char*)ENTRY_POINT != 0x68		/* PUSH n */
2717 	 || *(unsigned char*)(ENTRY_POINT+5) != 0xe8	/* CALL */
2718 	 || *(unsigned char*)(ENTRY_POINT+10) != 0xeb	/* JMP */
2719 	 || *(unsigned char*)(ENTRY_POINT+12) != 0xe8)	/* CALL */
2720 	return 0;					/* ! EMX executable */
2721     /* Fix alignment */
2722     Copy((char*)(ENTRY_POINT+1), &layout, 1, struct layout_table_t*);
2723     return !(layout->flags & 2);
2724 }
2725 
2726 void
2727 Perl_OS2_init(char **env)
2728 {
2729     Perl_OS2_init3(env, 0, 0);
2730 }
2731 
2732 void
2733 Perl_OS2_init3(char **env, void **preg, int flags)
2734 {
2735     char *shell;
2736 
2737     _uflags (_UF_SBRK_MODEL, _UF_SBRK_ARBITRARY);
2738     MALLOC_INIT;
2739 
2740     check_emx_runtime(env, (EXCEPTIONREGISTRATIONRECORD *)preg);
2741 
2742     settmppath();
2743     OS2_Perl_data.xs_init = &Xs_OS2_init;
2744     if ( (shell = getenv("PERL_SH_DRIVE")) ) {
2745 	New(1304, PL_sh_path, strlen(SH_PATH) + 1, char);
2746 	strcpy(PL_sh_path, SH_PATH);
2747 	PL_sh_path[0] = shell[0];
2748     } else if ( (shell = getenv("PERL_SH_DIR")) ) {
2749 	int l = strlen(shell), i;
2750 	if (shell[l-1] == '/' || shell[l-1] == '\\') {
2751 	    l--;
2752 	}
2753 	New(1304, PL_sh_path, l + 8, char);
2754 	strncpy(PL_sh_path, shell, l);
2755 	strcpy(PL_sh_path + l, "/sh.exe");
2756 	for (i = 0; i < l; i++) {
2757 	    if (PL_sh_path[i] == '\\') PL_sh_path[i] = '/';
2758 	}
2759     }
2760     MUTEX_INIT(&start_thread_mutex);
2761     os2_mytype = my_type();		/* Do it before morphing.  Needed? */
2762     /* Some DLLs reset FP flags on load.  We may have been linked with them */
2763     _control87(MCW_EM, MCW_EM);
2764 }
2765 
2766 #undef tmpnam
2767 #undef tmpfile
2768 
2769 char *
2770 my_tmpnam (char *str)
2771 {
2772     char *p = getenv("TMP"), *tpath;
2773 
2774     if (!p) p = getenv("TEMP");
2775     tpath = tempnam(p, "pltmp");
2776     if (str && tpath) {
2777 	strcpy(str, tpath);
2778 	return str;
2779     }
2780     return tpath;
2781 }
2782 
2783 FILE *
2784 my_tmpfile ()
2785 {
2786     struct stat s;
2787 
2788     stat(".", &s);
2789     if (s.st_mode & S_IWOTH) {
2790 	return tmpfile();
2791     }
2792     return fopen(my_tmpnam(NULL), "w+b"); /* Race condition, but
2793 					     grants TMP. */
2794 }
2795 
2796 #undef rmdir
2797 
2798 int
2799 my_rmdir (__const__ char *s)
2800 {
2801     char buf[MAXPATHLEN];
2802     STRLEN l = strlen(s);
2803 
2804     if (s[l-1] == '/' || s[l-1] == '\\') {	/* EMX rmdir fails... */
2805 	strcpy(buf,s);
2806 	buf[l - 1] = 0;
2807 	s = buf;
2808     }
2809     return rmdir(s);
2810 }
2811 
2812 #undef mkdir
2813 
2814 int
2815 my_mkdir (__const__ char *s, long perm)
2816 {
2817     char buf[MAXPATHLEN];
2818     STRLEN l = strlen(s);
2819 
2820     if (s[l-1] == '/' || s[l-1] == '\\') {	/* EMX mkdir fails... */
2821 	strcpy(buf,s);
2822 	buf[l - 1] = 0;
2823 	s = buf;
2824     }
2825     return mkdir(s, perm);
2826 }
2827 
2828 #undef flock
2829 
2830 /* This code was contributed by Rocco Caputo. */
2831 int
2832 my_flock(int handle, int o)
2833 {
2834   FILELOCK      rNull, rFull;
2835   ULONG         timeout, handle_type, flag_word;
2836   APIRET        rc;
2837   int           blocking, shared;
2838   static int	use_my = -1;
2839 
2840   if (use_my == -1) {
2841     char *s = getenv("USE_PERL_FLOCK");
2842     if (s)
2843 	use_my = atoi(s);
2844     else
2845 	use_my = 1;
2846   }
2847   if (!(_emx_env & 0x200) || !use_my)
2848     return flock(handle, o);	/* Delegate to EMX. */
2849 
2850                                         /* is this a file? */
2851   if ((DosQueryHType(handle, &handle_type, &flag_word) != 0) ||
2852       (handle_type & 0xFF))
2853   {
2854     errno = EBADF;
2855     return -1;
2856   }
2857                                         /* set lock/unlock ranges */
2858   rNull.lOffset = rNull.lRange = rFull.lOffset = 0;
2859   rFull.lRange = 0x7FFFFFFF;
2860                                         /* set timeout for blocking */
2861   timeout = ((blocking = !(o & LOCK_NB))) ? 100 : 1;
2862                                         /* shared or exclusive? */
2863   shared = (o & LOCK_SH) ? 1 : 0;
2864                                         /* do not block the unlock */
2865   if (o & (LOCK_UN | LOCK_SH | LOCK_EX)) {
2866     rc = DosSetFileLocks(handle, &rFull, &rNull, timeout, shared);
2867     switch (rc) {
2868       case 0:
2869         errno = 0;
2870         return 0;
2871       case ERROR_INVALID_HANDLE:
2872         errno = EBADF;
2873         return -1;
2874       case ERROR_SHARING_BUFFER_EXCEEDED:
2875         errno = ENOLCK;
2876         return -1;
2877       case ERROR_LOCK_VIOLATION:
2878         break;                          /* not an error */
2879       case ERROR_INVALID_PARAMETER:
2880       case ERROR_ATOMIC_LOCK_NOT_SUPPORTED:
2881       case ERROR_READ_LOCKS_NOT_SUPPORTED:
2882         errno = EINVAL;
2883         return -1;
2884       case ERROR_INTERRUPT:
2885         errno = EINTR;
2886         return -1;
2887       default:
2888         errno = EINVAL;
2889         return -1;
2890     }
2891   }
2892                                         /* lock may block */
2893   if (o & (LOCK_SH | LOCK_EX)) {
2894                                         /* for blocking operations */
2895     for (;;) {
2896       rc =
2897         DosSetFileLocks(
2898                 handle,
2899                 &rNull,
2900                 &rFull,
2901                 timeout,
2902                 shared
2903         );
2904       switch (rc) {
2905         case 0:
2906           errno = 0;
2907           return 0;
2908         case ERROR_INVALID_HANDLE:
2909           errno = EBADF;
2910           return -1;
2911         case ERROR_SHARING_BUFFER_EXCEEDED:
2912           errno = ENOLCK;
2913           return -1;
2914         case ERROR_LOCK_VIOLATION:
2915           if (!blocking) {
2916             errno = EWOULDBLOCK;
2917             return -1;
2918           }
2919           break;
2920         case ERROR_INVALID_PARAMETER:
2921         case ERROR_ATOMIC_LOCK_NOT_SUPPORTED:
2922         case ERROR_READ_LOCKS_NOT_SUPPORTED:
2923           errno = EINVAL;
2924           return -1;
2925         case ERROR_INTERRUPT:
2926           errno = EINTR;
2927           return -1;
2928         default:
2929           errno = EINVAL;
2930           return -1;
2931       }
2932                                         /* give away timeslice */
2933       DosSleep(1);
2934     }
2935   }
2936 
2937   errno = 0;
2938   return 0;
2939 }
2940 
2941 static int pwent_cnt;
2942 static int _my_pwent = -1;
2943 
2944 static int
2945 use_my_pwent(void)
2946 {
2947   if (_my_pwent == -1) {
2948     char *s = getenv("USE_PERL_PWENT");
2949     if (s)
2950 	_my_pwent = atoi(s);
2951     else
2952 	_my_pwent = 1;
2953   }
2954   return _my_pwent;
2955 }
2956 
2957 #undef setpwent
2958 #undef getpwent
2959 #undef endpwent
2960 
2961 void
2962 my_setpwent(void)
2963 {
2964   if (!use_my_pwent()) {
2965     setpwent();			/* Delegate to EMX. */
2966     return;
2967   }
2968   pwent_cnt = 0;
2969 }
2970 
2971 void
2972 my_endpwent(void)
2973 {
2974   if (!use_my_pwent()) {
2975     endpwent();			/* Delegate to EMX. */
2976     return;
2977   }
2978 }
2979 
2980 struct passwd *
2981 my_getpwent (void)
2982 {
2983   if (!use_my_pwent())
2984     return getpwent();			/* Delegate to EMX. */
2985   if (pwent_cnt++)
2986     return 0;				/* Return one entry only */
2987   return getpwuid(0);
2988 }
2989 
2990 static int grent_cnt;
2991 
2992 void
2993 setgrent(void)
2994 {
2995   grent_cnt = 0;
2996 }
2997 
2998 void
2999 endgrent(void)
3000 {
3001 }
3002 
3003 struct group *
3004 getgrent (void)
3005 {
3006   if (grent_cnt++)
3007     return 0;				/* Return one entry only */
3008   return getgrgid(0);
3009 }
3010 
3011 #undef getpwuid
3012 #undef getpwnam
3013 
3014 /* Too long to be a crypt() of anything, so it is not-a-valid pw_passwd. */
3015 static const char pw_p[] = "Jf0Wb/BzMFvk7K7lrzK";
3016 
3017 static struct passwd *
3018 passw_wrap(struct passwd *p)
3019 {
3020     static struct passwd pw;
3021     char *s;
3022 
3023     if (!p || (p->pw_passwd && *p->pw_passwd)) /* Not a dangerous password */
3024 	return p;
3025     pw = *p;
3026     s = getenv("PW_PASSWD");
3027     if (!s)
3028 	s = (char*)pw_p;		/* Make match impossible */
3029 
3030     pw.pw_passwd = s;
3031     return &pw;
3032 }
3033 
3034 struct passwd *
3035 my_getpwuid (uid_t id)
3036 {
3037     return passw_wrap(getpwuid(id));
3038 }
3039 
3040 struct passwd *
3041 my_getpwnam (__const__ char *n)
3042 {
3043     return passw_wrap(getpwnam(n));
3044 }
3045 
3046 char *
3047 gcvt_os2 (double value, int digits, char *buffer)
3048 {
3049   return gcvt (value, digits, buffer);
3050 }
3051