Undeprecating $]
[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
6b88bc9c 621 if (PL_Argv[0][0] != '/' && PL_Argv[0][0] != '\\'
622 && !(PL_Argv[0][0] && PL_Argv[0][1] == ':'
623 && (PL_Argv[0][2] == '/' || PL_Argv[0][2] != '\\'))
2c2e0e8c 624 ) /* will spawnvp use PATH? */
c0c09dfd 625 TAINT_ENV(); /* testing IFS here is overkill, probably */
760ac839 626 /* We should check PERL_SH* and PERLLIB_* as well? */
2d8e6c8d 627 if (!really || !*(tmps = SvPV(really, n_a)))
6b88bc9c 628 tmps = PL_Argv[0];
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);
1980 }
1981 XSRETURN(1);
1982}
1983
1984XS(XS_Cwd_sys_abspath)
1985{
1986 dXSARGS;
1987 if (items < 1 || items > 2)
23da6c43 1988 Perl_croak_nocontext("Usage: Cwd::sys_abspath(path, dir = NULL)");
3bbf9c2b 1989 {
2d8e6c8d 1990 STRLEN n_a;
1991 char * path = (char *)SvPV(ST(0),n_a);
f5f423e4 1992 char * dir, *s, *t, *e;
3bbf9c2b 1993 char p[MAXPATHLEN];
1994 char * RETVAL;
f5f423e4 1995 int l;
1996 SV *sv;
3bbf9c2b 1997
1998 if (items < 2)
1999 dir = NULL;
2000 else {
2d8e6c8d 2001 dir = (char *)SvPV(ST(1),n_a);
3bbf9c2b 2002 }
2003 if (path[0] == '.' && (path[1] == '/' || path[1] == '\\')) {
2004 path += 2;
2005 }
2006 if (dir == NULL) {
2007 if (_abspath(p, path, MAXPATHLEN) == 0) {
2008 RETVAL = p;
2009 } else {
2010 RETVAL = NULL;
2011 }
2012 } else {
2013 /* Absolute with drive: */
2014 if ( sys_is_absolute(path) ) {
2015 if (_abspath(p, path, MAXPATHLEN) == 0) {
2016 RETVAL = p;
2017 } else {
2018 RETVAL = NULL;
2019 }
2020 } else if (path[0] == '/' || path[0] == '\\') {
2021 /* Rooted, but maybe on different drive. */
2022 if (isALPHA(dir[0]) && dir[1] == ':' ) {
2023 char p1[MAXPATHLEN];
2024
2025 /* Need to prepend the drive. */
2026 p1[0] = dir[0];
2027 p1[1] = dir[1];
2028 Copy(path, p1 + 2, strlen(path) + 1, char);
2029 RETVAL = p;
2030 if (_abspath(p, p1, MAXPATHLEN) == 0) {
2031 RETVAL = p;
2032 } else {
2033 RETVAL = NULL;
2034 }
2035 } else if (_abspath(p, path, MAXPATHLEN) == 0) {
2036 RETVAL = p;
2037 } else {
2038 RETVAL = NULL;
2039 }
2040 } else {
2041 /* Either path is relative, or starts with a drive letter. */
2042 /* If the path starts with a drive letter, then dir is
2043 relevant only if
2044 a/b) it is absolute/x:relative on the same drive.
2045 c) path is on current drive, and dir is rooted
2046 In all the cases it is safe to drop the drive part
2047 of the path. */
2048 if ( !sys_is_relative(path) ) {
3bbf9c2b 2049 if ( ( ( sys_is_absolute(dir)
2050 || (isALPHA(dir[0]) && dir[1] == ':'
2051 && strnicmp(dir, path,1) == 0))
2052 && strnicmp(dir, path,1) == 0)
2053 || ( !(isALPHA(dir[0]) && dir[1] == ':')
2054 && toupper(path[0]) == current_drive())) {
2055 path += 2;
2056 } else if (_abspath(p, path, MAXPATHLEN) == 0) {
2057 RETVAL = p; goto done;
2058 } else {
2059 RETVAL = NULL; goto done;
2060 }
2061 }
2062 {
2063 /* Need to prepend the absolute path of dir. */
2064 char p1[MAXPATHLEN];
2065
2066 if (_abspath(p1, dir, MAXPATHLEN) == 0) {
2067 int l = strlen(p1);
2068
2069 if (p1[ l - 1 ] != '/') {
2070 p1[ l ] = '/';
2071 l++;
2072 }
2073 Copy(path, p1 + l, strlen(path) + 1, char);
2074 if (_abspath(p, p1, MAXPATHLEN) == 0) {
2075 RETVAL = p;
2076 } else {
2077 RETVAL = NULL;
2078 }
2079 } else {
2080 RETVAL = NULL;
2081 }
2082 }
2083 done:
2084 }
2085 }
f5f423e4 2086 if (!RETVAL)
2087 XSRETURN_EMPTY;
2088 /* Backslashes are already converted to slashes. */
2089 /* Remove trailing slashes */
2090 l = strlen(RETVAL);
2091 while (l > 0 && RETVAL[l-1] == '/')
2092 l--;
3bbf9c2b 2093 ST(0) = sv_newmortal();
f5f423e4 2094 sv_setpvn( sv = (SV*)ST(0), RETVAL, l);
45ee47cb 2095 /* Remove duplicate slashes, skipping the first three, which
2096 may be parts of a server-based path */
2097 s = t = 3 + SvPV_force(sv, n_a);
f5f423e4 2098 e = SvEND(sv);
45ee47cb 2099 /* Do not worry about multibyte chars here, this would contradict the
2100 eventual UTFization, and currently most other places break too... */
f5f423e4 2101 while (s < e) {
2102 if (s[0] == t[-1] && s[0] == '/')
2103 s++; /* Skip duplicate / */
2104 else
2105 *t++ = *s++;
2106 }
45ee47cb 2107 if (t < e) {
2108 *t = 0;
2109 SvCUR_set(sv, t - SvPVX(sv));
2110 }
3bbf9c2b 2111 }
2112 XSRETURN(1);
2113}
72ea3524 2114typedef APIRET (*PELP)(PSZ path, ULONG type);
2115
5a9d0041 2116/* Kernels after 2000/09/15 understand this too: */
2117#ifndef LIBPATHSTRICT
2118# define LIBPATHSTRICT 3
2119#endif
2120
72ea3524 2121APIRET
5a9d0041 2122ExtLIBPATH(ULONG ord, PSZ path, IV type)
72ea3524 2123{
5a9d0041 2124 ULONG what;
35bc1fdc 2125 PFN f = loadByOrdinal(ord, 1); /* Guarantied to load or die! */
5a9d0041 2126
5a9d0041 2127 if (type > 0)
2128 what = END_LIBPATH;
2129 else if (type == 0)
2130 what = BEGIN_LIBPATH;
2131 else
2132 what = LIBPATHSTRICT;
35bc1fdc 2133 return (*(PELP)f)(path, what);
72ea3524 2134}
3bbf9c2b 2135
5a9d0041 2136#define extLibpath(to,type) \
35bc1fdc 2137 (CheckOSError(ExtLIBPATH(ORD_DosQueryExtLibpath, (to), (type))) ? NULL : (to) )
3bbf9c2b 2138
2139#define extLibpath_set(p,type) \
35bc1fdc 2140 (!CheckOSError(ExtLIBPATH(ORD_DosSetExtLibpath, (p), (type))))
3bbf9c2b 2141
2142XS(XS_Cwd_extLibpath)
2143{
2144 dXSARGS;
2145 if (items < 0 || items > 1)
23da6c43 2146 Perl_croak_nocontext("Usage: Cwd::extLibpath(type = 0)");
3bbf9c2b 2147 {
5a9d0041 2148 IV type;
3bbf9c2b 2149 char to[1024];
2150 U32 rc;
2151 char * RETVAL;
2152
2153 if (items < 1)
2154 type = 0;
2155 else {
5a9d0041 2156 type = SvIV(ST(0));
3bbf9c2b 2157 }
2158
5a9d0041 2159 to[0] = 1; to[1] = 0; /* Sometimes no error reported */
2160 RETVAL = extLibpath(to, type);
2161 if (RETVAL && RETVAL[0] == 1 && RETVAL[1] == 0)
2162 Perl_croak_nocontext("panic Cwd::extLibpath parameter");
3bbf9c2b 2163 ST(0) = sv_newmortal();
2164 sv_setpv((SV*)ST(0), RETVAL);
2165 }
2166 XSRETURN(1);
2167}
2168
2169XS(XS_Cwd_extLibpath_set)
2170{
2171 dXSARGS;
2172 if (items < 1 || items > 2)
23da6c43 2173 Perl_croak_nocontext("Usage: Cwd::extLibpath_set(s, type = 0)");
3bbf9c2b 2174 {
2d8e6c8d 2175 STRLEN n_a;
2176 char * s = (char *)SvPV(ST(0),n_a);
5a9d0041 2177 IV type;
3bbf9c2b 2178 U32 rc;
2179 bool RETVAL;
2180
2181 if (items < 2)
2182 type = 0;
2183 else {
5a9d0041 2184 type = SvIV(ST(1));
3bbf9c2b 2185 }
2186
2187 RETVAL = extLibpath_set(s, type);
54310121 2188 ST(0) = boolSV(RETVAL);
3bbf9c2b 2189 if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
2190 }
2191 XSRETURN(1);
2192}
2193
5ba48348 2194#define get_control87() _control87(0,0)
2195#define set_control87 _control87
2196
2197XS(XS_OS2__control87)
2198{
2199 dXSARGS;
2200 if (items != 2)
2201 croak("Usage: OS2::_control87(new,mask)");
2202 {
2203 unsigned new = (unsigned)SvIV(ST(0));
2204 unsigned mask = (unsigned)SvIV(ST(1));
2205 unsigned RETVAL;
2206
2207 RETVAL = _control87(new, mask);
2208 ST(0) = sv_newmortal();
2209 sv_setiv(ST(0), (IV)RETVAL);
2210 }
2211 XSRETURN(1);
2212}
2213
2214XS(XS_OS2_get_control87)
2215{
2216 dXSARGS;
2217 if (items != 0)
2218 croak("Usage: OS2::get_control87()");
2219 {
2220 unsigned RETVAL;
2221
2222 RETVAL = get_control87();
2223 ST(0) = sv_newmortal();
2224 sv_setiv(ST(0), (IV)RETVAL);
2225 }
2226 XSRETURN(1);
2227}
2228
2229
2230XS(XS_OS2_set_control87)
2231{
2232 dXSARGS;
2233 if (items < 0 || items > 2)
2234 croak("Usage: OS2::set_control87(new=MCW_EM, mask=MCW_EM)");
2235 {
2236 unsigned new;
2237 unsigned mask;
2238 unsigned RETVAL;
2239
2240 if (items < 1)
2241 new = MCW_EM;
2242 else {
2243 new = (unsigned)SvIV(ST(0));
2244 }
2245
2246 if (items < 2)
2247 mask = MCW_EM;
2248 else {
2249 mask = (unsigned)SvIV(ST(1));
2250 }
2251
2252 RETVAL = set_control87(new, mask);
2253 ST(0) = sv_newmortal();
2254 sv_setiv(ST(0), (IV)RETVAL);
2255 }
2256 XSRETURN(1);
2257}
2258
3bbf9c2b 2259int
23da6c43 2260Xs_OS2_init(pTHX)
3bbf9c2b 2261{
2262 char *file = __FILE__;
2263 {
2264 GV *gv;
55497cff 2265
2266 if (_emx_env & 0x200) { /* OS/2 */
2267 newXS("File::Copy::syscopy", XS_File__Copy_syscopy, file);
2268 newXS("Cwd::extLibpath", XS_Cwd_extLibpath, file);
2269 newXS("Cwd::extLibpath_set", XS_Cwd_extLibpath_set, file);
2270 }
4bfbfac5 2271 newXS("OS2::Error", XS_OS2_Error, file);
2272 newXS("OS2::Errors2Drive", XS_OS2_Errors2Drive, file);
2273 newXS("OS2::SysInfo", XS_OS2_SysInfo, file);
2274 newXS("OS2::BootDrive", XS_OS2_BootDrive, file);
2275 newXS("OS2::MorphPM", XS_OS2_MorphPM, file);
2276 newXS("OS2::UnMorphPM", XS_OS2_UnMorphPM, file);
2277 newXS("OS2::Serve_Messages", XS_OS2_Serve_Messages, file);
2278 newXS("OS2::Process_Messages", XS_OS2_Process_Messages, file);
3bbf9c2b 2279 newXS("DynaLoader::mod2fname", XS_DynaLoader_mod2fname, file);
2280 newXS("Cwd::current_drive", XS_Cwd_current_drive, file);
2281 newXS("Cwd::sys_chdir", XS_Cwd_sys_chdir, file);
2282 newXS("Cwd::change_drive", XS_Cwd_change_drive, file);
2283 newXS("Cwd::sys_is_absolute", XS_Cwd_sys_is_absolute, file);
2284 newXS("Cwd::sys_is_rooted", XS_Cwd_sys_is_rooted, file);
2285 newXS("Cwd::sys_is_relative", XS_Cwd_sys_is_relative, file);
2286 newXS("Cwd::sys_cwd", XS_Cwd_sys_cwd, file);
2287 newXS("Cwd::sys_abspath", XS_Cwd_sys_abspath, file);
5ba48348 2288 newXSproto("OS2::_control87", XS_OS2__control87, file, "$$");
2289 newXSproto("OS2::get_control87", XS_OS2_get_control87, file, "");
2290 newXSproto("OS2::set_control87", XS_OS2_set_control87, file, ";$$");
3bbf9c2b 2291 gv = gv_fetchpv("OS2::is_aout", TRUE, SVt_PV);
2292 GvMULTI_on(gv);
2293#ifdef PERL_IS_AOUT
2294 sv_setiv(GvSV(gv), 1);
764df951 2295#endif
2296 gv = gv_fetchpv("OS2::can_fork", TRUE, SVt_PV);
2297 GvMULTI_on(gv);
2298 sv_setiv(GvSV(gv), exe_is_aout());
4bfbfac5 2299 gv = gv_fetchpv("OS2::emx_rev", TRUE, SVt_PV);
2300 GvMULTI_on(gv);
2301 sv_setiv(GvSV(gv), _emx_rev);
2302 sv_setpv(GvSV(gv), _emx_vprt);
2303 SvIOK_on(GvSV(gv));
2304 gv = gv_fetchpv("OS2::emx_env", TRUE, SVt_PV);
2305 GvMULTI_on(gv);
2306 sv_setiv(GvSV(gv), _emx_env);
2307 gv = gv_fetchpv("OS2::os_ver", TRUE, SVt_PV);
2308 GvMULTI_on(gv);
2309 sv_setnv(GvSV(gv), _osmajor + 0.001 * _osminor);
9fed8b87 2310 gv = gv_fetchpv("OS2::nsyserror", TRUE, SVt_PV);
2311 GvMULTI_on(gv);
2312 sv_setiv(GvSV(gv), 1); /* DEFAULT: Show number on syserror */
3bbf9c2b 2313 }
2d766320 2314 return 0;
3bbf9c2b 2315}
2316
2317OS2_Perl_data_t OS2_Perl_data;
2318
764df951 2319extern void _emx_init(void*);
2320
2321static void jmp_out_of_atexit(void);
2322
2323#define FORCE_EMX_INIT_CONTRACT_ARGV 1
2324#define FORCE_EMX_INIT_INSTALL_ATEXIT 2
2325
2326static void
2327my_emx_init(void *layout) {
2328 static volatile void *p = 0; /* Cannot be on stack! */
2329
2330 /* Can't just call emx_init(), since it moves the stack pointer */
2331 /* It also busts a lot of registers, so be extra careful */
2332 __asm__( "pushf\n"
2333 "pusha\n"
2334 "movl %%esp, %1\n"
2335 "push %0\n"
2336 "call __emx_init\n"
2337 "movl %1, %%esp\n"
2338 "popa\n"
2339 "popf\n" : : "r" (layout), "m" (p) );
2340}
2341
2342struct layout_table_t {
2343 ULONG text_base;
2344 ULONG text_end;
2345 ULONG data_base;
2346 ULONG data_end;
2347 ULONG bss_base;
2348 ULONG bss_end;
2349 ULONG heap_base;
2350 ULONG heap_end;
2351 ULONG heap_brk;
2352 ULONG heap_off;
2353 ULONG os2_dll;
2354 ULONG stack_base;
2355 ULONG stack_end;
2356 ULONG flags;
2357 ULONG reserved[2];
2358 char options[64];
2359};
2360
2361static ULONG
2362my_os_version() {
2363 static ULONG res; /* Cannot be on stack! */
2364
2365 /* Can't just call emx_init(), since it moves the stack pointer */
2366 /* It also busts a lot of registers, so be extra careful */
2367 __asm__( "pushf\n"
2368 "pusha\n"
2369 "call ___os_version\n"
2370 "movl %%eax, %0\n"
2371 "popa\n"
2372 "popf\n" : "=m" (res) );
2373
2374 return res;
2375}
2376
2377static void
2378force_init_emx_runtime(EXCEPTIONREGISTRATIONRECORD *preg, ULONG flags)
2379{
2380 /* Calling emx_init() will bust the top of stack: it installs an
2381 exception handler and puts argv data there. */
2382 char *oldarg, *oldenv;
2383 void *oldstackend, *oldstack;
2384 PPIB pib;
2385 PTIB tib;
2386 static ULONG os2_dll;
2387 ULONG rc, error = 0, out;
2388 char buf[512];
2389 static struct layout_table_t layout_table;
2390 struct {
2391 char buf[48*1024]; /* _emx_init() requires 32K, cmd.exe has 64K only */
2392 double alignment1;
2393 EXCEPTIONREGISTRATIONRECORD xreg;
2394 } *newstack;
2395 char *s;
2396
2397 layout_table.os2_dll = (ULONG)&os2_dll;
2398 layout_table.flags = 0x02000002; /* flags: application, OMF */
2399
2400 DosGetInfoBlocks(&tib, &pib);
2401 oldarg = pib->pib_pchcmd;
2402 oldenv = pib->pib_pchenv;
2403 oldstack = tib->tib_pstack;
2404 oldstackend = tib->tib_pstacklimit;
2405
2406 /* Minimize the damage to the stack via reducing the size of argv. */
2407 if (flags & FORCE_EMX_INIT_CONTRACT_ARGV) {
2408 pib->pib_pchcmd = "\0\0"; /* Need 3 concatenated strings */
2409 pib->pib_pchcmd = "\0"; /* Ended by an extra \0. */
2410 }
2411
2412 newstack = alloca(sizeof(*newstack));
2413 /* Emulate the stack probe */
2414 s = ((char*)newstack) + sizeof(*newstack);
2415 while (s > (char*)newstack) {
2416 s[-1] = 0;
2417 s -= 4096;
2418 }
2419
2420 /* Reassigning stack is documented to work */
2421 tib->tib_pstack = (void*)newstack;
2422 tib->tib_pstacklimit = (void*)((char*)newstack + sizeof(*newstack));
2423
2424 /* Can't just call emx_init(), since it moves the stack pointer */
2425 my_emx_init((void*)&layout_table);
2426
2427 /* Remove the exception handler, cannot use it - too low on the stack.
2428 Check whether it is inside the new stack. */
2429 buf[0] = 0;
2430 if (tib->tib_pexchain >= tib->tib_pstacklimit
2431 || tib->tib_pexchain < tib->tib_pstack) {
2432 error = 1;
2433 sprintf(buf,
2434 "panic: ExceptionHandler misplaced: not %#lx <= %#lx < %#lx\n",
2435 (unsigned long)tib->tib_pstack,
2436 (unsigned long)tib->tib_pexchain,
2437 (unsigned long)tib->tib_pstacklimit);
2438 goto finish;
2439 }
2440 if (tib->tib_pexchain != &(newstack->xreg)) {
2441 sprintf(buf, "ExceptionHandler misplaced: %#lx != %#lx\n",
2442 (unsigned long)tib->tib_pexchain,
2443 (unsigned long)&(newstack->xreg));
2444 }
2445 rc = DosUnsetExceptionHandler((EXCEPTIONREGISTRATIONRECORD *)tib->tib_pexchain);
2446 if (rc)
2447 sprintf(buf + strlen(buf),
2448 "warning: DosUnsetExceptionHandler rc=%#lx=%lu\n", rc, rc);
2449
2450 if (preg) {
2451 /* ExceptionRecords should be on stack, in a correct order. Sigh... */
2452 preg->prev_structure = 0;
2453 preg->ExceptionHandler = _emx_exception;
2454 rc = DosSetExceptionHandler(preg);
2455 if (rc) {
2456 sprintf(buf + strlen(buf),
2457 "warning: DosSetExceptionHandler rc=%#lx=%lu\n", rc, rc);
2458 DosWrite(2, buf, strlen(buf), &out);
2459 emx_exception_init = 1; /* Do it around spawn*() calls */
2460 }
2461 } else
2462 emx_exception_init = 1; /* Do it around spawn*() calls */
2463
2464 finish:
2465 /* Restore the damage */
2466 pib->pib_pchcmd = oldarg;
2467 pib->pib_pchcmd = oldenv;
2468 tib->tib_pstacklimit = oldstackend;
2469 tib->tib_pstack = oldstack;
2470 emx_runtime_init = 1;
2471 if (buf[0])
2472 DosWrite(2, buf, strlen(buf), &out);
2473 if (error)
2474 exit(56);
2475}
2476
2477jmp_buf at_exit_buf;
2478int longjmp_at_exit;
2479
2480static void
2481jmp_out_of_atexit(void)
2482{
2483 if (longjmp_at_exit)
2484 longjmp(at_exit_buf, 1);
2485}
2486
2487extern void _CRT_term(void);
2488
2489int emx_runtime_secondary;
2490
2491void
2492Perl_OS2_term(void **p, int exitstatus, int flags)
2493{
2494 if (!emx_runtime_secondary)
2495 return;
2496
2497 /* The principal executable is not running the same CRTL, so there
2498 is nobody to shutdown *this* CRTL except us... */
2499 if (flags & FORCE_EMX_DEINIT_EXIT) {
2500 if (p && !emx_exception_init)
2501 DosUnsetExceptionHandler((EXCEPTIONREGISTRATIONRECORD *)p);
2502 /* Do not run the executable's CRTL's termination routines */
2503 exit(exitstatus); /* Run at-exit, flush buffers, etc */
2504 }
2505 /* Run at-exit list, and jump out at the end */
2506 if ((flags & FORCE_EMX_DEINIT_RUN_ATEXIT) && !setjmp(at_exit_buf)) {
2507 longjmp_at_exit = 1;
2508 exit(exitstatus); /* The first pass through "if" */
2509 }
2510
2511 /* Get here if we managed to jump out of exit(), or did not run atexit. */
2512 longjmp_at_exit = 0; /* Maybe exit() is called again? */
2513#if 0 /* _atexit_n is not exported */
2514 if (flags & FORCE_EMX_DEINIT_RUN_ATEXIT)
2515 _atexit_n = 0; /* Remove the atexit() handlers */
2516#endif
2517 /* Will segfault on program termination if we leave this dangling... */
2518 if (p && !emx_exception_init)
2519 DosUnsetExceptionHandler((EXCEPTIONREGISTRATIONRECORD *)p);
2520 /* Typically there is no need to do this, done from _DLL_InitTerm() */
2521 if (flags & FORCE_EMX_DEINIT_CRT_TERM)
2522 _CRT_term(); /* Flush buffers, etc. */
2523 /* Now it is a good time to call exit() in the caller's CRTL... */
2524}
2525
2526#include <emx/startup.h>
2527
2528extern ULONG __os_version(); /* See system.doc */
2529
2530static int emx_wasnt_initialized;
2531
2532void
2533check_emx_runtime(char **env, EXCEPTIONREGISTRATIONRECORD *preg)
2534{
2535 ULONG v_crt, v_emx;
2536
2537 /* If _environ is not set, this code sits in a DLL which
2538 uses a CRT DLL which not compatible with the executable's
2539 CRT library. Some parts of the DLL are not initialized.
2540 */
2541 if (_environ != NULL)
2542 return; /* Properly initialized */
2543
2544 /* If the executable does not use EMX.DLL, EMX.DLL is not completely
2545 initialized either. Uninitialized EMX.DLL returns 0 in the low
2546 nibble of __os_version(). */
2547 v_emx = my_os_version();
2548
2549 /* _osmajor and _osminor are normally set in _DLL_InitTerm of CRT DLL
2550 (=>_CRT_init=>_entry2) via a call to __os_version(), then
2551 reset when the EXE initialization code calls _text=>_init=>_entry2.
2552 The first time they are wrongly set to 0; the second time the
2553 EXE initialization code had already called emx_init=>initialize1
2554 which correctly set version_major, version_minor used by
2555 __os_version(). */
2556 v_crt = (_osmajor | _osminor);
2557
2558 if ((_emx_env & 0x200) && !(v_emx & 0xFFFF)) { /* OS/2, EMX uninit. */
2559 force_init_emx_runtime( preg,
2560 FORCE_EMX_INIT_CONTRACT_ARGV
2561 | FORCE_EMX_INIT_INSTALL_ATEXIT );
2562 emx_wasnt_initialized = 1;
2563 /* Update CRTL data basing on now-valid EMX runtime data */
2564 if (!v_crt) { /* The only wrong data are the versions. */
2565 v_emx = my_os_version(); /* *Now* it works */
2566 *(unsigned char *)&_osmajor = v_emx & 0xFF; /* Cast out const */
2567 *(unsigned char *)&_osminor = (v_emx>>8) & 0xFF;
2568 }
2569 }
2570 emx_runtime_secondary = 1;
2571 /* if (flags & FORCE_EMX_INIT_INSTALL_ATEXIT) */
2572 atexit(jmp_out_of_atexit); /* Allow run of atexit() w/o exit() */
2573
2574 if (!env) { /* Fetch from the process info block */
2575 int c = 0;
2576 PPIB pib;
2577 PTIB tib;
2578 char *e, **ep;
2579
2580 DosGetInfoBlocks(&tib, &pib);
2581 e = pib->pib_pchenv;
2582 while (*e) { /* Get count */
2583 c++;
2584 e = e + strlen(e) + 1;
2585 }
2586 e = pib->pib_pchenv;
2587 while (*e) { /* Get count */
2588 c++;
2589 e = e + strlen(e) + 1;
2590 }
2591 New(1307, env, c + 1, char*);
2592 ep = env;
2593 e = pib->pib_pchenv;
2594 while (c--) {
2595 *ep++ = e;
2596 e = e + strlen(e) + 1;
2597 }
2598 *ep = NULL;
2599 }
2600 _environ = _org_environ = env;
2601}
2602
2603#define ENTRY_POINT 0x10000
2604
2605static int
2606exe_is_aout(void)
2607{
2608 struct layout_table_t *layout;
2609 if (emx_wasnt_initialized)
2610 return 0;
2611 /* Now we know that the principal executable is an EMX application
2612 - unless somebody did already play with delayed initialization... */
2613 /* With EMX applications to determine whether it is AOUT one needs
2614 to examine the start of the executable to find "layout" */
2615 if ( *(unsigned char*)ENTRY_POINT != 0x68 /* PUSH n */
2616 || *(unsigned char*)(ENTRY_POINT+5) != 0xe8 /* CALL */
2617 || *(unsigned char*)(ENTRY_POINT+10) != 0xeb /* JMP */
2618 || *(unsigned char*)(ENTRY_POINT+12) != 0xe8) /* CALL */
2619 return 0; /* ! EMX executable */
2620 /* Fix alignment */
2621 Copy((char*)(ENTRY_POINT+1), &layout, 1, struct layout_table_t*);
2622 return !(layout->flags & 2);
2623}
2624
3bbf9c2b 2625void
aa689395 2626Perl_OS2_init(char **env)
3bbf9c2b 2627{
764df951 2628 Perl_OS2_init3(env, 0, 0);
2629}
2630
2631void
2632Perl_OS2_init3(char **env, void **preg, int flags)
2633{
3bbf9c2b 2634 char *shell;
2635
764df951 2636 _uflags (_UF_SBRK_MODEL, _UF_SBRK_ARBITRARY);
18f739ee 2637 MALLOC_INIT;
764df951 2638
2639 check_emx_runtime(env, (EXCEPTIONREGISTRATIONRECORD *)preg);
2640
3bbf9c2b 2641 settmppath();
2642 OS2_Perl_data.xs_init = &Xs_OS2_init;
2643 if ( (shell = getenv("PERL_SH_DRIVE")) ) {
6b88bc9c 2644 New(1304, PL_sh_path, strlen(SH_PATH) + 1, char);
2645 strcpy(PL_sh_path, SH_PATH);
2646 PL_sh_path[0] = shell[0];
3bbf9c2b 2647 } else if ( (shell = getenv("PERL_SH_DIR")) ) {
ff68c719 2648 int l = strlen(shell), i;
3bbf9c2b 2649 if (shell[l-1] == '/' || shell[l-1] == '\\') {
2650 l--;
2651 }
6b88bc9c 2652 New(1304, PL_sh_path, l + 8, char);
2653 strncpy(PL_sh_path, shell, l);
2654 strcpy(PL_sh_path + l, "/sh.exe");
ff68c719 2655 for (i = 0; i < l; i++) {
6b88bc9c 2656 if (PL_sh_path[i] == '\\') PL_sh_path[i] = '/';
ff68c719 2657 }
3bbf9c2b 2658 }
dd96f567 2659 MUTEX_INIT(&start_thread_mutex);
017f25f1 2660 os2_mytype = my_type(); /* Do it before morphing. Needed? */
5ba48348 2661 /* Some DLLs reset FP flags on load. We may have been linked with them */
2662 _control87(MCW_EM, MCW_EM);
3bbf9c2b 2663}
2664
55497cff 2665#undef tmpnam
2666#undef tmpfile
2667
2668char *
2669my_tmpnam (char *str)
2670{
2671 char *p = getenv("TMP"), *tpath;
55497cff 2672
2673 if (!p) p = getenv("TEMP");
2674 tpath = tempnam(p, "pltmp");
2675 if (str && tpath) {
2676 strcpy(str, tpath);
2677 return str;
2678 }
2679 return tpath;
2680}
2681
2682FILE *
2683my_tmpfile ()
2684{
2685 struct stat s;
2686
2687 stat(".", &s);
2688 if (s.st_mode & S_IWOTH) {
2689 return tmpfile();
2690 }
2691 return fopen(my_tmpnam(NULL), "w+b"); /* Race condition, but
2692 grants TMP. */
2693}
367f3c24 2694
5ba48348 2695#undef rmdir
2696
2697int
2698my_rmdir (__const__ char *s)
2699{
2700 char buf[MAXPATHLEN];
2701 STRLEN l = strlen(s);
2702
2703 if (s[l-1] == '/' || s[l-1] == '\\') { /* EMX rmdir fails... */
2704 strcpy(buf,s);
2705 buf[l - 1] = 0;
2706 s = buf;
2707 }
2708 return rmdir(s);
2709}
2710
2711#undef mkdir
2712
2713int
2714my_mkdir (__const__ char *s, long perm)
2715{
2716 char buf[MAXPATHLEN];
2717 STRLEN l = strlen(s);
2718
2719 if (s[l-1] == '/' || s[l-1] == '\\') { /* EMX mkdir fails... */
2720 strcpy(buf,s);
2721 buf[l - 1] = 0;
2722 s = buf;
2723 }
2724 return mkdir(s, perm);
2725}
2726
367f3c24 2727#undef flock
2728
2729/* This code was contributed by Rocco Caputo. */
2730int
dd96f567 2731my_flock(int handle, int o)
367f3c24 2732{
2733 FILELOCK rNull, rFull;
2734 ULONG timeout, handle_type, flag_word;
2735 APIRET rc;
2736 int blocking, shared;
2737 static int use_my = -1;
2738
2739 if (use_my == -1) {
2740 char *s = getenv("USE_PERL_FLOCK");
2741 if (s)
2742 use_my = atoi(s);
2743 else
2744 use_my = 1;
2745 }
2746 if (!(_emx_env & 0x200) || !use_my)
dd96f567 2747 return flock(handle, o); /* Delegate to EMX. */
367f3c24 2748
2749 // is this a file?
2750 if ((DosQueryHType(handle, &handle_type, &flag_word) != 0) ||
2751 (handle_type & 0xFF))
2752 {
2753 errno = EBADF;
2754 return -1;
2755 }
2756 // set lock/unlock ranges
2757 rNull.lOffset = rNull.lRange = rFull.lOffset = 0;
2758 rFull.lRange = 0x7FFFFFFF;
2759 // set timeout for blocking
dd96f567 2760 timeout = ((blocking = !(o & LOCK_NB))) ? 100 : 1;
367f3c24 2761 // shared or exclusive?
dd96f567 2762 shared = (o & LOCK_SH) ? 1 : 0;
367f3c24 2763 // do not block the unlock
dd96f567 2764 if (o & (LOCK_UN | LOCK_SH | LOCK_EX)) {
367f3c24 2765 rc = DosSetFileLocks(handle, &rFull, &rNull, timeout, shared);
2766 switch (rc) {
2767 case 0:
2768 errno = 0;
2769 return 0;
2770 case ERROR_INVALID_HANDLE:
2771 errno = EBADF;
2772 return -1;
2773 case ERROR_SHARING_BUFFER_EXCEEDED:
2774 errno = ENOLCK;
2775 return -1;
2776 case ERROR_LOCK_VIOLATION:
2777 break; // not an error
2778 case ERROR_INVALID_PARAMETER:
2779 case ERROR_ATOMIC_LOCK_NOT_SUPPORTED:
2780 case ERROR_READ_LOCKS_NOT_SUPPORTED:
2781 errno = EINVAL;
2782 return -1;
2783 case ERROR_INTERRUPT:
2784 errno = EINTR;
2785 return -1;
2786 default:
2787 errno = EINVAL;
2788 return -1;
2789 }
2790 }
2791 // lock may block
dd96f567 2792 if (o & (LOCK_SH | LOCK_EX)) {
367f3c24 2793 // for blocking operations
2794 for (;;) {
2795 rc =
2796 DosSetFileLocks(
2797 handle,
2798 &rNull,
2799 &rFull,
2800 timeout,
2801 shared
2802 );
2803 switch (rc) {
2804 case 0:
2805 errno = 0;
2806 return 0;
2807 case ERROR_INVALID_HANDLE:
2808 errno = EBADF;
2809 return -1;
2810 case ERROR_SHARING_BUFFER_EXCEEDED:
2811 errno = ENOLCK;
2812 return -1;
2813 case ERROR_LOCK_VIOLATION:
2814 if (!blocking) {
2815 errno = EWOULDBLOCK;
2816 return -1;
2817 }
2818 break;
2819 case ERROR_INVALID_PARAMETER:
2820 case ERROR_ATOMIC_LOCK_NOT_SUPPORTED:
2821 case ERROR_READ_LOCKS_NOT_SUPPORTED:
2822 errno = EINVAL;
2823 return -1;
2824 case ERROR_INTERRUPT:
2825 errno = EINTR;
2826 return -1;
2827 default:
2828 errno = EINVAL;
2829 return -1;
2830 }
2831 // give away timeslice
2832 DosSleep(1);
2833 }
2834 }
2835
2836 errno = 0;
2837 return 0;
2838}
f72c975a 2839
2840static int pwent_cnt;
2841static int _my_pwent = -1;
2842
2843static int
2844use_my_pwent(void)
2845{
2846 if (_my_pwent == -1) {
2847 char *s = getenv("USE_PERL_PWENT");
2848 if (s)
2849 _my_pwent = atoi(s);
2850 else
2851 _my_pwent = 1;
2852 }
2853 return _my_pwent;
2854}
2855
2856#undef setpwent
2857#undef getpwent
2858#undef endpwent
2859
2860void
2861my_setpwent(void)
2862{
2863 if (!use_my_pwent()) {
2864 setpwent(); /* Delegate to EMX. */
2865 return;
2866 }
2867 pwent_cnt = 0;
2868}
2869
2870void
2871my_endpwent(void)
2872{
2873 if (!use_my_pwent()) {
2874 endpwent(); /* Delegate to EMX. */
2875 return;
2876 }
2877}
2878
2879struct passwd *
2880my_getpwent (void)
2881{
2882 if (!use_my_pwent())
2883 return getpwent(); /* Delegate to EMX. */
2884 if (pwent_cnt++)
2885 return 0; // Return one entry only
2886 return getpwuid(0);
2887}
2888
2889static int grent_cnt;
2890
2891void
2892setgrent(void)
2893{
2894 grent_cnt = 0;
2895}
2896
2897void
2898endgrent(void)
2899{
2900}
2901
2902struct group *
2903getgrent (void)
2904{
2905 if (grent_cnt++)
2906 return 0; // Return one entry only
2907 return getgrgid(0);
2908}
2909
2910#undef getpwuid
2911#undef getpwnam
2912
2913/* Too long to be a crypt() of anything, so it is not-a-valid pw_passwd. */
2914static const char pw_p[] = "Jf0Wb/BzMFvk7K7lrzK";
2915
2916static struct passwd *
2917passw_wrap(struct passwd *p)
2918{
2919 static struct passwd pw;
2920 char *s;
2921
2922 if (!p || (p->pw_passwd && *p->pw_passwd)) /* Not a dangerous password */
2923 return p;
2924 pw = *p;
2925 s = getenv("PW_PASSWD");
2926 if (!s)
2927 s = (char*)pw_p; /* Make match impossible */
2928
2929 pw.pw_passwd = s;
2930 return &pw;
2931}
2932
2933struct passwd *
2934my_getpwuid (uid_t id)
2935{
2936 return passw_wrap(getpwuid(id));
2937}
2938
2939struct passwd *
2940my_getpwnam (__const__ char *n)
2941{
2942 return passw_wrap(getpwnam(n));
2943}