[win32] merge change#985 from maintbranch
[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
4633a7c4 6#include <os2.h>
7
8/*
9 * Various Unix compatibility functions for OS/2
10 */
11
12#include <stdio.h>
13#include <errno.h>
14#include <limits.h>
15#include <process.h>
72ea3524 16#include <fcntl.h>
4633a7c4 17
18#include "EXTERN.h"
19#include "perl.h"
20
dd96f567 21#ifdef USE_THREADS
22
23typedef void (*emx_startroutine)(void *);
24typedef void* (*pthreads_startroutine)(void *);
25
26enum pthreads_state {
27 pthreads_st_none = 0,
28 pthreads_st_run,
29 pthreads_st_exited,
30 pthreads_st_detached,
31 pthreads_st_waited,
32};
33const char *pthreads_states[] = {
34 "uninit",
35 "running",
36 "exited",
37 "detached",
38 "waited for",
39};
40
41typedef struct {
42 void *status;
6ee623d5 43 perl_cond cond;
dd96f567 44 enum pthreads_state state;
45} thread_join_t;
46
47thread_join_t *thread_join_data;
48int thread_join_count;
6ee623d5 49perl_mutex start_thread_mutex;
dd96f567 50
51int
6ee623d5 52pthread_join(perl_os_thread tid, void **status)
dd96f567 53{
54 MUTEX_LOCK(&start_thread_mutex);
55 switch (thread_join_data[tid].state) {
56 case pthreads_st_exited:
57 thread_join_data[tid].state = pthreads_st_none; /* Ready to reuse */
58 MUTEX_UNLOCK(&start_thread_mutex);
59 *status = thread_join_data[tid].status;
60 break;
61 case pthreads_st_waited:
62 MUTEX_UNLOCK(&start_thread_mutex);
63 croak("join with a thread with a waiter");
64 break;
65 case pthreads_st_run:
66 thread_join_data[tid].state = pthreads_st_waited;
67 COND_INIT(&thread_join_data[tid].cond);
68 MUTEX_UNLOCK(&start_thread_mutex);
69 COND_WAIT(&thread_join_data[tid].cond, NULL);
70 COND_DESTROY(&thread_join_data[tid].cond);
71 thread_join_data[tid].state = pthreads_st_none; /* Ready to reuse */
72 *status = thread_join_data[tid].status;
73 break;
74 default:
75 MUTEX_UNLOCK(&start_thread_mutex);
76 croak("join: unknown thread state: '%s'",
77 pthreads_states[thread_join_data[tid].state]);
78 break;
79 }
80 return 0;
81}
82
83void
84pthread_startit(void *arg)
85{
86 /* Thread is already started, we need to transfer control only */
87 pthreads_startroutine start_routine = *((pthreads_startroutine*)arg);
88 int tid = pthread_self();
89 void *retval;
90
91 arg = ((void**)arg)[1];
92 if (tid >= thread_join_count) {
93 int oc = thread_join_count;
94
95 thread_join_count = tid + 5 + tid/5;
96 if (thread_join_data) {
97 Renew(thread_join_data, thread_join_count, thread_join_t);
98 Zero(thread_join_data + oc, thread_join_count - oc, thread_join_t);
99 } else {
100 Newz(1323, thread_join_data, thread_join_count, thread_join_t);
101 }
102 }
103 if (thread_join_data[tid].state != pthreads_st_none)
104 croak("attempt to reuse thread id %i", tid);
105 thread_join_data[tid].state = pthreads_st_run;
106 /* Now that we copied/updated the guys, we may release the caller... */
107 MUTEX_UNLOCK(&start_thread_mutex);
108 thread_join_data[tid].status = (*start_routine)(arg);
109 switch (thread_join_data[tid].state) {
110 case pthreads_st_waited:
111 COND_SIGNAL(&thread_join_data[tid].cond);
112 break;
113 default:
114 thread_join_data[tid].state = pthreads_st_exited;
115 break;
116 }
117}
118
119int
6ee623d5 120pthread_create(perl_os_thread *tid, const pthread_attr_t *attr,
dd96f567 121 void *(*start_routine)(void*), void *arg)
122{
123 void *args[2];
124
125 args[0] = (void*)start_routine;
126 args[1] = arg;
127
128 MUTEX_LOCK(&start_thread_mutex);
129 *tid = _beginthread(pthread_startit, /*stack*/ NULL,
130 /*stacksize*/ 10*1024*1024, (void*)args);
131 MUTEX_LOCK(&start_thread_mutex);
132 MUTEX_UNLOCK(&start_thread_mutex);
133 return *tid ? 0 : EINVAL;
134}
135
136int
6ee623d5 137pthread_detach(perl_os_thread tid)
dd96f567 138{
139 MUTEX_LOCK(&start_thread_mutex);
140 switch (thread_join_data[tid].state) {
141 case pthreads_st_waited:
142 MUTEX_UNLOCK(&start_thread_mutex);
143 croak("detach on a thread with a waiter");
144 break;
145 case pthreads_st_run:
146 thread_join_data[tid].state = pthreads_st_detached;
147 MUTEX_UNLOCK(&start_thread_mutex);
148 break;
149 default:
150 MUTEX_UNLOCK(&start_thread_mutex);
151 croak("detach: unknown thread state: '%s'",
152 pthreads_states[thread_join_data[tid].state]);
153 break;
154 }
155 return 0;
156}
157
158/* This is a very bastardized version: */
159int
6ee623d5 160os2_cond_wait(perl_cond *c, perl_mutex *m)
dd96f567 161{
162 int rc;
163 if ((rc = DosResetEventSem(*c,&na)) && (rc != ERROR_ALREADY_RESET))
164 croak("panic: COND_WAIT-reset: rc=%i", rc);
165 if (m) MUTEX_UNLOCK(m);
166 if (CheckOSError(DosWaitEventSem(*c,SEM_INDEFINITE_WAIT)))
167 croak("panic: COND_WAIT: rc=%i", rc);
168 if (m) MUTEX_LOCK(m);
169}
170#endif
171
4633a7c4 172/*****************************************************************************/
72ea3524 173/* 2.1 would not resolve symbols on demand, and has no ExtLIBPATH. */
174static PFN ExtFCN[2]; /* Labeled by ord below. */
175static USHORT loadOrd[2] = { 874, 873 }; /* Query=874, Set=873. */
176#define ORD_QUERY_ELP 0
177#define ORD_SET_ELP 1
178
179APIRET
180loadByOrd(ULONG ord)
181{
182 if (ExtFCN[ord] == NULL) {
183 static HMODULE hdosc = 0;
184 BYTE buf[20];
185 PFN fcn;
186 APIRET rc;
187
188 if ((!hdosc && CheckOSError(DosLoadModule(buf, sizeof buf,
189 "doscalls", &hdosc)))
190 || CheckOSError(DosQueryProcAddr(hdosc, loadOrd[ord], NULL, &fcn)))
191 die("This version of OS/2 does not support doscalls.%i",
192 loadOrd[ord]);
193 ExtFCN[ord] = fcn;
194 }
195 if ((long)ExtFCN[ord] == -1) die("panic queryaddr");
196}
197
4633a7c4 198/* priorities */
6f064249 199static signed char priors[] = {0, 1, 3, 2}; /* Last two interchanged,
200 self inverse. */
201#define QSS_INI_BUFFER 1024
4633a7c4 202
6f064249 203PQTOPLEVEL
204get_sysinfo(ULONG pid, ULONG flags)
4633a7c4 205{
6f064249 206 char *pbuffer;
207 ULONG rc, buf_len = QSS_INI_BUFFER;
208
fc36a67e 209 New(1322, pbuffer, buf_len, char);
6f064249 210 /* QSS_PROCESS | QSS_MODULE | QSS_SEMAPHORES | QSS_SHARED */
211 rc = QuerySysState(flags, pid, pbuffer, buf_len);
212 while (rc == ERROR_BUFFER_OVERFLOW) {
213 Renew(pbuffer, buf_len *= 2, char);
df3ef7a9 214 rc = QuerySysState(flags, pid, pbuffer, buf_len);
6f064249 215 }
216 if (rc) {
217 FillOSError(rc);
218 Safefree(pbuffer);
219 return 0;
220 }
221 return (PQTOPLEVEL)pbuffer;
222}
223
224#define PRIO_ERR 0x1111
225
226static ULONG
227sys_prio(pid)
228{
229 ULONG prio;
230 PQTOPLEVEL psi;
231
232 psi = get_sysinfo(pid, QSS_PROCESS);
233 if (!psi) {
234 return PRIO_ERR;
235 }
236 if (pid != psi->procdata->pid) {
237 Safefree(psi);
238 croak("panic: wrong pid in sysinfo");
239 }
240 prio = psi->procdata->threads->priority;
241 Safefree(psi);
242 return prio;
243}
244
245int
246setpriority(int which, int pid, int val)
247{
248 ULONG rc, prio;
249 PQTOPLEVEL psi;
250
251 prio = sys_prio(pid);
252
55497cff 253 if (!(_emx_env & 0x200)) return 0; /* Nop if not OS/2. */
6f064249 254 if (priors[(32 - val) >> 5] + 1 == (prio >> 8)) {
255 /* Do not change class. */
256 return CheckOSError(DosSetPriority((pid < 0)
257 ? PRTYS_PROCESSTREE : PRTYS_PROCESS,
258 0,
259 (32 - val) % 32 - (prio & 0xFF),
260 abs(pid)))
261 ? -1 : 0;
262 } else /* if ((32 - val) % 32 == (prio & 0xFF)) */ {
263 /* Documentation claims one can change both class and basevalue,
264 * but I find it wrong. */
265 /* Change class, but since delta == 0 denotes absolute 0, correct. */
266 if (CheckOSError(DosSetPriority((pid < 0)
267 ? PRTYS_PROCESSTREE : PRTYS_PROCESS,
268 priors[(32 - val) >> 5] + 1,
269 0,
270 abs(pid))))
271 return -1;
272 if ( ((32 - val) % 32) == 0 ) return 0;
273 return CheckOSError(DosSetPriority((pid < 0)
274 ? PRTYS_PROCESSTREE : PRTYS_PROCESS,
275 0,
276 (32 - val) % 32,
277 abs(pid)))
278 ? -1 : 0;
279 }
280/* else return CheckOSError(DosSetPriority((pid < 0) */
281/* ? PRTYS_PROCESSTREE : PRTYS_PROCESS, */
282/* priors[(32 - val) >> 5] + 1, */
283/* (32 - val) % 32 - (prio & 0xFF), */
284/* abs(pid))) */
285/* ? -1 : 0; */
4633a7c4 286}
287
6f064249 288int
289getpriority(int which /* ignored */, int pid)
4633a7c4 290{
291 TIB *tib;
292 PIB *pib;
6f064249 293 ULONG rc, ret;
294
55497cff 295 if (!(_emx_env & 0x200)) return 0; /* Nop if not OS/2. */
6f064249 296 /* DosGetInfoBlocks has old priority! */
297/* if (CheckOSError(DosGetInfoBlocks(&tib, &pib))) return -1; */
298/* if (pid != pib->pib_ulpid) { */
299 ret = sys_prio(pid);
300 if (ret == PRIO_ERR) {
301 return -1;
302 }
303/* } else */
304/* ret = tib->tib_ptib2->tib2_ulpri; */
305 return (1 - priors[((ret >> 8) - 1)])*32 - (ret & 0xFF);
4633a7c4 306}
307
308/*****************************************************************************/
309/* spawn */
72ea3524 310typedef void (*Sigfunc) _((int));
311
4633a7c4 312static int
313result(int flag, int pid)
314{
315 int r, status;
316 Signal_t (*ihand)(); /* place to save signal during system() */
317 Signal_t (*qhand)(); /* place to save signal during system() */
760ac839 318#ifndef __EMX__
319 RESULTCODES res;
320 int rpid;
321#endif
4633a7c4 322
760ac839 323 if (pid < 0 || flag != 0)
4633a7c4 324 return pid;
325
760ac839 326#ifdef __EMX__
72ea3524 327 ihand = rsignal(SIGINT, SIG_IGN);
328 qhand = rsignal(SIGQUIT, SIG_IGN);
c0c09dfd 329 do {
330 r = wait4pid(pid, &status, 0);
331 } while (r == -1 && errno == EINTR);
72ea3524 332 rsignal(SIGINT, ihand);
333 rsignal(SIGQUIT, qhand);
4633a7c4 334
335 statusvalue = (U16)status;
336 if (r < 0)
337 return -1;
338 return status & 0xFFFF;
760ac839 339#else
72ea3524 340 ihand = rsignal(SIGINT, SIG_IGN);
760ac839 341 r = DosWaitChild(DCWA_PROCESS, DCWW_WAIT, &res, &rpid, pid);
72ea3524 342 rsignal(SIGINT, ihand);
760ac839 343 statusvalue = res.codeResult << 8 | res.codeTerminate;
344 if (r)
345 return -1;
346 return statusvalue;
347#endif
4633a7c4 348}
349
491527d0 350#define EXECF_SPAWN 0
351#define EXECF_EXEC 1
352#define EXECF_TRUEEXEC 2
353#define EXECF_SPAWN_NOWAIT 3
354
355/* Spawn/exec a program, revert to shell if needed. */
356/* global Argv[] contains arguments. */
357
4633a7c4 358int
491527d0 359do_aspawn(really, flag, execf)
4633a7c4 360SV *really;
491527d0 361U32 flag;
362U32 execf;
4633a7c4 363{
dd96f567 364 dTHR;
491527d0 365 int trueflag = flag;
366 int rc, secondtry = 0, err;
367 char *tmps;
368 char buf[256], *s = 0;
369 char *args[4];
370 static char * fargs[4]
371 = { "/bin/sh", "-c", "\"$@\"", "spawn-via-shell", };
372 char **argsp = fargs;
373 char nargs = 4;
374
4633a7c4 375 if (flag == P_WAIT)
376 flag = P_NOWAIT;
377
491527d0 378 retry:
379 if (strEQ(Argv[0],"/bin/sh"))
380 Argv[0] = sh_path;
3bbf9c2b 381
382 if (Argv[0][0] != '/' && Argv[0][0] != '\\'
383 && !(Argv[0][0] && Argv[0][1] == ':'
384 && (Argv[0][2] == '/' || Argv[0][2] != '\\'))
760ac839 385 ) /* will swawnvp use PATH? */
c0c09dfd 386 TAINT_ENV(); /* testing IFS here is overkill, probably */
760ac839 387 /* We should check PERL_SH* and PERLLIB_* as well? */
491527d0 388 if (!really || !*(tmps = SvPV(really, na)))
389 tmps = Argv[0];
390#if 0
391 rc = result(trueflag, spawnvp(flag,tmps,Argv));
392#else
393 if (execf == EXECF_TRUEEXEC)
394 rc = execvp(tmps,Argv);
395 else if (execf == EXECF_EXEC)
396 rc = spawnvp(trueflag | P_OVERLAY,tmps,Argv);
397 else if (execf == EXECF_SPAWN_NOWAIT)
398 rc = spawnvp(trueflag | P_NOWAIT,tmps,Argv);
399 else /* EXECF_SPAWN */
400 rc = result(trueflag,
401 spawnvp(trueflag | P_NOWAIT,tmps,Argv));
402#endif
e29f6e02 403 if (rc < 0 && secondtry == 0
491527d0 404 && (tmps == Argv[0])) { /* Cannot transfer `really' via shell. */
e29f6e02 405 err = errno;
406 if (err == ENOENT) { /* No such file. */
407 /* One reason may be that EMX added .exe. We suppose
491527d0 408 that .exe-less files are automatically shellable.
409 It might have also been .cmd file without
410 extension. */
e29f6e02 411 char *no_dir;
412 (no_dir = strrchr(Argv[0], '/'))
413 || (no_dir = strrchr(Argv[0], '\\'))
414 || (no_dir = Argv[0]);
415 if (!strchr(no_dir, '.')) {
416 struct stat buffer;
417 if (stat(Argv[0], &buffer) != -1) { /* File exists. */
418 /* Maybe we need to specify the full name here? */
419 goto doshell;
491527d0 420 } else {
421 /* Try adding script extensions to the file name */
422 char *scr;
423 if ((scr = find_script(Argv[0], TRUE, NULL, 0))) {
424 FILE *file = fopen(scr, "r");
425 char *s = 0, *s1;
426
427 Argv[0] = scr;
428 if (!file)
429 goto panic_file;
430 if (!fgets(buf, sizeof buf, file)) {
431 fclose(file);
432 goto panic_file;
433 }
434 if (fclose(file) != 0) { /* Failure */
435 panic_file:
436 warn("Error reading \"%s\": %s",
437 scr, Strerror(errno));
438 goto doshell;
439 }
440 if (buf[0] == '#') {
441 if (buf[1] == '!')
442 s = buf + 2;
443 } else if (buf[0] == 'e') {
444 if (strnEQ(buf, "extproc", 7)
445 && isSPACE(buf[7]))
446 s = buf + 8;
447 } else if (buf[0] == 'E') {
448 if (strnEQ(buf, "EXTPROC", 7)
449 && isSPACE(buf[7]))
450 s = buf + 8;
451 }
452 if (!s)
453 goto doshell;
454 s1 = s;
455 nargs = 0;
456 argsp = args;
457 while (1) {
458 while (isSPACE(*s))
459 s++;
460 if (*s == 0)
461 break;
462 if (nargs == 4) {
463 nargs = -1;
464 break;
465 }
466 args[nargs++] = s;
467 while (*s && !isSPACE(*s))
468 s++;
469 if (*s == 0)
470 break;
471 *s++ = 0;
472 }
473 if (nargs == -1) {
474 warn("Too many args on %.*s line of \"%s\"",
475 s1 - buf, buf, scr);
476 nargs = 4;
477 argsp = fargs;
478 }
479 goto doshell;
480 }
e29f6e02 481 }
482 }
491527d0 483 /* Restore errno */
484 errno = err;
e29f6e02 485 } else if (err == ENOEXEC) { /* Need to send to shell. */
486 doshell:
491527d0 487 {
488 char **a = Argv;
489
490 while (a[1]) /* Get to the end */
491 a++;
e29f6e02 492 while (a >= Argv) {
491527d0 493 *(a + nargs) = *a; /* Argv was preallocated to be
494 long enough. */
e29f6e02 495 a--;
496 }
491527d0 497 while (nargs-- >= 0)
498 Argv[nargs] = argsp[nargs];
e29f6e02 499 secondtry = 1;
500 goto retry;
491527d0 501 }
e29f6e02 502 }
503 }
4633a7c4 504 if (rc < 0 && dowarn)
491527d0 505 warn("Can't %s \"%s\": %s\n",
506 ((execf != EXECF_EXEC && execf != EXECF_TRUEEXEC)
507 ? "spawn" : "exec"),
508 Argv[0], Strerror(err));
509 if (rc < 0 && (execf != EXECF_SPAWN_NOWAIT)
510 && ((trueflag & 0xFF) == P_WAIT))
511 rc = 255 << 8; /* Emulate the fork(). */
512
513 return rc;
514}
515
516int
517do_aspawn(really,mark,sp)
518SV *really;
519register SV **mark;
520register SV **sp;
521{
522 dTHR;
523 register char **a;
524 char *tmps = NULL;
525 int rc;
526 int flag = P_WAIT, trueflag, err, secondtry = 0;
527
528 if (sp > mark) {
529 New(1301,Argv, sp - mark + 3, char*);
530 a = Argv;
531
532 if (mark < sp && SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
533 ++mark;
534 flag = SvIVx(*mark);
535 }
536
537 while (++mark <= sp) {
538 if (*mark)
539 *a++ = SvPVx(*mark, na);
540 else
541 *a++ = "";
542 }
543 *a = Nullch;
544
545 rc = do_spawn_ve(really, flag, EXECF_SPAWN);
4633a7c4 546 } else
547 rc = -1;
548 do_execfree();
549 return rc;
550}
551
491527d0 552/* Try converting 1-arg form to (usually shell-less) multi-arg form. */
4633a7c4 553int
760ac839 554do_spawn2(cmd, execf)
4633a7c4 555char *cmd;
760ac839 556int execf;
4633a7c4 557{
558 register char **a;
559 register char *s;
560 char flags[10];
3bbf9c2b 561 char *shell, *copt, *news = NULL;
a0914d8e 562 int rc, added_shell = 0, err, seenspace = 0;
e29f6e02 563 char fullcmd[MAXNAMLEN + 1];
4633a7c4 564
c0c09dfd 565#ifdef TRYSHELL
566 if ((shell = getenv("EMXSHELL")) != NULL)
567 copt = "-c";
568 else if ((shell = getenv("SHELL")) != NULL)
4633a7c4 569 copt = "-c";
570 else if ((shell = getenv("COMSPEC")) != NULL)
571 copt = "/C";
572 else
573 shell = "cmd.exe";
c0c09dfd 574#else
575 /* Consensus on perl5-porters is that it is _very_ important to
576 have a shell which will not change between computers with the
577 same architecture, to avoid "action on a distance".
578 And to have simple build, this shell should be sh. */
ff68c719 579 shell = sh_path;
c0c09dfd 580 copt = "-c";
581#endif
582
583 while (*cmd && isSPACE(*cmd))
584 cmd++;
4633a7c4 585
3bbf9c2b 586 if (strnEQ(cmd,"/bin/sh",7) && isSPACE(cmd[7])) {
ff68c719 587 STRLEN l = strlen(sh_path);
3bbf9c2b 588
2cc2f81f 589 New(1302, news, strlen(cmd) - 7 + l + 1, char);
ff68c719 590 strcpy(news, sh_path);
3bbf9c2b 591 strcpy(news + l, cmd + 7);
592 cmd = news;
e29f6e02 593 added_shell = 1;
3bbf9c2b 594 }
595
4633a7c4 596 /* save an extra exec if possible */
597 /* see if there are shell metacharacters in it */
598
c0c09dfd 599 if (*cmd == '.' && isSPACE(cmd[1]))
600 goto doshell;
601
602 if (strnEQ(cmd,"exec",4) && isSPACE(cmd[4]))
603 goto doshell;
604
605 for (s = cmd; *s && isALPHA(*s); s++) ; /* catch VAR=val gizmo */
606 if (*s == '=')
607 goto doshell;
608
4633a7c4 609 for (s = cmd; *s; s++) {
c0c09dfd 610 if (*s != ' ' && !isALPHA(*s) && strchr("$&*(){}[]'\";\\|?<>~`\n",*s)) {
3bbf9c2b 611 if (*s == '\n' && s[1] == '\0') {
4633a7c4 612 *s = '\0';
613 break;
a0914d8e 614 } else if (*s == '\\' && !seenspace) {
615 continue; /* Allow backslashes in names */
4633a7c4 616 }
491527d0 617 /* We do not convert this to do_spawn_ve since shell
618 should be smart enough to start itself gloriously. */
c0c09dfd 619 doshell:
760ac839 620 if (execf == EXECF_TRUEEXEC)
621 return execl(shell,shell,copt,cmd,(char*)0);
622 else if (execf == EXECF_EXEC)
623 return spawnl(P_OVERLAY,shell,shell,copt,cmd,(char*)0);
72ea3524 624 else if (execf == EXECF_SPAWN_NOWAIT)
625 return spawnl(P_NOWAIT,shell,shell,copt,cmd,(char*)0);
760ac839 626 /* In the ak code internal P_NOWAIT is P_WAIT ??? */
c0c09dfd 627 rc = result(P_WAIT,
760ac839 628 spawnl(P_NOWAIT,shell,shell,copt,cmd,(char*)0));
c0c09dfd 629 if (rc < 0 && dowarn)
760ac839 630 warn("Can't %s \"%s\": %s",
631 (execf == EXECF_SPAWN ? "spawn" : "exec"),
632 shell, Strerror(errno));
c0c09dfd 633 if (rc < 0) rc = 255 << 8; /* Emulate the fork(). */
3bbf9c2b 634 if (news) Safefree(news);
c0c09dfd 635 return rc;
a0914d8e 636 } else if (*s == ' ' || *s == '\t') {
637 seenspace = 1;
4633a7c4 638 }
639 }
c0c09dfd 640
491527d0 641 /* cmd="a" may lead to "sh", "-c", "\"$@\"", "a", "a.cmd", NULL */
642 New(1303,Argv, (s - cmd + 11) / 2, char*);
4633a7c4 643 Cmd = savepvn(cmd, s-cmd);
644 a = Argv;
645 for (s = Cmd; *s;) {
646 while (*s && isSPACE(*s)) s++;
647 if (*s)
648 *(a++) = s;
649 while (*s && !isSPACE(*s)) s++;
650 if (*s)
651 *s++ = '\0';
652 }
653 *a = Nullch;
491527d0 654 if (Argv[0])
655 rc = do_spawn_ve(NULL, 0, execf);
656 else
4633a7c4 657 rc = -1;
3bbf9c2b 658 if (news) Safefree(news);
4633a7c4 659 do_execfree();
660 return rc;
661}
662
760ac839 663int
664do_spawn(cmd)
665char *cmd;
666{
667 return do_spawn2(cmd, EXECF_SPAWN);
668}
669
72ea3524 670int
671do_spawn_nowait(cmd)
672char *cmd;
673{
674 return do_spawn2(cmd, EXECF_SPAWN_NOWAIT);
675}
676
760ac839 677bool
678do_exec(cmd)
679char *cmd;
680{
681 return do_spawn2(cmd, EXECF_EXEC);
682}
683
684bool
685os2exec(cmd)
686char *cmd;
687{
688 return do_spawn2(cmd, EXECF_TRUEEXEC);
689}
690
3bbf9c2b 691PerlIO *
692my_syspopen(cmd,mode)
c0c09dfd 693char *cmd;
694char *mode;
695{
72ea3524 696#ifndef USE_POPEN
697
698 int p[2];
699 register I32 this, that, newfd;
700 register I32 pid, rc;
3bbf9c2b 701 PerlIO *res;
702 SV *sv;
72ea3524 703
72ea3524 704 /* `this' is what we use in the parent, `that' in the child. */
705 this = (*mode == 'w');
706 that = !this;
707 if (tainting) {
708 taint_env();
709 taint_proper("Insecure %s%s", "EXEC");
710 }
c2267164 711 if (pipe(p) < 0)
712 return Nullfp;
72ea3524 713 /* Now we need to spawn the child. */
714 newfd = dup(*mode == 'r'); /* Preserve std* */
715 if (p[that] != (*mode == 'r')) {
716 dup2(p[that], *mode == 'r');
717 close(p[that]);
718 }
719 /* Where is `this' and newfd now? */
720 fcntl(p[this], F_SETFD, FD_CLOEXEC);
721 fcntl(newfd, F_SETFD, FD_CLOEXEC);
722 pid = do_spawn_nowait(cmd);
723 if (newfd != (*mode == 'r')) {
724 dup2(newfd, *mode == 'r'); /* Return std* back. */
725 close(newfd);
726 }
491527d0 727 if (p[that] == (*mode == 'r'))
728 close(p[that]);
72ea3524 729 if (pid == -1) {
730 close(p[this]);
731 return NULL;
732 }
733 if (p[that] < p[this]) {
734 dup2(p[this], p[that]);
735 close(p[this]);
736 p[this] = p[that];
737 }
738 sv = *av_fetch(fdpid,p[this],TRUE);
739 (void)SvUPGRADE(sv,SVt_IV);
740 SvIVX(sv) = pid;
741 forkprocess = pid;
742 return PerlIO_fdopen(p[this], mode);
3bbf9c2b 743
72ea3524 744#else /* USE_POPEN */
745
746 PerlIO *res;
747 SV *sv;
748
749# ifdef TRYSHELL
3bbf9c2b 750 res = popen(cmd, mode);
72ea3524 751# else
c0c09dfd 752 char *shell = getenv("EMXSHELL");
3bbf9c2b 753
ff68c719 754 my_setenv("EMXSHELL", sh_path);
c0c09dfd 755 res = popen(cmd, mode);
756 my_setenv("EMXSHELL", shell);
72ea3524 757# endif
3bbf9c2b 758 sv = *av_fetch(fdpid, PerlIO_fileno(res), TRUE);
759 (void)SvUPGRADE(sv,SVt_IV);
760 SvIVX(sv) = -1; /* A cooky. */
761 return res;
72ea3524 762
763#endif /* USE_POPEN */
764
c0c09dfd 765}
766
3bbf9c2b 767/******************************************************************/
4633a7c4 768
769#ifndef HAS_FORK
770int
771fork(void)
772{
773 die(no_func, "Unsupported function fork");
774 errno = EINVAL;
775 return -1;
776}
777#endif
778
3bbf9c2b 779/*******************************************************************/
4633a7c4 780/* not implemented in EMX 0.9a */
781
782void * ctermid(x) { return 0; }
eacfb5f1 783
784#ifdef MYTTYNAME /* was not in emx0.9a */
4633a7c4 785void * ttyname(x) { return 0; }
eacfb5f1 786#endif
4633a7c4 787
3bbf9c2b 788/******************************************************************/
760ac839 789/* my socket forwarders - EMX lib only provides static forwarders */
790
791static HMODULE htcp = 0;
792
793static void *
794tcp0(char *name)
795{
796 static BYTE buf[20];
797 PFN fcn;
55497cff 798
799 if (!(_emx_env & 0x200)) croak("%s requires OS/2", name); /* Die if not OS/2. */
760ac839 800 if (!htcp)
801 DosLoadModule(buf, sizeof buf, "tcp32dll", &htcp);
802 if (htcp && DosQueryProcAddr(htcp, 0, name, &fcn) == 0)
803 return (void *) ((void * (*)(void)) fcn) ();
804 return 0;
805}
806
807static void
808tcp1(char *name, int arg)
809{
810 static BYTE buf[20];
811 PFN fcn;
55497cff 812
813 if (!(_emx_env & 0x200)) croak("%s requires OS/2", name); /* Die if not OS/2. */
760ac839 814 if (!htcp)
815 DosLoadModule(buf, sizeof buf, "tcp32dll", &htcp);
816 if (htcp && DosQueryProcAddr(htcp, 0, name, &fcn) == 0)
817 ((void (*)(int)) fcn) (arg);
818}
819
820void * gethostent() { return tcp0("GETHOSTENT"); }
821void * getnetent() { return tcp0("GETNETENT"); }
822void * getprotoent() { return tcp0("GETPROTOENT"); }
823void * getservent() { return tcp0("GETSERVENT"); }
824void sethostent(x) { tcp1("SETHOSTENT", x); }
825void setnetent(x) { tcp1("SETNETENT", x); }
826void setprotoent(x) { tcp1("SETPROTOENT", x); }
827void setservent(x) { tcp1("SETSERVENT", x); }
828void endhostent() { tcp0("ENDHOSTENT"); }
829void endnetent() { tcp0("ENDNETENT"); }
830void endprotoent() { tcp0("ENDPROTOENT"); }
831void endservent() { tcp0("ENDSERVENT"); }
832
833/*****************************************************************************/
834/* not implemented in C Set++ */
835
836#ifndef __EMX__
837int setuid(x) { errno = EINVAL; return -1; }
838int setgid(x) { errno = EINVAL; return -1; }
839#endif
4633a7c4 840
841/*****************************************************************************/
842/* stat() hack for char/block device */
843
844#if OS2_STAT_HACK
845
846 /* First attempt used DosQueryFSAttach which crashed the system when
847 used with 5.001. Now just look for /dev/. */
848
849int
850os2_stat(char *name, struct stat *st)
851{
852 static int ino = SHRT_MAX;
853
854 if (stricmp(name, "/dev/con") != 0
855 && stricmp(name, "/dev/tty") != 0)
856 return stat(name, st);
857
858 memset(st, 0, sizeof *st);
859 st->st_mode = S_IFCHR|0666;
860 st->st_ino = (ino-- & 0x7FFF);
861 st->st_nlink = 1;
862 return 0;
863}
864
865#endif
c0c09dfd 866
760ac839 867#ifdef USE_PERL_SBRK
c0c09dfd 868
760ac839 869/* SBRK() emulation, mostly moved to malloc.c. */
c0c09dfd 870
871void *
760ac839 872sys_alloc(int size) {
873 void *got;
874 APIRET rc = DosAllocMem(&got, size, PAG_COMMIT | PAG_WRITE);
875
c0c09dfd 876 if (rc == ERROR_NOT_ENOUGH_MEMORY) {
877 return (void *) -1;
878 } else if ( rc ) die("Got an error from DosAllocMem: %li", (long)rc);
760ac839 879 return got;
c0c09dfd 880}
760ac839 881
882#endif /* USE_PERL_SBRK */
c0c09dfd 883
884/* tmp path */
885
886char *tmppath = TMPPATH1;
887
888void
889settmppath()
890{
891 char *p = getenv("TMP"), *tpath;
892 int len;
893
894 if (!p) p = getenv("TEMP");
895 if (!p) return;
896 len = strlen(p);
897 tpath = (char *)malloc(len + strlen(TMPPATH1) + 2);
898 strcpy(tpath, p);
899 tpath[len] = '/';
900 strcpy(tpath + len + 1, TMPPATH1);
901 tmppath = tpath;
902}
7a2f0d5b 903
904#include "XSUB.h"
905
906XS(XS_File__Copy_syscopy)
907{
908 dXSARGS;
909 if (items < 2 || items > 3)
910 croak("Usage: File::Copy::syscopy(src,dst,flag=0)");
911 {
912 char * src = (char *)SvPV(ST(0),na);
913 char * dst = (char *)SvPV(ST(1),na);
914 U32 flag;
915 int RETVAL, rc;
916
917 if (items < 3)
918 flag = 0;
919 else {
920 flag = (unsigned long)SvIV(ST(2));
921 }
922
6f064249 923 RETVAL = !CheckOSError(DosCopy(src, dst, flag));
7a2f0d5b 924 ST(0) = sv_newmortal();
925 sv_setiv(ST(0), (IV)RETVAL);
926 }
927 XSRETURN(1);
928}
929
6f064249 930char *
931mod2fname(sv)
932 SV *sv;
933{
934 static char fname[9];
760ac839 935 int pos = 6, len, avlen;
936 unsigned int sum = 0;
6f064249 937 AV *av;
938 SV *svp;
939 char *s;
940
941 if (!SvROK(sv)) croak("Not a reference given to mod2fname");
942 sv = SvRV(sv);
943 if (SvTYPE(sv) != SVt_PVAV)
944 croak("Not array reference given to mod2fname");
760ac839 945
946 avlen = av_len((AV*)sv);
947 if (avlen < 0)
6f064249 948 croak("Empty array reference given to mod2fname");
760ac839 949
950 s = SvPV(*av_fetch((AV*)sv, avlen, FALSE), na);
6f064249 951 strncpy(fname, s, 8);
760ac839 952 len = strlen(s);
953 if (len < 6) pos = len;
954 while (*s) {
955 sum = 33 * sum + *(s++); /* Checksumming first chars to
956 * get the capitalization into c.s. */
957 }
958 avlen --;
959 while (avlen >= 0) {
960 s = SvPV(*av_fetch((AV*)sv, avlen, FALSE), na);
961 while (*s) {
962 sum = 33 * sum + *(s++); /* 7 is primitive mod 13. */
963 }
964 avlen --;
965 }
6ee623d5 966#ifdef USE_THREADS
967 sum++; /* Avoid conflict of DLLs in memory. */
968#endif
760ac839 969 fname[pos] = 'A' + (sum % 26);
970 fname[pos + 1] = 'A' + (sum / 26 % 26);
971 fname[pos + 2] = '\0';
6f064249 972 return (char *)fname;
973}
974
975XS(XS_DynaLoader_mod2fname)
976{
977 dXSARGS;
978 if (items != 1)
979 croak("Usage: DynaLoader::mod2fname(sv)");
980 {
981 SV * sv = ST(0);
982 char * RETVAL;
983
984 RETVAL = mod2fname(sv);
985 ST(0) = sv_newmortal();
986 sv_setpv((SV*)ST(0), RETVAL);
987 }
988 XSRETURN(1);
989}
990
991char *
992os2error(int rc)
993{
994 static char buf[300];
995 ULONG len;
996
55497cff 997 if (!(_emx_env & 0x200)) return ""; /* Nop if not OS/2. */
6f064249 998 if (rc == 0)
999 return NULL;
1000 if (DosGetMessage(NULL, 0, buf, sizeof buf - 1, rc, "OSO001.MSG", &len))
1001 sprintf(buf, "OS/2 system error code %d=0x%x", rc, rc);
1002 else
1003 buf[len] = '\0';
1004 return buf;
1005}
1006
760ac839 1007char *
1008perllib_mangle(char *s, unsigned int l)
1009{
1010 static char *newp, *oldp;
1011 static int newl, oldl, notfound;
1012 static char ret[STATIC_FILE_LENGTH+1];
1013
1014 if (!newp && !notfound) {
1015 newp = getenv("PERLLIB_PREFIX");
1016 if (newp) {
ff68c719 1017 char *s;
1018
760ac839 1019 oldp = newp;
89078e0f 1020 while (*newp && !isSPACE(*newp) && *newp != ';') {
760ac839 1021 newp++; oldl++; /* Skip digits. */
1022 }
1023 while (*newp && (isSPACE(*newp) || *newp == ';')) {
1024 newp++; /* Skip whitespace. */
1025 }
1026 newl = strlen(newp);
1027 if (newl == 0 || oldl == 0) {
1028 die("Malformed PERLLIB_PREFIX");
1029 }
ff68c719 1030 strcpy(ret, newp);
1031 s = ret;
1032 while (*s) {
1033 if (*s == '\\') *s = '/';
1034 s++;
1035 }
760ac839 1036 } else {
1037 notfound = 1;
1038 }
1039 }
1040 if (!newp) {
1041 return s;
1042 }
1043 if (l == 0) {
1044 l = strlen(s);
1045 }
3bbf9c2b 1046 if (l < oldl || strnicmp(oldp, s, oldl) != 0) {
760ac839 1047 return s;
1048 }
1049 if (l + newl - oldl > STATIC_FILE_LENGTH || newl > STATIC_FILE_LENGTH) {
1050 die("Malformed PERLLIB_PREFIX");
1051 }
89078e0f 1052 strcpy(ret + newl, s + oldl);
760ac839 1053 return ret;
1054}
6f064249 1055
1056extern void dlopen();
1057void *fakedl = &dlopen; /* Pull in dynaloading part. */
3bbf9c2b 1058
1059#define sys_is_absolute(path) ( isALPHA((path)[0]) && (path)[1] == ':' \
1060 && ((path)[2] == '/' || (path)[2] == '\\'))
1061#define sys_is_rooted _fnisabs
1062#define sys_is_relative _fnisrel
1063#define current_drive _getdrive
1064
1065#undef chdir /* Was _chdir2. */
1066#define sys_chdir(p) (chdir(p) == 0)
1067#define change_drive(d) (_chdrive(d), (current_drive() == toupper(d)))
1068
1069XS(XS_Cwd_current_drive)
1070{
1071 dXSARGS;
1072 if (items != 0)
1073 croak("Usage: Cwd::current_drive()");
1074 {
1075 char RETVAL;
1076
1077 RETVAL = current_drive();
1078 ST(0) = sv_newmortal();
1079 sv_setpvn(ST(0), (char *)&RETVAL, 1);
1080 }
1081 XSRETURN(1);
1082}
1083
1084XS(XS_Cwd_sys_chdir)
1085{
1086 dXSARGS;
1087 if (items != 1)
1088 croak("Usage: Cwd::sys_chdir(path)");
1089 {
1090 char * path = (char *)SvPV(ST(0),na);
1091 bool RETVAL;
1092
1093 RETVAL = sys_chdir(path);
54310121 1094 ST(0) = boolSV(RETVAL);
3bbf9c2b 1095 if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
1096 }
1097 XSRETURN(1);
1098}
1099
1100XS(XS_Cwd_change_drive)
1101{
1102 dXSARGS;
1103 if (items != 1)
1104 croak("Usage: Cwd::change_drive(d)");
1105 {
1106 char d = (char)*SvPV(ST(0),na);
1107 bool RETVAL;
1108
1109 RETVAL = change_drive(d);
54310121 1110 ST(0) = boolSV(RETVAL);
3bbf9c2b 1111 if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
1112 }
1113 XSRETURN(1);
1114}
1115
1116XS(XS_Cwd_sys_is_absolute)
1117{
1118 dXSARGS;
1119 if (items != 1)
1120 croak("Usage: Cwd::sys_is_absolute(path)");
1121 {
1122 char * path = (char *)SvPV(ST(0),na);
1123 bool RETVAL;
1124
1125 RETVAL = sys_is_absolute(path);
54310121 1126 ST(0) = boolSV(RETVAL);
3bbf9c2b 1127 if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
1128 }
1129 XSRETURN(1);
1130}
1131
1132XS(XS_Cwd_sys_is_rooted)
1133{
1134 dXSARGS;
1135 if (items != 1)
1136 croak("Usage: Cwd::sys_is_rooted(path)");
1137 {
1138 char * path = (char *)SvPV(ST(0),na);
1139 bool RETVAL;
1140
1141 RETVAL = sys_is_rooted(path);
54310121 1142 ST(0) = boolSV(RETVAL);
3bbf9c2b 1143 if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
1144 }
1145 XSRETURN(1);
1146}
1147
1148XS(XS_Cwd_sys_is_relative)
1149{
1150 dXSARGS;
1151 if (items != 1)
1152 croak("Usage: Cwd::sys_is_relative(path)");
1153 {
1154 char * path = (char *)SvPV(ST(0),na);
1155 bool RETVAL;
1156
1157 RETVAL = sys_is_relative(path);
54310121 1158 ST(0) = boolSV(RETVAL);
3bbf9c2b 1159 if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
1160 }
1161 XSRETURN(1);
1162}
1163
1164XS(XS_Cwd_sys_cwd)
1165{
1166 dXSARGS;
1167 if (items != 0)
1168 croak("Usage: Cwd::sys_cwd()");
1169 {
1170 char p[MAXPATHLEN];
1171 char * RETVAL;
1172 RETVAL = _getcwd2(p, MAXPATHLEN);
1173 ST(0) = sv_newmortal();
1174 sv_setpv((SV*)ST(0), RETVAL);
1175 }
1176 XSRETURN(1);
1177}
1178
1179XS(XS_Cwd_sys_abspath)
1180{
1181 dXSARGS;
1182 if (items < 1 || items > 2)
1183 croak("Usage: Cwd::sys_abspath(path, dir = NULL)");
1184 {
1185 char * path = (char *)SvPV(ST(0),na);
1186 char * dir;
1187 char p[MAXPATHLEN];
1188 char * RETVAL;
1189
1190 if (items < 2)
1191 dir = NULL;
1192 else {
1193 dir = (char *)SvPV(ST(1),na);
1194 }
1195 if (path[0] == '.' && (path[1] == '/' || path[1] == '\\')) {
1196 path += 2;
1197 }
1198 if (dir == NULL) {
1199 if (_abspath(p, path, MAXPATHLEN) == 0) {
1200 RETVAL = p;
1201 } else {
1202 RETVAL = NULL;
1203 }
1204 } else {
1205 /* Absolute with drive: */
1206 if ( sys_is_absolute(path) ) {
1207 if (_abspath(p, path, MAXPATHLEN) == 0) {
1208 RETVAL = p;
1209 } else {
1210 RETVAL = NULL;
1211 }
1212 } else if (path[0] == '/' || path[0] == '\\') {
1213 /* Rooted, but maybe on different drive. */
1214 if (isALPHA(dir[0]) && dir[1] == ':' ) {
1215 char p1[MAXPATHLEN];
1216
1217 /* Need to prepend the drive. */
1218 p1[0] = dir[0];
1219 p1[1] = dir[1];
1220 Copy(path, p1 + 2, strlen(path) + 1, char);
1221 RETVAL = p;
1222 if (_abspath(p, p1, MAXPATHLEN) == 0) {
1223 RETVAL = p;
1224 } else {
1225 RETVAL = NULL;
1226 }
1227 } else if (_abspath(p, path, MAXPATHLEN) == 0) {
1228 RETVAL = p;
1229 } else {
1230 RETVAL = NULL;
1231 }
1232 } else {
1233 /* Either path is relative, or starts with a drive letter. */
1234 /* If the path starts with a drive letter, then dir is
1235 relevant only if
1236 a/b) it is absolute/x:relative on the same drive.
1237 c) path is on current drive, and dir is rooted
1238 In all the cases it is safe to drop the drive part
1239 of the path. */
1240 if ( !sys_is_relative(path) ) {
1241 int is_drived;
1242
1243 if ( ( ( sys_is_absolute(dir)
1244 || (isALPHA(dir[0]) && dir[1] == ':'
1245 && strnicmp(dir, path,1) == 0))
1246 && strnicmp(dir, path,1) == 0)
1247 || ( !(isALPHA(dir[0]) && dir[1] == ':')
1248 && toupper(path[0]) == current_drive())) {
1249 path += 2;
1250 } else if (_abspath(p, path, MAXPATHLEN) == 0) {
1251 RETVAL = p; goto done;
1252 } else {
1253 RETVAL = NULL; goto done;
1254 }
1255 }
1256 {
1257 /* Need to prepend the absolute path of dir. */
1258 char p1[MAXPATHLEN];
1259
1260 if (_abspath(p1, dir, MAXPATHLEN) == 0) {
1261 int l = strlen(p1);
1262
1263 if (p1[ l - 1 ] != '/') {
1264 p1[ l ] = '/';
1265 l++;
1266 }
1267 Copy(path, p1 + l, strlen(path) + 1, char);
1268 if (_abspath(p, p1, MAXPATHLEN) == 0) {
1269 RETVAL = p;
1270 } else {
1271 RETVAL = NULL;
1272 }
1273 } else {
1274 RETVAL = NULL;
1275 }
1276 }
1277 done:
1278 }
1279 }
1280 ST(0) = sv_newmortal();
1281 sv_setpv((SV*)ST(0), RETVAL);
1282 }
1283 XSRETURN(1);
1284}
72ea3524 1285typedef APIRET (*PELP)(PSZ path, ULONG type);
1286
1287APIRET
1288ExtLIBPATH(ULONG ord, PSZ path, ULONG type)
1289{
1290 loadByOrd(ord); /* Guarantied to load or die! */
1291 return (*(PELP)ExtFCN[ord])(path, type);
1292}
3bbf9c2b 1293
72ea3524 1294#define extLibpath(type) \
1295 (CheckOSError(ExtLIBPATH(ORD_QUERY_ELP, to, ((type) ? END_LIBPATH \
1296 : BEGIN_LIBPATH))) \
3bbf9c2b 1297 ? NULL : to )
1298
1299#define extLibpath_set(p,type) \
72ea3524 1300 (!CheckOSError(ExtLIBPATH(ORD_SET_ELP, (p), ((type) ? END_LIBPATH \
1301 : BEGIN_LIBPATH))))
3bbf9c2b 1302
1303XS(XS_Cwd_extLibpath)
1304{
1305 dXSARGS;
1306 if (items < 0 || items > 1)
1307 croak("Usage: Cwd::extLibpath(type = 0)");
1308 {
1309 bool type;
1310 char to[1024];
1311 U32 rc;
1312 char * RETVAL;
1313
1314 if (items < 1)
1315 type = 0;
1316 else {
1317 type = (int)SvIV(ST(0));
1318 }
1319
1320 RETVAL = extLibpath(type);
1321 ST(0) = sv_newmortal();
1322 sv_setpv((SV*)ST(0), RETVAL);
1323 }
1324 XSRETURN(1);
1325}
1326
1327XS(XS_Cwd_extLibpath_set)
1328{
1329 dXSARGS;
1330 if (items < 1 || items > 2)
1331 croak("Usage: Cwd::extLibpath_set(s, type = 0)");
1332 {
1333 char * s = (char *)SvPV(ST(0),na);
1334 bool type;
1335 U32 rc;
1336 bool RETVAL;
1337
1338 if (items < 2)
1339 type = 0;
1340 else {
1341 type = (int)SvIV(ST(1));
1342 }
1343
1344 RETVAL = extLibpath_set(s, type);
54310121 1345 ST(0) = boolSV(RETVAL);
3bbf9c2b 1346 if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
1347 }
1348 XSRETURN(1);
1349}
1350
1351int
1352Xs_OS2_init()
1353{
1354 char *file = __FILE__;
1355 {
1356 GV *gv;
55497cff 1357
1358 if (_emx_env & 0x200) { /* OS/2 */
1359 newXS("File::Copy::syscopy", XS_File__Copy_syscopy, file);
1360 newXS("Cwd::extLibpath", XS_Cwd_extLibpath, file);
1361 newXS("Cwd::extLibpath_set", XS_Cwd_extLibpath_set, file);
1362 }
3bbf9c2b 1363 newXS("DynaLoader::mod2fname", XS_DynaLoader_mod2fname, file);
1364 newXS("Cwd::current_drive", XS_Cwd_current_drive, file);
1365 newXS("Cwd::sys_chdir", XS_Cwd_sys_chdir, file);
1366 newXS("Cwd::change_drive", XS_Cwd_change_drive, file);
1367 newXS("Cwd::sys_is_absolute", XS_Cwd_sys_is_absolute, file);
1368 newXS("Cwd::sys_is_rooted", XS_Cwd_sys_is_rooted, file);
1369 newXS("Cwd::sys_is_relative", XS_Cwd_sys_is_relative, file);
1370 newXS("Cwd::sys_cwd", XS_Cwd_sys_cwd, file);
1371 newXS("Cwd::sys_abspath", XS_Cwd_sys_abspath, file);
3bbf9c2b 1372 gv = gv_fetchpv("OS2::is_aout", TRUE, SVt_PV);
1373 GvMULTI_on(gv);
1374#ifdef PERL_IS_AOUT
1375 sv_setiv(GvSV(gv), 1);
1376#endif
1377 }
1378}
1379
1380OS2_Perl_data_t OS2_Perl_data;
1381
1382void
aa689395 1383Perl_OS2_init(char **env)
3bbf9c2b 1384{
1385 char *shell;
1386
18f739ee 1387 MALLOC_INIT;
3bbf9c2b 1388 settmppath();
1389 OS2_Perl_data.xs_init = &Xs_OS2_init;
aa689395 1390 if (environ == NULL) {
1391 environ = env;
1392 }
3bbf9c2b 1393 if ( (shell = getenv("PERL_SH_DRIVE")) ) {
fc36a67e 1394 New(1304, sh_path, strlen(SH_PATH) + 1, char);
ff68c719 1395 strcpy(sh_path, SH_PATH);
3bbf9c2b 1396 sh_path[0] = shell[0];
1397 } else if ( (shell = getenv("PERL_SH_DIR")) ) {
ff68c719 1398 int l = strlen(shell), i;
3bbf9c2b 1399 if (shell[l-1] == '/' || shell[l-1] == '\\') {
1400 l--;
1401 }
fc36a67e 1402 New(1304, sh_path, l + 8, char);
3bbf9c2b 1403 strncpy(sh_path, shell, l);
1404 strcpy(sh_path + l, "/sh.exe");
ff68c719 1405 for (i = 0; i < l; i++) {
1406 if (sh_path[i] == '\\') sh_path[i] = '/';
1407 }
3bbf9c2b 1408 }
dd96f567 1409 MUTEX_INIT(&start_thread_mutex);
3bbf9c2b 1410}
1411
55497cff 1412#undef tmpnam
1413#undef tmpfile
1414
1415char *
1416my_tmpnam (char *str)
1417{
1418 char *p = getenv("TMP"), *tpath;
1419 int len;
1420
1421 if (!p) p = getenv("TEMP");
1422 tpath = tempnam(p, "pltmp");
1423 if (str && tpath) {
1424 strcpy(str, tpath);
1425 return str;
1426 }
1427 return tpath;
1428}
1429
1430FILE *
1431my_tmpfile ()
1432{
1433 struct stat s;
1434
1435 stat(".", &s);
1436 if (s.st_mode & S_IWOTH) {
1437 return tmpfile();
1438 }
1439 return fopen(my_tmpnam(NULL), "w+b"); /* Race condition, but
1440 grants TMP. */
1441}
367f3c24 1442
1443#undef flock
1444
1445/* This code was contributed by Rocco Caputo. */
1446int
dd96f567 1447my_flock(int handle, int o)
367f3c24 1448{
1449 FILELOCK rNull, rFull;
1450 ULONG timeout, handle_type, flag_word;
1451 APIRET rc;
1452 int blocking, shared;
1453 static int use_my = -1;
1454
1455 if (use_my == -1) {
1456 char *s = getenv("USE_PERL_FLOCK");
1457 if (s)
1458 use_my = atoi(s);
1459 else
1460 use_my = 1;
1461 }
1462 if (!(_emx_env & 0x200) || !use_my)
dd96f567 1463 return flock(handle, o); /* Delegate to EMX. */
367f3c24 1464
1465 // is this a file?
1466 if ((DosQueryHType(handle, &handle_type, &flag_word) != 0) ||
1467 (handle_type & 0xFF))
1468 {
1469 errno = EBADF;
1470 return -1;
1471 }
1472 // set lock/unlock ranges
1473 rNull.lOffset = rNull.lRange = rFull.lOffset = 0;
1474 rFull.lRange = 0x7FFFFFFF;
1475 // set timeout for blocking
dd96f567 1476 timeout = ((blocking = !(o & LOCK_NB))) ? 100 : 1;
367f3c24 1477 // shared or exclusive?
dd96f567 1478 shared = (o & LOCK_SH) ? 1 : 0;
367f3c24 1479 // do not block the unlock
dd96f567 1480 if (o & (LOCK_UN | LOCK_SH | LOCK_EX)) {
367f3c24 1481 rc = DosSetFileLocks(handle, &rFull, &rNull, timeout, shared);
1482 switch (rc) {
1483 case 0:
1484 errno = 0;
1485 return 0;
1486 case ERROR_INVALID_HANDLE:
1487 errno = EBADF;
1488 return -1;
1489 case ERROR_SHARING_BUFFER_EXCEEDED:
1490 errno = ENOLCK;
1491 return -1;
1492 case ERROR_LOCK_VIOLATION:
1493 break; // not an error
1494 case ERROR_INVALID_PARAMETER:
1495 case ERROR_ATOMIC_LOCK_NOT_SUPPORTED:
1496 case ERROR_READ_LOCKS_NOT_SUPPORTED:
1497 errno = EINVAL;
1498 return -1;
1499 case ERROR_INTERRUPT:
1500 errno = EINTR;
1501 return -1;
1502 default:
1503 errno = EINVAL;
1504 return -1;
1505 }
1506 }
1507 // lock may block
dd96f567 1508 if (o & (LOCK_SH | LOCK_EX)) {
367f3c24 1509 // for blocking operations
1510 for (;;) {
1511 rc =
1512 DosSetFileLocks(
1513 handle,
1514 &rNull,
1515 &rFull,
1516 timeout,
1517 shared
1518 );
1519 switch (rc) {
1520 case 0:
1521 errno = 0;
1522 return 0;
1523 case ERROR_INVALID_HANDLE:
1524 errno = EBADF;
1525 return -1;
1526 case ERROR_SHARING_BUFFER_EXCEEDED:
1527 errno = ENOLCK;
1528 return -1;
1529 case ERROR_LOCK_VIOLATION:
1530 if (!blocking) {
1531 errno = EWOULDBLOCK;
1532 return -1;
1533 }
1534 break;
1535 case ERROR_INVALID_PARAMETER:
1536 case ERROR_ATOMIC_LOCK_NOT_SUPPORTED:
1537 case ERROR_READ_LOCKS_NOT_SUPPORTED:
1538 errno = EINVAL;
1539 return -1;
1540 case ERROR_INTERRUPT:
1541 errno = EINTR;
1542 return -1;
1543 default:
1544 errno = EINVAL;
1545 return -1;
1546 }
1547 // give away timeslice
1548 DosSleep(1);
1549 }
1550 }
1551
1552 errno = 0;
1553 return 0;
1554}