Integrate mainperl.
[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;
3aefca04 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;
3aefca04 49perl_mutex start_thread_mutex;
dd96f567 50
51int
3aefca04 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
3aefca04 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
3aefca04 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
3aefca04 160os2_cond_wait(perl_cond *c, perl_mutex *m)
dd96f567 161{
162 int rc;
6b88bc9c 163 if ((rc = DosResetEventSem(*c,&PL_na)) && (rc != ERROR_ALREADY_RESET))
dd96f567 164 croak("panic: COND_WAIT-reset: rc=%i", rc);
165 if (m) MUTEX_UNLOCK(m);
91643db9 166 if (CheckOSError(DosWaitEventSem(*c,SEM_INDEFINITE_WAIT))
167 && (rc != ERROR_INTERRUPT))
dd96f567 168 croak("panic: COND_WAIT: rc=%i", rc);
91643db9 169 if (rc == ERROR_INTERRUPT)
170 errno = EINTR;
dd96f567 171 if (m) MUTEX_LOCK(m);
172}
173#endif
174
4633a7c4 175/*****************************************************************************/
72ea3524 176/* 2.1 would not resolve symbols on demand, and has no ExtLIBPATH. */
177static PFN ExtFCN[2]; /* Labeled by ord below. */
178static USHORT loadOrd[2] = { 874, 873 }; /* Query=874, Set=873. */
179#define ORD_QUERY_ELP 0
180#define ORD_SET_ELP 1
181
182APIRET
183loadByOrd(ULONG ord)
184{
185 if (ExtFCN[ord] == NULL) {
186 static HMODULE hdosc = 0;
187 BYTE buf[20];
188 PFN fcn;
189 APIRET rc;
190
191 if ((!hdosc && CheckOSError(DosLoadModule(buf, sizeof buf,
192 "doscalls", &hdosc)))
193 || CheckOSError(DosQueryProcAddr(hdosc, loadOrd[ord], NULL, &fcn)))
194 die("This version of OS/2 does not support doscalls.%i",
195 loadOrd[ord]);
196 ExtFCN[ord] = fcn;
197 }
198 if ((long)ExtFCN[ord] == -1) die("panic queryaddr");
199}
200
4633a7c4 201/* priorities */
6f064249 202static signed char priors[] = {0, 1, 3, 2}; /* Last two interchanged,
203 self inverse. */
204#define QSS_INI_BUFFER 1024
4633a7c4 205
6f064249 206PQTOPLEVEL
207get_sysinfo(ULONG pid, ULONG flags)
4633a7c4 208{
6f064249 209 char *pbuffer;
210 ULONG rc, buf_len = QSS_INI_BUFFER;
211
fc36a67e 212 New(1322, pbuffer, buf_len, char);
6f064249 213 /* QSS_PROCESS | QSS_MODULE | QSS_SEMAPHORES | QSS_SHARED */
214 rc = QuerySysState(flags, pid, pbuffer, buf_len);
215 while (rc == ERROR_BUFFER_OVERFLOW) {
216 Renew(pbuffer, buf_len *= 2, char);
df3ef7a9 217 rc = QuerySysState(flags, pid, pbuffer, buf_len);
6f064249 218 }
219 if (rc) {
220 FillOSError(rc);
221 Safefree(pbuffer);
222 return 0;
223 }
224 return (PQTOPLEVEL)pbuffer;
225}
226
227#define PRIO_ERR 0x1111
228
229static ULONG
230sys_prio(pid)
231{
232 ULONG prio;
233 PQTOPLEVEL psi;
234
235 psi = get_sysinfo(pid, QSS_PROCESS);
236 if (!psi) {
237 return PRIO_ERR;
238 }
239 if (pid != psi->procdata->pid) {
240 Safefree(psi);
241 croak("panic: wrong pid in sysinfo");
242 }
243 prio = psi->procdata->threads->priority;
244 Safefree(psi);
245 return prio;
246}
247
248int
249setpriority(int which, int pid, int val)
250{
251 ULONG rc, prio;
252 PQTOPLEVEL psi;
253
254 prio = sys_prio(pid);
255
55497cff 256 if (!(_emx_env & 0x200)) return 0; /* Nop if not OS/2. */
6f064249 257 if (priors[(32 - val) >> 5] + 1 == (prio >> 8)) {
258 /* Do not change class. */
259 return CheckOSError(DosSetPriority((pid < 0)
260 ? PRTYS_PROCESSTREE : PRTYS_PROCESS,
261 0,
262 (32 - val) % 32 - (prio & 0xFF),
263 abs(pid)))
264 ? -1 : 0;
265 } else /* if ((32 - val) % 32 == (prio & 0xFF)) */ {
266 /* Documentation claims one can change both class and basevalue,
267 * but I find it wrong. */
268 /* Change class, but since delta == 0 denotes absolute 0, correct. */
269 if (CheckOSError(DosSetPriority((pid < 0)
270 ? PRTYS_PROCESSTREE : PRTYS_PROCESS,
271 priors[(32 - val) >> 5] + 1,
272 0,
273 abs(pid))))
274 return -1;
275 if ( ((32 - val) % 32) == 0 ) return 0;
276 return CheckOSError(DosSetPriority((pid < 0)
277 ? PRTYS_PROCESSTREE : PRTYS_PROCESS,
278 0,
279 (32 - val) % 32,
280 abs(pid)))
281 ? -1 : 0;
282 }
283/* else return CheckOSError(DosSetPriority((pid < 0) */
284/* ? PRTYS_PROCESSTREE : PRTYS_PROCESS, */
285/* priors[(32 - val) >> 5] + 1, */
286/* (32 - val) % 32 - (prio & 0xFF), */
287/* abs(pid))) */
288/* ? -1 : 0; */
4633a7c4 289}
290
6f064249 291int
292getpriority(int which /* ignored */, int pid)
4633a7c4 293{
294 TIB *tib;
295 PIB *pib;
6f064249 296 ULONG rc, ret;
297
55497cff 298 if (!(_emx_env & 0x200)) return 0; /* Nop if not OS/2. */
6f064249 299 /* DosGetInfoBlocks has old priority! */
300/* if (CheckOSError(DosGetInfoBlocks(&tib, &pib))) return -1; */
301/* if (pid != pib->pib_ulpid) { */
302 ret = sys_prio(pid);
303 if (ret == PRIO_ERR) {
304 return -1;
305 }
306/* } else */
307/* ret = tib->tib_ptib2->tib2_ulpri; */
308 return (1 - priors[((ret >> 8) - 1)])*32 - (ret & 0xFF);
4633a7c4 309}
310
311/*****************************************************************************/
312/* spawn */
2c2e0e8c 313
314/* There is no big sense to make it thread-specific, since signals
315 are delivered to thread 1 only. XXXX Maybe make it into an array? */
316static int spawn_pid;
317static int spawn_killed;
318
319static Signal_t
320spawn_sighandler(int sig)
321{
322 /* Some programs do not arrange for the keyboard signals to be
323 delivered to them. We need to deliver the signal manually. */
324 /* We may get a signal only if
325 a) kid does not receive keyboard signal: deliver it;
326 b) kid already died, and we get a signal. We may only hope
327 that the pid number was not reused.
328 */
329
330 if (spawn_killed)
331 sig = SIGKILL; /* Try harder. */
332 kill(spawn_pid, sig);
333 spawn_killed = 1;
334}
72ea3524 335
4633a7c4 336static int
337result(int flag, int pid)
338{
339 int r, status;
340 Signal_t (*ihand)(); /* place to save signal during system() */
341 Signal_t (*qhand)(); /* place to save signal during system() */
760ac839 342#ifndef __EMX__
343 RESULTCODES res;
344 int rpid;
345#endif
4633a7c4 346
760ac839 347 if (pid < 0 || flag != 0)
4633a7c4 348 return pid;
349
760ac839 350#ifdef __EMX__
2c2e0e8c 351 spawn_pid = pid;
352 spawn_killed = 0;
353 ihand = rsignal(SIGINT, &spawn_sighandler);
354 qhand = rsignal(SIGQUIT, &spawn_sighandler);
c0c09dfd 355 do {
356 r = wait4pid(pid, &status, 0);
357 } while (r == -1 && errno == EINTR);
72ea3524 358 rsignal(SIGINT, ihand);
359 rsignal(SIGQUIT, qhand);
4633a7c4 360
6b88bc9c 361 PL_statusvalue = (U16)status;
4633a7c4 362 if (r < 0)
363 return -1;
364 return status & 0xFFFF;
760ac839 365#else
72ea3524 366 ihand = rsignal(SIGINT, SIG_IGN);
760ac839 367 r = DosWaitChild(DCWA_PROCESS, DCWW_WAIT, &res, &rpid, pid);
72ea3524 368 rsignal(SIGINT, ihand);
6b88bc9c 369 PL_statusvalue = res.codeResult << 8 | res.codeTerminate;
760ac839 370 if (r)
371 return -1;
6b88bc9c 372 return PL_statusvalue;
760ac839 373#endif
4633a7c4 374}
375
491527d0 376#define EXECF_SPAWN 0
377#define EXECF_EXEC 1
378#define EXECF_TRUEEXEC 2
379#define EXECF_SPAWN_NOWAIT 3
380
017f25f1 381/* const char* const ptypes[] = { "FS", "DOS", "VIO", "PM", "DETACH" }; */
382
383static int
384my_type()
385{
386 int rc;
387 TIB *tib;
388 PIB *pib;
389
390 if (!(_emx_env & 0x200)) return 1; /* not OS/2. */
391 if (CheckOSError(DosGetInfoBlocks(&tib, &pib)))
392 return -1;
393
394 return (pib->pib_ultype);
395}
396
397static ULONG
398file_type(char *path)
399{
400 int rc;
401 ULONG apptype;
402
403 if (!(_emx_env & 0x200))
404 croak("file_type not implemented on DOS"); /* not OS/2. */
405 if (CheckOSError(DosQueryAppType(path, &apptype))) {
406 switch (rc) {
407 case ERROR_FILE_NOT_FOUND:
408 case ERROR_PATH_NOT_FOUND:
409 return -1;
410 case ERROR_ACCESS_DENIED: /* Directory with this name found? */
411 return -3;
412 default: /* Found, but not an
413 executable, or some other
414 read error. */
415 return -2;
416 }
417 }
418 return apptype;
419}
420
421static ULONG os2_mytype;
422
491527d0 423/* Spawn/exec a program, revert to shell if needed. */
6b88bc9c 424/* global PL_Argv[] contains arguments. */
491527d0 425
4633a7c4 426int
2c2e0e8c 427do_spawn_ve(really, flag, execf, inicmd)
4633a7c4 428SV *really;
491527d0 429U32 flag;
430U32 execf;
2c2e0e8c 431char *inicmd;
4633a7c4 432{
dd96f567 433 dTHR;
491527d0 434 int trueflag = flag;
a97be121 435 int rc, pass = 1;
491527d0 436 char *tmps;
b56ec344 437 char buf[256], *s = 0, scrbuf[280];
491527d0 438 char *args[4];
439 static char * fargs[4]
440 = { "/bin/sh", "-c", "\"$@\"", "spawn-via-shell", };
441 char **argsp = fargs;
442 char nargs = 4;
017f25f1 443 int force_shell;
491527d0 444
4633a7c4 445 if (flag == P_WAIT)
446 flag = P_NOWAIT;
447
491527d0 448 retry:
6b88bc9c 449 if (strEQ(PL_Argv[0],"/bin/sh"))
450 PL_Argv[0] = PL_sh_path;
3bbf9c2b 451
6b88bc9c 452 if (PL_Argv[0][0] != '/' && PL_Argv[0][0] != '\\'
453 && !(PL_Argv[0][0] && PL_Argv[0][1] == ':'
454 && (PL_Argv[0][2] == '/' || PL_Argv[0][2] != '\\'))
2c2e0e8c 455 ) /* will spawnvp use PATH? */
c0c09dfd 456 TAINT_ENV(); /* testing IFS here is overkill, probably */
760ac839 457 /* We should check PERL_SH* and PERLLIB_* as well? */
6b88bc9c 458 if (!really || !*(tmps = SvPV(really, PL_na)))
459 tmps = PL_Argv[0];
017f25f1 460
461 reread:
462 force_shell = 0;
463 if (_emx_env & 0x200) { /* OS/2. */
464 int type = file_type(tmps);
465 type_again:
466 if (type == -1) { /* Not found */
467 errno = ENOENT;
468 rc = -1;
469 goto do_script;
470 }
471 else if (type == -2) { /* Not an EXE */
472 errno = ENOEXEC;
473 rc = -1;
474 goto do_script;
475 }
476 else if (type == -3) { /* Is a directory? */
477 /* Special-case this */
478 char tbuf[512];
479 int l = strlen(tmps);
480
481 if (l + 5 <= sizeof tbuf) {
482 strcpy(tbuf, tmps);
483 strcpy(tbuf + l, ".exe");
484 type = file_type(tbuf);
485 if (type >= -3)
486 goto type_again;
487 }
488
489 errno = ENOEXEC;
490 rc = -1;
491 goto do_script;
492 }
493 switch (type & 7) {
494 /* Ignore WINDOWCOMPAT and FAPI, start them the same type we are. */
495 case FAPPTYP_WINDOWAPI:
496 {
497 if (os2_mytype != 3) { /* not PM */
498 if (flag == P_NOWAIT)
499 flag = P_PM;
500 else if ((flag & 7) != P_PM && (flag & 7) != P_SESSION)
501 warn("Starting PM process with flag=%d, mytype=%d",
502 flag, os2_mytype);
503 }
504 }
505 break;
506 case FAPPTYP_NOTWINDOWCOMPAT:
507 {
508 if (os2_mytype != 0) { /* not full screen */
509 if (flag == P_NOWAIT)
510 flag = P_SESSION;
511 else if ((flag & 7) != P_SESSION)
512 warn("Starting Full Screen process with flag=%d, mytype=%d",
513 flag, os2_mytype);
514 }
515 }
516 break;
517 case FAPPTYP_NOTSPEC:
518 /* Let the shell handle this... */
519 force_shell = 1;
520 goto doshell_args;
521 break;
522 }
523 }
524
491527d0 525#if 0
6b88bc9c 526 rc = result(trueflag, spawnvp(flag,tmps,PL_Argv));
491527d0 527#else
528 if (execf == EXECF_TRUEEXEC)
6b88bc9c 529 rc = execvp(tmps,PL_Argv);
491527d0 530 else if (execf == EXECF_EXEC)
6b88bc9c 531 rc = spawnvp(trueflag | P_OVERLAY,tmps,PL_Argv);
491527d0 532 else if (execf == EXECF_SPAWN_NOWAIT)
017f25f1 533 rc = spawnvp(flag,tmps,PL_Argv);
491527d0 534 else /* EXECF_SPAWN */
535 rc = result(trueflag,
017f25f1 536 spawnvp(flag,tmps,PL_Argv));
491527d0 537#endif
2c2e0e8c 538 if (rc < 0 && pass == 1
6b88bc9c 539 && (tmps == PL_Argv[0])) { /* Cannot transfer `really' via shell. */
017f25f1 540 do_script:
541 {
a97be121 542 int err = errno;
543
2c2e0e8c 544 if (err == ENOENT || err == ENOEXEC) {
545 /* No such file, or is a script. */
546 /* Try adding script extensions to the file name, and
547 search on PATH. */
6b88bc9c 548 char *scr = find_script(PL_Argv[0], TRUE, NULL, 0);
b56ec344 549 int l = strlen(scr);
550
551 if (l >= sizeof scrbuf) {
552 Safefree(scr);
553 longbuf:
554 croak("Size of scriptname too big: %d", l);
555 }
556 strcpy(scrbuf, scr);
557 Safefree(scr);
558 scr = scrbuf;
2c2e0e8c 559
560 if (scr) {
561 FILE *file = fopen(scr, "r");
562 char *s = 0, *s1;
563
6b88bc9c 564 PL_Argv[0] = scr;
2c2e0e8c 565 if (!file)
566 goto panic_file;
017f25f1 567 if (!fgets(buf, sizeof buf, file)) { /* Empty... */
017f25f1 568
569 buf[0] = 0;
2c2e0e8c 570 fclose(file);
017f25f1 571 /* Special case: maybe from -Zexe build, so
572 there is an executable around (contrary to
573 documentation, DosQueryAppType sometimes (?)
574 does not append ".exe", so we could have
575 reached this place). */
b56ec344 576 if (l + 5 < sizeof scrbuf) {
577 strcpy(scrbuf + l, ".exe");
578 if (PerlLIO_stat(scrbuf,&PL_statbuf) >= 0
017f25f1 579 && !S_ISDIR(PL_statbuf.st_mode)) {
580 /* Found */
581 tmps = scr;
582 pass++;
583 goto reread;
b56ec344 584 } else
585 scrbuf[l] = 0;
586 } else
587 goto longbuf;
2c2e0e8c 588 }
589 if (fclose(file) != 0) { /* Failure */
590 panic_file:
591 warn("Error reading \"%s\": %s",
592 scr, Strerror(errno));
593 buf[0] = 0; /* Not #! */
594 goto doshell_args;
595 }
596 if (buf[0] == '#') {
597 if (buf[1] == '!')
598 s = buf + 2;
599 } else if (buf[0] == 'e') {
600 if (strnEQ(buf, "extproc", 7)
601 && isSPACE(buf[7]))
602 s = buf + 8;
603 } else if (buf[0] == 'E') {
604 if (strnEQ(buf, "EXTPROC", 7)
605 && isSPACE(buf[7]))
606 s = buf + 8;
607 }
608 if (!s) {
609 buf[0] = 0; /* Not #! */
610 goto doshell_args;
611 }
612
613 s1 = s;
614 nargs = 0;
615 argsp = args;
616 while (1) {
617 /* Do better than pdksh: allow a few args,
618 strip trailing whitespace. */
619 while (isSPACE(*s))
620 s++;
621 if (*s == 0)
622 break;
623 if (nargs == 4) {
624 nargs = -1;
625 break;
626 }
627 args[nargs++] = s;
628 while (*s && !isSPACE(*s))
629 s++;
630 if (*s == 0)
631 break;
632 *s++ = 0;
633 }
634 if (nargs == -1) {
635 warn("Too many args on %.*s line of \"%s\"",
636 s1 - buf, buf, scr);
637 nargs = 4;
638 argsp = fargs;
639 }
640 doshell_args:
641 {
6b88bc9c 642 char **a = PL_Argv;
2c2e0e8c 643 char *exec_args[2];
644
017f25f1 645 if (force_shell
646 || (!buf[0] && file)) { /* File without magic */
2c2e0e8c 647 /* In fact we tried all what pdksh would
648 try. There is no point in calling
649 pdksh, we may just emulate its logic. */
650 char *shell = getenv("EXECSHELL");
651 char *shell_opt = NULL;
652
653 if (!shell) {
654 char *s;
655
656 shell_opt = "/c";
657 shell = getenv("OS2_SHELL");
658 if (inicmd) { /* No spaces at start! */
659 s = inicmd;
660 while (*s && !isSPACE(*s)) {
661 if (*s++ = '/') {
662 inicmd = NULL; /* Cannot use */
663 break;
664 }
665 }
666 }
667 if (!inicmd) {
6b88bc9c 668 s = PL_Argv[0];
2c2e0e8c 669 while (*s) {
670 /* Dosish shells will choke on slashes
671 in paths, fortunately, this is
672 important for zeroth arg only. */
673 if (*s == '/')
674 *s = '\\';
675 s++;
676 }
491527d0 677 }
491527d0 678 }
2c2e0e8c 679 /* If EXECSHELL is set, we do not set */
680
681 if (!shell)
682 shell = ((_emx_env & 0x200)
683 ? "c:/os2/cmd.exe"
684 : "c:/command.com");
685 nargs = shell_opt ? 2 : 1; /* shell file args */
686 exec_args[0] = shell;
687 exec_args[1] = shell_opt;
688 argsp = exec_args;
689 if (nargs == 2 && inicmd) {
690 /* Use the original cmd line */
691 /* XXXX This is good only until we refuse
692 quoted arguments... */
6b88bc9c 693 PL_Argv[0] = inicmd;
694 PL_Argv[1] = Nullch;
491527d0 695 }
2c2e0e8c 696 } else if (!buf[0] && inicmd) { /* No file */
697 /* Start with the original cmdline. */
698 /* XXXX This is good only until we refuse
699 quoted arguments... */
700
6b88bc9c 701 PL_Argv[0] = inicmd;
702 PL_Argv[1] = Nullch;
2c2e0e8c 703 nargs = 2; /* shell -c */
704 }
705
706 while (a[1]) /* Get to the end */
707 a++;
708 a++; /* Copy finil NULL too */
6b88bc9c 709 while (a >= PL_Argv) {
710 *(a + nargs) = *a; /* PL_Argv was preallocated to be
2c2e0e8c 711 long enough. */
712 a--;
491527d0 713 }
2c2e0e8c 714 while (nargs-- >= 0)
6b88bc9c 715 PL_Argv[nargs] = argsp[nargs];
2c2e0e8c 716 /* Enable pathless exec if #! (as pdksh). */
717 pass = (buf[0] == '#' ? 2 : 3);
718 goto retry;
e29f6e02 719 }
720 }
2c2e0e8c 721 /* Not found: restore errno */
491527d0 722 errno = err;
2c2e0e8c 723 }
017f25f1 724 }
a97be121 725 } else if (rc < 0 && pass == 2 && errno == ENOENT) { /* File not found */
6b88bc9c 726 char *no_dir = strrchr(PL_Argv[0], '/');
2c2e0e8c 727
728 /* Do as pdksh port does: if not found with /, try without
729 path. */
730 if (no_dir) {
6b88bc9c 731 PL_Argv[0] = no_dir + 1;
2c2e0e8c 732 pass++;
e29f6e02 733 goto retry;
734 }
735 }
6b88bc9c 736 if (rc < 0 && PL_dowarn)
491527d0 737 warn("Can't %s \"%s\": %s\n",
738 ((execf != EXECF_EXEC && execf != EXECF_TRUEEXEC)
739 ? "spawn" : "exec"),
a97be121 740 PL_Argv[0], Strerror(errno));
491527d0 741 if (rc < 0 && (execf != EXECF_SPAWN_NOWAIT)
742 && ((trueflag & 0xFF) == P_WAIT))
743 rc = 255 << 8; /* Emulate the fork(). */
744
745 return rc;
746}
747
2c2e0e8c 748/* Array spawn. */
491527d0 749int
750do_aspawn(really,mark,sp)
751SV *really;
752register SV **mark;
753register SV **sp;
754{
755 dTHR;
756 register char **a;
757 char *tmps = NULL;
758 int rc;
759 int flag = P_WAIT, trueflag, err, secondtry = 0;
760
761 if (sp > mark) {
6b88bc9c 762 New(1301,PL_Argv, sp - mark + 3, char*);
763 a = PL_Argv;
491527d0 764
765 if (mark < sp && SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
766 ++mark;
767 flag = SvIVx(*mark);
768 }
769
770 while (++mark <= sp) {
771 if (*mark)
6b88bc9c 772 *a++ = SvPVx(*mark, PL_na);
491527d0 773 else
774 *a++ = "";
775 }
776 *a = Nullch;
777
2c2e0e8c 778 rc = do_spawn_ve(really, flag, EXECF_SPAWN, NULL);
4633a7c4 779 } else
780 rc = -1;
781 do_execfree();
782 return rc;
783}
784
491527d0 785/* Try converting 1-arg form to (usually shell-less) multi-arg form. */
4633a7c4 786int
760ac839 787do_spawn2(cmd, execf)
4633a7c4 788char *cmd;
760ac839 789int execf;
4633a7c4 790{
791 register char **a;
792 register char *s;
793 char flags[10];
3bbf9c2b 794 char *shell, *copt, *news = NULL;
2c2e0e8c 795 int rc, err, seenspace = 0;
e29f6e02 796 char fullcmd[MAXNAMLEN + 1];
4633a7c4 797
c0c09dfd 798#ifdef TRYSHELL
799 if ((shell = getenv("EMXSHELL")) != NULL)
800 copt = "-c";
801 else if ((shell = getenv("SHELL")) != NULL)
4633a7c4 802 copt = "-c";
803 else if ((shell = getenv("COMSPEC")) != NULL)
804 copt = "/C";
805 else
806 shell = "cmd.exe";
c0c09dfd 807#else
808 /* Consensus on perl5-porters is that it is _very_ important to
809 have a shell which will not change between computers with the
810 same architecture, to avoid "action on a distance".
811 And to have simple build, this shell should be sh. */
6b88bc9c 812 shell = PL_sh_path;
c0c09dfd 813 copt = "-c";
814#endif
815
816 while (*cmd && isSPACE(*cmd))
817 cmd++;
4633a7c4 818
3bbf9c2b 819 if (strnEQ(cmd,"/bin/sh",7) && isSPACE(cmd[7])) {
6b88bc9c 820 STRLEN l = strlen(PL_sh_path);
3bbf9c2b 821
2cc2f81f 822 New(1302, news, strlen(cmd) - 7 + l + 1, char);
6b88bc9c 823 strcpy(news, PL_sh_path);
3bbf9c2b 824 strcpy(news + l, cmd + 7);
825 cmd = news;
826 }
827
4633a7c4 828 /* save an extra exec if possible */
829 /* see if there are shell metacharacters in it */
830
c0c09dfd 831 if (*cmd == '.' && isSPACE(cmd[1]))
832 goto doshell;
833
834 if (strnEQ(cmd,"exec",4) && isSPACE(cmd[4]))
835 goto doshell;
836
837 for (s = cmd; *s && isALPHA(*s); s++) ; /* catch VAR=val gizmo */
838 if (*s == '=')
839 goto doshell;
840
4633a7c4 841 for (s = cmd; *s; s++) {
c0c09dfd 842 if (*s != ' ' && !isALPHA(*s) && strchr("$&*(){}[]'\";\\|?<>~`\n",*s)) {
3bbf9c2b 843 if (*s == '\n' && s[1] == '\0') {
4633a7c4 844 *s = '\0';
845 break;
a0914d8e 846 } else if (*s == '\\' && !seenspace) {
847 continue; /* Allow backslashes in names */
4633a7c4 848 }
491527d0 849 /* We do not convert this to do_spawn_ve since shell
850 should be smart enough to start itself gloriously. */
c0c09dfd 851 doshell:
760ac839 852 if (execf == EXECF_TRUEEXEC)
2c2e0e8c 853 rc = execl(shell,shell,copt,cmd,(char*)0);
760ac839 854 else if (execf == EXECF_EXEC)
2c2e0e8c 855 rc = spawnl(P_OVERLAY,shell,shell,copt,cmd,(char*)0);
72ea3524 856 else if (execf == EXECF_SPAWN_NOWAIT)
2c2e0e8c 857 rc = spawnl(P_NOWAIT,shell,shell,copt,cmd,(char*)0);
858 else {
859 /* In the ak code internal P_NOWAIT is P_WAIT ??? */
860 rc = result(P_WAIT,
861 spawnl(P_NOWAIT,shell,shell,copt,cmd,(char*)0));
6b88bc9c 862 if (rc < 0 && PL_dowarn)
2c2e0e8c 863 warn("Can't %s \"%s\": %s",
864 (execf == EXECF_SPAWN ? "spawn" : "exec"),
865 shell, Strerror(errno));
866 if (rc < 0) rc = 255 << 8; /* Emulate the fork(). */
867 }
868 if (news)
869 Safefree(news);
c0c09dfd 870 return rc;
a0914d8e 871 } else if (*s == ' ' || *s == '\t') {
872 seenspace = 1;
4633a7c4 873 }
874 }
c0c09dfd 875
491527d0 876 /* cmd="a" may lead to "sh", "-c", "\"$@\"", "a", "a.cmd", NULL */
6b88bc9c 877 New(1303,PL_Argv, (s - cmd + 11) / 2, char*);
878 PL_Cmd = savepvn(cmd, s-cmd);
879 a = PL_Argv;
880 for (s = PL_Cmd; *s;) {
4633a7c4 881 while (*s && isSPACE(*s)) s++;
882 if (*s)
883 *(a++) = s;
884 while (*s && !isSPACE(*s)) s++;
885 if (*s)
886 *s++ = '\0';
887 }
888 *a = Nullch;
6b88bc9c 889 if (PL_Argv[0])
2c2e0e8c 890 rc = do_spawn_ve(NULL, 0, execf, cmd);
491527d0 891 else
4633a7c4 892 rc = -1;
2c2e0e8c 893 if (news)
894 Safefree(news);
4633a7c4 895 do_execfree();
896 return rc;
897}
898
760ac839 899int
900do_spawn(cmd)
901char *cmd;
902{
903 return do_spawn2(cmd, EXECF_SPAWN);
904}
905
72ea3524 906int
907do_spawn_nowait(cmd)
908char *cmd;
909{
910 return do_spawn2(cmd, EXECF_SPAWN_NOWAIT);
911}
912
760ac839 913bool
914do_exec(cmd)
915char *cmd;
916{
017f25f1 917 do_spawn2(cmd, EXECF_EXEC);
918 return FALSE;
760ac839 919}
920
921bool
922os2exec(cmd)
923char *cmd;
924{
925 return do_spawn2(cmd, EXECF_TRUEEXEC);
926}
927
3bbf9c2b 928PerlIO *
929my_syspopen(cmd,mode)
c0c09dfd 930char *cmd;
931char *mode;
932{
72ea3524 933#ifndef USE_POPEN
934
935 int p[2];
936 register I32 this, that, newfd;
937 register I32 pid, rc;
3bbf9c2b 938 PerlIO *res;
939 SV *sv;
72ea3524 940
72ea3524 941 /* `this' is what we use in the parent, `that' in the child. */
942 this = (*mode == 'w');
943 that = !this;
6b88bc9c 944 if (PL_tainting) {
72ea3524 945 taint_env();
946 taint_proper("Insecure %s%s", "EXEC");
947 }
c2267164 948 if (pipe(p) < 0)
949 return Nullfp;
72ea3524 950 /* Now we need to spawn the child. */
951 newfd = dup(*mode == 'r'); /* Preserve std* */
952 if (p[that] != (*mode == 'r')) {
953 dup2(p[that], *mode == 'r');
954 close(p[that]);
955 }
956 /* Where is `this' and newfd now? */
957 fcntl(p[this], F_SETFD, FD_CLOEXEC);
958 fcntl(newfd, F_SETFD, FD_CLOEXEC);
959 pid = do_spawn_nowait(cmd);
960 if (newfd != (*mode == 'r')) {
961 dup2(newfd, *mode == 'r'); /* Return std* back. */
962 close(newfd);
963 }
491527d0 964 if (p[that] == (*mode == 'r'))
965 close(p[that]);
72ea3524 966 if (pid == -1) {
967 close(p[this]);
968 return NULL;
969 }
970 if (p[that] < p[this]) {
971 dup2(p[this], p[that]);
972 close(p[this]);
973 p[this] = p[that];
974 }
6b88bc9c 975 sv = *av_fetch(PL_fdpid,p[this],TRUE);
72ea3524 976 (void)SvUPGRADE(sv,SVt_IV);
977 SvIVX(sv) = pid;
6b88bc9c 978 PL_forkprocess = pid;
72ea3524 979 return PerlIO_fdopen(p[this], mode);
3bbf9c2b 980
72ea3524 981#else /* USE_POPEN */
982
983 PerlIO *res;
984 SV *sv;
985
986# ifdef TRYSHELL
3bbf9c2b 987 res = popen(cmd, mode);
72ea3524 988# else
c0c09dfd 989 char *shell = getenv("EMXSHELL");
3bbf9c2b 990
6b88bc9c 991 my_setenv("EMXSHELL", PL_sh_path);
c0c09dfd 992 res = popen(cmd, mode);
993 my_setenv("EMXSHELL", shell);
72ea3524 994# endif
6b88bc9c 995 sv = *av_fetch(PL_fdpid, PerlIO_fileno(res), TRUE);
3bbf9c2b 996 (void)SvUPGRADE(sv,SVt_IV);
997 SvIVX(sv) = -1; /* A cooky. */
998 return res;
72ea3524 999
1000#endif /* USE_POPEN */
1001
c0c09dfd 1002}
1003
3bbf9c2b 1004/******************************************************************/
4633a7c4 1005
1006#ifndef HAS_FORK
1007int
1008fork(void)
1009{
1010 die(no_func, "Unsupported function fork");
1011 errno = EINVAL;
1012 return -1;
1013}
1014#endif
1015
3bbf9c2b 1016/*******************************************************************/
4633a7c4 1017/* not implemented in EMX 0.9a */
1018
1019void * ctermid(x) { return 0; }
eacfb5f1 1020
1021#ifdef MYTTYNAME /* was not in emx0.9a */
4633a7c4 1022void * ttyname(x) { return 0; }
eacfb5f1 1023#endif
4633a7c4 1024
3bbf9c2b 1025/******************************************************************/
760ac839 1026/* my socket forwarders - EMX lib only provides static forwarders */
1027
1028static HMODULE htcp = 0;
1029
1030static void *
1031tcp0(char *name)
1032{
1033 static BYTE buf[20];
1034 PFN fcn;
55497cff 1035
1036 if (!(_emx_env & 0x200)) croak("%s requires OS/2", name); /* Die if not OS/2. */
760ac839 1037 if (!htcp)
1038 DosLoadModule(buf, sizeof buf, "tcp32dll", &htcp);
1039 if (htcp && DosQueryProcAddr(htcp, 0, name, &fcn) == 0)
1040 return (void *) ((void * (*)(void)) fcn) ();
1041 return 0;
1042}
1043
1044static void
1045tcp1(char *name, int arg)
1046{
1047 static BYTE buf[20];
1048 PFN fcn;
55497cff 1049
1050 if (!(_emx_env & 0x200)) croak("%s requires OS/2", name); /* Die if not OS/2. */
760ac839 1051 if (!htcp)
1052 DosLoadModule(buf, sizeof buf, "tcp32dll", &htcp);
1053 if (htcp && DosQueryProcAddr(htcp, 0, name, &fcn) == 0)
1054 ((void (*)(int)) fcn) (arg);
1055}
1056
1057void * gethostent() { return tcp0("GETHOSTENT"); }
1058void * getnetent() { return tcp0("GETNETENT"); }
1059void * getprotoent() { return tcp0("GETPROTOENT"); }
1060void * getservent() { return tcp0("GETSERVENT"); }
1061void sethostent(x) { tcp1("SETHOSTENT", x); }
1062void setnetent(x) { tcp1("SETNETENT", x); }
1063void setprotoent(x) { tcp1("SETPROTOENT", x); }
1064void setservent(x) { tcp1("SETSERVENT", x); }
1065void endhostent() { tcp0("ENDHOSTENT"); }
1066void endnetent() { tcp0("ENDNETENT"); }
1067void endprotoent() { tcp0("ENDPROTOENT"); }
1068void endservent() { tcp0("ENDSERVENT"); }
1069
1070/*****************************************************************************/
1071/* not implemented in C Set++ */
1072
1073#ifndef __EMX__
1074int setuid(x) { errno = EINVAL; return -1; }
1075int setgid(x) { errno = EINVAL; return -1; }
1076#endif
4633a7c4 1077
1078/*****************************************************************************/
1079/* stat() hack for char/block device */
1080
1081#if OS2_STAT_HACK
1082
1083 /* First attempt used DosQueryFSAttach which crashed the system when
1084 used with 5.001. Now just look for /dev/. */
1085
1086int
1087os2_stat(char *name, struct stat *st)
1088{
1089 static int ino = SHRT_MAX;
1090
1091 if (stricmp(name, "/dev/con") != 0
1092 && stricmp(name, "/dev/tty") != 0)
1093 return stat(name, st);
1094
1095 memset(st, 0, sizeof *st);
1096 st->st_mode = S_IFCHR|0666;
1097 st->st_ino = (ino-- & 0x7FFF);
1098 st->st_nlink = 1;
1099 return 0;
1100}
1101
1102#endif
c0c09dfd 1103
760ac839 1104#ifdef USE_PERL_SBRK
c0c09dfd 1105
760ac839 1106/* SBRK() emulation, mostly moved to malloc.c. */
c0c09dfd 1107
1108void *
760ac839 1109sys_alloc(int size) {
1110 void *got;
1111 APIRET rc = DosAllocMem(&got, size, PAG_COMMIT | PAG_WRITE);
1112
c0c09dfd 1113 if (rc == ERROR_NOT_ENOUGH_MEMORY) {
1114 return (void *) -1;
1115 } else if ( rc ) die("Got an error from DosAllocMem: %li", (long)rc);
760ac839 1116 return got;
c0c09dfd 1117}
760ac839 1118
1119#endif /* USE_PERL_SBRK */
c0c09dfd 1120
1121/* tmp path */
1122
1123char *tmppath = TMPPATH1;
1124
1125void
1126settmppath()
1127{
1128 char *p = getenv("TMP"), *tpath;
1129 int len;
1130
1131 if (!p) p = getenv("TEMP");
1132 if (!p) return;
1133 len = strlen(p);
1134 tpath = (char *)malloc(len + strlen(TMPPATH1) + 2);
1135 strcpy(tpath, p);
1136 tpath[len] = '/';
1137 strcpy(tpath + len + 1, TMPPATH1);
1138 tmppath = tpath;
1139}
7a2f0d5b 1140
1141#include "XSUB.h"
1142
1143XS(XS_File__Copy_syscopy)
1144{
1145 dXSARGS;
1146 if (items < 2 || items > 3)
1147 croak("Usage: File::Copy::syscopy(src,dst,flag=0)");
1148 {
6b88bc9c 1149 char * src = (char *)SvPV(ST(0),PL_na);
1150 char * dst = (char *)SvPV(ST(1),PL_na);
7a2f0d5b 1151 U32 flag;
1152 int RETVAL, rc;
1153
1154 if (items < 3)
1155 flag = 0;
1156 else {
1157 flag = (unsigned long)SvIV(ST(2));
1158 }
1159
6f064249 1160 RETVAL = !CheckOSError(DosCopy(src, dst, flag));
7a2f0d5b 1161 ST(0) = sv_newmortal();
1162 sv_setiv(ST(0), (IV)RETVAL);
1163 }
1164 XSRETURN(1);
1165}
1166
017f25f1 1167#include "patchlevel.h"
1168
6f064249 1169char *
1170mod2fname(sv)
1171 SV *sv;
1172{
1173 static char fname[9];
760ac839 1174 int pos = 6, len, avlen;
1175 unsigned int sum = 0;
6f064249 1176 AV *av;
1177 SV *svp;
1178 char *s;
1179
1180 if (!SvROK(sv)) croak("Not a reference given to mod2fname");
1181 sv = SvRV(sv);
1182 if (SvTYPE(sv) != SVt_PVAV)
1183 croak("Not array reference given to mod2fname");
760ac839 1184
1185 avlen = av_len((AV*)sv);
1186 if (avlen < 0)
6f064249 1187 croak("Empty array reference given to mod2fname");
760ac839 1188
6b88bc9c 1189 s = SvPV(*av_fetch((AV*)sv, avlen, FALSE), PL_na);
6f064249 1190 strncpy(fname, s, 8);
760ac839 1191 len = strlen(s);
1192 if (len < 6) pos = len;
1193 while (*s) {
1194 sum = 33 * sum + *(s++); /* Checksumming first chars to
1195 * get the capitalization into c.s. */
1196 }
1197 avlen --;
1198 while (avlen >= 0) {
6b88bc9c 1199 s = SvPV(*av_fetch((AV*)sv, avlen, FALSE), PL_na);
760ac839 1200 while (*s) {
1201 sum = 33 * sum + *(s++); /* 7 is primitive mod 13. */
1202 }
1203 avlen --;
1204 }
3aefca04 1205#ifdef USE_THREADS
1206 sum++; /* Avoid conflict of DLLs in memory. */
1207#endif
017f25f1 1208 sum += PATCHLEVEL * 200 + SUBVERSION * 2; /* */
760ac839 1209 fname[pos] = 'A' + (sum % 26);
1210 fname[pos + 1] = 'A' + (sum / 26 % 26);
1211 fname[pos + 2] = '\0';
6f064249 1212 return (char *)fname;
1213}
1214
1215XS(XS_DynaLoader_mod2fname)
1216{
1217 dXSARGS;
1218 if (items != 1)
1219 croak("Usage: DynaLoader::mod2fname(sv)");
1220 {
1221 SV * sv = ST(0);
1222 char * RETVAL;
1223
1224 RETVAL = mod2fname(sv);
1225 ST(0) = sv_newmortal();
1226 sv_setpv((SV*)ST(0), RETVAL);
1227 }
1228 XSRETURN(1);
1229}
1230
1231char *
1232os2error(int rc)
1233{
1234 static char buf[300];
1235 ULONG len;
1236
55497cff 1237 if (!(_emx_env & 0x200)) return ""; /* Nop if not OS/2. */
6f064249 1238 if (rc == 0)
1239 return NULL;
1240 if (DosGetMessage(NULL, 0, buf, sizeof buf - 1, rc, "OSO001.MSG", &len))
1241 sprintf(buf, "OS/2 system error code %d=0x%x", rc, rc);
1242 else
1243 buf[len] = '\0';
017f25f1 1244 if (len > 0 && buf[len - 1] == '\n')
1245 buf[len - 1] = '\0';
1246 if (len > 1 && buf[len - 2] == '\r')
1247 buf[len - 2] = '\0';
1248 if (len > 2 && buf[len - 3] == '.')
1249 buf[len - 3] = '\0';
6f064249 1250 return buf;
1251}
1252
760ac839 1253char *
1254perllib_mangle(char *s, unsigned int l)
1255{
1256 static char *newp, *oldp;
1257 static int newl, oldl, notfound;
1258 static char ret[STATIC_FILE_LENGTH+1];
1259
1260 if (!newp && !notfound) {
1261 newp = getenv("PERLLIB_PREFIX");
1262 if (newp) {
ff68c719 1263 char *s;
1264
760ac839 1265 oldp = newp;
89078e0f 1266 while (*newp && !isSPACE(*newp) && *newp != ';') {
760ac839 1267 newp++; oldl++; /* Skip digits. */
1268 }
1269 while (*newp && (isSPACE(*newp) || *newp == ';')) {
1270 newp++; /* Skip whitespace. */
1271 }
1272 newl = strlen(newp);
1273 if (newl == 0 || oldl == 0) {
1274 die("Malformed PERLLIB_PREFIX");
1275 }
ff68c719 1276 strcpy(ret, newp);
1277 s = ret;
1278 while (*s) {
1279 if (*s == '\\') *s = '/';
1280 s++;
1281 }
760ac839 1282 } else {
1283 notfound = 1;
1284 }
1285 }
1286 if (!newp) {
1287 return s;
1288 }
1289 if (l == 0) {
1290 l = strlen(s);
1291 }
3bbf9c2b 1292 if (l < oldl || strnicmp(oldp, s, oldl) != 0) {
760ac839 1293 return s;
1294 }
1295 if (l + newl - oldl > STATIC_FILE_LENGTH || newl > STATIC_FILE_LENGTH) {
1296 die("Malformed PERLLIB_PREFIX");
1297 }
89078e0f 1298 strcpy(ret + newl, s + oldl);
760ac839 1299 return ret;
1300}
6f064249 1301
1302extern void dlopen();
1303void *fakedl = &dlopen; /* Pull in dynaloading part. */
3bbf9c2b 1304
1305#define sys_is_absolute(path) ( isALPHA((path)[0]) && (path)[1] == ':' \
1306 && ((path)[2] == '/' || (path)[2] == '\\'))
1307#define sys_is_rooted _fnisabs
1308#define sys_is_relative _fnisrel
1309#define current_drive _getdrive
1310
1311#undef chdir /* Was _chdir2. */
1312#define sys_chdir(p) (chdir(p) == 0)
1313#define change_drive(d) (_chdrive(d), (current_drive() == toupper(d)))
1314
1315XS(XS_Cwd_current_drive)
1316{
1317 dXSARGS;
1318 if (items != 0)
1319 croak("Usage: Cwd::current_drive()");
1320 {
1321 char RETVAL;
1322
1323 RETVAL = current_drive();
1324 ST(0) = sv_newmortal();
1325 sv_setpvn(ST(0), (char *)&RETVAL, 1);
1326 }
1327 XSRETURN(1);
1328}
1329
1330XS(XS_Cwd_sys_chdir)
1331{
1332 dXSARGS;
1333 if (items != 1)
1334 croak("Usage: Cwd::sys_chdir(path)");
1335 {
6b88bc9c 1336 char * path = (char *)SvPV(ST(0),PL_na);
3bbf9c2b 1337 bool RETVAL;
1338
1339 RETVAL = sys_chdir(path);
54310121 1340 ST(0) = boolSV(RETVAL);
3bbf9c2b 1341 if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
1342 }
1343 XSRETURN(1);
1344}
1345
1346XS(XS_Cwd_change_drive)
1347{
1348 dXSARGS;
1349 if (items != 1)
1350 croak("Usage: Cwd::change_drive(d)");
1351 {
6b88bc9c 1352 char d = (char)*SvPV(ST(0),PL_na);
3bbf9c2b 1353 bool RETVAL;
1354
1355 RETVAL = change_drive(d);
54310121 1356 ST(0) = boolSV(RETVAL);
3bbf9c2b 1357 if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
1358 }
1359 XSRETURN(1);
1360}
1361
1362XS(XS_Cwd_sys_is_absolute)
1363{
1364 dXSARGS;
1365 if (items != 1)
1366 croak("Usage: Cwd::sys_is_absolute(path)");
1367 {
6b88bc9c 1368 char * path = (char *)SvPV(ST(0),PL_na);
3bbf9c2b 1369 bool RETVAL;
1370
1371 RETVAL = sys_is_absolute(path);
54310121 1372 ST(0) = boolSV(RETVAL);
3bbf9c2b 1373 if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
1374 }
1375 XSRETURN(1);
1376}
1377
1378XS(XS_Cwd_sys_is_rooted)
1379{
1380 dXSARGS;
1381 if (items != 1)
1382 croak("Usage: Cwd::sys_is_rooted(path)");
1383 {
6b88bc9c 1384 char * path = (char *)SvPV(ST(0),PL_na);
3bbf9c2b 1385 bool RETVAL;
1386
1387 RETVAL = sys_is_rooted(path);
54310121 1388 ST(0) = boolSV(RETVAL);
3bbf9c2b 1389 if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
1390 }
1391 XSRETURN(1);
1392}
1393
1394XS(XS_Cwd_sys_is_relative)
1395{
1396 dXSARGS;
1397 if (items != 1)
1398 croak("Usage: Cwd::sys_is_relative(path)");
1399 {
6b88bc9c 1400 char * path = (char *)SvPV(ST(0),PL_na);
3bbf9c2b 1401 bool RETVAL;
1402
1403 RETVAL = sys_is_relative(path);
54310121 1404 ST(0) = boolSV(RETVAL);
3bbf9c2b 1405 if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
1406 }
1407 XSRETURN(1);
1408}
1409
1410XS(XS_Cwd_sys_cwd)
1411{
1412 dXSARGS;
1413 if (items != 0)
1414 croak("Usage: Cwd::sys_cwd()");
1415 {
1416 char p[MAXPATHLEN];
1417 char * RETVAL;
1418 RETVAL = _getcwd2(p, MAXPATHLEN);
1419 ST(0) = sv_newmortal();
1420 sv_setpv((SV*)ST(0), RETVAL);
1421 }
1422 XSRETURN(1);
1423}
1424
1425XS(XS_Cwd_sys_abspath)
1426{
1427 dXSARGS;
1428 if (items < 1 || items > 2)
1429 croak("Usage: Cwd::sys_abspath(path, dir = NULL)");
1430 {
6b88bc9c 1431 char * path = (char *)SvPV(ST(0),PL_na);
3bbf9c2b 1432 char * dir;
1433 char p[MAXPATHLEN];
1434 char * RETVAL;
1435
1436 if (items < 2)
1437 dir = NULL;
1438 else {
6b88bc9c 1439 dir = (char *)SvPV(ST(1),PL_na);
3bbf9c2b 1440 }
1441 if (path[0] == '.' && (path[1] == '/' || path[1] == '\\')) {
1442 path += 2;
1443 }
1444 if (dir == NULL) {
1445 if (_abspath(p, path, MAXPATHLEN) == 0) {
1446 RETVAL = p;
1447 } else {
1448 RETVAL = NULL;
1449 }
1450 } else {
1451 /* Absolute with drive: */
1452 if ( sys_is_absolute(path) ) {
1453 if (_abspath(p, path, MAXPATHLEN) == 0) {
1454 RETVAL = p;
1455 } else {
1456 RETVAL = NULL;
1457 }
1458 } else if (path[0] == '/' || path[0] == '\\') {
1459 /* Rooted, but maybe on different drive. */
1460 if (isALPHA(dir[0]) && dir[1] == ':' ) {
1461 char p1[MAXPATHLEN];
1462
1463 /* Need to prepend the drive. */
1464 p1[0] = dir[0];
1465 p1[1] = dir[1];
1466 Copy(path, p1 + 2, strlen(path) + 1, char);
1467 RETVAL = p;
1468 if (_abspath(p, p1, MAXPATHLEN) == 0) {
1469 RETVAL = p;
1470 } else {
1471 RETVAL = NULL;
1472 }
1473 } else if (_abspath(p, path, MAXPATHLEN) == 0) {
1474 RETVAL = p;
1475 } else {
1476 RETVAL = NULL;
1477 }
1478 } else {
1479 /* Either path is relative, or starts with a drive letter. */
1480 /* If the path starts with a drive letter, then dir is
1481 relevant only if
1482 a/b) it is absolute/x:relative on the same drive.
1483 c) path is on current drive, and dir is rooted
1484 In all the cases it is safe to drop the drive part
1485 of the path. */
1486 if ( !sys_is_relative(path) ) {
1487 int is_drived;
1488
1489 if ( ( ( sys_is_absolute(dir)
1490 || (isALPHA(dir[0]) && dir[1] == ':'
1491 && strnicmp(dir, path,1) == 0))
1492 && strnicmp(dir, path,1) == 0)
1493 || ( !(isALPHA(dir[0]) && dir[1] == ':')
1494 && toupper(path[0]) == current_drive())) {
1495 path += 2;
1496 } else if (_abspath(p, path, MAXPATHLEN) == 0) {
1497 RETVAL = p; goto done;
1498 } else {
1499 RETVAL = NULL; goto done;
1500 }
1501 }
1502 {
1503 /* Need to prepend the absolute path of dir. */
1504 char p1[MAXPATHLEN];
1505
1506 if (_abspath(p1, dir, MAXPATHLEN) == 0) {
1507 int l = strlen(p1);
1508
1509 if (p1[ l - 1 ] != '/') {
1510 p1[ l ] = '/';
1511 l++;
1512 }
1513 Copy(path, p1 + l, strlen(path) + 1, char);
1514 if (_abspath(p, p1, MAXPATHLEN) == 0) {
1515 RETVAL = p;
1516 } else {
1517 RETVAL = NULL;
1518 }
1519 } else {
1520 RETVAL = NULL;
1521 }
1522 }
1523 done:
1524 }
1525 }
1526 ST(0) = sv_newmortal();
1527 sv_setpv((SV*)ST(0), RETVAL);
1528 }
1529 XSRETURN(1);
1530}
72ea3524 1531typedef APIRET (*PELP)(PSZ path, ULONG type);
1532
1533APIRET
1534ExtLIBPATH(ULONG ord, PSZ path, ULONG type)
1535{
1536 loadByOrd(ord); /* Guarantied to load or die! */
1537 return (*(PELP)ExtFCN[ord])(path, type);
1538}
3bbf9c2b 1539
72ea3524 1540#define extLibpath(type) \
1541 (CheckOSError(ExtLIBPATH(ORD_QUERY_ELP, to, ((type) ? END_LIBPATH \
1542 : BEGIN_LIBPATH))) \
3bbf9c2b 1543 ? NULL : to )
1544
1545#define extLibpath_set(p,type) \
72ea3524 1546 (!CheckOSError(ExtLIBPATH(ORD_SET_ELP, (p), ((type) ? END_LIBPATH \
1547 : BEGIN_LIBPATH))))
3bbf9c2b 1548
1549XS(XS_Cwd_extLibpath)
1550{
1551 dXSARGS;
1552 if (items < 0 || items > 1)
1553 croak("Usage: Cwd::extLibpath(type = 0)");
1554 {
1555 bool type;
1556 char to[1024];
1557 U32 rc;
1558 char * RETVAL;
1559
1560 if (items < 1)
1561 type = 0;
1562 else {
1563 type = (int)SvIV(ST(0));
1564 }
1565
1566 RETVAL = extLibpath(type);
1567 ST(0) = sv_newmortal();
1568 sv_setpv((SV*)ST(0), RETVAL);
1569 }
1570 XSRETURN(1);
1571}
1572
1573XS(XS_Cwd_extLibpath_set)
1574{
1575 dXSARGS;
1576 if (items < 1 || items > 2)
1577 croak("Usage: Cwd::extLibpath_set(s, type = 0)");
1578 {
6b88bc9c 1579 char * s = (char *)SvPV(ST(0),PL_na);
3bbf9c2b 1580 bool type;
1581 U32 rc;
1582 bool RETVAL;
1583
1584 if (items < 2)
1585 type = 0;
1586 else {
1587 type = (int)SvIV(ST(1));
1588 }
1589
1590 RETVAL = extLibpath_set(s, type);
54310121 1591 ST(0) = boolSV(RETVAL);
3bbf9c2b 1592 if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
1593 }
1594 XSRETURN(1);
1595}
1596
1597int
1598Xs_OS2_init()
1599{
1600 char *file = __FILE__;
1601 {
1602 GV *gv;
55497cff 1603
1604 if (_emx_env & 0x200) { /* OS/2 */
1605 newXS("File::Copy::syscopy", XS_File__Copy_syscopy, file);
1606 newXS("Cwd::extLibpath", XS_Cwd_extLibpath, file);
1607 newXS("Cwd::extLibpath_set", XS_Cwd_extLibpath_set, file);
1608 }
3bbf9c2b 1609 newXS("DynaLoader::mod2fname", XS_DynaLoader_mod2fname, file);
1610 newXS("Cwd::current_drive", XS_Cwd_current_drive, file);
1611 newXS("Cwd::sys_chdir", XS_Cwd_sys_chdir, file);
1612 newXS("Cwd::change_drive", XS_Cwd_change_drive, file);
1613 newXS("Cwd::sys_is_absolute", XS_Cwd_sys_is_absolute, file);
1614 newXS("Cwd::sys_is_rooted", XS_Cwd_sys_is_rooted, file);
1615 newXS("Cwd::sys_is_relative", XS_Cwd_sys_is_relative, file);
1616 newXS("Cwd::sys_cwd", XS_Cwd_sys_cwd, file);
1617 newXS("Cwd::sys_abspath", XS_Cwd_sys_abspath, file);
3bbf9c2b 1618 gv = gv_fetchpv("OS2::is_aout", TRUE, SVt_PV);
1619 GvMULTI_on(gv);
1620#ifdef PERL_IS_AOUT
1621 sv_setiv(GvSV(gv), 1);
1622#endif
1623 }
1624}
1625
1626OS2_Perl_data_t OS2_Perl_data;
1627
1628void
aa689395 1629Perl_OS2_init(char **env)
3bbf9c2b 1630{
1631 char *shell;
1632
18f739ee 1633 MALLOC_INIT;
3bbf9c2b 1634 settmppath();
1635 OS2_Perl_data.xs_init = &Xs_OS2_init;
aa689395 1636 if (environ == NULL) {
1637 environ = env;
1638 }
3bbf9c2b 1639 if ( (shell = getenv("PERL_SH_DRIVE")) ) {
6b88bc9c 1640 New(1304, PL_sh_path, strlen(SH_PATH) + 1, char);
1641 strcpy(PL_sh_path, SH_PATH);
1642 PL_sh_path[0] = shell[0];
3bbf9c2b 1643 } else if ( (shell = getenv("PERL_SH_DIR")) ) {
ff68c719 1644 int l = strlen(shell), i;
3bbf9c2b 1645 if (shell[l-1] == '/' || shell[l-1] == '\\') {
1646 l--;
1647 }
6b88bc9c 1648 New(1304, PL_sh_path, l + 8, char);
1649 strncpy(PL_sh_path, shell, l);
1650 strcpy(PL_sh_path + l, "/sh.exe");
ff68c719 1651 for (i = 0; i < l; i++) {
6b88bc9c 1652 if (PL_sh_path[i] == '\\') PL_sh_path[i] = '/';
ff68c719 1653 }
3bbf9c2b 1654 }
dd96f567 1655 MUTEX_INIT(&start_thread_mutex);
017f25f1 1656 os2_mytype = my_type(); /* Do it before morphing. Needed? */
3bbf9c2b 1657}
1658
55497cff 1659#undef tmpnam
1660#undef tmpfile
1661
1662char *
1663my_tmpnam (char *str)
1664{
1665 char *p = getenv("TMP"), *tpath;
1666 int len;
1667
1668 if (!p) p = getenv("TEMP");
1669 tpath = tempnam(p, "pltmp");
1670 if (str && tpath) {
1671 strcpy(str, tpath);
1672 return str;
1673 }
1674 return tpath;
1675}
1676
1677FILE *
1678my_tmpfile ()
1679{
1680 struct stat s;
1681
1682 stat(".", &s);
1683 if (s.st_mode & S_IWOTH) {
1684 return tmpfile();
1685 }
1686 return fopen(my_tmpnam(NULL), "w+b"); /* Race condition, but
1687 grants TMP. */
1688}
367f3c24 1689
1690#undef flock
1691
1692/* This code was contributed by Rocco Caputo. */
1693int
dd96f567 1694my_flock(int handle, int o)
367f3c24 1695{
1696 FILELOCK rNull, rFull;
1697 ULONG timeout, handle_type, flag_word;
1698 APIRET rc;
1699 int blocking, shared;
1700 static int use_my = -1;
1701
1702 if (use_my == -1) {
1703 char *s = getenv("USE_PERL_FLOCK");
1704 if (s)
1705 use_my = atoi(s);
1706 else
1707 use_my = 1;
1708 }
1709 if (!(_emx_env & 0x200) || !use_my)
dd96f567 1710 return flock(handle, o); /* Delegate to EMX. */
367f3c24 1711
1712 // is this a file?
1713 if ((DosQueryHType(handle, &handle_type, &flag_word) != 0) ||
1714 (handle_type & 0xFF))
1715 {
1716 errno = EBADF;
1717 return -1;
1718 }
1719 // set lock/unlock ranges
1720 rNull.lOffset = rNull.lRange = rFull.lOffset = 0;
1721 rFull.lRange = 0x7FFFFFFF;
1722 // set timeout for blocking
dd96f567 1723 timeout = ((blocking = !(o & LOCK_NB))) ? 100 : 1;
367f3c24 1724 // shared or exclusive?
dd96f567 1725 shared = (o & LOCK_SH) ? 1 : 0;
367f3c24 1726 // do not block the unlock
dd96f567 1727 if (o & (LOCK_UN | LOCK_SH | LOCK_EX)) {
367f3c24 1728 rc = DosSetFileLocks(handle, &rFull, &rNull, timeout, shared);
1729 switch (rc) {
1730 case 0:
1731 errno = 0;
1732 return 0;
1733 case ERROR_INVALID_HANDLE:
1734 errno = EBADF;
1735 return -1;
1736 case ERROR_SHARING_BUFFER_EXCEEDED:
1737 errno = ENOLCK;
1738 return -1;
1739 case ERROR_LOCK_VIOLATION:
1740 break; // not an error
1741 case ERROR_INVALID_PARAMETER:
1742 case ERROR_ATOMIC_LOCK_NOT_SUPPORTED:
1743 case ERROR_READ_LOCKS_NOT_SUPPORTED:
1744 errno = EINVAL;
1745 return -1;
1746 case ERROR_INTERRUPT:
1747 errno = EINTR;
1748 return -1;
1749 default:
1750 errno = EINVAL;
1751 return -1;
1752 }
1753 }
1754 // lock may block
dd96f567 1755 if (o & (LOCK_SH | LOCK_EX)) {
367f3c24 1756 // for blocking operations
1757 for (;;) {
1758 rc =
1759 DosSetFileLocks(
1760 handle,
1761 &rNull,
1762 &rFull,
1763 timeout,
1764 shared
1765 );
1766 switch (rc) {
1767 case 0:
1768 errno = 0;
1769 return 0;
1770 case ERROR_INVALID_HANDLE:
1771 errno = EBADF;
1772 return -1;
1773 case ERROR_SHARING_BUFFER_EXCEEDED:
1774 errno = ENOLCK;
1775 return -1;
1776 case ERROR_LOCK_VIOLATION:
1777 if (!blocking) {
1778 errno = EWOULDBLOCK;
1779 return -1;
1780 }
1781 break;
1782 case ERROR_INVALID_PARAMETER:
1783 case ERROR_ATOMIC_LOCK_NOT_SUPPORTED:
1784 case ERROR_READ_LOCKS_NOT_SUPPORTED:
1785 errno = EINVAL;
1786 return -1;
1787 case ERROR_INTERRUPT:
1788 errno = EINTR;
1789 return -1;
1790 default:
1791 errno = EINVAL;
1792 return -1;
1793 }
1794 // give away timeslice
1795 DosSleep(1);
1796 }
1797 }
1798
1799 errno = 0;
1800 return 0;
1801}