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