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