This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
corelist: updated for threads libraries
[perl5.git] / os2 / os2.c
... / ...
CommitLineData
1#define INCL_DOS
2#define INCL_NOPM
3#define INCL_DOSFILEMGR
4#define INCL_DOSMEMMGR
5#define INCL_DOSERRORS
6#define INCL_WINERRORS
7#define INCL_WINSYS
8/* These 3 are needed for compile if os2.h includes os2tk.h, not os2emx.h */
9#define INCL_DOSPROCESS
10#define SPU_DISABLESUPPRESSION 0
11#define SPU_ENABLESUPPRESSION 1
12#include <os2.h>
13#include "dlfcn.h"
14#include <emx/syscalls.h>
15#include <sys/emxload.h>
16
17#include <sys/uflags.h>
18
19/*
20 * Various Unix compatibility functions for OS/2
21 */
22
23#include <stdio.h>
24#include <errno.h>
25#include <limits.h>
26#include <process.h>
27#include <fcntl.h>
28#include <pwd.h>
29#include <grp.h>
30
31#define PERLIO_NOT_STDIO 0
32
33#include "EXTERN.h"
34#include "perl.h"
35
36enum module_name_how { mod_name_handle, mod_name_shortname, mod_name_full,
37 mod_name_C_function = 0x100, mod_name_HMODULE = 0x200};
38
39/* Find module name to which *this* subroutine is compiled */
40#define module_name(how) module_name_at(&module_name_at, how)
41
42static SV* module_name_at(void *pp, enum module_name_how how);
43
44void
45croak_with_os2error(char *s)
46{
47 Perl_croak_nocontext("%s: %s", s, os2error(Perl_rc));
48}
49
50struct PMWIN_entries_t PMWIN_entries;
51
52/*****************************************************************************/
53/* 2.1 would not resolve symbols on demand, and has no ExtLIBPATH. */
54
55struct dll_handle_t {
56 const char *modname;
57 HMODULE handle;
58 int requires_pm;
59};
60
61static struct dll_handle_t dll_handles[] = {
62 {"doscalls", 0, 0},
63 {"tcp32dll", 0, 0},
64 {"pmwin", 0, 1},
65 {"rexx", 0, 0},
66 {"rexxapi", 0, 0},
67 {"sesmgr", 0, 0},
68 {"pmshapi", 0, 1},
69 {"pmwp", 0, 1},
70 {"pmgpi", 0, 1},
71 {NULL, 0},
72};
73
74enum dll_handle_e {
75 dll_handle_doscalls,
76 dll_handle_tcp32dll,
77 dll_handle_pmwin,
78 dll_handle_rexx,
79 dll_handle_rexxapi,
80 dll_handle_sesmgr,
81 dll_handle_pmshapi,
82 dll_handle_pmwp,
83 dll_handle_pmgpi,
84 dll_handle_LAST,
85};
86
87#define doscalls_handle (dll_handles[dll_handle_doscalls])
88#define tcp_handle (dll_handles[dll_handle_tcp32dll])
89#define pmwin_handle (dll_handles[dll_handle_pmwin])
90#define rexx_handle (dll_handles[dll_handle_rexx])
91#define rexxapi_handle (dll_handles[dll_handle_rexxapi])
92#define sesmgr_handle (dll_handles[dll_handle_sesmgr])
93#define pmshapi_handle (dll_handles[dll_handle_pmshapi])
94#define pmwp_handle (dll_handles[dll_handle_pmwp])
95#define pmgpi_handle (dll_handles[dll_handle_pmgpi])
96
97/* The following local-scope data is not yet included:
98 fargs.140 // const => OK
99 ino.165 // locked - and the access is almost cosmetic
100 layout_table.260 // startup only, locked
101 osv_res.257 // startup only, locked
102 old_esp.254 // startup only, locked
103 priors // const ==> OK
104 use_my_flock.283 // locked
105 emx_init_done.268 // locked
106 dll_handles // locked
107 hmtx_emx_init.267 // THIS is the lock for startup
108 perlos2_state_mutex // THIS is the lock for all the rest
109BAD:
110 perlos2_state // see below
111*/
112/* The following global-scope data is not yet included:
113 OS2_Perl_data
114 pthreads_states // const now?
115 start_thread_mutex
116 thread_join_count // protected
117 thread_join_data // protected
118 tmppath
119
120 pDosVerifyPidTid
121
122 Perl_OS2_init3() - should it be protected?
123*/
124OS2_Perl_data_t OS2_Perl_data;
125
126static struct perlos2_state_t {
127 int po2__my_pwent; /* = -1; */
128 int po2_DOS_harderr_state; /* = -1; */
129 signed char po2_DOS_suppression_state; /* = -1; */
130
131 PFN po2_ExtFCN[ORD_NENTRIES]; /* Labeled by ord ORD_*. */
132/* struct PMWIN_entries_t po2_PMWIN_entries; */
133
134 int po2_emx_wasnt_initialized;
135
136 char po2_fname[9];
137 int po2_rmq_cnt;
138
139 int po2_grent_cnt;
140
141 char *po2_newp;
142 char *po2_oldp;
143 int po2_newl;
144 int po2_oldl;
145 int po2_notfound;
146 char po2_mangle_ret[STATIC_FILE_LENGTH+1];
147 ULONG po2_os2_dll_fake;
148 ULONG po2_os2_mytype;
149 ULONG po2_os2_mytype_ini;
150 int po2_pidtid_lookup;
151 struct passwd po2_pw;
152
153 int po2_pwent_cnt;
154 char po2_pthreads_state_buf[80];
155 char po2_os2error_buf[300];
156/* There is no big sense to make it thread-specific, since signals
157 are delivered to thread 1 only. XXXX Maybe make it into an array? */
158 int po2_spawn_pid;
159 int po2_spawn_killed;
160
161 jmp_buf po2_at_exit_buf;
162 int po2_longjmp_at_exit;
163 int po2_emx_runtime_init; /* If 1, we need to manually init it */
164 int po2_emx_exception_init; /* If 1, we need to manually set it */
165 int po2_emx_runtime_secondary;
166 char* (*po2_perllib_mangle_installed)(char *s, unsigned int l);
167 char* po2_perl_sh_installed;
168 PGINFOSEG po2_gTable;
169 PLINFOSEG po2_lTable;
170} perlos2_state = {
171 -1, /* po2__my_pwent */
172 -1, /* po2_DOS_harderr_state */
173 -1, /* po2_DOS_suppression_state */
174};
175
176#define Perl_po2() (&perlos2_state)
177
178#define ExtFCN (Perl_po2()->po2_ExtFCN)
179/* #define PMWIN_entries (Perl_po2()->po2_PMWIN_entries) */
180#define emx_wasnt_initialized (Perl_po2()->po2_emx_wasnt_initialized)
181#define fname (Perl_po2()->po2_fname)
182#define rmq_cnt (Perl_po2()->po2_rmq_cnt)
183#define grent_cnt (Perl_po2()->po2_grent_cnt)
184#define newp (Perl_po2()->po2_newp)
185#define oldp (Perl_po2()->po2_oldp)
186#define newl (Perl_po2()->po2_newl)
187#define oldl (Perl_po2()->po2_oldl)
188#define notfound (Perl_po2()->po2_notfound)
189#define mangle_ret (Perl_po2()->po2_mangle_ret)
190#define os2_dll_fake (Perl_po2()->po2_os2_dll_fake)
191#define os2_mytype (Perl_po2()->po2_os2_mytype)
192#define os2_mytype_ini (Perl_po2()->po2_os2_mytype_ini)
193#define pidtid_lookup (Perl_po2()->po2_pidtid_lookup)
194#define pw (Perl_po2()->po2_pw)
195#define pwent_cnt (Perl_po2()->po2_pwent_cnt)
196#define _my_pwent (Perl_po2()->po2__my_pwent)
197#define pthreads_state_buf (Perl_po2()->po2_pthreads_state_buf)
198#define os2error_buf (Perl_po2()->po2_os2error_buf)
199/* There is no big sense to make it thread-specific, since signals
200 are delivered to thread 1 only. XXXX Maybe make it into an array? */
201#define spawn_pid (Perl_po2()->po2_spawn_pid)
202#define spawn_killed (Perl_po2()->po2_spawn_killed)
203#define DOS_harderr_state (Perl_po2()->po2_DOS_harderr_state)
204#define DOS_suppression_state (Perl_po2()->po2_DOS_suppression_state)
205
206#define at_exit_buf (Perl_po2()->po2_at_exit_buf)
207#define longjmp_at_exit (Perl_po2()->po2_longjmp_at_exit)
208#define emx_runtime_init (Perl_po2()->po2_emx_runtime_init)
209#define emx_exception_init (Perl_po2()->po2_emx_exception_init)
210#define emx_runtime_secondary (Perl_po2()->po2_emx_runtime_secondary)
211#define perllib_mangle_installed (Perl_po2()->po2_perllib_mangle_installed)
212#define perl_sh_installed (Perl_po2()->po2_perl_sh_installed)
213#define gTable (Perl_po2()->po2_gTable)
214#define lTable (Perl_po2()->po2_lTable)
215
216const Perl_PFN * const pExtFCN = (Perl_po2()->po2_ExtFCN);
217
218#if defined(USE_5005THREADS) || defined(USE_ITHREADS)
219
220typedef void (*emx_startroutine)(void *);
221typedef void* (*pthreads_startroutine)(void *);
222
223enum pthreads_state {
224 pthreads_st_none = 0,
225 pthreads_st_run,
226 pthreads_st_exited,
227 pthreads_st_detached,
228 pthreads_st_waited,
229 pthreads_st_norun,
230 pthreads_st_exited_waited,
231};
232const char * const pthreads_states[] = {
233 "uninit",
234 "running",
235 "exited",
236 "detached",
237 "waited for",
238 "could not start",
239 "exited, then waited on",
240};
241
242enum pthread_exists { pthread_not_existant = -0xff };
243
244static const char*
245pthreads_state_string(enum pthreads_state state)
246{
247 if (state < 0 || state >= sizeof(pthreads_states)/sizeof(*pthreads_states)) {
248 snprintf(pthreads_state_buf, sizeof(pthreads_state_buf),
249 "unknown thread state %d", (int)state);
250 return pthreads_state_buf;
251 }
252 return pthreads_states[state];
253}
254
255typedef struct {
256 void *status;
257 perl_cond cond;
258 enum pthreads_state state;
259} thread_join_t;
260
261thread_join_t *thread_join_data;
262int thread_join_count;
263perl_mutex start_thread_mutex;
264static perl_mutex perlos2_state_mutex;
265
266
267int
268pthread_join(perl_os_thread tid, void **status)
269{
270 MUTEX_LOCK(&start_thread_mutex);
271 if (tid < 1 || tid >= thread_join_count) {
272 MUTEX_UNLOCK(&start_thread_mutex);
273 if (tid != pthread_not_existant)
274 Perl_croak_nocontext("panic: join with a thread with strange ordinal %d", (int)tid);
275 Perl_warn_nocontext("panic: join with a thread which could not start");
276 *status = 0;
277 return 0;
278 }
279 switch (thread_join_data[tid].state) {
280 case pthreads_st_exited:
281 thread_join_data[tid].state = pthreads_st_exited_waited;
282 *status = thread_join_data[tid].status;
283 MUTEX_UNLOCK(&start_thread_mutex);
284 COND_SIGNAL(&thread_join_data[tid].cond);
285 break;
286 case pthreads_st_waited:
287 MUTEX_UNLOCK(&start_thread_mutex);
288 Perl_croak_nocontext("join with a thread with a waiter");
289 break;
290 case pthreads_st_norun:
291 {
292 int state = (int)thread_join_data[tid].status;
293
294 thread_join_data[tid].state = pthreads_st_none;
295 MUTEX_UNLOCK(&start_thread_mutex);
296 Perl_croak_nocontext("panic: join with a thread which could not run"
297 " due to attempt of tid reuse (state='%s')",
298 pthreads_state_string(state));
299 break;
300 }
301 case pthreads_st_run:
302 {
303 perl_cond cond;
304
305 thread_join_data[tid].state = pthreads_st_waited;
306 thread_join_data[tid].status = (void *)status;
307 COND_INIT(&thread_join_data[tid].cond);
308 cond = thread_join_data[tid].cond;
309 COND_WAIT(&thread_join_data[tid].cond, &start_thread_mutex);
310 COND_DESTROY(&cond);
311 MUTEX_UNLOCK(&start_thread_mutex);
312 break;
313 }
314 default:
315 MUTEX_UNLOCK(&start_thread_mutex);
316 Perl_croak_nocontext("panic: join with thread in unknown thread state: '%s'",
317 pthreads_state_string(thread_join_data[tid].state));
318 break;
319 }
320 return 0;
321}
322
323typedef struct {
324 pthreads_startroutine sub;
325 void *arg;
326 void *ctx;
327} pthr_startit;
328
329/* The lock is used:
330 a) Since we temporarily usurp the caller interp, so malloc() may
331 use it to decide on debugging the call;
332 b) Since *args is on the caller's stack.
333 */
334void
335pthread_startit(void *arg1)
336{
337 /* Thread is already started, we need to transfer control only */
338 pthr_startit args = *(pthr_startit *)arg1;
339 int tid = pthread_self();
340 void *rc;
341 int state;
342
343 if (tid <= 1) {
344 /* Can't croak, the setjmp() is not in scope... */
345 char buf[80];
346
347 snprintf(buf, sizeof(buf),
348 "panic: thread with strange ordinal %d created\n\r", tid);
349 write(2,buf,strlen(buf));
350 MUTEX_UNLOCK(&start_thread_mutex);
351 return;
352 }
353 /* Until args.sub resets it, makes debugging Perl_malloc() work: */
354 PERL_SET_CONTEXT(0);
355 if (tid >= thread_join_count) {
356 int oc = thread_join_count;
357
358 thread_join_count = tid + 5 + tid/5;
359 if (thread_join_data) {
360 Renew(thread_join_data, thread_join_count, thread_join_t);
361 Zero(thread_join_data + oc, thread_join_count - oc, thread_join_t);
362 } else {
363 Newxz(thread_join_data, thread_join_count, thread_join_t);
364 }
365 }
366 if (thread_join_data[tid].state != pthreads_st_none) {
367 /* Can't croak, the setjmp() is not in scope... */
368 char buf[80];
369
370 snprintf(buf, sizeof(buf),
371 "panic: attempt to reuse thread id %d (state='%s')\n\r",
372 tid, pthreads_state_string(thread_join_data[tid].state));
373 write(2,buf,strlen(buf));
374 thread_join_data[tid].status = (void*)thread_join_data[tid].state;
375 thread_join_data[tid].state = pthreads_st_norun;
376 MUTEX_UNLOCK(&start_thread_mutex);
377 return;
378 }
379 thread_join_data[tid].state = pthreads_st_run;
380 /* Now that we copied/updated the guys, we may release the caller... */
381 MUTEX_UNLOCK(&start_thread_mutex);
382 rc = (*args.sub)(args.arg);
383 MUTEX_LOCK(&start_thread_mutex);
384 switch (thread_join_data[tid].state) {
385 case pthreads_st_waited:
386 COND_SIGNAL(&thread_join_data[tid].cond);
387 thread_join_data[tid].state = pthreads_st_none;
388 *((void**)thread_join_data[tid].status) = rc;
389 break;
390 case pthreads_st_detached:
391 thread_join_data[tid].state = pthreads_st_none;
392 break;
393 case pthreads_st_run:
394 /* Somebody can wait on us; cannot exit, since OS can reuse the tid
395 and our waiter will get somebody else's status. */
396 thread_join_data[tid].state = pthreads_st_exited;
397 thread_join_data[tid].status = rc;
398 COND_INIT(&thread_join_data[tid].cond);
399 COND_WAIT(&thread_join_data[tid].cond, &start_thread_mutex);
400 COND_DESTROY(&thread_join_data[tid].cond);
401 thread_join_data[tid].state = pthreads_st_none; /* Ready to reuse */
402 break;
403 default:
404 state = thread_join_data[tid].state;
405 MUTEX_UNLOCK(&start_thread_mutex);
406 Perl_croak_nocontext("panic: unexpected thread state on exit: '%s'",
407 pthreads_state_string(state));
408 }
409 MUTEX_UNLOCK(&start_thread_mutex);
410}
411
412int
413pthread_create(perl_os_thread *tidp, const pthread_attr_t *attr,
414 void *(*start_routine)(void*), void *arg)
415{
416 dTHX;
417 pthr_startit args;
418
419 args.sub = (void*)start_routine;
420 args.arg = arg;
421 args.ctx = PERL_GET_CONTEXT;
422
423 MUTEX_LOCK(&start_thread_mutex);
424 /* Test suite creates 31 extra threads;
425 on machine without shared-memory-hogs this stack sizeis OK with 31: */
426 *tidp = _beginthread(pthread_startit, /*stack*/ NULL,
427 /*stacksize*/ 4*1024*1024, (void*)&args);
428 if (*tidp == -1) {
429 *tidp = pthread_not_existant;
430 MUTEX_UNLOCK(&start_thread_mutex);
431 return EINVAL;
432 }
433 MUTEX_LOCK(&start_thread_mutex); /* Wait for init to proceed */
434 MUTEX_UNLOCK(&start_thread_mutex);
435 return 0;
436}
437
438int
439pthread_detach(perl_os_thread tid)
440{
441 MUTEX_LOCK(&start_thread_mutex);
442 if (tid < 1 || tid >= thread_join_count) {
443 MUTEX_UNLOCK(&start_thread_mutex);
444 if (tid != pthread_not_existant)
445 Perl_croak_nocontext("panic: detach of a thread with strange ordinal %d", (int)tid);
446 Perl_warn_nocontext("detach of a thread which could not start");
447 return 0;
448 }
449 switch (thread_join_data[tid].state) {
450 case pthreads_st_waited:
451 MUTEX_UNLOCK(&start_thread_mutex);
452 Perl_croak_nocontext("detach on a thread with a waiter");
453 break;
454 case pthreads_st_run:
455 thread_join_data[tid].state = pthreads_st_detached;
456 MUTEX_UNLOCK(&start_thread_mutex);
457 break;
458 case pthreads_st_exited:
459 MUTEX_UNLOCK(&start_thread_mutex);
460 COND_SIGNAL(&thread_join_data[tid].cond);
461 break;
462 case pthreads_st_detached:
463 MUTEX_UNLOCK(&start_thread_mutex);
464 Perl_warn_nocontext("detach on an already detached thread");
465 break;
466 case pthreads_st_norun:
467 {
468 int state = (int)thread_join_data[tid].status;
469
470 thread_join_data[tid].state = pthreads_st_none;
471 MUTEX_UNLOCK(&start_thread_mutex);
472 Perl_croak_nocontext("panic: detaching thread which could not run"
473 " due to attempt of tid reuse (state='%s')",
474 pthreads_state_string(state));
475 break;
476 }
477 default:
478 MUTEX_UNLOCK(&start_thread_mutex);
479 Perl_croak_nocontext("panic: detach of a thread with unknown thread state: '%s'",
480 pthreads_state_string(thread_join_data[tid].state));
481 break;
482 }
483 return 0;
484}
485
486/* This is a very bastardized version; may be OK due to edge trigger of Wait */
487int
488os2_cond_wait(perl_cond *c, perl_mutex *m)
489{
490 int rc;
491 STRLEN n_a;
492 if ((rc = DosResetEventSem(*c,&n_a)) && (rc != ERROR_ALREADY_RESET))
493 Perl_rc = CheckOSError(rc), croak_with_os2error("panic: COND_WAIT-reset");
494 if (m) MUTEX_UNLOCK(m);
495 if (CheckOSError(DosWaitEventSem(*c,SEM_INDEFINITE_WAIT))
496 && (rc != ERROR_INTERRUPT))
497 croak_with_os2error("panic: COND_WAIT");
498 if (rc == ERROR_INTERRUPT)
499 errno = EINTR;
500 if (m) MUTEX_LOCK(m);
501 return 0;
502}
503#endif
504
505static int exe_is_aout(void);
506
507/* This should match enum entries_ordinals defined in os2ish.h. */
508static const struct {
509 struct dll_handle_t *dll;
510 const char *entryname;
511 int entrypoint;
512} loadOrdinals[] = {
513 {&doscalls_handle, NULL, 874}, /* DosQueryExtLibpath */
514 {&doscalls_handle, NULL, 873}, /* DosSetExtLibpath */
515 {&doscalls_handle, NULL, 460}, /* DosVerifyPidTid */
516 {&tcp_handle, "SETHOSTENT", 0},
517 {&tcp_handle, "SETNETENT" , 0},
518 {&tcp_handle, "SETPROTOENT", 0},
519 {&tcp_handle, "SETSERVENT", 0},
520 {&tcp_handle, "GETHOSTENT", 0},
521 {&tcp_handle, "GETNETENT" , 0},
522 {&tcp_handle, "GETPROTOENT", 0},
523 {&tcp_handle, "GETSERVENT", 0},
524 {&tcp_handle, "ENDHOSTENT", 0},
525 {&tcp_handle, "ENDNETENT", 0},
526 {&tcp_handle, "ENDPROTOENT", 0},
527 {&tcp_handle, "ENDSERVENT", 0},
528 {&pmwin_handle, NULL, 763}, /* WinInitialize */
529 {&pmwin_handle, NULL, 716}, /* WinCreateMsgQueue */
530 {&pmwin_handle, NULL, 726}, /* WinDestroyMsgQueue */
531 {&pmwin_handle, NULL, 918}, /* WinPeekMsg */
532 {&pmwin_handle, NULL, 915}, /* WinGetMsg */
533 {&pmwin_handle, NULL, 912}, /* WinDispatchMsg */
534 {&pmwin_handle, NULL, 753}, /* WinGetLastError */
535 {&pmwin_handle, NULL, 705}, /* WinCancelShutdown */
536 /* These are needed in extensions.
537 How to protect PMSHAPI: it comes through EMX functions? */
538 {&rexx_handle, "RexxStart", 0},
539 {&rexx_handle, "RexxVariablePool", 0},
540 {&rexxapi_handle, "RexxRegisterFunctionExe", 0},
541 {&rexxapi_handle, "RexxDeregisterFunction", 0},
542 {&sesmgr_handle, "DOSSMSETTITLE", 0}, /* Would not work runtime-loaded */
543 {&pmshapi_handle, "PRF32QUERYPROFILESIZE", 0},
544 {&pmshapi_handle, "PRF32OPENPROFILE", 0},
545 {&pmshapi_handle, "PRF32CLOSEPROFILE", 0},
546 {&pmshapi_handle, "PRF32QUERYPROFILE", 0},
547 {&pmshapi_handle, "PRF32RESET", 0},
548 {&pmshapi_handle, "PRF32QUERYPROFILEDATA", 0},
549 {&pmshapi_handle, "PRF32WRITEPROFILEDATA", 0},
550
551 /* At least some of these do not work by name, since they need
552 WIN32 instead of WIN... */
553#if 0
554 These were generated with
555 nm I:\emx\lib\os2.a | fgrep -f API-list | grep = > API-list-entries
556 perl -wnle "next unless /^0+\s+E\s+_(\w+)=(\w+).(\d+)/; print qq( ORD_$1,)" API-list-entries > API-list-ORD_
557 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
558#endif
559 {&pmshapi_handle, NULL, 123}, /* WinChangeSwitchEntry */
560 {&pmshapi_handle, NULL, 124}, /* WinQuerySwitchEntry */
561 {&pmshapi_handle, NULL, 125}, /* WinQuerySwitchHandle */
562 {&pmshapi_handle, NULL, 126}, /* WinQuerySwitchList */
563 {&pmshapi_handle, NULL, 131}, /* WinSwitchToProgram */
564 {&pmwin_handle, NULL, 702}, /* WinBeginEnumWindows */
565 {&pmwin_handle, NULL, 737}, /* WinEndEnumWindows */
566 {&pmwin_handle, NULL, 740}, /* WinEnumDlgItem */
567 {&pmwin_handle, NULL, 756}, /* WinGetNextWindow */
568 {&pmwin_handle, NULL, 768}, /* WinIsChild */
569 {&pmwin_handle, NULL, 799}, /* WinQueryActiveWindow */
570 {&pmwin_handle, NULL, 805}, /* WinQueryClassName */
571 {&pmwin_handle, NULL, 817}, /* WinQueryFocus */
572 {&pmwin_handle, NULL, 834}, /* WinQueryWindow */
573 {&pmwin_handle, NULL, 837}, /* WinQueryWindowPos */
574 {&pmwin_handle, NULL, 838}, /* WinQueryWindowProcess */
575 {&pmwin_handle, NULL, 841}, /* WinQueryWindowText */
576 {&pmwin_handle, NULL, 842}, /* WinQueryWindowTextLength */
577 {&pmwin_handle, NULL, 860}, /* WinSetFocus */
578 {&pmwin_handle, NULL, 875}, /* WinSetWindowPos */
579 {&pmwin_handle, NULL, 877}, /* WinSetWindowText */
580 {&pmwin_handle, NULL, 883}, /* WinShowWindow */
581 {&pmwin_handle, NULL, 772}, /* WinIsWindow */
582 {&pmwin_handle, NULL, 899}, /* WinWindowFromId */
583 {&pmwin_handle, NULL, 900}, /* WinWindowFromPoint */
584 {&pmwin_handle, NULL, 919}, /* WinPostMsg */
585 {&pmwin_handle, NULL, 735}, /* WinEnableWindow */
586 {&pmwin_handle, NULL, 736}, /* WinEnableWindowUpdate */
587 {&pmwin_handle, NULL, 773}, /* WinIsWindowEnabled */
588 {&pmwin_handle, NULL, 774}, /* WinIsWindowShowing */
589 {&pmwin_handle, NULL, 775}, /* WinIsWindowVisible */
590 {&pmwin_handle, NULL, 839}, /* WinQueryWindowPtr */
591 {&pmwin_handle, NULL, 843}, /* WinQueryWindowULong */
592 {&pmwin_handle, NULL, 844}, /* WinQueryWindowUShort */
593 {&pmwin_handle, NULL, 874}, /* WinSetWindowBits */
594 {&pmwin_handle, NULL, 876}, /* WinSetWindowPtr */
595 {&pmwin_handle, NULL, 878}, /* WinSetWindowULong */
596 {&pmwin_handle, NULL, 879}, /* WinSetWindowUShort */
597 {&pmwin_handle, NULL, 813}, /* WinQueryDesktopWindow */
598 {&pmwin_handle, NULL, 851}, /* WinSetActiveWindow */
599 {&doscalls_handle, NULL, 360}, /* DosQueryModFromEIP */
600 {&doscalls_handle, NULL, 582}, /* Dos32QueryHeaderInfo */
601 {&doscalls_handle, NULL, 362}, /* DosTmrQueryFreq */
602 {&doscalls_handle, NULL, 363}, /* DosTmrQueryTime */
603 {&pmwp_handle, NULL, 262}, /* WinQueryActiveDesktopPathname */
604 {&pmwin_handle, NULL, 765}, /* WinInvalidateRect */
605 {&pmwin_handle, NULL, 906}, /* WinCreateFrameControl */
606 {&pmwin_handle, NULL, 807}, /* WinQueryClipbrdFmtInfo */
607 {&pmwin_handle, NULL, 808}, /* WinQueryClipbrdOwner */
608 {&pmwin_handle, NULL, 809}, /* WinQueryClipbrdViewer */
609 {&pmwin_handle, NULL, 806}, /* WinQueryClipbrdData */
610 {&pmwin_handle, NULL, 793}, /* WinOpenClipbrd */
611 {&pmwin_handle, NULL, 707}, /* WinCloseClipbrd */
612 {&pmwin_handle, NULL, 854}, /* WinSetClipbrdData */
613 {&pmwin_handle, NULL, 855}, /* WinSetClipbrdOwner */
614 {&pmwin_handle, NULL, 856}, /* WinSetClipbrdViewer */
615 {&pmwin_handle, NULL, 739}, /* WinEnumClipbrdFmts */
616 {&pmwin_handle, NULL, 733}, /* WinEmptyClipbrd */
617 {&pmwin_handle, NULL, 700}, /* WinAddAtom */
618 {&pmwin_handle, NULL, 744}, /* WinFindAtom */
619 {&pmwin_handle, NULL, 721}, /* WinDeleteAtom */
620 {&pmwin_handle, NULL, 803}, /* WinQueryAtomUsage */
621 {&pmwin_handle, NULL, 802}, /* WinQueryAtomName */
622 {&pmwin_handle, NULL, 801}, /* WinQueryAtomLength */
623 {&pmwin_handle, NULL, 830}, /* WinQuerySystemAtomTable */
624 {&pmwin_handle, NULL, 714}, /* WinCreateAtomTable */
625 {&pmwin_handle, NULL, 724}, /* WinDestroyAtomTable */
626 {&pmwin_handle, NULL, 794}, /* WinOpenWindowDC */
627 {&pmgpi_handle, NULL, 610}, /* DevOpenDC */
628 {&pmgpi_handle, NULL, 606}, /* DevQueryCaps */
629 {&pmgpi_handle, NULL, 604}, /* DevCloseDC */
630 {&pmwin_handle, NULL, 789}, /* WinMessageBox */
631 {&pmwin_handle, NULL, 1015}, /* WinMessageBox2 */
632 {&pmwin_handle, NULL, 829}, /* WinQuerySysValue */
633 {&pmwin_handle, NULL, 873}, /* WinSetSysValue */
634 {&pmwin_handle, NULL, 701}, /* WinAlarm */
635 {&pmwin_handle, NULL, 745}, /* WinFlashWindow */
636 {&pmwin_handle, NULL, 780}, /* WinLoadPointer */
637 {&pmwin_handle, NULL, 828}, /* WinQuerySysPointer */
638 {&doscalls_handle, NULL, 417}, /* DosReplaceModule */
639 {&doscalls_handle, NULL, 976}, /* DosPerfSysCall */
640 {&rexxapi_handle, "RexxRegisterSubcomExe", 0},
641};
642
643HMODULE
644loadModule(const char *modname, int fail)
645{
646 HMODULE h = (HMODULE)dlopen(modname, 0);
647
648 if (!h && fail)
649 Perl_croak_nocontext("Error loading module '%s': %s",
650 modname, dlerror());
651 return h;
652}
653
654/* const char* const ptypes[] = { "FS", "DOS", "VIO", "PM", "DETACH" }; */
655
656static int
657my_type()
658{
659 int rc;
660 TIB *tib;
661 PIB *pib;
662
663 if (!(_emx_env & 0x200)) return 1; /* not OS/2. */
664 if (CheckOSError(DosGetInfoBlocks(&tib, &pib)))
665 return -1;
666
667 return (pib->pib_ultype);
668}
669
670static void
671my_type_set(int type)
672{
673 int rc;
674 TIB *tib;
675 PIB *pib;
676
677 if (!(_emx_env & 0x200))
678 Perl_croak_nocontext("Can't set type on DOS"); /* not OS/2. */
679 if (CheckOSError(DosGetInfoBlocks(&tib, &pib)))
680 croak_with_os2error("Error getting info blocks");
681 pib->pib_ultype = type;
682}
683
684PFN
685loadByOrdinal(enum entries_ordinals ord, int fail)
686{
687 if (sizeof(loadOrdinals)/sizeof(loadOrdinals[0]) != ORD_NENTRIES)
688 Perl_croak_nocontext(
689 "Wrong size of loadOrdinals array: expected %d, actual %d",
690 sizeof(loadOrdinals)/sizeof(loadOrdinals[0]), ORD_NENTRIES);
691 if (ExtFCN[ord] == NULL) {
692 PFN fcn = (PFN)-1;
693 APIRET rc;
694
695 if (!loadOrdinals[ord].dll->handle) {
696 if (loadOrdinals[ord].dll->requires_pm && my_type() < 2) { /* FS */
697 char *s = getenv("PERL_ASIF_PM");
698
699 if (!s || !atoi(s)) {
700 /* The module will not function well without PM.
701 The usual way to detect PM is the existence of the mutex
702 \SEM32\PMDRAG.SEM. */
703 HMTX hMtx = 0;
704
705 if (CheckOSError(DosOpenMutexSem("\\SEM32\\PMDRAG.SEM",
706 &hMtx)))
707 Perl_croak_nocontext("Looks like we have no PM; will not load DLL %s without $ENV{PERL_ASIF_PM}",
708 loadOrdinals[ord].dll->modname);
709 DosCloseMutexSem(hMtx);
710 }
711 }
712 MUTEX_LOCK(&perlos2_state_mutex);
713 loadOrdinals[ord].dll->handle
714 = loadModule(loadOrdinals[ord].dll->modname, fail);
715 MUTEX_UNLOCK(&perlos2_state_mutex);
716 }
717 if (!loadOrdinals[ord].dll->handle)
718 return 0; /* Possible with FAIL==0 only */
719 if (CheckOSError(DosQueryProcAddr(loadOrdinals[ord].dll->handle,
720 loadOrdinals[ord].entrypoint,
721 loadOrdinals[ord].entryname,&fcn))) {
722 char buf[20], *s = (char*)loadOrdinals[ord].entryname;
723
724 if (!fail)
725 return 0;
726 if (!s)
727 sprintf(s = buf, "%d", loadOrdinals[ord].entrypoint);
728 Perl_croak_nocontext(
729 "This version of OS/2 does not support %s.%s",
730 loadOrdinals[ord].dll->modname, s);
731 }
732 ExtFCN[ord] = fcn;
733 }
734 if ((long)ExtFCN[ord] == -1)
735 Perl_croak_nocontext("panic queryaddr");
736 return ExtFCN[ord];
737}
738
739void
740init_PMWIN_entries(void)
741{
742 int i;
743
744 for (i = ORD_WinInitialize; i <= ORD_WinCancelShutdown; i++)
745 ((PFN*)&PMWIN_entries)[i - ORD_WinInitialize] = loadByOrdinal(i, 1);
746}
747
748/*****************************************************/
749/* socket forwarders without linking with tcpip DLLs */
750
751DeclFuncByORD(struct hostent *, gethostent, ORD_GETHOSTENT, (void), ())
752DeclFuncByORD(struct netent *, getnetent, ORD_GETNETENT, (void), ())
753DeclFuncByORD(struct protoent *, getprotoent, ORD_GETPROTOENT, (void), ())
754DeclFuncByORD(struct servent *, getservent, ORD_GETSERVENT, (void), ())
755
756DeclVoidFuncByORD(sethostent, ORD_SETHOSTENT, (int x), (x))
757DeclVoidFuncByORD(setnetent, ORD_SETNETENT, (int x), (x))
758DeclVoidFuncByORD(setprotoent, ORD_SETPROTOENT, (int x), (x))
759DeclVoidFuncByORD(setservent, ORD_SETSERVENT, (int x), (x))
760
761DeclVoidFuncByORD(endhostent, ORD_ENDHOSTENT, (void), ())
762DeclVoidFuncByORD(endnetent, ORD_ENDNETENT, (void), ())
763DeclVoidFuncByORD(endprotoent, ORD_ENDPROTOENT, (void), ())
764DeclVoidFuncByORD(endservent, ORD_ENDSERVENT, (void), ())
765
766/* priorities */
767static const signed char priors[] = {0, 1, 3, 2}; /* Last two interchanged,
768 self inverse. */
769#define QSS_INI_BUFFER 1024
770
771ULONG (*pDosVerifyPidTid) (PID pid, TID tid);
772
773PQTOPLEVEL
774get_sysinfo(ULONG pid, ULONG flags)
775{
776 char *pbuffer;
777 ULONG rc, buf_len = QSS_INI_BUFFER;
778 PQTOPLEVEL psi;
779
780 if (pid) {
781 if (!pidtid_lookup) {
782 pidtid_lookup = 1;
783 *(PFN*)&pDosVerifyPidTid = loadByOrdinal(ORD_DosVerifyPidTid, 0);
784 }
785 if (pDosVerifyPidTid) { /* Warp3 or later */
786 /* Up to some fixpak QuerySysState() kills the system if a non-existent
787 pid is used. */
788 if (CheckOSError(pDosVerifyPidTid(pid, 1)))
789 return 0;
790 }
791 }
792 Newx(pbuffer, buf_len, char);
793 /* QSS_PROCESS | QSS_MODULE | QSS_SEMAPHORES | QSS_SHARED */
794 rc = QuerySysState(flags, pid, pbuffer, buf_len);
795 while (rc == ERROR_BUFFER_OVERFLOW) {
796 Renew(pbuffer, buf_len *= 2, char);
797 rc = QuerySysState(flags, pid, pbuffer, buf_len);
798 }
799 if (rc) {
800 FillOSError(rc);
801 Safefree(pbuffer);
802 return 0;
803 }
804 psi = (PQTOPLEVEL)pbuffer;
805 if (psi && pid && psi->procdata && pid != psi->procdata->pid) {
806 Safefree(psi);
807 Perl_croak_nocontext("panic: wrong pid in sysinfo");
808 }
809 return psi;
810}
811
812#define PRIO_ERR 0x1111
813
814static ULONG
815sys_prio(pid)
816{
817 ULONG prio;
818 PQTOPLEVEL psi;
819
820 if (!pid)
821 return PRIO_ERR;
822 psi = get_sysinfo(pid, QSS_PROCESS);
823 if (!psi)
824 return PRIO_ERR;
825 prio = psi->procdata->threads->priority;
826 Safefree(psi);
827 return prio;
828}
829
830int
831setpriority(int which, int pid, int val)
832{
833 ULONG rc, prio = sys_prio(pid);
834
835 if (!(_emx_env & 0x200)) return 0; /* Nop if not OS/2. */
836 if (priors[(32 - val) >> 5] + 1 == (prio >> 8)) {
837 /* Do not change class. */
838 return CheckOSError(DosSetPriority((pid < 0)
839 ? PRTYS_PROCESSTREE : PRTYS_PROCESS,
840 0,
841 (32 - val) % 32 - (prio & 0xFF),
842 abs(pid)))
843 ? -1 : 0;
844 } else /* if ((32 - val) % 32 == (prio & 0xFF)) */ {
845 /* Documentation claims one can change both class and basevalue,
846 * but I find it wrong. */
847 /* Change class, but since delta == 0 denotes absolute 0, correct. */
848 if (CheckOSError(DosSetPriority((pid < 0)
849 ? PRTYS_PROCESSTREE : PRTYS_PROCESS,
850 priors[(32 - val) >> 5] + 1,
851 0,
852 abs(pid))))
853 return -1;
854 if ( ((32 - val) % 32) == 0 ) return 0;
855 return CheckOSError(DosSetPriority((pid < 0)
856 ? PRTYS_PROCESSTREE : PRTYS_PROCESS,
857 0,
858 (32 - val) % 32,
859 abs(pid)))
860 ? -1 : 0;
861 }
862}
863
864int
865getpriority(int which /* ignored */, int pid)
866{
867 ULONG ret;
868
869 if (!(_emx_env & 0x200)) return 0; /* Nop if not OS/2. */
870 ret = sys_prio(pid);
871 if (ret == PRIO_ERR) {
872 return -1;
873 }
874 return (1 - priors[((ret >> 8) - 1)])*32 - (ret & 0xFF);
875}
876
877/*****************************************************************************/
878/* spawn */
879
880
881
882static Signal_t
883spawn_sighandler(int sig)
884{
885 /* Some programs do not arrange for the keyboard signals to be
886 delivered to them. We need to deliver the signal manually. */
887 /* We may get a signal only if
888 a) kid does not receive keyboard signal: deliver it;
889 b) kid already died, and we get a signal. We may only hope
890 that the pid number was not reused.
891 */
892
893 if (spawn_killed)
894 sig = SIGKILL; /* Try harder. */
895 kill(spawn_pid, sig);
896 spawn_killed = 1;
897}
898
899static int
900result(pTHX_ int flag, int pid)
901{
902 int r, status;
903 Signal_t (*ihand)(); /* place to save signal during system() */
904 Signal_t (*qhand)(); /* place to save signal during system() */
905#ifndef __EMX__
906 RESULTCODES res;
907 int rpid;
908#endif
909
910 if (pid < 0 || flag != 0)
911 return pid;
912
913#ifdef __EMX__
914 spawn_pid = pid;
915 spawn_killed = 0;
916 ihand = rsignal(SIGINT, &spawn_sighandler);
917 qhand = rsignal(SIGQUIT, &spawn_sighandler);
918 do {
919 r = wait4pid(pid, &status, 0);
920 } while (r == -1 && errno == EINTR);
921 rsignal(SIGINT, ihand);
922 rsignal(SIGQUIT, qhand);
923
924 PL_statusvalue = (U16)status;
925 if (r < 0)
926 return -1;
927 return status & 0xFFFF;
928#else
929 ihand = rsignal(SIGINT, SIG_IGN);
930 r = DosWaitChild(DCWA_PROCESS, DCWW_WAIT, &res, &rpid, pid);
931 rsignal(SIGINT, ihand);
932 PL_statusvalue = res.codeResult << 8 | res.codeTerminate;
933 if (r)
934 return -1;
935 return PL_statusvalue;
936#endif
937}
938
939enum execf_t {
940 EXECF_SPAWN,
941 EXECF_EXEC,
942 EXECF_TRUEEXEC,
943 EXECF_SPAWN_NOWAIT,
944 EXECF_SPAWN_BYFLAG,
945 EXECF_SYNC
946};
947
948static ULONG
949file_type(char *path)
950{
951 int rc;
952 ULONG apptype;
953
954 if (!(_emx_env & 0x200))
955 Perl_croak_nocontext("file_type not implemented on DOS"); /* not OS/2. */
956 if (CheckOSError(DosQueryAppType(path, &apptype))) {
957 switch (rc) {
958 case ERROR_FILE_NOT_FOUND:
959 case ERROR_PATH_NOT_FOUND:
960 return -1;
961 case ERROR_ACCESS_DENIED: /* Directory with this name found? */
962 return -3;
963 default: /* Found, but not an
964 executable, or some other
965 read error. */
966 return -2;
967 }
968 }
969 return apptype;
970}
971
972/* Spawn/exec a program, revert to shell if needed. */
973/* global PL_Argv[] contains arguments. */
974
975extern ULONG _emx_exception ( EXCEPTIONREPORTRECORD *,
976 EXCEPTIONREGISTRATIONRECORD *,
977 CONTEXTRECORD *,
978 void *);
979
980int
981do_spawn_ve(pTHX_ SV *really, U32 flag, U32 execf, char *inicmd, U32 addflag)
982{
983 int trueflag = flag;
984 int rc, pass = 1;
985 char *real_name = NULL; /* Shut down the warning */
986 char const * args[4];
987 static const char * const fargs[4]
988 = { "/bin/sh", "-c", "\"$@\"", "spawn-via-shell", };
989 const char * const *argsp = fargs;
990 int nargs = 4;
991 int force_shell;
992 int new_stderr = -1, nostderr = 0;
993 int fl_stderr = 0;
994 STRLEN n_a;
995 char *buf;
996 PerlIO *file;
997
998 if (flag == P_WAIT)
999 flag = P_NOWAIT;
1000 if (really && !*(real_name = SvPV(really, n_a)))
1001 really = NULL;
1002
1003 retry:
1004 if (strEQ(PL_Argv[0],"/bin/sh"))
1005 PL_Argv[0] = PL_sh_path;
1006
1007 /* We should check PERL_SH* and PERLLIB_* as well? */
1008 if (!really || pass >= 2)
1009 real_name = PL_Argv[0];
1010 if (real_name[0] != '/' && real_name[0] != '\\'
1011 && !(real_name[0] && real_name[1] == ':'
1012 && (real_name[2] == '/' || real_name[2] != '\\'))
1013 ) /* will spawnvp use PATH? */
1014 TAINT_ENV(); /* testing IFS here is overkill, probably */
1015
1016 reread:
1017 force_shell = 0;
1018 if (_emx_env & 0x200) { /* OS/2. */
1019 int type = file_type(real_name);
1020 type_again:
1021 if (type == -1) { /* Not found */
1022 errno = ENOENT;
1023 rc = -1;
1024 goto do_script;
1025 }
1026 else if (type == -2) { /* Not an EXE */
1027 errno = ENOEXEC;
1028 rc = -1;
1029 goto do_script;
1030 }
1031 else if (type == -3) { /* Is a directory? */
1032 /* Special-case this */
1033 char tbuf[512];
1034 int l = strlen(real_name);
1035
1036 if (l + 5 <= sizeof tbuf) {
1037 strcpy(tbuf, real_name);
1038 strcpy(tbuf + l, ".exe");
1039 type = file_type(tbuf);
1040 if (type >= -3)
1041 goto type_again;
1042 }
1043
1044 errno = ENOEXEC;
1045 rc = -1;
1046 goto do_script;
1047 }
1048 switch (type & 7) {
1049 /* Ignore WINDOWCOMPAT and FAPI, start them the same type we are. */
1050 case FAPPTYP_WINDOWAPI:
1051 { /* Apparently, kids are started basing on startup type, not the morphed type */
1052 if (os2_mytype != 3) { /* not PM */
1053 if (flag == P_NOWAIT)
1054 flag = P_PM;
1055 else if ((flag & 7) != P_PM && (flag & 7) != P_SESSION && ckWARN(WARN_EXEC))
1056 Perl_warner(aTHX_ packWARN(WARN_EXEC), "Starting PM process with flag=%d, mytype=%d",
1057 flag, os2_mytype);
1058 }
1059 }
1060 break;
1061 case FAPPTYP_NOTWINDOWCOMPAT:
1062 {
1063 if (os2_mytype != 0) { /* not full screen */
1064 if (flag == P_NOWAIT)
1065 flag = P_SESSION;
1066 else if ((flag & 7) != P_SESSION && ckWARN(WARN_EXEC))
1067 Perl_warner(aTHX_ packWARN(WARN_EXEC), "Starting Full Screen process with flag=%d, mytype=%d",
1068 flag, os2_mytype);
1069 }
1070 }
1071 break;
1072 case FAPPTYP_NOTSPEC:
1073 /* Let the shell handle this... */
1074 force_shell = 1;
1075 buf = ""; /* Pacify a warning */
1076 file = 0; /* Pacify a warning */
1077 goto doshell_args;
1078 break;
1079 }
1080 }
1081
1082 if (addflag) {
1083 addflag = 0;
1084 new_stderr = dup(2); /* Preserve stderr */
1085 if (new_stderr == -1) {
1086 if (errno == EBADF)
1087 nostderr = 1;
1088 else {
1089 rc = -1;
1090 goto finish;
1091 }
1092 } else
1093 fl_stderr = fcntl(2, F_GETFD);
1094 rc = dup2(1,2);
1095 if (rc == -1)
1096 goto finish;
1097 fcntl(new_stderr, F_SETFD, FD_CLOEXEC);
1098 }
1099
1100#if 0
1101 rc = result(aTHX_ trueflag, spawnvp(flag,real_name,PL_Argv));
1102#else
1103 if (execf == EXECF_TRUEEXEC)
1104 rc = execvp(real_name,PL_Argv);
1105 else if (execf == EXECF_EXEC)
1106 rc = spawnvp(trueflag | P_OVERLAY,real_name,PL_Argv);
1107 else if (execf == EXECF_SPAWN_NOWAIT)
1108 rc = spawnvp(flag,real_name,PL_Argv);
1109 else if (execf == EXECF_SYNC)
1110 rc = spawnvp(trueflag,real_name,PL_Argv);
1111 else /* EXECF_SPAWN, EXECF_SPAWN_BYFLAG */
1112 rc = result(aTHX_ trueflag,
1113 spawnvp(flag,real_name,PL_Argv));
1114#endif
1115 if (rc < 0 && pass == 1) {
1116 do_script:
1117 if (real_name == PL_Argv[0]) {
1118 int err = errno;
1119
1120 if (err == ENOENT || err == ENOEXEC) {
1121 /* No such file, or is a script. */
1122 /* Try adding script extensions to the file name, and
1123 search on PATH. */
1124 char *scr = find_script(PL_Argv[0], TRUE, NULL, 0);
1125
1126 if (scr) {
1127 char *s = 0, *s1;
1128 SV *scrsv = sv_2mortal(newSVpv(scr, 0));
1129 SV *bufsv = sv_newmortal();
1130
1131 Safefree(scr);
1132 scr = SvPV(scrsv, n_a); /* free()ed later */
1133
1134 file = PerlIO_open(scr, "r");
1135 PL_Argv[0] = scr;
1136 if (!file)
1137 goto panic_file;
1138
1139 buf = sv_gets(bufsv, file, 0 /* No append */);
1140 if (!buf)
1141 buf = ""; /* XXX Needed? */
1142 if (!buf[0]) { /* Empty... */
1143 struct stat statbuf;
1144 PerlIO_close(file);
1145 /* Special case: maybe from -Zexe build, so
1146 there is an executable around (contrary to
1147 documentation, DosQueryAppType sometimes (?)
1148 does not append ".exe", so we could have
1149 reached this place). */
1150 sv_catpv(scrsv, ".exe");
1151 PL_Argv[0] = scr = SvPV(scrsv, n_a); /* Reload */
1152 if (PerlLIO_stat(scr,&statbuf) >= 0
1153 && !S_ISDIR(statbuf.st_mode)) { /* Found */
1154 real_name = scr;
1155 pass++;
1156 goto reread;
1157 } else { /* Restore */
1158 SvCUR_set(scrsv, SvCUR(scrsv) - 4);
1159 *SvEND(scrsv) = 0;
1160 }
1161 }
1162 if (PerlIO_close(file) != 0) { /* Failure */
1163 panic_file:
1164 if (ckWARN(WARN_EXEC))
1165 Perl_warner(aTHX_ packWARN(WARN_EXEC), "Error reading \"%s\": %s",
1166 scr, Strerror(errno));
1167 buf = ""; /* Not #! */
1168 goto doshell_args;
1169 }
1170 if (buf[0] == '#') {
1171 if (buf[1] == '!')
1172 s = buf + 2;
1173 } else if (buf[0] == 'e') {
1174 if (strnEQ(buf, "extproc", 7)
1175 && isSPACE(buf[7]))
1176 s = buf + 8;
1177 } else if (buf[0] == 'E') {
1178 if (strnEQ(buf, "EXTPROC", 7)
1179 && isSPACE(buf[7]))
1180 s = buf + 8;
1181 }
1182 if (!s) {
1183 buf = ""; /* Not #! */
1184 goto doshell_args;
1185 }
1186
1187 s1 = s;
1188 nargs = 0;
1189 argsp = args;
1190 while (1) {
1191 /* Do better than pdksh: allow a few args,
1192 strip trailing whitespace. */
1193 while (isSPACE(*s))
1194 s++;
1195 if (*s == 0)
1196 break;
1197 if (nargs == 4) {
1198 nargs = -1;
1199 break;
1200 }
1201 args[nargs++] = s;
1202 while (*s && !isSPACE(*s))
1203 s++;
1204 if (*s == 0)
1205 break;
1206 *s++ = 0;
1207 }
1208 if (nargs == -1) {
1209 Perl_warner(aTHX_ packWARN(WARN_EXEC), "Too many args on %.*s line of \"%s\"",
1210 s1 - buf, buf, scr);
1211 nargs = 4;
1212 argsp = fargs;
1213 }
1214 /* Can jump from far, buf/file invalid if force_shell: */
1215 doshell_args:
1216 {
1217 char **a = PL_Argv;
1218 const char *exec_args[2];
1219
1220 if (force_shell
1221 || (!buf[0] && file)) { /* File without magic */
1222 /* In fact we tried all what pdksh would
1223 try. There is no point in calling
1224 pdksh, we may just emulate its logic. */
1225 char *shell = getenv("EXECSHELL");
1226 char *shell_opt = NULL;
1227
1228 if (!shell) {
1229 char *s;
1230
1231 shell_opt = "/c";
1232 shell = getenv("OS2_SHELL");
1233 if (inicmd) { /* No spaces at start! */
1234 s = inicmd;
1235 while (*s && !isSPACE(*s)) {
1236 if (*s++ == '/') {
1237 inicmd = NULL; /* Cannot use */
1238 break;
1239 }
1240 }
1241 }
1242 if (!inicmd) {
1243 s = PL_Argv[0];
1244 while (*s) {
1245 /* Dosish shells will choke on slashes
1246 in paths, fortunately, this is
1247 important for zeroth arg only. */
1248 if (*s == '/')
1249 *s = '\\';
1250 s++;
1251 }
1252 }
1253 }
1254 /* If EXECSHELL is set, we do not set */
1255
1256 if (!shell)
1257 shell = ((_emx_env & 0x200)
1258 ? "c:/os2/cmd.exe"
1259 : "c:/command.com");
1260 nargs = shell_opt ? 2 : 1; /* shell file args */
1261 exec_args[0] = shell;
1262 exec_args[1] = shell_opt;
1263 argsp = exec_args;
1264 if (nargs == 2 && inicmd) {
1265 /* Use the original cmd line */
1266 /* XXXX This is good only until we refuse
1267 quoted arguments... */
1268 PL_Argv[0] = inicmd;
1269 PL_Argv[1] = NULL;
1270 }
1271 } else if (!buf[0] && inicmd) { /* No file */
1272 /* Start with the original cmdline. */
1273 /* XXXX This is good only until we refuse
1274 quoted arguments... */
1275
1276 PL_Argv[0] = inicmd;
1277 PL_Argv[1] = NULL;
1278 nargs = 2; /* shell -c */
1279 }
1280
1281 while (a[1]) /* Get to the end */
1282 a++;
1283 a++; /* Copy finil NULL too */
1284 while (a >= PL_Argv) {
1285 *(a + nargs) = *a; /* PL_Argv was preallocated to be
1286 long enough. */
1287 a--;
1288 }
1289 while (--nargs >= 0) /* XXXX Discard const... */
1290 PL_Argv[nargs] = (char*)argsp[nargs];
1291 /* Enable pathless exec if #! (as pdksh). */
1292 pass = (buf[0] == '#' ? 2 : 3);
1293 goto retry;
1294 }
1295 }
1296 /* Not found: restore errno */
1297 errno = err;
1298 }
1299 } else if (errno == ENOEXEC) { /* Cannot transfer `real_name' via shell. */
1300 if (rc < 0 && ckWARN(WARN_EXEC))
1301 Perl_warner(aTHX_ packWARN(WARN_EXEC), "Can't %s script `%s' with ARGV[0] being `%s'",
1302 ((execf != EXECF_EXEC && execf != EXECF_TRUEEXEC)
1303 ? "spawn" : "exec"),
1304 real_name, PL_Argv[0]);
1305 goto warned;
1306 } else if (errno == ENOENT) { /* Cannot transfer `real_name' via shell. */
1307 if (rc < 0 && ckWARN(WARN_EXEC))
1308 Perl_warner(aTHX_ packWARN(WARN_EXEC), "Can't %s `%s' with ARGV[0] being `%s' (looking for executables only, not found)",
1309 ((execf != EXECF_EXEC && execf != EXECF_TRUEEXEC)
1310 ? "spawn" : "exec"),
1311 real_name, PL_Argv[0]);
1312 goto warned;
1313 }
1314 } else if (rc < 0 && pass == 2 && errno == ENOENT) { /* File not found */
1315 char *no_dir = strrchr(PL_Argv[0], '/');
1316
1317 /* Do as pdksh port does: if not found with /, try without
1318 path. */
1319 if (no_dir) {
1320 PL_Argv[0] = no_dir + 1;
1321 pass++;
1322 goto retry;
1323 }
1324 }
1325 if (rc < 0 && ckWARN(WARN_EXEC))
1326 Perl_warner(aTHX_ packWARN(WARN_EXEC), "Can't %s \"%s\": %s\n",
1327 ((execf != EXECF_EXEC && execf != EXECF_TRUEEXEC)
1328 ? "spawn" : "exec"),
1329 real_name, Strerror(errno));
1330 warned:
1331 if (rc < 0 && (execf != EXECF_SPAWN_NOWAIT)
1332 && ((trueflag & 0xFF) == P_WAIT))
1333 rc = -1;
1334
1335 finish:
1336 if (new_stderr != -1) { /* How can we use error codes? */
1337 dup2(new_stderr, 2);
1338 close(new_stderr);
1339 fcntl(2, F_SETFD, fl_stderr);
1340 } else if (nostderr)
1341 close(2);
1342 return rc;
1343}
1344
1345/* Try converting 1-arg form to (usually shell-less) multi-arg form. */
1346int
1347do_spawn3(pTHX_ char *cmd, int execf, int flag)
1348{
1349 char **a;
1350 char *s;
1351 char *shell, *copt, *news = NULL;
1352 int rc, seenspace = 0, mergestderr = 0;
1353
1354#ifdef TRYSHELL
1355 if ((shell = getenv("EMXSHELL")) != NULL)
1356 copt = "-c";
1357 else if ((shell = getenv("SHELL")) != NULL)
1358 copt = "-c";
1359 else if ((shell = getenv("COMSPEC")) != NULL)
1360 copt = "/C";
1361 else
1362 shell = "cmd.exe";
1363#else
1364 /* Consensus on perl5-porters is that it is _very_ important to
1365 have a shell which will not change between computers with the
1366 same architecture, to avoid "action on a distance".
1367 And to have simple build, this shell should be sh. */
1368 shell = PL_sh_path;
1369 copt = "-c";
1370#endif
1371
1372 while (*cmd && isSPACE(*cmd))
1373 cmd++;
1374
1375 if (strnEQ(cmd,"/bin/sh",7) && isSPACE(cmd[7])) {
1376 STRLEN l = strlen(PL_sh_path);
1377
1378 Newx(news, strlen(cmd) - 7 + l + 1, char);
1379 strcpy(news, PL_sh_path);
1380 strcpy(news + l, cmd + 7);
1381 cmd = news;
1382 }
1383
1384 /* save an extra exec if possible */
1385 /* see if there are shell metacharacters in it */
1386
1387 if (*cmd == '.' && isSPACE(cmd[1]))
1388 goto doshell;
1389
1390 if (strnEQ(cmd,"exec",4) && isSPACE(cmd[4]))
1391 goto doshell;
1392
1393 for (s = cmd; *s && isALPHA(*s); s++) ; /* catch VAR=val gizmo */
1394 if (*s == '=')
1395 goto doshell;
1396
1397 for (s = cmd; *s; s++) {
1398 if (*s != ' ' && !isALPHA(*s) && strchr("$&*(){}[]'\";\\|?<>~`\n",*s)) {
1399 if (*s == '\n' && s[1] == '\0') {
1400 *s = '\0';
1401 break;
1402 } else if (*s == '\\' && !seenspace) {
1403 continue; /* Allow backslashes in names */
1404 } else if (*s == '>' && s >= cmd + 3
1405 && s[-1] == '2' && s[1] == '&' && s[2] == '1'
1406 && isSPACE(s[-2]) ) {
1407 char *t = s + 3;
1408
1409 while (*t && isSPACE(*t))
1410 t++;
1411 if (!*t) {
1412 s[-2] = '\0';
1413 mergestderr = 1;
1414 break; /* Allow 2>&1 as the last thing */
1415 }
1416 }
1417 /* We do not convert this to do_spawn_ve since shell
1418 should be smart enough to start itself gloriously. */
1419 doshell:
1420 if (execf == EXECF_TRUEEXEC)
1421 rc = execl(shell,shell,copt,cmd,(char*)0);
1422 else if (execf == EXECF_EXEC)
1423 rc = spawnl(P_OVERLAY,shell,shell,copt,cmd,(char*)0);
1424 else if (execf == EXECF_SPAWN_NOWAIT)
1425 rc = spawnl(P_NOWAIT,shell,shell,copt,cmd,(char*)0);
1426 else if (execf == EXECF_SPAWN_BYFLAG)
1427 rc = spawnl(flag,shell,shell,copt,cmd,(char*)0);
1428 else {
1429 /* In the ak code internal P_NOWAIT is P_WAIT ??? */
1430 if (execf == EXECF_SYNC)
1431 rc = spawnl(P_WAIT,shell,shell,copt,cmd,(char*)0);
1432 else
1433 rc = result(aTHX_ P_WAIT,
1434 spawnl(P_NOWAIT,shell,shell,copt,cmd,(char*)0));
1435 if (rc < 0 && ckWARN(WARN_EXEC))
1436 Perl_warner(aTHX_ packWARN(WARN_EXEC), "Can't %s \"%s\": %s",
1437 (execf == EXECF_SPAWN ? "spawn" : "exec"),
1438 shell, Strerror(errno));
1439 if (rc < 0)
1440 rc = -1;
1441 }
1442 if (news)
1443 Safefree(news);
1444 return rc;
1445 } else if (*s == ' ' || *s == '\t') {
1446 seenspace = 1;
1447 }
1448 }
1449
1450 /* cmd="a" may lead to "sh", "-c", "\"$@\"", "a", "a.cmd", NULL */
1451 Newx(PL_Argv, (s - cmd + 11) / 2, char*);
1452 PL_Cmd = savepvn(cmd, s-cmd);
1453 a = PL_Argv;
1454 for (s = PL_Cmd; *s;) {
1455 while (*s && isSPACE(*s)) s++;
1456 if (*s)
1457 *(a++) = s;
1458 while (*s && !isSPACE(*s)) s++;
1459 if (*s)
1460 *s++ = '\0';
1461 }
1462 *a = NULL;
1463 if (PL_Argv[0])
1464 rc = do_spawn_ve(aTHX_ NULL, flag, execf, cmd, mergestderr);
1465 else
1466 rc = -1;
1467 if (news)
1468 Safefree(news);
1469 do_execfree();
1470 return rc;
1471}
1472
1473#define ASPAWN_WAIT 0
1474#define ASPAWN_EXEC 1
1475#define ASPAWN_NOWAIT 2
1476
1477/* Array spawn/exec. */
1478int
1479os2_aspawn_4(pTHX_ SV *really, SV **args, I32 cnt, int execing)
1480{
1481 SV **argp = (SV **)args;
1482 SV **last = argp + cnt;
1483 char **a;
1484 int rc;
1485 int flag = P_WAIT, flag_set = 0;
1486 STRLEN n_a;
1487
1488 if (cnt) {
1489 Newx(PL_Argv, cnt + 3, char*); /* 3 extra to expand #! */
1490 a = PL_Argv;
1491
1492 if (cnt > 1 && SvNIOKp(*argp) && !SvPOKp(*argp)) {
1493 flag = SvIVx(*argp);
1494 flag_set = 1;
1495 } else
1496 --argp;
1497
1498 while (++argp < last) {
1499 if (*argp)
1500 *a++ = SvPVx(*argp, n_a);
1501 else
1502 *a++ = "";
1503 }
1504 *a = NULL;
1505
1506 if ( flag_set && (a == PL_Argv + 1)
1507 && !really && execing == ASPAWN_WAIT ) { /* One arg? */
1508 rc = do_spawn3(aTHX_ a[-1], EXECF_SPAWN_BYFLAG, flag);
1509 } else {
1510 const int execf[3] = {EXECF_SPAWN, EXECF_EXEC, EXECF_SPAWN_NOWAIT};
1511
1512 rc = do_spawn_ve(aTHX_ really, flag, execf[execing], NULL, 0);
1513 }
1514 } else
1515 rc = -1;
1516 do_execfree();
1517 return rc;
1518}
1519
1520/* Array spawn. */
1521int
1522os2_do_aspawn(pTHX_ SV *really, SV **vmark, SV **vsp)
1523{
1524 return os2_aspawn_4(aTHX_ really, vmark + 1, vsp - vmark, ASPAWN_WAIT);
1525}
1526
1527/* Array exec. */
1528bool
1529Perl_do_aexec(pTHX_ SV* really, SV** vmark, SV** vsp)
1530{
1531 return os2_aspawn_4(aTHX_ really, vmark + 1, vsp - vmark, ASPAWN_EXEC);
1532}
1533
1534int
1535os2_do_spawn(pTHX_ char *cmd)
1536{
1537 return do_spawn3(aTHX_ cmd, EXECF_SPAWN, 0);
1538}
1539
1540int
1541do_spawn_nowait(pTHX_ char *cmd)
1542{
1543 return do_spawn3(aTHX_ cmd, EXECF_SPAWN_NOWAIT,0);
1544}
1545
1546bool
1547Perl_do_exec(pTHX_ const char *cmd)
1548{
1549 do_spawn3(aTHX_ cmd, EXECF_EXEC, 0);
1550 return FALSE;
1551}
1552
1553bool
1554os2exec(pTHX_ char *cmd)
1555{
1556 return do_spawn3(aTHX_ cmd, EXECF_TRUEEXEC, 0);
1557}
1558
1559PerlIO *
1560my_syspopen4(pTHX_ char *cmd, char *mode, I32 cnt, SV** args)
1561{
1562#ifndef USE_POPEN
1563 int p[2];
1564 I32 this, that, newfd;
1565 I32 pid;
1566 SV *sv;
1567 int fh_fl = 0; /* Pacify the warning */
1568
1569 /* `this' is what we use in the parent, `that' in the child. */
1570 this = (*mode == 'w');
1571 that = !this;
1572 if (TAINTING_get) {
1573 taint_env();
1574 taint_proper("Insecure %s%s", "EXEC");
1575 }
1576 if (pipe(p) < 0)
1577 return NULL;
1578 /* Now we need to spawn the child. */
1579 if (p[this] == (*mode == 'r')) { /* if fh 0/1 was initially closed. */
1580 int new = dup(p[this]);
1581
1582 if (new == -1)
1583 goto closepipes;
1584 close(p[this]);
1585 p[this] = new;
1586 }
1587 newfd = dup(*mode == 'r'); /* Preserve std* */
1588 if (newfd == -1) {
1589 /* This cannot happen due to fh being bad after pipe(), since
1590 pipe() should have created fh 0 and 1 even if they were
1591 initially closed. But we closed p[this] before. */
1592 if (errno != EBADF) {
1593 closepipes:
1594 close(p[0]);
1595 close(p[1]);
1596 return NULL;
1597 }
1598 } else
1599 fh_fl = fcntl(*mode == 'r', F_GETFD);
1600 if (p[that] != (*mode == 'r')) { /* if fh 0/1 was initially closed. */
1601 dup2(p[that], *mode == 'r');
1602 close(p[that]);
1603 }
1604 /* Where is `this' and newfd now? */
1605 fcntl(p[this], F_SETFD, FD_CLOEXEC);
1606 if (newfd != -1)
1607 fcntl(newfd, F_SETFD, FD_CLOEXEC);
1608 if (cnt) { /* Args: "Real cmd", before first arg, the last, execing */
1609 pid = os2_aspawn_4(aTHX_ NULL, args, cnt, ASPAWN_NOWAIT);
1610 } else
1611 pid = do_spawn_nowait(aTHX_ cmd);
1612 if (newfd == -1)
1613 close(*mode == 'r'); /* It was closed initially */
1614 else if (newfd != (*mode == 'r')) { /* Probably this check is not needed */
1615 dup2(newfd, *mode == 'r'); /* Return std* back. */
1616 close(newfd);
1617 fcntl(*mode == 'r', F_SETFD, fh_fl);
1618 } else
1619 fcntl(*mode == 'r', F_SETFD, fh_fl);
1620 if (p[that] == (*mode == 'r'))
1621 close(p[that]);
1622 if (pid == -1) {
1623 close(p[this]);
1624 return NULL;
1625 }
1626 if (p[that] < p[this]) { /* Make fh as small as possible */
1627 dup2(p[this], p[that]);
1628 close(p[this]);
1629 p[this] = p[that];
1630 }
1631 sv = *av_fetch(PL_fdpid,p[this],TRUE);
1632 (void)SvUPGRADE(sv,SVt_IV);
1633 SvIVX(sv) = pid;
1634 PL_forkprocess = pid;
1635 return PerlIO_fdopen(p[this], mode);
1636
1637#else /* USE_POPEN */
1638
1639 PerlIO *res;
1640 SV *sv;
1641
1642 if (cnt)
1643 Perl_croak(aTHX_ "List form of piped open not implemented");
1644
1645# ifdef TRYSHELL
1646 res = popen(cmd, mode);
1647# else
1648 char *shell = getenv("EMXSHELL");
1649
1650 my_setenv("EMXSHELL", PL_sh_path);
1651 res = popen(cmd, mode);
1652 my_setenv("EMXSHELL", shell);
1653# endif
1654 sv = *av_fetch(PL_fdpid, PerlIO_fileno(res), TRUE);
1655 (void)SvUPGRADE(sv,SVt_IV);
1656 SvIVX(sv) = -1; /* A cooky. */
1657 return res;
1658
1659#endif /* USE_POPEN */
1660
1661}
1662
1663PerlIO *
1664my_syspopen(pTHX_ char *cmd, char *mode)
1665{
1666 return my_syspopen4(aTHX_ cmd, mode, 0, NULL);
1667}
1668
1669/******************************************************************/
1670
1671#ifndef HAS_FORK
1672int
1673fork(void)
1674{
1675 Perl_croak_nocontext(PL_no_func, "Unsupported function fork");
1676 errno = EINVAL;
1677 return -1;
1678}
1679#endif
1680
1681/*******************************************************************/
1682/* not implemented in EMX 0.9d */
1683
1684char * ctermid(char *s) { return 0; }
1685
1686#ifdef MYTTYNAME /* was not in emx0.9a */
1687void * ttyname(x) { return 0; }
1688#endif
1689
1690/*****************************************************************************/
1691/* not implemented in C Set++ */
1692
1693#ifndef __EMX__
1694int setuid(x) { errno = EINVAL; return -1; }
1695int setgid(x) { errno = EINVAL; return -1; }
1696#endif
1697
1698/*****************************************************************************/
1699/* stat() hack for char/block device */
1700
1701#if OS2_STAT_HACK
1702
1703enum os2_stat_extra { /* EMX 0.9d fix 4 defines up to 0100000 */
1704 os2_stat_archived = 0x1000000, /* 0100000000 */
1705 os2_stat_hidden = 0x2000000, /* 0200000000 */
1706 os2_stat_system = 0x4000000, /* 0400000000 */
1707 os2_stat_force = 0x8000000, /* Do not ignore flags on chmod */
1708};
1709
1710#define OS2_STAT_SPECIAL (os2_stat_system | os2_stat_archived | os2_stat_hidden)
1711
1712static void
1713massage_os2_attr(struct stat *st)
1714{
1715 if ( ((st->st_mode & S_IFMT) != S_IFREG
1716 && (st->st_mode & S_IFMT) != S_IFDIR)
1717 || !(st->st_attr & (FILE_ARCHIVED | FILE_HIDDEN | FILE_SYSTEM)))
1718 return;
1719
1720 if ( st->st_attr & FILE_ARCHIVED )
1721 st->st_mode |= (os2_stat_archived | os2_stat_force);
1722 if ( st->st_attr & FILE_HIDDEN )
1723 st->st_mode |= (os2_stat_hidden | os2_stat_force);
1724 if ( st->st_attr & FILE_SYSTEM )
1725 st->st_mode |= (os2_stat_system | os2_stat_force);
1726}
1727
1728 /* First attempt used DosQueryFSAttach which crashed the system when
1729 used with 5.001. Now just look for /dev/. */
1730int
1731os2_stat(const char *name, struct stat *st)
1732{
1733 static int ino = SHRT_MAX;
1734 STRLEN l = strlen(name);
1735
1736 if ( ( l < 8 || l > 9) || strnicmp(name, "/dev/", 5) != 0
1737 || ( stricmp(name + 5, "con") != 0
1738 && stricmp(name + 5, "tty") != 0
1739 && stricmp(name + 5, "nul") != 0
1740 && stricmp(name + 5, "null") != 0) ) {
1741 int s = stat(name, st);
1742
1743 if (s)
1744 return s;
1745 massage_os2_attr(st);
1746 return 0;
1747 }
1748
1749 memset(st, 0, sizeof *st);
1750 st->st_mode = S_IFCHR|0666;
1751 MUTEX_LOCK(&perlos2_state_mutex);
1752 st->st_ino = (ino-- & 0x7FFF);
1753 MUTEX_UNLOCK(&perlos2_state_mutex);
1754 st->st_nlink = 1;
1755 return 0;
1756}
1757
1758int
1759os2_fstat(int handle, struct stat *st)
1760{
1761 int s = fstat(handle, st);
1762
1763 if (s)
1764 return s;
1765 massage_os2_attr(st);
1766 return 0;
1767}
1768
1769#undef chmod
1770int
1771os2_chmod (const char *name, int pmode) /* Modelled after EMX src/lib/io/chmod.c */
1772{
1773 int attr, rc;
1774
1775 if (!(pmode & os2_stat_force))
1776 return chmod(name, pmode);
1777
1778 attr = __chmod (name, 0, 0); /* Get attributes */
1779 if (attr < 0)
1780 return -1;
1781 if (pmode & S_IWRITE)
1782 attr &= ~FILE_READONLY;
1783 else
1784 attr |= FILE_READONLY;
1785 /* New logic */
1786 attr &= ~(FILE_ARCHIVED | FILE_HIDDEN | FILE_SYSTEM);
1787
1788 if ( pmode & os2_stat_archived )
1789 attr |= FILE_ARCHIVED;
1790 if ( pmode & os2_stat_hidden )
1791 attr |= FILE_HIDDEN;
1792 if ( pmode & os2_stat_system )
1793 attr |= FILE_SYSTEM;
1794
1795 rc = __chmod (name, 1, attr);
1796 if (rc >= 0) rc = 0;
1797 return rc;
1798}
1799
1800#endif
1801
1802#ifdef USE_PERL_SBRK
1803
1804/* SBRK() emulation, mostly moved to malloc.c. */
1805
1806void *
1807sys_alloc(int size) {
1808 void *got;
1809 APIRET rc = DosAllocMem(&got, size, PAG_COMMIT | PAG_WRITE);
1810
1811 if (rc == ERROR_NOT_ENOUGH_MEMORY) {
1812 return (void *) -1;
1813 } else if ( rc )
1814 Perl_croak_nocontext("Got an error from DosAllocMem: %li", (long)rc);
1815 return got;
1816}
1817
1818#endif /* USE_PERL_SBRK */
1819
1820/* tmp path */
1821
1822const char *tmppath = TMPPATH1;
1823
1824void
1825settmppath()
1826{
1827 char *p = getenv("TMP"), *tpath;
1828 int len;
1829
1830 if (!p) p = getenv("TEMP");
1831 if (!p) p = getenv("TMPDIR");
1832 if (!p) return;
1833 len = strlen(p);
1834 tpath = (char *)malloc(len + strlen(TMPPATH1) + 2);
1835 if (tpath) {
1836 strcpy(tpath, p);
1837 tpath[len] = '/';
1838 strcpy(tpath + len + 1, TMPPATH1);
1839 tmppath = tpath;
1840 }
1841}
1842
1843#include "XSUB.h"
1844
1845XS(XS_File__Copy_syscopy)
1846{
1847 dXSARGS;
1848 if (items < 2 || items > 3)
1849 Perl_croak_nocontext("Usage: File::Copy::syscopy(src,dst,flag=0)");
1850 {
1851 STRLEN n_a;
1852 char * src = (char *)SvPV(ST(0),n_a);
1853 char * dst = (char *)SvPV(ST(1),n_a);
1854 U32 flag;
1855 int RETVAL, rc;
1856 dXSTARG;
1857
1858 if (items < 3)
1859 flag = 0;
1860 else {
1861 flag = (unsigned long)SvIV(ST(2));
1862 }
1863
1864 RETVAL = !CheckOSError(DosCopy(src, dst, flag));
1865 XSprePUSH; PUSHi((IV)RETVAL);
1866 }
1867 XSRETURN(1);
1868}
1869
1870/* APIRET APIENTRY DosReplaceModule (PCSZ pszOld, PCSZ pszNew, PCSZ pszBackup); */
1871
1872DeclOSFuncByORD(ULONG,replaceModule,ORD_DosReplaceModule,
1873 (char *old, char *new, char *backup), (old, new, backup))
1874
1875XS(XS_OS2_replaceModule); /* prototype to pass -Wmissing-prototypes */
1876XS(XS_OS2_replaceModule)
1877{
1878 dXSARGS;
1879 if (items < 1 || items > 3)
1880 Perl_croak(aTHX_ "Usage: OS2::replaceModule(target [, source [, backup]])");
1881 {
1882 char * target = (char *)SvPV_nolen(ST(0));
1883 char * source = (items < 2) ? NULL : (char *)SvPV_nolen(ST(1));
1884 char * backup = (items < 3) ? NULL : (char *)SvPV_nolen(ST(2));
1885
1886 if (!replaceModule(target, source, backup))
1887 croak_with_os2error("replaceModule() error");
1888 }
1889 XSRETURN_YES;
1890}
1891
1892/* APIRET APIENTRY DosPerfSysCall(ULONG ulCommand, ULONG ulParm1,
1893 ULONG ulParm2, ULONG ulParm3); */
1894
1895DeclOSFuncByORD(ULONG,perfSysCall,ORD_DosPerfSysCall,
1896 (ULONG ulCommand, ULONG ulParm1, ULONG ulParm2, ULONG ulParm3),
1897 (ulCommand, ulParm1, ulParm2, ulParm3))
1898
1899#ifndef CMD_KI_RDCNT
1900# define CMD_KI_RDCNT 0x63
1901#endif
1902#ifndef CMD_KI_GETQTY
1903# define CMD_KI_GETQTY 0x41
1904#endif
1905#ifndef QSV_NUMPROCESSORS
1906# define QSV_NUMPROCESSORS 26
1907#endif
1908
1909typedef unsigned long long myCPUUTIL[4]; /* time/idle/busy/intr */
1910
1911/*
1912NO_OUTPUT ULONG
1913perfSysCall(ULONG ulCommand, ULONG ulParm1, ULONG ulParm2, ULONG ulParm3)
1914 PREINIT:
1915 ULONG rc;
1916 POSTCALL:
1917 if (!RETVAL)
1918 croak_with_os2error("perfSysCall() error");
1919 */
1920
1921static int
1922numprocessors(void)
1923{
1924 ULONG res;
1925
1926 if (DosQuerySysInfo(QSV_NUMPROCESSORS, QSV_NUMPROCESSORS, (PVOID)&res, sizeof(res)))
1927 return 1; /* Old system? */
1928 return res;
1929}
1930
1931XS(XS_OS2_perfSysCall); /* prototype to pass -Wmissing-prototypes */
1932XS(XS_OS2_perfSysCall)
1933{
1934 dXSARGS;
1935 if (items < 0 || items > 4)
1936 Perl_croak(aTHX_ "Usage: OS2::perfSysCall(ulCommand = CMD_KI_RDCNT, ulParm1= 0, ulParm2= 0, ulParm3= 0)");
1937 SP -= items;
1938 {
1939 dXSTARG;
1940 ULONG RETVAL, ulCommand, ulParm1, ulParm2, ulParm3, res;
1941 myCPUUTIL u[64];
1942 int total = 0, tot2 = 0;
1943
1944 if (items < 1)
1945 ulCommand = CMD_KI_RDCNT;
1946 else {
1947 ulCommand = (ULONG)SvUV(ST(0));
1948 }
1949
1950 if (items < 2) {
1951 total = (ulCommand == CMD_KI_RDCNT ? numprocessors() : 0);
1952 ulParm1 = (total ? (ULONG)u : 0);
1953
1954 if (total > C_ARRAY_LENGTH(u))
1955 croak("Unexpected number of processors: %d", total);
1956 } else {
1957 ulParm1 = (ULONG)SvUV(ST(1));
1958 }
1959
1960 if (items < 3) {
1961 tot2 = (ulCommand == CMD_KI_GETQTY);
1962 ulParm2 = (tot2 ? (ULONG)&res : 0);
1963 } else {
1964 ulParm2 = (ULONG)SvUV(ST(2));
1965 }
1966
1967 if (items < 4)
1968 ulParm3 = 0;
1969 else {
1970 ulParm3 = (ULONG)SvUV(ST(3));
1971 }
1972
1973 RETVAL = perfSysCall(ulCommand, ulParm1, ulParm2, ulParm3);
1974 if (!RETVAL)
1975 croak_with_os2error("perfSysCall() error");
1976 XSprePUSH;
1977 if (total) {
1978 int i,j;
1979
1980 if (GIMME_V != G_ARRAY) {
1981 PUSHn(u[0][0]); /* Total ticks on the first processor */
1982 XSRETURN(1);
1983 }
1984 EXTEND(SP, 4*total);
1985 for (i=0; i < total; i++)
1986 for (j=0; j < 4; j++)
1987 PUSHs(sv_2mortal(newSVnv(u[i][j])));
1988 XSRETURN(4*total);
1989 }
1990 if (tot2) {
1991 PUSHu(res);
1992 XSRETURN(1);
1993 }
1994 }
1995 XSRETURN_EMPTY;
1996}
1997
1998#define PERL_PATCHLEVEL_H_IMPLICIT /* Do not init local_patches. */
1999#include "patchlevel.h"
2000#undef PERL_PATCHLEVEL_H_IMPLICIT
2001
2002char *
2003mod2fname(pTHX_ SV *sv)
2004{
2005 int pos = 6, len, avlen;
2006 unsigned int sum = 0;
2007 char *s;
2008 STRLEN n_a;
2009
2010 if (!SvROK(sv)) Perl_croak_nocontext("Not a reference given to mod2fname");
2011 sv = SvRV(sv);
2012 if (SvTYPE(sv) != SVt_PVAV)
2013 Perl_croak_nocontext("Not array reference given to mod2fname");
2014
2015 avlen = av_tindex((AV*)sv);
2016 if (avlen < 0)
2017 Perl_croak_nocontext("Empty array reference given to mod2fname");
2018
2019 s = SvPV(*av_fetch((AV*)sv, avlen, FALSE), n_a);
2020 strncpy(fname, s, 8);
2021 len = strlen(s);
2022 if (len < 6) pos = len;
2023 while (*s) {
2024 sum = 33 * sum + *(s++); /* Checksumming first chars to
2025 * get the capitalization into c.s. */
2026 }
2027 avlen --;
2028 while (avlen >= 0) {
2029 s = SvPV(*av_fetch((AV*)sv, avlen, FALSE), n_a);
2030 while (*s) {
2031 sum = 33 * sum + *(s++); /* 7 is primitive mod 13. */
2032 }
2033 avlen --;
2034 }
2035 /* We always load modules as *specific* DLLs, and with the full name.
2036 When loading a specific DLL by its full name, one cannot get a
2037 different DLL, even if a DLL with the same basename is loaded already.
2038 Thus there is no need to include the version into the mangling scheme. */
2039#if 0
2040 sum += PERL_VERSION * 200 + PERL_SUBVERSION * 2; /* Up to 5.6.1 */
2041#else
2042# ifndef COMPATIBLE_VERSION_SUM /* Binary compatibility with the 5.00553 binary */
2043# define COMPATIBLE_VERSION_SUM (5 * 200 + 53 * 2)
2044# endif
2045 sum += COMPATIBLE_VERSION_SUM;
2046#endif
2047 fname[pos] = 'A' + (sum % 26);
2048 fname[pos + 1] = 'A' + (sum / 26 % 26);
2049 fname[pos + 2] = '\0';
2050 return (char *)fname;
2051}
2052
2053XS(XS_DynaLoader_mod2fname)
2054{
2055 dXSARGS;
2056 if (items != 1)
2057 Perl_croak_nocontext("Usage: DynaLoader::mod2fname(sv)");
2058 {
2059 SV * sv = ST(0);
2060 char * RETVAL;
2061 dXSTARG;
2062
2063 RETVAL = mod2fname(aTHX_ sv);
2064 sv_setpv(TARG, RETVAL);
2065 XSprePUSH; PUSHTARG;
2066 }
2067 XSRETURN(1);
2068}
2069
2070char *
2071os2error(int rc)
2072{
2073 dTHX;
2074 ULONG len;
2075 char *s;
2076 int number = SvTRUE(get_sv("OS2::nsyserror", GV_ADD));
2077
2078 if (!(_emx_env & 0x200)) return ""; /* Nop if not OS/2. */
2079 if (rc == 0)
2080 return "";
2081 if (number) {
2082 sprintf(os2error_buf, "SYS%04d=%#x: ", rc, rc);
2083 s = os2error_buf + strlen(os2error_buf);
2084 } else
2085 s = os2error_buf;
2086 if (DosGetMessage(NULL, 0, s, sizeof(os2error_buf) - 1 - (s-os2error_buf),
2087 rc, "OSO001.MSG", &len)) {
2088 char *name = "";
2089
2090 if (!number) {
2091 sprintf(os2error_buf, "SYS%04d=%#x: ", rc, rc);
2092 s = os2error_buf + strlen(os2error_buf);
2093 }
2094 switch (rc) {
2095 case PMERR_INVALID_HWND:
2096 name = "PMERR_INVALID_HWND";
2097 break;
2098 case PMERR_INVALID_HMQ:
2099 name = "PMERR_INVALID_HMQ";
2100 break;
2101 case PMERR_CALL_FROM_WRONG_THREAD:
2102 name = "PMERR_CALL_FROM_WRONG_THREAD";
2103 break;
2104 case PMERR_NO_MSG_QUEUE:
2105 name = "PMERR_NO_MSG_QUEUE";
2106 break;
2107 case PMERR_NOT_IN_A_PM_SESSION:
2108 name = "PMERR_NOT_IN_A_PM_SESSION";
2109 break;
2110 case PMERR_INVALID_ATOM:
2111 name = "PMERR_INVALID_ATOM";
2112 break;
2113 case PMERR_INVALID_HATOMTBL:
2114 name = "PMERR_INVALID_HATOMTMB";
2115 break;
2116 case PMERR_INVALID_INTEGER_ATOM:
2117 name = "PMERR_INVALID_INTEGER_ATOM";
2118 break;
2119 case PMERR_INVALID_ATOM_NAME:
2120 name = "PMERR_INVALID_ATOM_NAME";
2121 break;
2122 case PMERR_ATOM_NAME_NOT_FOUND:
2123 name = "PMERR_ATOM_NAME_NOT_FOUND";
2124 break;
2125 }
2126 sprintf(s, "%s%s[No description found in OSO001.MSG]",
2127 name, (*name ? "=" : ""));
2128 } else {
2129 s[len] = '\0';
2130 if (len && s[len - 1] == '\n')
2131 s[--len] = 0;
2132 if (len && s[len - 1] == '\r')
2133 s[--len] = 0;
2134 if (len && s[len - 1] == '.')
2135 s[--len] = 0;
2136 if (len >= 10 && number && strnEQ(s, os2error_buf, 7)
2137 && s[7] == ':' && s[8] == ' ')
2138 /* Some messages start with SYSdddd:, some not */
2139 Move(s + 9, s, (len -= 9) + 1, char);
2140 }
2141 return os2error_buf;
2142}
2143
2144void
2145ResetWinError(void)
2146{
2147 WinError_2_Perl_rc;
2148}
2149
2150void
2151CroakWinError(int die, char *name)
2152{
2153 FillWinError;
2154 if (die && Perl_rc)
2155 croak_with_os2error(name ? name : "Win* API call");
2156}
2157
2158static char *
2159dllname2buffer(pTHX_ char *buf, STRLEN l)
2160{
2161 char *o;
2162 STRLEN ll;
2163 SV *dll = NULL;
2164
2165 dll = module_name(mod_name_full);
2166 o = SvPV(dll, ll);
2167 if (ll < l)
2168 memcpy(buf,o,ll);
2169 SvREFCNT_dec(dll);
2170 return (ll >= l ? "???" : buf);
2171}
2172
2173static char *
2174execname2buffer(char *buf, STRLEN l, char *oname)
2175{
2176 char *p, *orig = oname, ok = oname != NULL;
2177
2178 if (_execname(buf, l) != 0) {
2179 if (!oname || strlen(oname) >= l)
2180 return oname;
2181 strcpy(buf, oname);
2182 ok = 0;
2183 }
2184 p = buf;
2185 while (*p) {
2186 if (*p == '\\')
2187 *p = '/';
2188 if (*p == '/') {
2189 if (ok && *oname != '/' && *oname != '\\')
2190 ok = 0;
2191 } else if (ok && tolower(*oname) != tolower(*p))
2192 ok = 0;
2193 p++;
2194 oname++;
2195 }
2196 if (ok) { /* orig matches the real name. Use orig: */
2197 strcpy(buf, orig); /* _execname() is always uppercased */
2198 p = buf;
2199 while (*p) {
2200 if (*p == '\\')
2201 *p = '/';
2202 p++;
2203 }
2204 }
2205 return buf;
2206}
2207
2208char *
2209os2_execname(pTHX)
2210{
2211 char buf[300], *p = execname2buffer(buf, sizeof buf, PL_origargv[0]);
2212
2213 p = savepv(p);
2214 SAVEFREEPV(p);
2215 return p;
2216}
2217
2218int
2219Perl_OS2_handler_install(void *handler, enum Perlos2_handler how)
2220{
2221 char *s, b[300];
2222
2223 switch (how) {
2224 case Perlos2_handler_mangle:
2225 perllib_mangle_installed = (char *(*)(char *s, unsigned int l))handler;
2226 return 1;
2227 case Perlos2_handler_perl_sh:
2228 s = (char *)handler;
2229 s = dir_subst(s, strlen(s), b, sizeof b, 0, "handler_perl_sh");
2230 perl_sh_installed = savepv(s);
2231 return 1;
2232 case Perlos2_handler_perllib_from:
2233 s = (char *)handler;
2234 s = dir_subst(s, strlen(s), b, sizeof b, 0, "handler_perllib_from");
2235 oldl = strlen(s);
2236 oldp = savepv(s);
2237 return 1;
2238 case Perlos2_handler_perllib_to:
2239 s = (char *)handler;
2240 s = dir_subst(s, strlen(s), b, sizeof b, 0, "handler_perllib_to");
2241 newl = strlen(s);
2242 newp = savepv(s);
2243 strcpy(mangle_ret, newp);
2244 s = mangle_ret - 1;
2245 while (*++s)
2246 if (*s == '\\')
2247 *s = '/';
2248 return 1;
2249 default:
2250 return 0;
2251 }
2252}
2253
2254/* Returns a malloc()ed copy */
2255char *
2256dir_subst(char *s, unsigned int l, char *b, unsigned int bl, enum dir_subst_e flags, char *msg)
2257{
2258 char *from, *to = b, *e = b; /* `to' assignment: shut down the warning */
2259 STRLEN froml = 0, tol = 0, rest = 0; /* froml: likewise */
2260
2261 if (l >= 2 && s[0] == '~') {
2262 switch (s[1]) {
2263 case 'i': case 'I':
2264 from = "installprefix"; break;
2265 case 'd': case 'D':
2266 from = "dll"; break;
2267 case 'e': case 'E':
2268 from = "exe"; break;
2269 default:
2270 from = NULL;
2271 froml = l + 1; /* Will not match */
2272 break;
2273 }
2274 if (from)
2275 froml = strlen(from) + 1;
2276 if (l >= froml && strnicmp(s + 2, from + 1, froml - 2) == 0) {
2277 int strip = 1;
2278
2279 switch (s[1]) {
2280 case 'i': case 'I':
2281 strip = 0;
2282 tol = strlen(INSTALL_PREFIX);
2283 if (tol >= bl) {
2284 if (flags & dir_subst_fatal)
2285 Perl_croak_nocontext("INSTALL_PREFIX too long: `%s'", INSTALL_PREFIX);
2286 else
2287 return NULL;
2288 }
2289 memcpy(b, INSTALL_PREFIX, tol + 1);
2290 to = b;
2291 e = b + tol;
2292 break;
2293 case 'd': case 'D':
2294 if (flags & dir_subst_fatal) {
2295 dTHX;
2296
2297 to = dllname2buffer(aTHX_ b, bl);
2298 } else { /* No Perl present yet */
2299 HMODULE self = find_myself();
2300 APIRET rc = DosQueryModuleName(self, bl, b);
2301
2302 if (rc)
2303 return 0;
2304 to = b - 1;
2305 while (*++to)
2306 if (*to == '\\')
2307 *to = '/';
2308 to = b;
2309 }
2310 break;
2311 case 'e': case 'E':
2312 if (flags & dir_subst_fatal) {
2313 dTHX;
2314
2315 to = execname2buffer(b, bl, PL_origargv[0]);
2316 } else
2317 to = execname2buffer(b, bl, NULL);
2318 break;
2319 }
2320 if (!to)
2321 return NULL;
2322 if (strip) {
2323 e = strrchr(to, '/');
2324 if (!e && (flags & dir_subst_fatal))
2325 Perl_croak_nocontext("%s: Can't parse EXE/DLL name: '%s'", msg, to);
2326 else if (!e)
2327 return NULL;
2328 *e = 0;
2329 }
2330 s += froml; l -= froml;
2331 if (!l)
2332 return to;
2333 if (!tol)
2334 tol = strlen(to);
2335
2336 while (l >= 3 && (s[0] == '/' || s[0] == '\\')
2337 && s[1] == '.' && s[2] == '.'
2338 && (l == 3 || s[3] == '/' || s[3] == '\\' || s[3] == ';')) {
2339 e = strrchr(b, '/');
2340 if (!e && (flags & dir_subst_fatal))
2341 Perl_croak_nocontext("%s: Error stripping dirs from EXE/DLL/INSTALLDIR name", msg);
2342 else if (!e)
2343 return NULL;
2344 *e = 0;
2345 l -= 3; s += 3;
2346 }
2347 if (l && s[0] != '/' && s[0] != '\\' && s[0] != ';')
2348 *e++ = '/';
2349 }
2350 } /* Else: copy as is */
2351 if (l && (flags & dir_subst_pathlike)) {
2352 STRLEN i = 0;
2353
2354 while ( i < l - 2 && s[i] != ';') /* May have ~char after `;' */
2355 i++;
2356 if (i < l - 2) { /* Found */
2357 rest = l - i - 1;
2358 l = i + 1;
2359 }
2360 }
2361 if (e + l >= b + bl) {
2362 if (flags & dir_subst_fatal)
2363 Perl_croak_nocontext("%s: name `%s%s' too long", msg, b, s);
2364 else
2365 return NULL;
2366 }
2367 memcpy(e, s, l);
2368 if (rest) {
2369 e = dir_subst(s + l, rest, e + l, bl - (e + l - b), flags, msg);
2370 return e ? b : e;
2371 }
2372 e[l] = 0;
2373 return b;
2374}
2375
2376char *
2377perllib_mangle_with(char *s, unsigned int l, char *from, unsigned int froml, char *to, unsigned int tol)
2378{
2379 if (!to)
2380 return s;
2381 if (l == 0)
2382 l = strlen(s);
2383 if (l < froml || strnicmp(from, s, froml) != 0)
2384 return s;
2385 if (l + tol - froml > STATIC_FILE_LENGTH || tol > STATIC_FILE_LENGTH)
2386 Perl_croak_nocontext("Malformed PERLLIB_PREFIX");
2387 if (to && to != mangle_ret)
2388 memcpy(mangle_ret, to, tol);
2389 strcpy(mangle_ret + tol, s + froml);
2390 return mangle_ret;
2391}
2392
2393char *
2394perllib_mangle(char *s, unsigned int l)
2395{
2396 char *name;
2397
2398 if (perllib_mangle_installed && (name = perllib_mangle_installed(s,l)))
2399 return name;
2400 if (!newp && !notfound) {
2401 newp = getenv(name = "PERLLIB_" STRINGIFY(PERL_REVISION)
2402 STRINGIFY(PERL_VERSION) STRINGIFY(PERL_SUBVERSION)
2403 "_PREFIX");
2404 if (!newp)
2405 newp = getenv(name = "PERLLIB_" STRINGIFY(PERL_REVISION)
2406 STRINGIFY(PERL_VERSION) "_PREFIX");
2407 if (!newp)
2408 newp = getenv(name = "PERLLIB_" STRINGIFY(PERL_REVISION) "_PREFIX");
2409 if (!newp)
2410 newp = getenv(name = "PERLLIB_PREFIX");
2411 if (newp) {
2412 char *s, b[300];
2413
2414 oldp = newp;
2415 while (*newp && !isSPACE(*newp) && *newp != ';')
2416 newp++; /* Skip old name. */
2417 oldl = newp - oldp;
2418 s = dir_subst(oldp, oldl, b, sizeof b, dir_subst_fatal, name);
2419 oldp = savepv(s);
2420 oldl = strlen(s);
2421 while (*newp && (isSPACE(*newp) || *newp == ';'))
2422 newp++; /* Skip whitespace. */
2423 Perl_OS2_handler_install((void *)newp, Perlos2_handler_perllib_to);
2424 if (newl == 0 || oldl == 0)
2425 Perl_croak_nocontext("Malformed %s", name);
2426 } else
2427 notfound = 1;
2428 }
2429 if (!newp)
2430 return s;
2431 if (l == 0)
2432 l = strlen(s);
2433 if (l < oldl || strnicmp(oldp, s, oldl) != 0)
2434 return s;
2435 if (l + newl - oldl > STATIC_FILE_LENGTH || newl > STATIC_FILE_LENGTH)
2436 Perl_croak_nocontext("Malformed PERLLIB_PREFIX");
2437 strcpy(mangle_ret + newl, s + oldl);
2438 return mangle_ret;
2439}
2440
2441unsigned long
2442Perl_hab_GET() /* Needed if perl.h cannot be included */
2443{
2444 return perl_hab_GET();
2445}
2446
2447static void
2448Create_HMQ(int serve, char *message) /* Assumes morphing */
2449{
2450 unsigned fpflag = _control87(0,0);
2451
2452 init_PMWIN_entries();
2453 /* 64 messages if before OS/2 3.0, ignored otherwise */
2454 Perl_hmq = (*PMWIN_entries.CreateMsgQueue)(perl_hab_GET(), 64);
2455 if (!Perl_hmq) {
2456 dTHX;
2457
2458 SAVEINT(rmq_cnt); /* Allow catch()ing. */
2459 if (rmq_cnt++)
2460 _exit(188); /* Panic can try to create a window. */
2461 CroakWinError(1, message ? message : "Cannot create a message queue");
2462 }
2463 if (serve != -1)
2464 (*PMWIN_entries.CancelShutdown)(Perl_hmq, !serve);
2465 /* We may have loaded some modules */
2466 _control87(fpflag, MCW_EM); /* Some modules reset FP flags on (un)load */
2467}
2468
2469#define REGISTERMQ_WILL_SERVE 1
2470#define REGISTERMQ_IMEDIATE_UNMORPH 2
2471
2472HMQ
2473Perl_Register_MQ(int serve)
2474{
2475 if (Perl_hmq_refcnt <= 0) {
2476 PPIB pib;
2477 PTIB tib;
2478
2479 Perl_hmq_refcnt = 0; /* Be extra safe */
2480 DosGetInfoBlocks(&tib, &pib);
2481 if (!Perl_morph_refcnt) {
2482 Perl_os2_initial_mode = pib->pib_ultype;
2483 /* Try morphing into a PM application. */
2484 if (pib->pib_ultype != 3) /* 2 is VIO */
2485 pib->pib_ultype = 3; /* 3 is PM */
2486 }
2487 Create_HMQ(-1, /* We do CancelShutdown ourselves */
2488 "Cannot create a message queue, or morph to a PM application");
2489 if ((serve & REGISTERMQ_IMEDIATE_UNMORPH)) {
2490 if (!Perl_morph_refcnt && Perl_os2_initial_mode != 3)
2491 pib->pib_ultype = Perl_os2_initial_mode;
2492 }
2493 }
2494 if (serve & REGISTERMQ_WILL_SERVE) {
2495 if ( Perl_hmq_servers <= 0 /* Safe to inform us on shutdown, */
2496 && Perl_hmq_refcnt > 0 ) /* this was switched off before... */
2497 (*PMWIN_entries.CancelShutdown)(Perl_hmq, 0);
2498 Perl_hmq_servers++;
2499 } else if (!Perl_hmq_servers) /* Do not inform us on shutdown */
2500 (*PMWIN_entries.CancelShutdown)(Perl_hmq, 1);
2501 Perl_hmq_refcnt++;
2502 if (!(serve & REGISTERMQ_IMEDIATE_UNMORPH))
2503 Perl_morph_refcnt++;
2504 return Perl_hmq;
2505}
2506
2507int
2508Perl_Serve_Messages(int force)
2509{
2510 int cnt = 0;
2511 QMSG msg;
2512
2513 if (Perl_hmq_servers > 0 && !force)
2514 return 0;
2515 if (Perl_hmq_refcnt <= 0)
2516 Perl_croak_nocontext("No message queue");
2517 while ((*PMWIN_entries.PeekMsg)(Perl_hab, &msg, NULLHANDLE, 0, 0, PM_REMOVE)) {
2518 cnt++;
2519 if (msg.msg == WM_QUIT)
2520 Perl_croak_nocontext("QUITing...");
2521 (*PMWIN_entries.DispatchMsg)(Perl_hab, &msg);
2522 }
2523 return cnt;
2524}
2525
2526int
2527Perl_Process_Messages(int force, I32 *cntp)
2528{
2529 QMSG msg;
2530
2531 if (Perl_hmq_servers > 0 && !force)
2532 return 0;
2533 if (Perl_hmq_refcnt <= 0)
2534 Perl_croak_nocontext("No message queue");
2535 while ((*PMWIN_entries.GetMsg)(Perl_hab, &msg, NULLHANDLE, 0, 0)) {
2536 if (cntp)
2537 (*cntp)++;
2538 (*PMWIN_entries.DispatchMsg)(Perl_hab, &msg);
2539 if (msg.msg == WM_DESTROY)
2540 return -1;
2541 if (msg.msg == WM_CREATE)
2542 return +1;
2543 }
2544 Perl_croak_nocontext("QUITing...");
2545}
2546
2547void
2548Perl_Deregister_MQ(int serve)
2549{
2550 if (serve & REGISTERMQ_WILL_SERVE)
2551 Perl_hmq_servers--;
2552
2553 if (--Perl_hmq_refcnt <= 0) {
2554 unsigned fpflag = _control87(0,0);
2555
2556 init_PMWIN_entries(); /* To be extra safe */
2557 (*PMWIN_entries.DestroyMsgQueue)(Perl_hmq);
2558 Perl_hmq = 0;
2559 /* We may have (un)loaded some modules */
2560 _control87(fpflag, MCW_EM); /* Some modules reset FP flags on (un)load */
2561 } else if ((serve & REGISTERMQ_WILL_SERVE) && Perl_hmq_servers <= 0)
2562 (*PMWIN_entries.CancelShutdown)(Perl_hmq, 1); /* Last server exited */
2563 if (!(serve & REGISTERMQ_IMEDIATE_UNMORPH) && (--Perl_morph_refcnt <= 0)) {
2564 /* Try morphing back from a PM application. */
2565 PPIB pib;
2566 PTIB tib;
2567
2568 DosGetInfoBlocks(&tib, &pib);
2569 if (pib->pib_ultype == 3) /* 3 is PM */
2570 pib->pib_ultype = Perl_os2_initial_mode;
2571 else
2572 Perl_warn_nocontext("Unexpected program mode %d when morphing back from PM",
2573 pib->pib_ultype);
2574 }
2575}
2576
2577#define sys_is_absolute(path) ( isALPHA((path)[0]) && (path)[1] == ':' \
2578 && ((path)[2] == '/' || (path)[2] == '\\'))
2579#define sys_is_rooted _fnisabs
2580#define sys_is_relative _fnisrel
2581#define current_drive _getdrive
2582
2583#undef chdir /* Was _chdir2. */
2584#define sys_chdir(p) (chdir(p) == 0)
2585#define change_drive(d) (_chdrive(d), (current_drive() == toupper(d)))
2586
2587XS(XS_OS2_Error)
2588{
2589 dXSARGS;
2590 if (items != 2)
2591 Perl_croak_nocontext("Usage: OS2::Error(harderr, exception)");
2592 {
2593 int arg1 = SvIV(ST(0));
2594 int arg2 = SvIV(ST(1));
2595 int a = ((arg1 ? FERR_ENABLEHARDERR : FERR_DISABLEHARDERR)
2596 | (arg2 ? FERR_ENABLEEXCEPTION : FERR_DISABLEEXCEPTION));
2597 int RETVAL = ((arg1 ? 1 : 0) | (arg2 ? 2 : 0));
2598 unsigned long rc;
2599
2600 if (CheckOSError(DosError(a)))
2601 Perl_croak_nocontext("DosError(%d) failed: %s", a, os2error(Perl_rc));
2602 ST(0) = sv_newmortal();
2603 if (DOS_harderr_state >= 0)
2604 sv_setiv(ST(0), DOS_harderr_state);
2605 DOS_harderr_state = RETVAL;
2606 }
2607 XSRETURN(1);
2608}
2609
2610XS(XS_OS2_Errors2Drive)
2611{
2612 dXSARGS;
2613 if (items != 1)
2614 Perl_croak_nocontext("Usage: OS2::Errors2Drive(drive)");
2615 {
2616 STRLEN n_a;
2617 SV *sv = ST(0);
2618 int suppress = SvOK(sv);
2619 char *s = suppress ? SvPV(sv, n_a) : NULL;
2620 char drive = (s ? *s : 0);
2621 unsigned long rc;
2622
2623 if (suppress && !isALPHA(drive))
2624 Perl_croak_nocontext("Non-char argument '%c' to OS2::Errors2Drive()", drive);
2625 if (CheckOSError(DosSuppressPopUps((suppress
2626 ? SPU_ENABLESUPPRESSION
2627 : SPU_DISABLESUPPRESSION),
2628 drive)))
2629 Perl_croak_nocontext("DosSuppressPopUps(%c) failed: %s", drive,
2630 os2error(Perl_rc));
2631 ST(0) = sv_newmortal();
2632 if (DOS_suppression_state > 0)
2633 sv_setpvn(ST(0), &DOS_suppression_state, 1);
2634 else if (DOS_suppression_state == 0)
2635 sv_setpvn(ST(0), "", 0);
2636 DOS_suppression_state = drive;
2637 }
2638 XSRETURN(1);
2639}
2640
2641int
2642async_mssleep(ULONG ms, int switch_priority) {
2643 /* This is similar to DosSleep(), but has 8ms granularity in time-critical
2644 threads even on Warp3. */
2645 HEV hevEvent1 = 0; /* Event semaphore handle */
2646 HTIMER htimerEvent1 = 0; /* Timer handle */
2647 APIRET rc = NO_ERROR; /* Return code */
2648 int ret = 1;
2649 ULONG priority = 0, nesting; /* Shut down the warnings */
2650 PPIB pib;
2651 PTIB tib;
2652 char *e = NULL;
2653 APIRET badrc;
2654
2655 if (!(_emx_env & 0x200)) /* DOS */
2656 return !_sleep2(ms);
2657
2658 os2cp_croak(DosCreateEventSem(NULL, /* Unnamed */
2659 &hevEvent1, /* Handle of semaphore returned */
2660 DC_SEM_SHARED, /* Shared needed for DosAsyncTimer */
2661 FALSE), /* Semaphore is in RESET state */
2662 "DosCreateEventSem");
2663
2664 if (ms >= switch_priority)
2665 switch_priority = 0;
2666 if (switch_priority) {
2667 if (CheckOSError(DosGetInfoBlocks(&tib, &pib)))
2668 switch_priority = 0;
2669 else {
2670 /* In Warp3, to switch scheduling to 8ms step, one needs to do
2671 DosAsyncTimer() in time-critical thread. On laters versions,
2672 more and more cases of wait-for-something are covered.
2673
2674 It turns out that on Warp3fp42 it is the priority at the time
2675 of DosAsyncTimer() which matters. Let's hope that this works
2676 with later versions too... XXXX
2677 */
2678 priority = (tib->tib_ptib2->tib2_ulpri);
2679 if ((priority & 0xFF00) == 0x0300) /* already time-critical */
2680 switch_priority = 0;
2681 /* Make us time-critical. Just modifying TIB is not enough... */
2682 /* tib->tib_ptib2->tib2_ulpri = 0x0300;*/
2683 /* We do not want to run at high priority if a signal causes us
2684 to longjmp() out of this section... */
2685 if (DosEnterMustComplete(&nesting))
2686 switch_priority = 0;
2687 else
2688 DosSetPriority(PRTYS_THREAD, PRTYC_TIMECRITICAL, 0, 0);
2689 }
2690 }
2691
2692 if ((badrc = DosAsyncTimer(ms,
2693 (HSEM) hevEvent1, /* Semaphore to post */
2694 &htimerEvent1))) /* Timer handler (returned) */
2695 e = "DosAsyncTimer";
2696
2697 if (switch_priority && tib->tib_ptib2->tib2_ulpri == 0x0300) {
2698 /* Nobody switched priority while we slept... Ignore errors... */
2699 /* tib->tib_ptib2->tib2_ulpri = priority; */ /* Get back... */
2700 if (!(rc = DosSetPriority(PRTYS_THREAD, (priority>>8) & 0xFF, 0, 0)))
2701 rc = DosSetPriority(PRTYS_THREAD, 0, priority & 0xFF, 0);
2702 }
2703 if (switch_priority)
2704 rc = DosExitMustComplete(&nesting); /* Ignore errors */
2705
2706 /* The actual blocking call is made with "normal" priority. This way we
2707 should not bother with DosSleep(0) etc. to compensate for us interrupting
2708 higher-priority threads. The goal is to prohibit the system spending too
2709 much time halt()ing, not to run us "no matter what". */
2710 if (!e) /* Wait for AsyncTimer event */
2711 badrc = DosWaitEventSem(hevEvent1, SEM_INDEFINITE_WAIT);
2712
2713 if (e) ; /* Do nothing */
2714 else if (badrc == ERROR_INTERRUPT)
2715 ret = 0;
2716 else if (badrc)
2717 e = "DosWaitEventSem";
2718 if ((rc = DosCloseEventSem(hevEvent1)) && !e) { /* Get rid of semaphore */
2719 e = "DosCloseEventSem";
2720 badrc = rc;
2721 }
2722 if (e)
2723 os2cp_croak(badrc, e);
2724 return ret;
2725}
2726
2727XS(XS_OS2_ms_sleep) /* for testing only... */
2728{
2729 dXSARGS;
2730 ULONG ms, lim;
2731
2732 if (items > 2 || items < 1)
2733 Perl_croak_nocontext("Usage: OS2::ms_sleep(wait_ms [, high_priority_limit])");
2734 ms = SvUV(ST(0));
2735 lim = items > 1 ? SvUV(ST(1)) : ms + 1;
2736 async_mssleep(ms, lim);
2737 XSRETURN_YES;
2738}
2739
2740ULONG (*pDosTmrQueryFreq) (PULONG);
2741ULONG (*pDosTmrQueryTime) (unsigned long long *);
2742
2743XS(XS_OS2_Timer)
2744{
2745 dXSARGS;
2746 static ULONG freq;
2747 unsigned long long count;
2748 ULONG rc;
2749
2750 if (items != 0)
2751 Perl_croak_nocontext("Usage: OS2::Timer()");
2752 if (!freq) {
2753 *(PFN*)&pDosTmrQueryFreq = loadByOrdinal(ORD_DosTmrQueryFreq, 0);
2754 *(PFN*)&pDosTmrQueryTime = loadByOrdinal(ORD_DosTmrQueryTime, 0);
2755 MUTEX_LOCK(&perlos2_state_mutex);
2756 if (!freq)
2757 if (CheckOSError(pDosTmrQueryFreq(&freq)))
2758 croak_with_os2error("DosTmrQueryFreq");
2759 MUTEX_UNLOCK(&perlos2_state_mutex);
2760 }
2761 if (CheckOSError(pDosTmrQueryTime(&count)))
2762 croak_with_os2error("DosTmrQueryTime");
2763 {
2764 dXSTARG;
2765
2766 XSprePUSH; PUSHn(((NV)count)/freq);
2767 }
2768 XSRETURN(1);
2769}
2770
2771XS(XS_OS2_msCounter)
2772{
2773 dXSARGS;
2774
2775 if (items != 0)
2776 Perl_croak_nocontext("Usage: OS2::msCounter()");
2777 {
2778 dXSTARG;
2779
2780 XSprePUSH; PUSHu(msCounter());
2781 }
2782 XSRETURN(1);
2783}
2784
2785XS(XS_OS2__InfoTable)
2786{
2787 dXSARGS;
2788 int is_local = 0;
2789
2790 if (items > 1)
2791 Perl_croak_nocontext("Usage: OS2::_infoTable([isLocal])");
2792 if (items == 1)
2793 is_local = (int)SvIV(ST(0));
2794 {
2795 dXSTARG;
2796
2797 XSprePUSH; PUSHu(InfoTable(is_local));
2798 }
2799 XSRETURN(1);
2800}
2801
2802static const char * const dc_fields[] = {
2803 "FAMILY",
2804 "IO_CAPS",
2805 "TECHNOLOGY",
2806 "DRIVER_VERSION",
2807 "WIDTH",
2808 "HEIGHT",
2809 "WIDTH_IN_CHARS",
2810 "HEIGHT_IN_CHARS",
2811 "HORIZONTAL_RESOLUTION",
2812 "VERTICAL_RESOLUTION",
2813 "CHAR_WIDTH",
2814 "CHAR_HEIGHT",
2815 "SMALL_CHAR_WIDTH",
2816 "SMALL_CHAR_HEIGHT",
2817 "COLORS",
2818 "COLOR_PLANES",
2819 "COLOR_BITCOUNT",
2820 "COLOR_TABLE_SUPPORT",
2821 "MOUSE_BUTTONS",
2822 "FOREGROUND_MIX_SUPPORT",
2823 "BACKGROUND_MIX_SUPPORT",
2824 "VIO_LOADABLE_FONTS",
2825 "WINDOW_BYTE_ALIGNMENT",
2826 "BITMAP_FORMATS",
2827 "RASTER_CAPS",
2828 "MARKER_HEIGHT",
2829 "MARKER_WIDTH",
2830 "DEVICE_FONTS",
2831 "GRAPHICS_SUBSET",
2832 "GRAPHICS_VERSION",
2833 "GRAPHICS_VECTOR_SUBSET",
2834 "DEVICE_WINDOWING",
2835 "ADDITIONAL_GRAPHICS",
2836 "PHYS_COLORS",
2837 "COLOR_INDEX",
2838 "GRAPHICS_CHAR_WIDTH",
2839 "GRAPHICS_CHAR_HEIGHT",
2840 "HORIZONTAL_FONT_RES",
2841 "VERTICAL_FONT_RES",
2842 "DEVICE_FONT_SIM",
2843 "LINEWIDTH_THICK",
2844 "DEVICE_POLYSET_POINTS",
2845};
2846
2847enum {
2848 DevCap_dc, DevCap_hwnd
2849};
2850
2851HDC (*pWinOpenWindowDC) (HWND hwnd);
2852HMF (*pDevCloseDC) (HDC hdc);
2853HDC (*pDevOpenDC) (HAB hab, LONG lType, PCSZ pszToken, LONG lCount,
2854 PDEVOPENDATA pdopData, HDC hdcComp);
2855BOOL (*pDevQueryCaps) (HDC hdc, LONG lStart, LONG lCount, PLONG alArray);
2856
2857
2858XS(XS_OS2_DevCap)
2859{
2860 dXSARGS;
2861 if (items > 2)
2862 Perl_croak_nocontext("Usage: OS2::DevCap()");
2863 {
2864 /* Device Capabilities Data Buffer (10 extra w.r.t. Warp 4.5) */
2865 LONG si[CAPS_DEVICE_POLYSET_POINTS - CAPS_FAMILY + 1];
2866 int i = 0, j = 0, how = DevCap_dc;
2867 HDC hScreenDC;
2868 DEVOPENSTRUC doStruc= {0L, (PSZ)"DISPLAY", NULL, 0L, 0L, 0L, 0L, 0L, 0L};
2869 ULONG rc1 = NO_ERROR;
2870 HWND hwnd;
2871 static volatile int devcap_loaded;
2872
2873 if (!devcap_loaded) {
2874 *(PFN*)&pWinOpenWindowDC = loadByOrdinal(ORD_WinOpenWindowDC, 0);
2875 *(PFN*)&pDevOpenDC = loadByOrdinal(ORD_DevOpenDC, 0);
2876 *(PFN*)&pDevCloseDC = loadByOrdinal(ORD_DevCloseDC, 0);
2877 *(PFN*)&pDevQueryCaps = loadByOrdinal(ORD_DevQueryCaps, 0);
2878 devcap_loaded = 1;
2879 }
2880
2881 if (items >= 2)
2882 how = SvIV(ST(1));
2883 if (!items) { /* Get device contents from PM */
2884 hScreenDC = pDevOpenDC(perl_hab_GET(), OD_MEMORY, (PSZ)"*", 0,
2885 (PDEVOPENDATA)&doStruc, NULLHANDLE);
2886 if (CheckWinError(hScreenDC))
2887 croak_with_os2error("DevOpenDC() failed");
2888 } else if (how == DevCap_dc)
2889 hScreenDC = (HDC)SvIV(ST(0));
2890 else { /* DevCap_hwnd */
2891 if (!Perl_hmq)
2892 Perl_croak(aTHX_ "Getting a window's device context without a message queue would lock PM");
2893 hwnd = (HWND)SvIV(ST(0));
2894 hScreenDC = pWinOpenWindowDC(hwnd); /* No need to DevCloseDC() */
2895 if (CheckWinError(hScreenDC))
2896 croak_with_os2error("WinOpenWindowDC() failed");
2897 }
2898 if (CheckWinError(pDevQueryCaps(hScreenDC,
2899 CAPS_FAMILY, /* W3 documented caps */
2900 CAPS_DEVICE_POLYSET_POINTS
2901 - CAPS_FAMILY + 1,
2902 si)))
2903 rc1 = Perl_rc;
2904 else {
2905 EXTEND(SP,2*(CAPS_DEVICE_POLYSET_POINTS - CAPS_FAMILY + 1));
2906 while (i < CAPS_DEVICE_POLYSET_POINTS - CAPS_FAMILY + 1) {
2907 ST(j) = sv_newmortal();
2908 sv_setpv(ST(j++), dc_fields[i]);
2909 ST(j) = sv_newmortal();
2910 sv_setiv(ST(j++), si[i]);
2911 i++;
2912 }
2913 i = CAPS_DEVICE_POLYSET_POINTS + 1;
2914 while (i < CAPS_DEVICE_POLYSET_POINTS + 11) { /* Just in case... */
2915 LONG l;
2916
2917 if (CheckWinError(pDevQueryCaps(hScreenDC, i, 1, &l)))
2918 break;
2919 EXTEND(SP, j + 2);
2920 ST(j) = sv_newmortal();
2921 sv_setiv(ST(j++), i);
2922 ST(j) = sv_newmortal();
2923 sv_setiv(ST(j++), l);
2924 i++;
2925 }
2926 }
2927 if (!items && CheckWinError(pDevCloseDC(hScreenDC)))
2928 Perl_warn_nocontext("DevCloseDC() failed: %s", os2error(Perl_rc));
2929 if (rc1)
2930 Perl_rc = rc1, croak_with_os2error("DevQueryCaps() failed");
2931 XSRETURN(j);
2932 }
2933}
2934
2935LONG (*pWinQuerySysValue) (HWND hwndDesktop, LONG iSysValue);
2936BOOL (*pWinSetSysValue) (HWND hwndDesktop, LONG iSysValue, LONG lValue);
2937
2938const char * const sv_keys[] = {
2939 "SWAPBUTTON",
2940 "DBLCLKTIME",
2941 "CXDBLCLK",
2942 "CYDBLCLK",
2943 "CXSIZEBORDER",
2944 "CYSIZEBORDER",
2945 "ALARM",
2946 "7",
2947 "8",
2948 "CURSORRATE",
2949 "FIRSTSCROLLRATE",
2950 "SCROLLRATE",
2951 "NUMBEREDLISTS",
2952 "WARNINGFREQ",
2953 "NOTEFREQ",
2954 "ERRORFREQ",
2955 "WARNINGDURATION",
2956 "NOTEDURATION",
2957 "ERRORDURATION",
2958 "19",
2959 "CXSCREEN",
2960 "CYSCREEN",
2961 "CXVSCROLL",
2962 "CYHSCROLL",
2963 "CYVSCROLLARROW",
2964 "CXHSCROLLARROW",
2965 "CXBORDER",
2966 "CYBORDER",
2967 "CXDLGFRAME",
2968 "CYDLGFRAME",
2969 "CYTITLEBAR",
2970 "CYVSLIDER",
2971 "CXHSLIDER",
2972 "CXMINMAXBUTTON",
2973 "CYMINMAXBUTTON",
2974 "CYMENU",
2975 "CXFULLSCREEN",
2976 "CYFULLSCREEN",
2977 "CXICON",
2978 "CYICON",
2979 "CXPOINTER",
2980 "CYPOINTER",
2981 "DEBUG",
2982 "CPOINTERBUTTONS",
2983 "POINTERLEVEL",
2984 "CURSORLEVEL",
2985 "TRACKRECTLEVEL",
2986 "CTIMERS",
2987 "MOUSEPRESENT",
2988 "CXALIGN",
2989 "CYALIGN",
2990 "DESKTOPWORKAREAYTOP",
2991 "DESKTOPWORKAREAYBOTTOM",
2992 "DESKTOPWORKAREAXRIGHT",
2993 "DESKTOPWORKAREAXLEFT",
2994 "55",
2995 "NOTRESERVED",
2996 "EXTRAKEYBEEP",
2997 "SETLIGHTS",
2998 "INSERTMODE",
2999 "60",
3000 "61",
3001 "62",
3002 "63",
3003 "MENUROLLDOWNDELAY",
3004 "MENUROLLUPDELAY",
3005 "ALTMNEMONIC",
3006 "TASKLISTMOUSEACCESS",
3007 "CXICONTEXTWIDTH",
3008 "CICONTEXTLINES",
3009 "CHORDTIME",
3010 "CXCHORD",
3011 "CYCHORD",
3012 "CXMOTIONSTART",
3013 "CYMOTIONSTART",
3014 "BEGINDRAG",
3015 "ENDDRAG",
3016 "SINGLESELECT",
3017 "OPEN",
3018 "CONTEXTMENU",
3019 "CONTEXTHELP",
3020 "TEXTEDIT",
3021 "BEGINSELECT",
3022 "ENDSELECT",
3023 "BEGINDRAGKB",
3024 "ENDDRAGKB",
3025 "SELECTKB",
3026 "OPENKB",
3027 "CONTEXTMENUKB",
3028 "CONTEXTHELPKB",
3029 "TEXTEDITKB",
3030 "BEGINSELECTKB",
3031 "ENDSELECTKB",
3032 "ANIMATION",
3033 "ANIMATIONSPEED",
3034 "MONOICONS",
3035 "KBDALTERED",
3036 "PRINTSCREEN", /* 97, the last one on one of the DDK header */
3037 "LOCKSTARTINPUT",
3038 "DYNAMICDRAG",
3039 "100",
3040 "101",
3041 "102",
3042 "103",
3043 "104",
3044 "105",
3045 "106",
3046 "107",
3047/* "CSYSVALUES",*/
3048 /* In recent DDK the limit is 108 */
3049};
3050
3051XS(XS_OS2_SysValues)
3052{
3053 dXSARGS;
3054 if (items > 2)
3055 Perl_croak_nocontext("Usage: OS2::SysValues(which = -1, hwndDesktop = HWND_DESKTOP)");
3056 {
3057 int i = 0, j = 0, which = -1;
3058 HWND hwnd = HWND_DESKTOP;
3059 static volatile int sv_loaded;
3060 LONG RETVAL;
3061
3062 if (!sv_loaded) {
3063 *(PFN*)&pWinQuerySysValue = loadByOrdinal(ORD_WinQuerySysValue, 0);
3064 sv_loaded = 1;
3065 }
3066
3067 if (items == 2)
3068 hwnd = (HWND)SvIV(ST(1));
3069 if (items >= 1)
3070 which = (int)SvIV(ST(0));
3071 if (which == -1) {
3072 EXTEND(SP,2*C_ARRAY_LENGTH(sv_keys));
3073 while (i < C_ARRAY_LENGTH(sv_keys)) {
3074 ResetWinError();
3075 RETVAL = pWinQuerySysValue(hwnd, i);
3076 if ( !RETVAL
3077 && !(sv_keys[i][0] >= '0' && sv_keys[i][0] <= '9'
3078 && i <= SV_PRINTSCREEN) ) {
3079 FillWinError;
3080 if (Perl_rc) {
3081 if (i > SV_PRINTSCREEN)
3082 break; /* May be not present on older systems */
3083 croak_with_os2error("SysValues():");
3084 }
3085
3086 }
3087 ST(j) = sv_newmortal();
3088 sv_setpv(ST(j++), sv_keys[i]);
3089 ST(j) = sv_newmortal();
3090 sv_setiv(ST(j++), RETVAL);
3091 i++;
3092 }
3093 XSRETURN(2 * i);
3094 } else {
3095 dXSTARG;
3096
3097 ResetWinError();
3098 RETVAL = pWinQuerySysValue(hwnd, which);
3099 if (!RETVAL) {
3100 FillWinError;
3101 if (Perl_rc)
3102 croak_with_os2error("SysValues():");
3103 }
3104 XSprePUSH; PUSHi((IV)RETVAL);
3105 }
3106 }
3107}
3108
3109XS(XS_OS2_SysValues_set)
3110{
3111 dXSARGS;
3112 if (items < 2 || items > 3)
3113 Perl_croak_nocontext("Usage: OS2::SysValues_set(which, val, hwndDesktop = HWND_DESKTOP)");
3114 {
3115 int which = (int)SvIV(ST(0));
3116 LONG val = (LONG)SvIV(ST(1));
3117 HWND hwnd = HWND_DESKTOP;
3118 static volatile int svs_loaded;
3119
3120 if (!svs_loaded) {
3121 *(PFN*)&pWinSetSysValue = loadByOrdinal(ORD_WinSetSysValue, 0);
3122 svs_loaded = 1;
3123 }
3124
3125 if (items == 3)
3126 hwnd = (HWND)SvIV(ST(2));
3127 if (CheckWinError(pWinSetSysValue(hwnd, which, val)))
3128 croak_with_os2error("SysValues_set()");
3129 }
3130 XSRETURN_YES;
3131}
3132
3133#define QSV_MAX_WARP3 QSV_MAX_COMP_LENGTH
3134
3135static const char * const si_fields[] = {
3136 "MAX_PATH_LENGTH",
3137 "MAX_TEXT_SESSIONS",
3138 "MAX_PM_SESSIONS",
3139 "MAX_VDM_SESSIONS",
3140 "BOOT_DRIVE",
3141 "DYN_PRI_VARIATION",
3142 "MAX_WAIT",
3143 "MIN_SLICE",
3144 "MAX_SLICE",
3145 "PAGE_SIZE",
3146 "VERSION_MAJOR",
3147 "VERSION_MINOR",
3148 "VERSION_REVISION",
3149 "MS_COUNT",
3150 "TIME_LOW",
3151 "TIME_HIGH",
3152 "TOTPHYSMEM",
3153 "TOTRESMEM",
3154 "TOTAVAILMEM",
3155 "MAXPRMEM",
3156 "MAXSHMEM",
3157 "TIMER_INTERVAL",
3158 "MAX_COMP_LENGTH",
3159 "FOREGROUND_FS_SESSION",
3160 "FOREGROUND_PROCESS", /* Warp 3 toolkit defines up to this */
3161 "NUMPROCESSORS",
3162 "MAXHPRMEM",
3163 "MAXHSHMEM",
3164 "MAXPROCESSES",
3165 "VIRTUALADDRESSLIMIT",
3166 "INT10ENABLED", /* From $TOOLKIT-ddk\DDK\video\rel\os2c\include\base\os2\bsedos.h */
3167};
3168
3169XS(XS_OS2_SysInfo)
3170{
3171 dXSARGS;
3172 if (items != 0)
3173 Perl_croak_nocontext("Usage: OS2::SysInfo()");
3174 {
3175 /* System Information Data Buffer (10 extra w.r.t. Warp 4.5) */
3176 ULONG si[C_ARRAY_LENGTH(si_fields) + 10];
3177 APIRET rc = NO_ERROR; /* Return code */
3178 int i = 0, j = 0, last = QSV_MAX_WARP3;
3179
3180 if (CheckOSError(DosQuerySysInfo(1L, /* Request documented system */
3181 last, /* info for Warp 3 */
3182 (PVOID)si,
3183 sizeof(si))))
3184 croak_with_os2error("DosQuerySysInfo() failed");
3185 while (++last <= C_ARRAY_LENGTH(si)) {
3186 if (CheckOSError(DosQuerySysInfo(last, last, /* One entry only */
3187 (PVOID)(si+last-1),
3188 sizeof(*si)))) {
3189 if (Perl_rc != ERROR_INVALID_PARAMETER)
3190 croak_with_os2error("DosQuerySysInfo() failed");
3191 break;
3192 }
3193 }
3194 last--; /* Count of successfully processed offsets */
3195 EXTEND(SP,2*last);
3196 while (i < last) {
3197 ST(j) = sv_newmortal();
3198 if (i < C_ARRAY_LENGTH(si_fields))
3199 sv_setpv(ST(j++), si_fields[i]);
3200 else
3201 sv_setiv(ST(j++), i + 1);
3202 ST(j) = sv_newmortal();
3203 sv_setuv(ST(j++), si[i]);
3204 i++;
3205 }
3206 XSRETURN(2 * last);
3207 }
3208}
3209
3210XS(XS_OS2_SysInfoFor)
3211{
3212 dXSARGS;
3213 int count = (items == 2 ? (int)SvIV(ST(1)) : 1);
3214
3215 if (items < 1 || items > 2)
3216 Perl_croak_nocontext("Usage: OS2::SysInfoFor(id[,count])");
3217 {
3218 /* System Information Data Buffer (10 extra w.r.t. Warp 4.5) */
3219 ULONG si[C_ARRAY_LENGTH(si_fields) + 10];
3220 APIRET rc = NO_ERROR; /* Return code */
3221 int i = 0;
3222 int start = (int)SvIV(ST(0));
3223
3224 if (count > C_ARRAY_LENGTH(si) || count <= 0)
3225 Perl_croak(aTHX_ "unexpected count %d for OS2::SysInfoFor()", count);
3226 if (CheckOSError(DosQuerySysInfo(start,
3227 start + count - 1,
3228 (PVOID)si,
3229 sizeof(si))))
3230 croak_with_os2error("DosQuerySysInfo() failed");
3231 EXTEND(SP,count);
3232 while (i < count) {
3233 ST(i) = sv_newmortal();
3234 sv_setiv(ST(i), si[i]);
3235 i++;
3236 }
3237 }
3238 XSRETURN(count);
3239}
3240
3241XS(XS_OS2_BootDrive)
3242{
3243 dXSARGS;
3244 if (items != 0)
3245 Perl_croak_nocontext("Usage: OS2::BootDrive()");
3246 {
3247 ULONG si[1] = {0}; /* System Information Data Buffer */
3248 APIRET rc = NO_ERROR; /* Return code */
3249 char c;
3250 dXSTARG;
3251
3252 if (CheckOSError(DosQuerySysInfo(QSV_BOOT_DRIVE, QSV_BOOT_DRIVE,
3253 (PVOID)si, sizeof(si))))
3254 croak_with_os2error("DosQuerySysInfo() failed");
3255 c = 'a' - 1 + si[0];
3256 sv_setpvn(TARG, &c, 1);
3257 XSprePUSH; PUSHTARG;
3258 }
3259 XSRETURN(1);
3260}
3261
3262XS(XS_OS2_Beep)
3263{
3264 dXSARGS;
3265 if (items > 2) /* Defaults as for WinAlarm(ERROR) */
3266 Perl_croak_nocontext("Usage: OS2::Beep(freq = 440, ms = 100)");
3267 {
3268 ULONG freq = (items > 0 ? (ULONG)SvUV(ST(0)) : 440);
3269 ULONG ms = (items > 1 ? (ULONG)SvUV(ST(1)) : 100);
3270 ULONG rc;
3271
3272 if (CheckOSError(DosBeep(freq, ms)))
3273 croak_with_os2error("SysValues_set()");
3274 }
3275 XSRETURN_YES;
3276}
3277
3278
3279
3280XS(XS_OS2_MorphPM)
3281{
3282 dXSARGS;
3283 if (items != 1)
3284 Perl_croak_nocontext("Usage: OS2::MorphPM(serve)");
3285 {
3286 bool serve = SvOK(ST(0));
3287 unsigned long pmq = perl_hmq_GET(serve);
3288 dXSTARG;
3289
3290 XSprePUSH; PUSHi((IV)pmq);
3291 }
3292 XSRETURN(1);
3293}
3294
3295XS(XS_OS2_UnMorphPM)
3296{
3297 dXSARGS;
3298 if (items != 1)
3299 Perl_croak_nocontext("Usage: OS2::UnMorphPM(serve)");
3300 {
3301 bool serve = SvOK(ST(0));
3302
3303 perl_hmq_UNSET(serve);
3304 }
3305 XSRETURN(0);
3306}
3307
3308XS(XS_OS2_Serve_Messages)
3309{
3310 dXSARGS;
3311 if (items != 1)
3312 Perl_croak_nocontext("Usage: OS2::Serve_Messages(force)");
3313 {
3314 bool force = SvOK(ST(0));
3315 unsigned long cnt = Perl_Serve_Messages(force);
3316 dXSTARG;
3317
3318 XSprePUSH; PUSHi((IV)cnt);
3319 }
3320 XSRETURN(1);
3321}
3322
3323XS(XS_OS2_Process_Messages)
3324{
3325 dXSARGS;
3326 if (items < 1 || items > 2)
3327 Perl_croak_nocontext("Usage: OS2::Process_Messages(force [, cnt])");
3328 {
3329 bool force = SvOK(ST(0));
3330 unsigned long cnt;
3331 dXSTARG;
3332
3333 if (items == 2) {
3334 I32 cntr;
3335 SV *sv = ST(1);
3336
3337 (void)SvIV(sv); /* Force SvIVX */
3338 if (!SvIOK(sv))
3339 Perl_croak_nocontext("Can't upgrade count to IV");
3340 cntr = SvIVX(sv);
3341 cnt = Perl_Process_Messages(force, &cntr);
3342 SvIVX(sv) = cntr;
3343 } else {
3344 cnt = Perl_Process_Messages(force, NULL);
3345 }
3346 XSprePUSH; PUSHi((IV)cnt);
3347 }
3348 XSRETURN(1);
3349}
3350
3351XS(XS_Cwd_current_drive)
3352{
3353 dXSARGS;
3354 if (items != 0)
3355 Perl_croak_nocontext("Usage: Cwd::current_drive()");
3356 {
3357 char RETVAL;
3358 dXSTARG;
3359
3360 RETVAL = current_drive();
3361 sv_setpvn(TARG, (char *)&RETVAL, 1);
3362 XSprePUSH; PUSHTARG;
3363 }
3364 XSRETURN(1);
3365}
3366
3367XS(XS_Cwd_sys_chdir)
3368{
3369 dXSARGS;
3370 if (items != 1)
3371 Perl_croak_nocontext("Usage: Cwd::sys_chdir(path)");
3372 {
3373 STRLEN n_a;
3374 char * path = (char *)SvPV(ST(0),n_a);
3375 bool RETVAL;
3376
3377 RETVAL = sys_chdir(path);
3378 ST(0) = boolSV(RETVAL);
3379 if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
3380 }
3381 XSRETURN(1);
3382}
3383
3384XS(XS_Cwd_change_drive)
3385{
3386 dXSARGS;
3387 if (items != 1)
3388 Perl_croak_nocontext("Usage: Cwd::change_drive(d)");
3389 {
3390 STRLEN n_a;
3391 char d = (char)*SvPV(ST(0),n_a);
3392 bool RETVAL;
3393
3394 RETVAL = change_drive(d);
3395 ST(0) = boolSV(RETVAL);
3396 if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
3397 }
3398 XSRETURN(1);
3399}
3400
3401XS(XS_Cwd_sys_is_absolute)
3402{
3403 dXSARGS;
3404 if (items != 1)
3405 Perl_croak_nocontext("Usage: Cwd::sys_is_absolute(path)");
3406 {
3407 STRLEN n_a;
3408 char * path = (char *)SvPV(ST(0),n_a);
3409 bool RETVAL;
3410
3411 RETVAL = sys_is_absolute(path);
3412 ST(0) = boolSV(RETVAL);
3413 if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
3414 }
3415 XSRETURN(1);
3416}
3417
3418XS(XS_Cwd_sys_is_rooted)
3419{
3420 dXSARGS;
3421 if (items != 1)
3422 Perl_croak_nocontext("Usage: Cwd::sys_is_rooted(path)");
3423 {
3424 STRLEN n_a;
3425 char * path = (char *)SvPV(ST(0),n_a);
3426 bool RETVAL;
3427
3428 RETVAL = sys_is_rooted(path);
3429 ST(0) = boolSV(RETVAL);
3430 if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
3431 }
3432 XSRETURN(1);
3433}
3434
3435XS(XS_Cwd_sys_is_relative)
3436{
3437 dXSARGS;
3438 if (items != 1)
3439 Perl_croak_nocontext("Usage: Cwd::sys_is_relative(path)");
3440 {
3441 STRLEN n_a;
3442 char * path = (char *)SvPV(ST(0),n_a);
3443 bool RETVAL;
3444
3445 RETVAL = sys_is_relative(path);
3446 ST(0) = boolSV(RETVAL);
3447 if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
3448 }
3449 XSRETURN(1);
3450}
3451
3452XS(XS_Cwd_sys_cwd)
3453{
3454 dXSARGS;
3455 if (items != 0)
3456 Perl_croak_nocontext("Usage: Cwd::sys_cwd()");
3457 {
3458 char p[MAXPATHLEN];
3459 char * RETVAL;
3460
3461 /* Can't use TARG, since tainting behaves differently */
3462 RETVAL = _getcwd2(p, MAXPATHLEN);
3463 ST(0) = sv_newmortal();
3464 sv_setpv(ST(0), RETVAL);
3465 SvTAINTED_on(ST(0));
3466 }
3467 XSRETURN(1);
3468}
3469
3470XS(XS_Cwd_sys_abspath)
3471{
3472 dXSARGS;
3473 if (items > 2)
3474 Perl_croak_nocontext("Usage: Cwd::sys_abspath(path = '.', dir = NULL)");
3475 {
3476 STRLEN n_a;
3477 char * path = items ? (char *)SvPV(ST(0),n_a) : ".";
3478 char * dir, *s, *t, *e;
3479 char p[MAXPATHLEN];
3480 char * RETVAL;
3481 int l;
3482 SV *sv;
3483
3484 if (items < 2)
3485 dir = NULL;
3486 else {
3487 dir = (char *)SvPV(ST(1),n_a);
3488 }
3489 if (path[0] == '.' && (path[1] == '/' || path[1] == '\\')) {
3490 path += 2;
3491 }
3492 if (dir == NULL) {
3493 if (_abspath(p, path, MAXPATHLEN) == 0) {
3494 RETVAL = p;
3495 } else {
3496 RETVAL = NULL;
3497 }
3498 } else {
3499 /* Absolute with drive: */
3500 if ( sys_is_absolute(path) ) {
3501 if (_abspath(p, path, MAXPATHLEN) == 0) {
3502 RETVAL = p;
3503 } else {
3504 RETVAL = NULL;
3505 }
3506 } else if (path[0] == '/' || path[0] == '\\') {
3507 /* Rooted, but maybe on different drive. */
3508 if (isALPHA(dir[0]) && dir[1] == ':' ) {
3509 char p1[MAXPATHLEN];
3510
3511 /* Need to prepend the drive. */
3512 p1[0] = dir[0];
3513 p1[1] = dir[1];
3514 Copy(path, p1 + 2, strlen(path) + 1, char);
3515 RETVAL = p;
3516 if (_abspath(p, p1, MAXPATHLEN) == 0) {
3517 RETVAL = p;
3518 } else {
3519 RETVAL = NULL;
3520 }
3521 } else if (_abspath(p, path, MAXPATHLEN) == 0) {
3522 RETVAL = p;
3523 } else {
3524 RETVAL = NULL;
3525 }
3526 } else {
3527 /* Either path is relative, or starts with a drive letter. */
3528 /* If the path starts with a drive letter, then dir is
3529 relevant only if
3530 a/b) it is absolute/x:relative on the same drive.
3531 c) path is on current drive, and dir is rooted
3532 In all the cases it is safe to drop the drive part
3533 of the path. */
3534 if ( !sys_is_relative(path) ) {
3535 if ( ( ( sys_is_absolute(dir)
3536 || (isALPHA(dir[0]) && dir[1] == ':'
3537 && strnicmp(dir, path,1) == 0))
3538 && strnicmp(dir, path,1) == 0)
3539 || ( !(isALPHA(dir[0]) && dir[1] == ':')
3540 && toupper(path[0]) == current_drive())) {
3541 path += 2;
3542 } else if (_abspath(p, path, MAXPATHLEN) == 0) {
3543 RETVAL = p; goto done;
3544 } else {
3545 RETVAL = NULL; goto done;
3546 }
3547 }
3548 {
3549 /* Need to prepend the absolute path of dir. */
3550 char p1[MAXPATHLEN];
3551
3552 if (_abspath(p1, dir, MAXPATHLEN) == 0) {
3553 int l = strlen(p1);
3554
3555 if (p1[ l - 1 ] != '/') {
3556 p1[ l ] = '/';
3557 l++;
3558 }
3559 Copy(path, p1 + l, strlen(path) + 1, char);
3560 if (_abspath(p, p1, MAXPATHLEN) == 0) {
3561 RETVAL = p;
3562 } else {
3563 RETVAL = NULL;
3564 }
3565 } else {
3566 RETVAL = NULL;
3567 }
3568 }
3569 done:
3570 }
3571 }
3572 if (!RETVAL)
3573 XSRETURN_EMPTY;
3574 /* Backslashes are already converted to slashes. */
3575 /* Remove trailing slashes */
3576 l = strlen(RETVAL);
3577 while (l > 0 && RETVAL[l-1] == '/')
3578 l--;
3579 ST(0) = sv_newmortal();
3580 sv_setpvn( sv = (SV*)ST(0), RETVAL, l);
3581 /* Remove duplicate slashes, skipping the first three, which
3582 may be parts of a server-based path */
3583 s = t = 3 + SvPV_force(sv, n_a);
3584 e = SvEND(sv);
3585 /* Do not worry about multibyte chars here, this would contradict the
3586 eventual UTFization, and currently most other places break too... */
3587 while (s < e) {
3588 if (s[0] == t[-1] && s[0] == '/')
3589 s++; /* Skip duplicate / */
3590 else
3591 *t++ = *s++;
3592 }
3593 if (t < e) {
3594 *t = 0;
3595 SvCUR_set(sv, t - SvPVX(sv));
3596 }
3597 if (!items)
3598 SvTAINTED_on(ST(0));
3599 }
3600 XSRETURN(1);
3601}
3602typedef APIRET (*PELP)(PSZ path, ULONG type);
3603
3604/* Kernels after 2000/09/15 understand this too: */
3605#ifndef LIBPATHSTRICT
3606# define LIBPATHSTRICT 3
3607#endif
3608
3609APIRET
3610ExtLIBPATH(ULONG ord, PSZ path, IV type, int fatal)
3611{
3612 ULONG what;
3613 PFN f = loadByOrdinal(ord, fatal); /* if fatal: load or die! */
3614
3615 if (!f) /* Impossible with fatal */
3616 return Perl_rc;
3617 if (type > 0)
3618 what = END_LIBPATH;
3619 else if (type == 0)
3620 what = BEGIN_LIBPATH;
3621 else
3622 what = LIBPATHSTRICT;
3623 return (*(PELP)f)(path, what);
3624}
3625
3626#define extLibpath(to,type, fatal) \
3627 (CheckOSError(ExtLIBPATH(ORD_DosQueryExtLibpath, (to), (type), fatal)) ? NULL : (to) )
3628
3629#define extLibpath_set(p,type, fatal) \
3630 (!CheckOSError(ExtLIBPATH(ORD_DosSetExtLibpath, (p), (type), fatal)))
3631
3632static void
3633early_error(char *msg1, char *msg2, char *msg3)
3634{ /* Buffer overflow detected; there is very little we can do... */
3635 ULONG rc;
3636
3637 DosWrite(2, msg1, strlen(msg1), &rc);
3638 DosWrite(2, msg2, strlen(msg2), &rc);
3639 DosWrite(2, msg3, strlen(msg3), &rc);
3640 DosExit(EXIT_PROCESS, 2);
3641}
3642
3643XS(XS_Cwd_extLibpath)
3644{
3645 dXSARGS;
3646 if (items < 0 || items > 1)
3647 Perl_croak_nocontext("Usage: OS2::extLibpath(type = 0)");
3648 {
3649 IV type;
3650 char to[1024];
3651 U32 rc;
3652 char * RETVAL;
3653 dXSTARG;
3654 STRLEN l;
3655
3656 if (items < 1)
3657 type = 0;
3658 else {
3659 type = SvIV(ST(0));
3660 }
3661
3662 to[0] = 1; to[1] = 0; /* Sometimes no error reported */
3663 RETVAL = extLibpath(to, type, 1); /* Make errors fatal */
3664 if (RETVAL && RETVAL[0] == 1 && RETVAL[1] == 0)
3665 Perl_croak_nocontext("panic OS2::extLibpath parameter");
3666 l = strlen(to);
3667 if (l >= sizeof(to))
3668 early_error("Buffer overflow while getting BEGIN/ENDLIBPATH: `",
3669 to, "'\r\n"); /* Will not return */
3670 sv_setpv(TARG, RETVAL);
3671 XSprePUSH; PUSHTARG;
3672 }
3673 XSRETURN(1);
3674}
3675
3676XS(XS_Cwd_extLibpath_set)
3677{
3678 dXSARGS;
3679 if (items < 1 || items > 2)
3680 Perl_croak_nocontext("Usage: OS2::extLibpath_set(s, type = 0)");
3681 {
3682 STRLEN n_a;
3683 char * s = (char *)SvPV(ST(0),n_a);
3684 IV type;
3685 U32 rc;
3686 bool RETVAL;
3687
3688 if (items < 2)
3689 type = 0;
3690 else {
3691 type = SvIV(ST(1));
3692 }
3693
3694 RETVAL = extLibpath_set(s, type, 1); /* Make errors fatal */
3695 ST(0) = boolSV(RETVAL);
3696 if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
3697 }
3698 XSRETURN(1);
3699}
3700
3701ULONG
3702fill_extLibpath(int type, char *pre, char *post, int replace, char *msg)
3703{
3704 char buf[2048], *to = buf, buf1[300], *s;
3705 STRLEN l;
3706 ULONG rc;
3707
3708 if (!pre && !post)
3709 return 0;
3710 if (pre) {
3711 pre = dir_subst(pre, strlen(pre), buf1, sizeof buf1, dir_subst_pathlike, msg);
3712 if (!pre)
3713 return ERROR_INVALID_PARAMETER;
3714 l = strlen(pre);
3715 if (l >= sizeof(buf)/2)
3716 return ERROR_BUFFER_OVERFLOW;
3717 s = pre - 1;
3718 while (*++s)
3719 if (*s == '/')
3720 *s = '\\'; /* Be extra cautious */
3721 memcpy(to, pre, l);
3722 if (!l || to[l-1] != ';')
3723 to[l++] = ';';
3724 to += l;
3725 }
3726
3727 if (!replace) {
3728 to[0] = 1; to[1] = 0; /* Sometimes no error reported */
3729 rc = ExtLIBPATH(ORD_DosQueryExtLibpath, to, type, 0); /* Do not croak */
3730 if (rc)
3731 return rc;
3732 if (to[0] == 1 && to[1] == 0)
3733 return ERROR_INVALID_PARAMETER;
3734 to += strlen(to);
3735 if (buf + sizeof(buf) - 1 <= to) /* Buffer overflow */
3736 early_error("Buffer overflow while getting BEGIN/ENDLIBPATH: `",
3737 buf, "'\r\n"); /* Will not return */
3738 if (to > buf && to[-1] != ';')
3739 *to++ = ';';
3740 }
3741 if (post) {
3742 post = dir_subst(post, strlen(post), buf1, sizeof buf1, dir_subst_pathlike, msg);
3743 if (!post)
3744 return ERROR_INVALID_PARAMETER;
3745 l = strlen(post);
3746 if (l + to - buf >= sizeof(buf) - 1)
3747 return ERROR_BUFFER_OVERFLOW;
3748 s = post - 1;
3749 while (*++s)
3750 if (*s == '/')
3751 *s = '\\'; /* Be extra cautious */
3752 memcpy(to, post, l);
3753 if (!l || to[l-1] != ';')
3754 to[l++] = ';';
3755 to += l;
3756 }
3757 *to = 0;
3758 rc = ExtLIBPATH(ORD_DosSetExtLibpath, buf, type, 0); /* Do not croak */
3759 return rc;
3760}
3761
3762/* Input: Address, BufLen
3763APIRET APIENTRY
3764DosQueryModFromEIP (HMODULE * hmod, ULONG * obj, ULONG BufLen, PCHAR Buf,
3765 ULONG * Offset, ULONG Address);
3766*/
3767
3768DeclOSFuncByORD(APIRET, _DosQueryModFromEIP,ORD_DosQueryModFromEIP,
3769 (HMODULE * hmod, ULONG * obj, ULONG BufLen, PCHAR Buf,
3770 ULONG * Offset, ULONG Address),
3771 (hmod, obj, BufLen, Buf, Offset, Address))
3772
3773static SV*
3774module_name_at(void *pp, enum module_name_how how)
3775{
3776 dTHX;
3777 char buf[MAXPATHLEN];
3778 char *p = buf;
3779 HMODULE mod;
3780 ULONG obj, offset, rc, addr = (ULONG)pp;
3781
3782 if (how & mod_name_HMODULE) {
3783 if ((how & ~mod_name_HMODULE) == mod_name_shortname)
3784 Perl_croak(aTHX_ "Can't get short module name from a handle");
3785 mod = (HMODULE)pp;
3786 how &= ~mod_name_HMODULE;
3787 } else if (!_DosQueryModFromEIP(&mod, &obj, sizeof(buf), buf, &offset, addr))
3788 return &PL_sv_undef;
3789 if (how == mod_name_handle)
3790 return newSVuv(mod);
3791 /* Full name... */
3792 if ( how != mod_name_shortname
3793 && CheckOSError(DosQueryModuleName(mod, sizeof(buf), buf)) )
3794 return &PL_sv_undef;
3795 while (*p) {
3796 if (*p == '\\')
3797 *p = '/';
3798 p++;
3799 }
3800 return newSVpv(buf, 0);
3801}
3802
3803static SV*
3804module_name_of_cv(SV *cv, enum module_name_how how)
3805{
3806 if (!cv || !SvROK(cv) || SvTYPE(SvRV(cv)) != SVt_PVCV || !CvXSUB(SvRV(cv))) {
3807 dTHX;
3808
3809 if (how & mod_name_C_function)
3810 return module_name_at((void*)SvIV(cv), how & ~mod_name_C_function);
3811 else if (how & mod_name_HMODULE)
3812 return module_name_at((void*)SvIV(cv), how);
3813 Perl_croak(aTHX_ "Not an XSUB reference");
3814 }
3815 return module_name_at(CvXSUB(SvRV(cv)), how);
3816}
3817
3818XS(XS_OS2_DLLname)
3819{
3820 dXSARGS;
3821 if (items > 2)
3822 Perl_croak(aTHX_ "Usage: OS2::DLLname( [ how, [\\&xsub] ] )");
3823 {
3824 SV * RETVAL;
3825 int how;
3826
3827 if (items < 1)
3828 how = mod_name_full;
3829 else {
3830 how = (int)SvIV(ST(0));
3831 }
3832 if (items < 2)
3833 RETVAL = module_name(how);
3834 else
3835 RETVAL = module_name_of_cv(ST(1), how);
3836 ST(0) = RETVAL;
3837 sv_2mortal(ST(0));
3838 }
3839 XSRETURN(1);
3840}
3841
3842DeclOSFuncByORD(INT, _Dos32QueryHeaderInfo, ORD_Dos32QueryHeaderInfo,
3843 (ULONG r1, ULONG r2, PVOID buf, ULONG szbuf, ULONG fnum),
3844 (r1, r2, buf, szbuf, fnum))
3845
3846XS(XS_OS2__headerInfo)
3847{
3848 dXSARGS;
3849 if (items > 4 || items < 2)
3850 Perl_croak(aTHX_ "Usage: OS2::_headerInfo(req,size[,handle,[offset]])");
3851 {
3852 ULONG req = (ULONG)SvIV(ST(0));
3853 STRLEN size = (STRLEN)SvIV(ST(1)), n_a;
3854 ULONG handle = (items >= 3 ? (ULONG)SvIV(ST(2)) : 0);
3855 ULONG offset = (items >= 4 ? (ULONG)SvIV(ST(3)) : 0);
3856
3857 if (size <= 0)
3858 Perl_croak(aTHX_ "OS2::_headerInfo(): unexpected size: %d", (int)size);
3859 ST(0) = newSVpvs("");
3860 SvGROW(ST(0), size + 1);
3861 sv_2mortal(ST(0));
3862
3863 if (!_Dos32QueryHeaderInfo(handle, offset, SvPV(ST(0), n_a), size, req))
3864 Perl_croak(aTHX_ "OS2::_headerInfo(%ld,%ld,%ld,%ld) error: %s",
3865 req, size, handle, offset, os2error(Perl_rc));
3866 SvCUR_set(ST(0), size);
3867 *SvEND(ST(0)) = 0;
3868 }
3869 XSRETURN(1);
3870}
3871
3872#define DQHI_QUERYLIBPATHSIZE 4
3873#define DQHI_QUERYLIBPATH 5
3874
3875XS(XS_OS2_libPath)
3876{
3877 dXSARGS;
3878 if (items != 0)
3879 Perl_croak(aTHX_ "Usage: OS2::libPath()");
3880 {
3881 ULONG size;
3882 STRLEN n_a;
3883
3884 if (!_Dos32QueryHeaderInfo(0, 0, &size, sizeof(size),
3885 DQHI_QUERYLIBPATHSIZE))
3886 Perl_croak(aTHX_ "OS2::_headerInfo(%ld,%ld,%ld,%ld) error: %s",
3887 DQHI_QUERYLIBPATHSIZE, sizeof(size), 0, 0,
3888 os2error(Perl_rc));
3889 ST(0) = newSVpvs("");
3890 SvGROW(ST(0), size + 1);
3891 sv_2mortal(ST(0));
3892
3893 /* We should be careful: apparently, this entry point does not
3894 pay attention to the size argument, so may overwrite
3895 unrelated data! */
3896 if (!_Dos32QueryHeaderInfo(0, 0, SvPV(ST(0), n_a), size,
3897 DQHI_QUERYLIBPATH))
3898 Perl_croak(aTHX_ "OS2::_headerInfo(%ld,%ld,%ld,%ld) error: %s",
3899 DQHI_QUERYLIBPATH, size, 0, 0, os2error(Perl_rc));
3900 SvCUR_set(ST(0), size);
3901 *SvEND(ST(0)) = 0;
3902 }
3903 XSRETURN(1);
3904}
3905
3906#define get_control87() _control87(0,0)
3907#define set_control87 _control87
3908
3909XS(XS_OS2__control87)
3910{
3911 dXSARGS;
3912 if (items != 2)
3913 Perl_croak(aTHX_ "Usage: OS2::_control87(new,mask)");
3914 {
3915 unsigned new = (unsigned)SvIV(ST(0));
3916 unsigned mask = (unsigned)SvIV(ST(1));
3917 unsigned RETVAL;
3918 dXSTARG;
3919
3920 RETVAL = _control87(new, mask);
3921 XSprePUSH; PUSHi((IV)RETVAL);
3922 }
3923 XSRETURN(1);
3924}
3925
3926XS(XS_OS2_mytype)
3927{
3928 dXSARGS;
3929 int which = 0;
3930
3931 if (items < 0 || items > 1)
3932 Perl_croak(aTHX_ "Usage: OS2::mytype([which])");
3933 if (items == 1)
3934 which = (int)SvIV(ST(0));
3935 {
3936 unsigned RETVAL;
3937 dXSTARG;
3938
3939 switch (which) {
3940 case 0:
3941 RETVAL = os2_mytype; /* Reset after fork */
3942 break;
3943 case 1:
3944 RETVAL = os2_mytype_ini; /* Before any fork */
3945 break;
3946 case 2:
3947 RETVAL = Perl_os2_initial_mode; /* Before first morphing */
3948 break;
3949 case 3:
3950 RETVAL = my_type(); /* Morphed type */
3951 break;
3952 default:
3953 Perl_croak(aTHX_ "OS2::mytype(which): unknown which=%d", which);
3954 }
3955 XSprePUSH; PUSHi((IV)RETVAL);
3956 }
3957 XSRETURN(1);
3958}
3959
3960
3961XS(XS_OS2_mytype_set)
3962{
3963 dXSARGS;
3964 int type;
3965
3966 if (items == 1)
3967 type = (int)SvIV(ST(0));
3968 else
3969 Perl_croak(aTHX_ "Usage: OS2::mytype_set(type)");
3970 my_type_set(type);
3971 XSRETURN_YES;
3972}
3973
3974
3975XS(XS_OS2_get_control87)
3976{
3977 dXSARGS;
3978 if (items != 0)
3979 Perl_croak(aTHX_ "Usage: OS2::get_control87()");
3980 {
3981 unsigned RETVAL;
3982 dXSTARG;
3983
3984 RETVAL = get_control87();
3985 XSprePUSH; PUSHi((IV)RETVAL);
3986 }
3987 XSRETURN(1);
3988}
3989
3990
3991XS(XS_OS2_set_control87)
3992{
3993 dXSARGS;
3994 if (items < 0 || items > 2)
3995 Perl_croak(aTHX_ "Usage: OS2::set_control87(new=MCW_EM, mask=MCW_EM)");
3996 {
3997 unsigned new;
3998 unsigned mask;
3999 unsigned RETVAL;
4000 dXSTARG;
4001
4002 if (items < 1)
4003 new = MCW_EM;
4004 else {
4005 new = (unsigned)SvIV(ST(0));
4006 }
4007
4008 if (items < 2)
4009 mask = MCW_EM;
4010 else {
4011 mask = (unsigned)SvIV(ST(1));
4012 }
4013
4014 RETVAL = set_control87(new, mask);
4015 XSprePUSH; PUSHi((IV)RETVAL);
4016 }
4017 XSRETURN(1);
4018}
4019
4020XS(XS_OS2_incrMaxFHandles) /* DosSetRelMaxFH */
4021{
4022 dXSARGS;
4023 if (items < 0 || items > 1)
4024 Perl_croak(aTHX_ "Usage: OS2::incrMaxFHandles(delta = 0)");
4025 {
4026 LONG delta;
4027 ULONG RETVAL, rc;
4028 dXSTARG;
4029
4030 if (items < 1)
4031 delta = 0;
4032 else
4033 delta = (LONG)SvIV(ST(0));
4034
4035 if (CheckOSError(DosSetRelMaxFH(&delta, &RETVAL)))
4036 croak_with_os2error("OS2::incrMaxFHandles(): DosSetRelMaxFH() error");
4037 XSprePUSH; PUSHu((UV)RETVAL);
4038 }
4039 XSRETURN(1);
4040}
4041
4042/* wait>0: force wait, wait<0: force nowait;
4043 if restore, save/restore flags; otherwise flags are in oflags.
4044
4045 Returns 1 if connected, 0 if not (due to nowait); croaks on error. */
4046static ULONG
4047connectNPipe(ULONG hpipe, int wait, ULONG restore, ULONG oflags)
4048{
4049 ULONG ret = ERROR_INTERRUPT, rc, flags;
4050
4051 if (restore && wait)
4052 os2cp_croak(DosQueryNPHState(hpipe, &oflags), "DosQueryNPHState()");
4053 /* DosSetNPHState fails if more bits than NP_NOWAIT|NP_READMODE_MESSAGE */
4054 oflags &= (NP_NOWAIT | NP_READMODE_MESSAGE);
4055 flags = (oflags & ~NP_NOWAIT) | (wait > 0 ? NP_WAIT : NP_NOWAIT);
4056 /* We know (o)flags unless wait == 0 && restore */
4057 if (wait && (flags != oflags))
4058 os2cp_croak(DosSetNPHState(hpipe, flags), "DosSetNPHState()");
4059 while (ret == ERROR_INTERRUPT)
4060 ret = DosConnectNPipe(hpipe);
4061 (void)CheckOSError(ret);
4062 if (restore && wait && (flags != oflags))
4063 os2cp_croak(DosSetNPHState(hpipe, oflags), "DosSetNPHState() back");
4064 /* We know flags unless wait == 0 && restore */
4065 if ( ((wait || restore) ? (flags & NP_NOWAIT) : 1)
4066 && (ret == ERROR_PIPE_NOT_CONNECTED) )
4067 return 0; /* normal return value */
4068 if (ret == NO_ERROR)
4069 return 1;
4070 croak_with_os2error("DosConnectNPipe()");
4071}
4072
4073/* With a lot of manual editing:
4074NO_OUTPUT ULONG
4075DosCreateNPipe(PCSZ pszName, OUTLIST HPIPE hpipe, ULONG ulOpenMode, int connect = 1, int count = 1, ULONG ulInbufLength = 8192, ULONG ulOutbufLength = ulInbufLength, ULONG ulPipeMode = count | NP_NOWAIT | NP_TYPE_BYTE | NP_READMODE_BYTE, ULONG ulTimeout = 0)
4076 PREINIT:
4077 ULONG rc;
4078 C_ARGS:
4079 pszName, &hpipe, ulOpenMode, ulPipeMode, ulInbufLength, ulOutbufLength, ulTimeout
4080 POSTCALL:
4081 if (CheckOSError(RETVAL))
4082 croak_with_os2error("OS2::mkpipe() error");
4083*/
4084XS(XS_OS2_pipe); /* prototype to pass -Wmissing-prototypes */
4085XS(XS_OS2_pipe)
4086{
4087 dXSARGS;
4088 if (items < 2 || items > 8)
4089 Perl_croak(aTHX_ "Usage: OS2::pipe(pszName, ulOpenMode, connect= 1, count= 1, ulInbufLength= 8192, ulOutbufLength= ulInbufLength, ulPipeMode= count | NP_NOWAIT | NP_TYPE_BYTE | NP_READMODE_BYTE, ulTimeout= 0)");
4090 {
4091 ULONG RETVAL;
4092 PCSZ pszName = ( SvOK(ST(0)) ? (PCSZ)SvPV_nolen(ST(0)) : NULL );
4093 HPIPE hpipe;
4094 SV *OpenMode = ST(1);
4095 ULONG ulOpenMode;
4096 int connect = 0, count, message_r = 0, message = 0, b = 0;
4097 ULONG ulInbufLength, ulOutbufLength, ulPipeMode, ulTimeout, rc;
4098 STRLEN len;
4099 char *s, buf[10], *s1, *perltype = NULL;
4100 PerlIO *perlio;
4101 double timeout;
4102
4103 if (!pszName || !*pszName)
4104 Perl_croak(aTHX_ "OS2::pipe(): empty pipe name");
4105 s = SvPV(OpenMode, len);
4106 if (len == 4 && strEQ(s, "wait")) { /* DosWaitNPipe() */
4107 ULONG ms = 0xFFFFFFFF, ret = ERROR_INTERRUPT; /* Indefinite */
4108
4109 if (items == 3) {
4110 timeout = (double)SvNV(ST(2));
4111 ms = timeout * 1000;
4112 if (timeout < 0)
4113 ms = 0xFFFFFFFF; /* Indefinite */
4114 else if (timeout && !ms)
4115 ms = 1;
4116 } else if (items > 3)
4117 Perl_croak(aTHX_ "OS2::pipe(): too many arguments for wait-for-connect: %ld", (long)items);
4118
4119 while (ret == ERROR_INTERRUPT)
4120 ret = DosWaitNPipe(pszName, ms); /* XXXX Update ms? */
4121 os2cp_croak(ret, "DosWaitNPipe()");
4122 XSRETURN_YES;
4123 }
4124 if (len == 4 && strEQ(s, "call")) { /* DosCallNPipe() */
4125 ULONG ms = 0xFFFFFFFF, got; /* Indefinite */
4126 STRLEN l;
4127 char *s;
4128 char buf[8192];
4129 STRLEN ll = sizeof(buf);
4130 char *b = buf;
4131
4132 if (items < 3 || items > 5)
4133 Perl_croak(aTHX_ "usage: OS2::pipe(pszName, 'call', write [, timeout= 0xFFFFFFFF, buffsize = 8192])");
4134 s = SvPV(ST(2), l);
4135 if (items >= 4) {
4136 timeout = (double)SvNV(ST(3));
4137 ms = timeout * 1000;
4138 if (timeout < 0)
4139 ms = 0xFFFFFFFF; /* Indefinite */
4140 else if (timeout && !ms)
4141 ms = 1;
4142 }
4143 if (items >= 5) {
4144 STRLEN lll = SvUV(ST(4));
4145 SV *sv = NEWSV(914, lll);
4146
4147 sv_2mortal(sv);
4148 ll = lll;
4149 b = SvPVX(sv);
4150 }
4151
4152 os2cp_croak(DosCallNPipe(pszName, s, l, b, ll, &got, ms),
4153 "DosCallNPipe()");
4154 XSRETURN_PVN(b, got);
4155 }
4156 s1 = buf;
4157 if (len && len <= 3 && !(*s >= '0' && *s <= '9')) {
4158 int r, w, R, W;
4159
4160 r = strchr(s, 'r') != 0;
4161 w = strchr(s, 'w') != 0;
4162 R = strchr(s, 'R') != 0;
4163 W = strchr(s, 'W') != 0;
4164 b = strchr(s, 'b') != 0;
4165 if (r + w + R + W + b != len || (r && R) || (w && W))
4166 Perl_croak(aTHX_ "OS2::pipe(): unknown OpenMode argument: `%s'", s);
4167 if ((r || R) && (w || W))
4168 ulOpenMode = NP_INHERIT | NP_NOWRITEBEHIND | NP_ACCESS_DUPLEX;
4169 else if (r || R)
4170 ulOpenMode = NP_INHERIT | NP_NOWRITEBEHIND | NP_ACCESS_INBOUND;
4171 else
4172 ulOpenMode = NP_INHERIT | NP_NOWRITEBEHIND | NP_ACCESS_OUTBOUND;
4173 if (R)
4174 message = message_r = 1;
4175 if (W)
4176 message = 1;
4177 else if (w && R)
4178 Perl_croak(aTHX_ "OS2::pipe(): can't have message read mode for non-message pipes");
4179 } else
4180 ulOpenMode = (ULONG)SvUV(OpenMode); /* ST(1) */
4181
4182 if ( (ulOpenMode & 0x3) == NP_ACCESS_DUPLEX
4183 || (ulOpenMode & 0x3) == NP_ACCESS_INBOUND )
4184 *s1++ = 'r';
4185 if ( (ulOpenMode & 0x3) == NP_ACCESS_DUPLEX )
4186 *s1++ = '+';
4187 if ( (ulOpenMode & 0x3) == NP_ACCESS_OUTBOUND )
4188 *s1++ = 'w';
4189 if (b)
4190 *s1++ = 'b';
4191 *s1 = 0;
4192 if ( (ulOpenMode & 0x3) == NP_ACCESS_DUPLEX )
4193 perltype = "+<&";
4194 else if ( (ulOpenMode & 0x3) == NP_ACCESS_OUTBOUND )
4195 perltype = ">&";
4196 else
4197 perltype = "<&";
4198
4199 if (items < 3)
4200 connect = -1; /* no wait */
4201 else if (SvTRUE(ST(2))) {
4202 s = SvPV(ST(2), len);
4203 if (len == 6 && strEQ(s, "nowait"))
4204 connect = -1; /* no wait */
4205 else if (len == 4 && strEQ(s, "wait"))
4206 connect = 1; /* wait */
4207 else
4208 Perl_croak(aTHX_ "OS2::pipe(): unknown connect argument: `%s'", s);
4209 }
4210
4211 if (items < 4)
4212 count = 1;
4213 else
4214 count = (int)SvIV(ST(3));
4215
4216 if (items < 5)
4217 ulInbufLength = 8192;
4218 else
4219 ulInbufLength = (ULONG)SvUV(ST(4));
4220
4221 if (items < 6)
4222 ulOutbufLength = ulInbufLength;
4223 else
4224 ulOutbufLength = (ULONG)SvUV(ST(5));
4225
4226 if (count < -1 || count == 0 || count >= 255)
4227 Perl_croak(aTHX_ "OS2::pipe(): count should be -1 or between 1 and 254: %ld", (long)count);
4228 if (count < 0 )
4229 count = 255; /* Unlimited */
4230
4231 ulPipeMode = count;
4232 if (items < 7)
4233 ulPipeMode |= (NP_WAIT
4234 | (message ? NP_TYPE_MESSAGE : NP_TYPE_BYTE)
4235 | (message_r ? NP_READMODE_MESSAGE : NP_READMODE_BYTE));
4236 else
4237 ulPipeMode |= (ULONG)SvUV(ST(6));
4238
4239 if (items < 8)
4240 timeout = 0;
4241 else
4242 timeout = (double)SvNV(ST(7));
4243 ulTimeout = timeout * 1000;
4244 if (timeout < 0)
4245 ulTimeout = 0xFFFFFFFF; /* Indefinite */
4246 else if (timeout && !ulTimeout)
4247 ulTimeout = 1;
4248
4249 RETVAL = DosCreateNPipe(pszName, &hpipe, ulOpenMode, ulPipeMode, ulInbufLength, ulOutbufLength, ulTimeout);
4250 if (CheckOSError(RETVAL))
4251 croak_with_os2error("OS2::pipe(): DosCreateNPipe() error");
4252
4253 if (connect)
4254 connectNPipe(hpipe, connect, 1, 0); /* XXXX wait, retval */
4255 hpipe = __imphandle(hpipe);
4256
4257 perlio = PerlIO_fdopen(hpipe, buf);
4258 ST(0) = sv_newmortal();
4259 {
4260 GV *gv = newGVgen("OS2::pipe");
4261 if ( do_open6(gv, perltype, strlen(perltype), perlio, NULL, 0) )
4262 sv_setsv(ST(0), sv_bless(newRV((SV*)gv), gv_stashpv("IO::Handle",1)));
4263 else
4264 ST(0) = &PL_sv_undef;
4265 }
4266 }
4267 XSRETURN(1);
4268}
4269
4270XS(XS_OS2_pipeCntl); /* prototype to pass -Wmissing-prototypes */
4271XS(XS_OS2_pipeCntl)
4272{
4273 dXSARGS;
4274 if (items < 2 || items > 3)
4275 Perl_croak(aTHX_ "Usage: OS2::pipeCntl(pipe, op [, wait])");
4276 {
4277 ULONG rc;
4278 PerlIO *perlio = IoIFP(sv_2io(ST(0)));
4279 IV fn = PerlIO_fileno(perlio);
4280 HPIPE hpipe = (HPIPE)fn;
4281 STRLEN len;
4282 char *s = SvPV(ST(1), len);
4283 int wait = 0, disconnect = 0, connect = 0, message = -1, query = 0;
4284 int peek = 0, state = 0, info = 0;
4285
4286 if (fn < 0)
4287 Perl_croak(aTHX_ "OS2::pipeCntl(): not a pipe");
4288 if (items == 3)
4289 wait = (SvTRUE(ST(2)) ? 1 : -1);
4290
4291 switch (len) {
4292 case 4:
4293 if (strEQ(s, "byte"))
4294 message = 0;
4295 else if (strEQ(s, "peek"))
4296 peek = 1;
4297 else if (strEQ(s, "info"))
4298 info = 1;
4299 else
4300 goto unknown;
4301 break;
4302 case 5:
4303 if (strEQ(s, "reset"))
4304 disconnect = connect = 1;
4305 else if (strEQ(s, "state"))
4306 query = 1;
4307 else
4308 goto unknown;
4309 break;
4310 case 7:
4311 if (strEQ(s, "connect"))
4312 connect = 1;
4313 else if (strEQ(s, "message"))
4314 message = 1;
4315 else
4316 goto unknown;
4317 break;
4318 case 9:
4319 if (!strEQ(s, "readstate"))
4320 goto unknown;
4321 state = 1;
4322 break;
4323 case 10:
4324 if (!strEQ(s, "disconnect"))
4325 goto unknown;
4326 disconnect = 1;
4327 break;
4328 default:
4329 unknown:
4330 Perl_croak(aTHX_ "OS2::pipeCntl(): unknown argument: `%s'", s);
4331 break;
4332 }
4333
4334 if (items == 3 && !connect)
4335 Perl_croak(aTHX_ "OS2::pipeCntl(): no wait argument for `%s'", s);
4336
4337 XSprePUSH; /* Do not need arguments any more */
4338 if (disconnect) {
4339 os2cp_croak(DosDisConnectNPipe(hpipe), "OS2::pipeCntl(): DosDisConnectNPipe()");
4340 PerlIO_clearerr(perlio);
4341 }
4342 if (connect) {
4343 if (!connectNPipe(hpipe, wait , 1, 0))
4344 XSRETURN_IV(-1);
4345 }
4346 if (query) {
4347 ULONG flags;
4348
4349 os2cp_croak(DosQueryNPHState(hpipe, &flags), "DosQueryNPHState()");
4350 XSRETURN_UV(flags);
4351 }
4352 if (peek || state || info) {
4353 ULONG BytesRead, PipeState;
4354 AVAILDATA BytesAvail;
4355
4356 os2cp_croak( DosPeekNPipe(hpipe, NULL, 0, &BytesRead, &BytesAvail,
4357 &PipeState), "DosPeekNPipe() for state");
4358 if (state) {
4359 EXTEND(SP, 3);
4360 mPUSHu(PipeState);
4361 /* Bytes (available/in-message) */
4362 mPUSHi(BytesAvail.cbpipe);
4363 mPUSHi(BytesAvail.cbmessage);
4364 XSRETURN(3);
4365 } else if (info) {
4366 /* L S S C C C/Z*
4367 ID of the (remote) computer
4368 buffers (out/in)
4369 instances (max/actual)
4370 */
4371 struct pipe_info_t {
4372 ULONG id; /* char id[4]; */
4373 PIPEINFO pInfo;
4374 char buf[512];
4375 } b;
4376 int size;
4377
4378 os2cp_croak( DosQueryNPipeInfo(hpipe, 1, &b.pInfo, sizeof(b) - STRUCT_OFFSET(struct pipe_info_t, pInfo)),
4379 "DosQueryNPipeInfo(1)");
4380 os2cp_croak( DosQueryNPipeInfo(hpipe, 2, &b.id, sizeof(b.id)),
4381 "DosQueryNPipeInfo(2)");
4382 size = b.pInfo.cbName;
4383 /* Trailing 0 is included in cbName - undocumented; so
4384 one should always extract with Z* */
4385 if (size) /* name length 254 or less */
4386 size--;
4387 else
4388 size = strlen(b.pInfo.szName);
4389 EXTEND(SP, 6);
4390 mPUSHp(b.pInfo.szName, size);
4391 mPUSHu(b.id);
4392 mPUSHi(b.pInfo.cbOut);
4393 mPUSHi(b.pInfo.cbIn);
4394 mPUSHi(b.pInfo.cbMaxInst);
4395 mPUSHi(b.pInfo.cbCurInst);
4396 XSRETURN(6);
4397 } else if (BytesAvail.cbpipe == 0) {
4398 XSRETURN_NO;
4399 } else {
4400 SV *tmp = NEWSV(914, BytesAvail.cbpipe);
4401 char *s = SvPVX(tmp);
4402
4403 sv_2mortal(tmp);
4404 os2cp_croak( DosPeekNPipe(hpipe, s, BytesAvail.cbpipe, &BytesRead,
4405 &BytesAvail, &PipeState), "DosPeekNPipe()");
4406 SvCUR_set(tmp, BytesRead);
4407 *SvEND(tmp) = 0;
4408 SvPOK_on(tmp);
4409 XSprePUSH; PUSHs(tmp);
4410 XSRETURN(1);
4411 }
4412 }
4413 if (message > -1) {
4414 ULONG oflags, flags;
4415
4416 os2cp_croak(DosQueryNPHState(hpipe, &oflags), "DosQueryNPHState()");
4417 /* DosSetNPHState fails if more bits than NP_NOWAIT|NP_READMODE_MESSAGE */
4418 oflags &= (NP_NOWAIT | NP_READMODE_MESSAGE);
4419 flags = (oflags & NP_NOWAIT)
4420 | (message ? NP_READMODE_MESSAGE : NP_READMODE_BYTE);
4421 if (flags != oflags)
4422 os2cp_croak(DosSetNPHState(hpipe, flags), "DosSetNPHState()");
4423 }
4424 }
4425 XSRETURN_YES;
4426}
4427
4428/*
4429NO_OUTPUT ULONG
4430DosOpen(PCSZ pszFileName, OUTLIST HFILE hFile, OUTLIST ULONG ulAction, ULONG ulOpenFlags, ULONG ulOpenMode = OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW, ULONG ulAttribute = FILE_NORMAL, ULONG ulFileSize = 0, PEAOP2 pEABuf = NULL);
4431 PREINIT:
4432 ULONG rc;
4433 C_ARGS:
4434 pszFileName, &hFile, &ulAction, ulFileSize, ulAttribute, ulOpenFlags, ulOpenMode, pEABuf
4435 POSTCALL:
4436 if (CheckOSError(RETVAL))
4437 croak_with_os2error("OS2::open() error");
4438*/
4439XS(XS_OS2_open); /* prototype to pass -Wmissing-prototypes */
4440XS(XS_OS2_open)
4441{
4442 dXSARGS;
4443 if (items < 2 || items > 6)
4444 Perl_croak(aTHX_ "Usage: OS2::open(pszFileName, ulOpenMode, ulOpenFlags= OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW, ulAttribute= FILE_NORMAL, ulFileSize= 0, pEABuf= NULL)");
4445 {
4446#line 39 "pipe.xs"
4447 ULONG rc;
4448#line 113 "pipe.c"
4449 ULONG RETVAL;
4450 PCSZ pszFileName = ( SvOK(ST(0)) ? (PCSZ)SvPV_nolen(ST(0)) : NULL );
4451 HFILE hFile;
4452 ULONG ulAction;
4453 ULONG ulOpenMode = (ULONG)SvUV(ST(1));
4454 ULONG ulOpenFlags;
4455 ULONG ulAttribute;
4456 ULONG ulFileSize;
4457 PEAOP2 pEABuf;
4458
4459 if (items < 3)
4460 ulOpenFlags = OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW;
4461 else {
4462 ulOpenFlags = (ULONG)SvUV(ST(2));
4463 }
4464
4465 if (items < 4)
4466 ulAttribute = FILE_NORMAL;
4467 else {
4468 ulAttribute = (ULONG)SvUV(ST(3));
4469 }
4470
4471 if (items < 5)
4472 ulFileSize = 0;
4473 else {
4474 ulFileSize = (ULONG)SvUV(ST(4));
4475 }
4476
4477 if (items < 6)
4478 pEABuf = NULL;
4479 else {
4480 pEABuf = (PEAOP2)SvUV(ST(5));
4481 }
4482
4483 RETVAL = DosOpen(pszFileName, &hFile, &ulAction, ulFileSize, ulAttribute, ulOpenFlags, ulOpenMode, pEABuf);
4484 if (CheckOSError(RETVAL))
4485 croak_with_os2error("OS2::open() error");
4486 XSprePUSH; EXTEND(SP,2);
4487 PUSHs(sv_newmortal());
4488 sv_setuv(ST(0), (UV)hFile);
4489 PUSHs(sv_newmortal());
4490 sv_setuv(ST(1), (UV)ulAction);
4491 }
4492 XSRETURN(2);
4493}
4494
4495int
4496Xs_OS2_init(pTHX)
4497{
4498 char *file = __FILE__;
4499 {
4500 GV *gv;
4501
4502 if (_emx_env & 0x200) { /* OS/2 */
4503 newXS("File::Copy::syscopy", XS_File__Copy_syscopy, file);
4504 newXS("Cwd::extLibpath", XS_Cwd_extLibpath, file);
4505 newXS("Cwd::extLibpath_set", XS_Cwd_extLibpath_set, file);
4506 newXS("OS2::extLibpath", XS_Cwd_extLibpath, file);
4507 newXS("OS2::extLibpath_set", XS_Cwd_extLibpath_set, file);
4508 }
4509 newXS("OS2::Error", XS_OS2_Error, file);
4510 newXS("OS2::Errors2Drive", XS_OS2_Errors2Drive, file);
4511 newXS("OS2::SysInfo", XS_OS2_SysInfo, file);
4512 newXSproto("OS2::DevCap", XS_OS2_DevCap, file, ";$$");
4513 newXSproto("OS2::SysInfoFor", XS_OS2_SysInfoFor, file, "$;$");
4514 newXS("OS2::BootDrive", XS_OS2_BootDrive, file);
4515 newXS("OS2::MorphPM", XS_OS2_MorphPM, file);
4516 newXS("OS2::UnMorphPM", XS_OS2_UnMorphPM, file);
4517 newXS("OS2::Serve_Messages", XS_OS2_Serve_Messages, file);
4518 newXS("OS2::Process_Messages", XS_OS2_Process_Messages, file);
4519 newXS("DynaLoader::mod2fname", XS_DynaLoader_mod2fname, file);
4520 newXS("Cwd::current_drive", XS_Cwd_current_drive, file);
4521 newXS("Cwd::sys_chdir", XS_Cwd_sys_chdir, file);
4522 newXS("Cwd::change_drive", XS_Cwd_change_drive, file);
4523 newXS("Cwd::sys_is_absolute", XS_Cwd_sys_is_absolute, file);
4524 newXS("Cwd::sys_is_rooted", XS_Cwd_sys_is_rooted, file);
4525 newXS("Cwd::sys_is_relative", XS_Cwd_sys_is_relative, file);
4526 newXS("Cwd::sys_cwd", XS_Cwd_sys_cwd, file);
4527 newXS("Cwd::sys_abspath", XS_Cwd_sys_abspath, file);
4528 newXS("OS2::replaceModule", XS_OS2_replaceModule, file);
4529 newXS("OS2::perfSysCall", XS_OS2_perfSysCall, file);
4530 newXSproto("OS2::_control87", XS_OS2__control87, file, "$$");
4531 newXSproto("OS2::get_control87", XS_OS2_get_control87, file, "");
4532 newXSproto("OS2::set_control87", XS_OS2_set_control87, file, ";$$");
4533 newXSproto("OS2::DLLname", XS_OS2_DLLname, file, ";$$");
4534 newXSproto("OS2::mytype", XS_OS2_mytype, file, ";$");
4535 newXSproto("OS2::mytype_set", XS_OS2_mytype_set, file, "$");
4536 newXSproto("OS2::_headerInfo", XS_OS2__headerInfo, file, "$$;$$");
4537 newXSproto("OS2::libPath", XS_OS2_libPath, file, "");
4538 newXSproto("OS2::Timer", XS_OS2_Timer, file, "");
4539 newXSproto("OS2::msCounter", XS_OS2_msCounter, file, "");
4540 newXSproto("OS2::ms_sleep", XS_OS2_ms_sleep, file, "$;$");
4541 newXSproto("OS2::_InfoTable", XS_OS2__InfoTable, file, ";$");
4542 newXSproto("OS2::incrMaxFHandles", XS_OS2_incrMaxFHandles, file, ";$");
4543 newXSproto("OS2::SysValues", XS_OS2_SysValues, file, ";$$");
4544 newXSproto("OS2::SysValues_set", XS_OS2_SysValues_set, file, "$$;$");
4545 newXSproto("OS2::Beep", XS_OS2_Beep, file, ";$$");
4546 newXSproto("OS2::pipe", XS_OS2_pipe, file, "$$;$$$$$$");
4547 newXSproto("OS2::pipeCntl", XS_OS2_pipeCntl, file, "$$;$");
4548 newXSproto("OS2::open", XS_OS2_open, file, "$$;$$$$");
4549 gv = gv_fetchpv("OS2::is_aout", TRUE, SVt_PV);
4550 GvMULTI_on(gv);
4551#ifdef PERL_IS_AOUT
4552 sv_setiv(GvSV(gv), 1);
4553#endif
4554 gv = gv_fetchpv("OS2::is_static", TRUE, SVt_PV);
4555 GvMULTI_on(gv);
4556#ifdef PERL_IS_AOUT
4557 sv_setiv(GvSV(gv), 1);
4558#endif
4559 gv = gv_fetchpv("OS2::can_fork", TRUE, SVt_PV);
4560 GvMULTI_on(gv);
4561 sv_setiv(GvSV(gv), exe_is_aout());
4562 gv = gv_fetchpv("OS2::emx_rev", TRUE, SVt_PV);
4563 GvMULTI_on(gv);
4564 sv_setiv(GvSV(gv), _emx_rev);
4565 sv_setpv(GvSV(gv), _emx_vprt);
4566 SvIOK_on(GvSV(gv));
4567 gv = gv_fetchpv("OS2::emx_env", TRUE, SVt_PV);
4568 GvMULTI_on(gv);
4569 sv_setiv(GvSV(gv), _emx_env);
4570 gv = gv_fetchpv("OS2::os_ver", TRUE, SVt_PV);
4571 GvMULTI_on(gv);
4572 sv_setnv(GvSV(gv), _osmajor + 0.001 * _osminor);
4573 gv = gv_fetchpv("OS2::nsyserror", TRUE, SVt_PV);
4574 GvMULTI_on(gv);
4575 sv_setiv(GvSV(gv), 1); /* DEFAULT: Show number on syserror */
4576 }
4577 return 0;
4578}
4579
4580extern void _emx_init(void*);
4581
4582static void jmp_out_of_atexit(void);
4583
4584#define FORCE_EMX_INIT_CONTRACT_ARGV 1
4585#define FORCE_EMX_INIT_INSTALL_ATEXIT 2
4586
4587static void
4588my_emx_init(void *layout) {
4589 static volatile void *old_esp = 0; /* Cannot be on stack! */
4590
4591 /* Can't just call emx_init(), since it moves the stack pointer */
4592 /* It also busts a lot of registers, so be extra careful */
4593 __asm__( "pushf\n"
4594 "pusha\n"
4595 "movl %%esp, %1\n"
4596 "push %0\n"
4597 "call __emx_init\n"
4598 "movl %1, %%esp\n"
4599 "popa\n"
4600 "popf\n" : : "r" (layout), "m" (old_esp) );
4601}
4602
4603struct layout_table_t {
4604 ULONG text_base;
4605 ULONG text_end;
4606 ULONG data_base;
4607 ULONG data_end;
4608 ULONG bss_base;
4609 ULONG bss_end;
4610 ULONG heap_base;
4611 ULONG heap_end;
4612 ULONG heap_brk;
4613 ULONG heap_off;
4614 ULONG os2_dll;
4615 ULONG stack_base;
4616 ULONG stack_end;
4617 ULONG flags;
4618 ULONG reserved[2];
4619 char options[64];
4620};
4621
4622static ULONG
4623my_os_version() {
4624 static ULONG osv_res; /* Cannot be on stack! */
4625
4626 /* Can't just call __os_version(), since it does not follow C
4627 calling convention: it busts a lot of registers, so be extra careful */
4628 __asm__( "pushf\n"
4629 "pusha\n"
4630 "call ___os_version\n"
4631 "movl %%eax, %0\n"
4632 "popa\n"
4633 "popf\n" : "=m" (osv_res) );
4634
4635 return osv_res;
4636}
4637
4638static void
4639force_init_emx_runtime(EXCEPTIONREGISTRATIONRECORD *preg, ULONG flags)
4640{
4641 /* Calling emx_init() will bust the top of stack: it installs an
4642 exception handler and puts argv data there. */
4643 char *oldarg, *oldenv;
4644 void *oldstackend, *oldstack;
4645 PPIB pib;
4646 PTIB tib;
4647 ULONG rc, error = 0, out;
4648 char buf[512];
4649 static struct layout_table_t layout_table;
4650 struct {
4651 char buf[48*1024]; /* _emx_init() requires 32K, cmd.exe has 64K only */
4652 double alignment1;
4653 EXCEPTIONREGISTRATIONRECORD xreg;
4654 } *newstack;
4655 char *s;
4656
4657 layout_table.os2_dll = (ULONG)&os2_dll_fake;
4658 layout_table.flags = 0x02000002; /* flags: application, OMF */
4659
4660 DosGetInfoBlocks(&tib, &pib);
4661 oldarg = pib->pib_pchcmd;
4662 oldenv = pib->pib_pchenv;
4663 oldstack = tib->tib_pstack;
4664 oldstackend = tib->tib_pstacklimit;
4665
4666 if ( (char*)&s < (char*)oldstack + 4*1024
4667 || (char *)oldstackend < (char*)oldstack + 52*1024 )
4668 early_error("It is a lunacy to try to run EMX Perl ",
4669 "with less than 64K of stack;\r\n",
4670 " at least with non-EMX starter...\r\n");
4671
4672 /* Minimize the damage to the stack via reducing the size of argv. */
4673 if (flags & FORCE_EMX_INIT_CONTRACT_ARGV) {
4674 pib->pib_pchcmd = "\0\0"; /* Need 3 concatenated strings */
4675 pib->pib_pchcmd = "\0"; /* Ended by an extra \0. */
4676 }
4677
4678 newstack = alloca(sizeof(*newstack));
4679 /* Emulate the stack probe */
4680 s = ((char*)newstack) + sizeof(*newstack);
4681 while (s > (char*)newstack) {
4682 s[-1] = 0;
4683 s -= 4096;
4684 }
4685
4686 /* Reassigning stack is documented to work */
4687 tib->tib_pstack = (void*)newstack;
4688 tib->tib_pstacklimit = (void*)((char*)newstack + sizeof(*newstack));
4689
4690 /* Can't just call emx_init(), since it moves the stack pointer */
4691 my_emx_init((void*)&layout_table);
4692
4693 /* Remove the exception handler, cannot use it - too low on the stack.
4694 Check whether it is inside the new stack. */
4695 buf[0] = 0;
4696 if (tib->tib_pexchain >= tib->tib_pstacklimit
4697 || tib->tib_pexchain < tib->tib_pstack) {
4698 error = 1;
4699 sprintf(buf,
4700 "panic: ExceptionHandler misplaced: not %#lx <= %#lx < %#lx\n",
4701 (unsigned long)tib->tib_pstack,
4702 (unsigned long)tib->tib_pexchain,
4703 (unsigned long)tib->tib_pstacklimit);
4704 goto finish;
4705 }
4706 if (tib->tib_pexchain != &(newstack->xreg)) {
4707 sprintf(buf, "ExceptionHandler misplaced: %#lx != %#lx\n",
4708 (unsigned long)tib->tib_pexchain,
4709 (unsigned long)&(newstack->xreg));
4710 }
4711 rc = DosUnsetExceptionHandler((EXCEPTIONREGISTRATIONRECORD *)tib->tib_pexchain);
4712 if (rc)
4713 sprintf(buf + strlen(buf),
4714 "warning: DosUnsetExceptionHandler rc=%#lx=%lu\n", rc, rc);
4715
4716 if (preg) {
4717 /* ExceptionRecords should be on stack, in a correct order. Sigh... */
4718 preg->prev_structure = 0;
4719 preg->ExceptionHandler = _emx_exception;
4720 rc = DosSetExceptionHandler(preg);
4721 if (rc) {
4722 sprintf(buf + strlen(buf),
4723 "warning: DosSetExceptionHandler rc=%#lx=%lu\n", rc, rc);
4724 DosWrite(2, buf, strlen(buf), &out);
4725 emx_exception_init = 1; /* Do it around spawn*() calls */
4726 }
4727 } else
4728 emx_exception_init = 1; /* Do it around spawn*() calls */
4729
4730 finish:
4731 /* Restore the damage */
4732 pib->pib_pchcmd = oldarg;
4733 pib->pib_pchcmd = oldenv;
4734 tib->tib_pstacklimit = oldstackend;
4735 tib->tib_pstack = oldstack;
4736 emx_runtime_init = 1;
4737 if (buf[0])
4738 DosWrite(2, buf, strlen(buf), &out);
4739 if (error)
4740 exit(56);
4741}
4742
4743static void
4744jmp_out_of_atexit(void)
4745{
4746 if (longjmp_at_exit)
4747 longjmp(at_exit_buf, 1);
4748}
4749
4750extern void _CRT_term(void);
4751
4752void
4753Perl_OS2_term(void **p, int exitstatus, int flags)
4754{
4755 if (!emx_runtime_secondary)
4756 return;
4757
4758 /* The principal executable is not running the same CRTL, so there
4759 is nobody to shutdown *this* CRTL except us... */
4760 if (flags & FORCE_EMX_DEINIT_EXIT) {
4761 if (p && !emx_exception_init)
4762 DosUnsetExceptionHandler((EXCEPTIONREGISTRATIONRECORD *)p);
4763 /* Do not run the executable's CRTL's termination routines */
4764 exit(exitstatus); /* Run at-exit, flush buffers, etc */
4765 }
4766 /* Run at-exit list, and jump out at the end */
4767 if ((flags & FORCE_EMX_DEINIT_RUN_ATEXIT) && !setjmp(at_exit_buf)) {
4768 longjmp_at_exit = 1;
4769 exit(exitstatus); /* The first pass through "if" */
4770 }
4771
4772 /* Get here if we managed to jump out of exit(), or did not run atexit. */
4773 longjmp_at_exit = 0; /* Maybe exit() is called again? */
4774#if 0 /* _atexit_n is not exported */
4775 if (flags & FORCE_EMX_DEINIT_RUN_ATEXIT)
4776 _atexit_n = 0; /* Remove the atexit() handlers */
4777#endif
4778 /* Will segfault on program termination if we leave this dangling... */
4779 if (p && !emx_exception_init)
4780 DosUnsetExceptionHandler((EXCEPTIONREGISTRATIONRECORD *)p);
4781 /* Typically there is no need to do this, done from _DLL_InitTerm() */
4782 if (flags & FORCE_EMX_DEINIT_CRT_TERM)
4783 _CRT_term(); /* Flush buffers, etc. */
4784 /* Now it is a good time to call exit() in the caller's CRTL... */
4785}
4786
4787#include <emx/startup.h>
4788
4789extern ULONG __os_version(); /* See system.doc */
4790
4791void
4792check_emx_runtime(char **env, EXCEPTIONREGISTRATIONRECORD *preg)
4793{
4794 ULONG v_crt, v_emx, count = 0, rc = NO_ERROR, rc1, maybe_inited = 0;
4795 static HMTX hmtx_emx_init = NULLHANDLE;
4796 static int emx_init_done = 0;
4797
4798 /* If _environ is not set, this code sits in a DLL which
4799 uses a CRT DLL which not compatible with the executable's
4800 CRT library. Some parts of the DLL are not initialized.
4801 */
4802 if (_environ != NULL)
4803 return; /* Properly initialized */
4804
4805 /* It is not DOS, so we may use OS/2 API now */
4806 /* Some data we manipulate is static; protect ourselves from
4807 calling the same API from a different thread. */
4808 DosEnterMustComplete(&count);
4809
4810 rc1 = DosEnterCritSec();
4811 if (!hmtx_emx_init)
4812 rc = DosCreateMutexSem(NULL, &hmtx_emx_init, 0, TRUE); /*Create owned*/
4813 else
4814 maybe_inited = 1;
4815
4816 if (rc != NO_ERROR)
4817 hmtx_emx_init = NULLHANDLE;
4818
4819 if (rc1 == NO_ERROR)
4820 DosExitCritSec();
4821 DosExitMustComplete(&count);
4822
4823 while (maybe_inited) { /* Other thread did or is doing the same now */
4824 if (emx_init_done)
4825 return;
4826 rc = DosRequestMutexSem(hmtx_emx_init,
4827 (ULONG) SEM_INDEFINITE_WAIT); /* Timeout (none) */
4828 if (rc == ERROR_INTERRUPT)
4829 continue;
4830 if (rc != NO_ERROR) {
4831 char buf[80];
4832 ULONG out;
4833
4834 sprintf(buf,
4835 "panic: EMX backdoor init: DosRequestMutexSem error: %lu=%#lx\n", rc, rc);
4836 DosWrite(2, buf, strlen(buf), &out);
4837 return;
4838 }
4839 DosReleaseMutexSem(hmtx_emx_init);
4840 return;
4841 }
4842
4843 /* If the executable does not use EMX.DLL, EMX.DLL is not completely
4844 initialized either. Uninitialized EMX.DLL returns 0 in the low
4845 nibble of __os_version(). */
4846 v_emx = my_os_version();
4847
4848 /* _osmajor and _osminor are normally set in _DLL_InitTerm of CRT DLL
4849 (=>_CRT_init=>_entry2) via a call to __os_version(), then
4850 reset when the EXE initialization code calls _text=>_init=>_entry2.
4851 The first time they are wrongly set to 0; the second time the
4852 EXE initialization code had already called emx_init=>initialize1
4853 which correctly set version_major, version_minor used by
4854 __os_version(). */
4855 v_crt = (_osmajor | _osminor);
4856
4857 if ((_emx_env & 0x200) && !(v_emx & 0xFFFF)) { /* OS/2, EMX uninit. */
4858 force_init_emx_runtime( preg,
4859 FORCE_EMX_INIT_CONTRACT_ARGV
4860 | FORCE_EMX_INIT_INSTALL_ATEXIT );
4861 emx_wasnt_initialized = 1;
4862 /* Update CRTL data basing on now-valid EMX runtime data */
4863 if (!v_crt) { /* The only wrong data are the versions. */
4864 v_emx = my_os_version(); /* *Now* it works */
4865 *(unsigned char *)&_osmajor = v_emx & 0xFF; /* Cast out const */
4866 *(unsigned char *)&_osminor = (v_emx>>8) & 0xFF;
4867 }
4868 }
4869 emx_runtime_secondary = 1;
4870 /* if (flags & FORCE_EMX_INIT_INSTALL_ATEXIT) */
4871 atexit(jmp_out_of_atexit); /* Allow run of atexit() w/o exit() */
4872
4873 if (env == NULL) { /* Fetch from the process info block */
4874 int c = 0;
4875 PPIB pib;
4876 PTIB tib;
4877 char *e, **ep;
4878
4879 DosGetInfoBlocks(&tib, &pib);
4880 e = pib->pib_pchenv;
4881 while (*e) { /* Get count */
4882 c++;
4883 e = e + strlen(e) + 1;
4884 }
4885 Newx(env, c + 1, char*);
4886 ep = env;
4887 e = pib->pib_pchenv;
4888 while (c--) {
4889 *ep++ = e;
4890 e = e + strlen(e) + 1;
4891 }
4892 *ep = NULL;
4893 }
4894 _environ = _org_environ = env;
4895 emx_init_done = 1;
4896 if (hmtx_emx_init)
4897 DosReleaseMutexSem(hmtx_emx_init);
4898}
4899
4900#define ENTRY_POINT 0x10000
4901
4902static int
4903exe_is_aout(void)
4904{
4905 struct layout_table_t *layout;
4906 if (emx_wasnt_initialized)
4907 return 0;
4908 /* Now we know that the principal executable is an EMX application
4909 - unless somebody did already play with delayed initialization... */
4910 /* With EMX applications to determine whether it is AOUT one needs
4911 to examine the start of the executable to find "layout" */
4912 if ( *(unsigned char*)ENTRY_POINT != 0x68 /* PUSH n */
4913 || *(unsigned char*)(ENTRY_POINT+5) != 0xe8 /* CALL */
4914 || *(unsigned char*)(ENTRY_POINT+10) != 0xeb /* JMP */
4915 || *(unsigned char*)(ENTRY_POINT+12) != 0xe8) /* CALL */
4916 return 0; /* ! EMX executable */
4917 /* Fix alignment */
4918 Copy((char*)(ENTRY_POINT+1), &layout, 1, struct layout_table_t*);
4919 return !(layout->flags & 2);
4920}
4921
4922void
4923Perl_OS2_init(char **env)
4924{
4925 Perl_OS2_init3(env, 0, 0);
4926}
4927
4928void
4929Perl_OS2_init3(char **env, void **preg, int flags)
4930{
4931 char *shell, *s;
4932 ULONG rc;
4933
4934 _uflags (_UF_SBRK_MODEL, _UF_SBRK_ARBITRARY);
4935 MALLOC_INIT;
4936
4937 check_emx_runtime(env, (EXCEPTIONREGISTRATIONRECORD *)preg);
4938
4939 settmppath();
4940 OS2_Perl_data.xs_init = &Xs_OS2_init;
4941 if (perl_sh_installed) {
4942 int l = strlen(perl_sh_installed);
4943
4944 Newx(PL_sh_path, l + 1, char);
4945 memcpy(PL_sh_path, perl_sh_installed, l + 1);
4946 } else if ( (shell = getenv("PERL_SH_DRIVE")) ) {
4947 Newx(PL_sh_path, strlen(SH_PATH) + 1, char);
4948 strcpy(PL_sh_path, SH_PATH);
4949 PL_sh_path[0] = shell[0];
4950 } else if ( (shell = getenv("PERL_SH_DIR")) ) {
4951 int l = strlen(shell), i;
4952
4953 while (l && (shell[l-1] == '/' || shell[l-1] == '\\'))
4954 l--;
4955 Newx(PL_sh_path, l + 8, char);
4956 strncpy(PL_sh_path, shell, l);
4957 strcpy(PL_sh_path + l, "/sh.exe");
4958 for (i = 0; i < l; i++) {
4959 if (PL_sh_path[i] == '\\') PL_sh_path[i] = '/';
4960 }
4961 }
4962#if defined(USE_5005THREADS) || defined(USE_ITHREADS)
4963 MUTEX_INIT(&start_thread_mutex);
4964 MUTEX_INIT(&perlos2_state_mutex);
4965#endif
4966 os2_mytype = my_type(); /* Do it before morphing. Needed? */
4967 os2_mytype_ini = os2_mytype;
4968 Perl_os2_initial_mode = -1; /* Uninit */
4969
4970 s = getenv("PERL_BEGINLIBPATH");
4971 if (s)
4972 rc = fill_extLibpath(0, s, NULL, 1, "PERL_BEGINLIBPATH");
4973 else
4974 rc = fill_extLibpath(0, getenv("PERL_PRE_BEGINLIBPATH"), getenv("PERL_POST_BEGINLIBPATH"), 0, "PERL_(PRE/POST)_BEGINLIBPATH");
4975 if (!rc) {
4976 s = getenv("PERL_ENDLIBPATH");
4977 if (s)
4978 rc = fill_extLibpath(1, s, NULL, 1, "PERL_ENDLIBPATH");
4979 else
4980 rc = fill_extLibpath(1, getenv("PERL_PRE_ENDLIBPATH"), getenv("PERL_POST_ENDLIBPATH"), 0, "PERL_(PRE/POST)_ENDLIBPATH");
4981 }
4982 if (rc) {
4983 char buf[1024];
4984
4985 snprintf(buf, sizeof buf, "Error setting BEGIN/ENDLIBPATH: %s\n",
4986 os2error(rc));
4987 DosWrite(2, buf, strlen(buf), &rc);
4988 exit(2);
4989 }
4990
4991 _emxload_env("PERL_EMXLOAD_SECS");
4992 /* Some DLLs reset FP flags on load. We may have been linked with them */
4993 _control87(MCW_EM, MCW_EM);
4994}
4995
4996int
4997fd_ok(int fd)
4998{
4999 static ULONG max_fh = 0;
5000
5001 if (!(_emx_env & 0x200)) return 1; /* not OS/2. */
5002 if (fd >= max_fh) { /* Renew */
5003 LONG delta = 0;
5004
5005 if (DosSetRelMaxFH(&delta, &max_fh)) /* Assume it OK??? */
5006 return 1;
5007 }
5008 return fd < max_fh;
5009}
5010
5011/* Kernels up to Oct 2003 trap on (invalid) dup(max_fh); [off-by-one + double fault]. */
5012int
5013dup2(int from, int to)
5014{
5015 if (fd_ok(from < to ? to : from))
5016 return _dup2(from, to);
5017 errno = EBADF;
5018 return -1;
5019}
5020
5021int
5022dup(int from)
5023{
5024 if (fd_ok(from))
5025 return _dup(from);
5026 errno = EBADF;
5027 return -1;
5028}
5029
5030#undef tmpnam
5031#undef tmpfile
5032
5033char *
5034my_tmpnam (char *str)
5035{
5036 char *p = getenv("TMP"), *tpath;
5037
5038 if (!p) p = getenv("TEMP");
5039 tpath = tempnam(p, "pltmp");
5040 if (str && tpath) {
5041 strcpy(str, tpath);
5042 return str;
5043 }
5044 return tpath;
5045}
5046
5047FILE *
5048my_tmpfile ()
5049{
5050 struct stat s;
5051
5052 stat(".", &s);
5053 if (s.st_mode & S_IWOTH) {
5054 return tmpfile();
5055 }
5056 return fopen(my_tmpnam(NULL), "w+b"); /* Race condition, but
5057 grants TMP. */
5058}
5059
5060#undef rmdir
5061
5062/* EMX flavors do not tolerate trailing slashes. t/op/mkdir.t has many
5063 trailing slashes, so we need to support this as well. */
5064
5065int
5066my_rmdir (__const__ char *s)
5067{
5068 char b[MAXPATHLEN];
5069 char *buf = b;
5070 STRLEN l = strlen(s);
5071 int rc;
5072
5073 if (s[l-1] == '/' || s[l-1] == '\\') { /* EMX mkdir fails... */
5074 if (l >= sizeof b)
5075 Newx(buf, l + 1, char);
5076 strcpy(buf,s);
5077 while (l > 1 && (s[l-1] == '/' || s[l-1] == '\\'))
5078 l--;
5079 buf[l] = 0;
5080 s = buf;
5081 }
5082 rc = rmdir(s);
5083 if (b != buf)
5084 Safefree(buf);
5085 return rc;
5086}
5087
5088#undef mkdir
5089
5090int
5091my_mkdir (__const__ char *s, long perm)
5092{
5093 char b[MAXPATHLEN];
5094 char *buf = b;
5095 STRLEN l = strlen(s);
5096 int rc;
5097
5098 if (s[l-1] == '/' || s[l-1] == '\\') { /* EMX mkdir fails... */
5099 if (l >= sizeof b)
5100 Newx(buf, l + 1, char);
5101 strcpy(buf,s);
5102 while (l > 1 && (s[l-1] == '/' || s[l-1] == '\\'))
5103 l--;
5104 buf[l] = 0;
5105 s = buf;
5106 }
5107 rc = mkdir(s, perm);
5108 if (b != buf)
5109 Safefree(buf);
5110 return rc;
5111}
5112
5113#undef flock
5114
5115/* This code was contributed by Rocco Caputo. */
5116int
5117my_flock(int handle, int o)
5118{
5119 FILELOCK rNull, rFull;
5120 ULONG timeout, handle_type, flag_word;
5121 APIRET rc;
5122 int blocking, shared;
5123 static int use_my_flock = -1;
5124
5125 if (use_my_flock == -1) {
5126 MUTEX_LOCK(&perlos2_state_mutex);
5127 if (use_my_flock == -1) {
5128 char *s = getenv("USE_PERL_FLOCK");
5129 if (s)
5130 use_my_flock = atoi(s);
5131 else
5132 use_my_flock = 1;
5133 }
5134 MUTEX_UNLOCK(&perlos2_state_mutex);
5135 }
5136 if (!(_emx_env & 0x200) || !use_my_flock)
5137 return flock(handle, o); /* Delegate to EMX. */
5138
5139 /* is this a file? */
5140 if ((DosQueryHType(handle, &handle_type, &flag_word) != 0) ||
5141 (handle_type & 0xFF))
5142 {
5143 errno = EBADF;
5144 return -1;
5145 }
5146 /* set lock/unlock ranges */
5147 rNull.lOffset = rNull.lRange = rFull.lOffset = 0;
5148 rFull.lRange = 0x7FFFFFFF;
5149 /* set timeout for blocking */
5150 timeout = ((blocking = !(o & LOCK_NB))) ? 100 : 1;
5151 /* shared or exclusive? */
5152 shared = (o & LOCK_SH) ? 1 : 0;
5153 /* do not block the unlock */
5154 if (o & (LOCK_UN | LOCK_SH | LOCK_EX)) {
5155 rc = DosSetFileLocks(handle, &rFull, &rNull, timeout, shared);
5156 switch (rc) {
5157 case 0:
5158 errno = 0;
5159 return 0;
5160 case ERROR_INVALID_HANDLE:
5161 errno = EBADF;
5162 return -1;
5163 case ERROR_SHARING_BUFFER_EXCEEDED:
5164 errno = ENOLCK;
5165 return -1;
5166 case ERROR_LOCK_VIOLATION:
5167 break; /* not an error */
5168 case ERROR_INVALID_PARAMETER:
5169 case ERROR_ATOMIC_LOCK_NOT_SUPPORTED:
5170 case ERROR_READ_LOCKS_NOT_SUPPORTED:
5171 errno = EINVAL;
5172 return -1;
5173 case ERROR_INTERRUPT:
5174 errno = EINTR;
5175 return -1;
5176 default:
5177 errno = EINVAL;
5178 return -1;
5179 }
5180 }
5181 /* lock may block */
5182 if (o & (LOCK_SH | LOCK_EX)) {
5183 /* for blocking operations */
5184 for (;;) {
5185 rc =
5186 DosSetFileLocks(
5187 handle,
5188 &rNull,
5189 &rFull,
5190 timeout,
5191 shared
5192 );
5193 switch (rc) {
5194 case 0:
5195 errno = 0;
5196 return 0;
5197 case ERROR_INVALID_HANDLE:
5198 errno = EBADF;
5199 return -1;
5200 case ERROR_SHARING_BUFFER_EXCEEDED:
5201 errno = ENOLCK;
5202 return -1;
5203 case ERROR_LOCK_VIOLATION:
5204 if (!blocking) {
5205 errno = EWOULDBLOCK;
5206 return -1;
5207 }
5208 break;
5209 case ERROR_INVALID_PARAMETER:
5210 case ERROR_ATOMIC_LOCK_NOT_SUPPORTED:
5211 case ERROR_READ_LOCKS_NOT_SUPPORTED:
5212 errno = EINVAL;
5213 return -1;
5214 case ERROR_INTERRUPT:
5215 errno = EINTR;
5216 return -1;
5217 default:
5218 errno = EINVAL;
5219 return -1;
5220 }
5221 /* give away timeslice */
5222 DosSleep(1);
5223 }
5224 }
5225
5226 errno = 0;
5227 return 0;
5228}
5229
5230static int
5231use_my_pwent(void)
5232{
5233 if (_my_pwent == -1) {
5234 char *s = getenv("USE_PERL_PWENT");
5235 if (s)
5236 _my_pwent = atoi(s);
5237 else
5238 _my_pwent = 1;
5239 }
5240 return _my_pwent;
5241}
5242
5243#undef setpwent
5244#undef getpwent
5245#undef endpwent
5246
5247void
5248my_setpwent(void)
5249{
5250 if (!use_my_pwent()) {
5251 setpwent(); /* Delegate to EMX. */
5252 return;
5253 }
5254 pwent_cnt = 0;
5255}
5256
5257void
5258my_endpwent(void)
5259{
5260 if (!use_my_pwent()) {
5261 endpwent(); /* Delegate to EMX. */
5262 return;
5263 }
5264}
5265
5266struct passwd *
5267my_getpwent (void)
5268{
5269 if (!use_my_pwent())
5270 return getpwent(); /* Delegate to EMX. */
5271 if (pwent_cnt++)
5272 return 0; /* Return one entry only */
5273 return getpwuid(0);
5274}
5275
5276void
5277setgrent(void)
5278{
5279 grent_cnt = 0;
5280}
5281
5282void
5283endgrent(void)
5284{
5285}
5286
5287struct group *
5288getgrent (void)
5289{
5290 if (grent_cnt++)
5291 return 0; /* Return one entry only */
5292 return getgrgid(0);
5293}
5294
5295#undef getpwuid
5296#undef getpwnam
5297
5298/* Too long to be a crypt() of anything, so it is not-a-valid pw_passwd. */
5299static const char pw_p[] = "Jf0Wb/BzMFvk7K7lrzK";
5300
5301static struct passwd *
5302passw_wrap(struct passwd *p)
5303{
5304 char *s;
5305
5306 if (!p || (p->pw_passwd && *p->pw_passwd)) /* Not a dangerous password */
5307 return p;
5308 pw = *p;
5309 s = getenv("PW_PASSWD");
5310 if (!s)
5311 s = (char*)pw_p; /* Make match impossible */
5312
5313 pw.pw_passwd = s;
5314 return &pw;
5315}
5316
5317struct passwd *
5318my_getpwuid (uid_t id)
5319{
5320 return passw_wrap(getpwuid(id));
5321}
5322
5323struct passwd *
5324my_getpwnam (__const__ char *n)
5325{
5326 return passw_wrap(getpwnam(n));
5327}
5328
5329char *
5330gcvt_os2 (double value, int digits, char *buffer)
5331{
5332 double absv = value > 0 ? value : -value;
5333 /* EMX implementation is lousy between 0.1 and 0.0001 (uses exponents below
5334 0.1), 1-digit stuff is ok below 0.001; multi-digit below 0.0001. */
5335 int buggy;
5336
5337 absv *= 10000;
5338 buggy = (absv < 1000 && (absv >= 10 || (absv > 1 && floor(absv) != absv)));
5339
5340 if (buggy) {
5341 char pat[12];
5342
5343 sprintf(pat, "%%.%dg", digits);
5344 sprintf(buffer, pat, value);
5345 return buffer;
5346 }
5347 return gcvt (value, digits, buffer);
5348}
5349
5350#undef fork
5351int fork_with_resources()
5352{
5353#if (defined(USE_5005THREADS) || defined(USE_ITHREADS)) && !defined(USE_SLOW_THREAD_SPECIFIC)
5354 dTHX;
5355 void *ctx = PERL_GET_CONTEXT;
5356#endif
5357 unsigned fpflag = _control87(0,0);
5358 int rc = fork();
5359
5360 if (rc == 0) { /* child */
5361#if (defined(USE_5005THREADS) || defined(USE_ITHREADS)) && !defined(USE_SLOW_THREAD_SPECIFIC)
5362 ALLOC_THREAD_KEY; /* Acquire the thread-local memory */
5363 PERL_SET_CONTEXT(ctx); /* Reinit the thread-local memory */
5364#endif
5365
5366 { /* Reload loaded-on-demand DLLs */
5367 struct dll_handle_t *dlls = dll_handles;
5368
5369 while (dlls->modname) {
5370 char dllname[260], fail[260];
5371 ULONG rc;
5372
5373 if (!dlls->handle) { /* Was not loaded */
5374 dlls++;
5375 continue;
5376 }
5377 /* It was loaded in the parent. We need to reload it. */
5378
5379 rc = DosQueryModuleName(dlls->handle, sizeof(dllname), dllname);
5380 if (rc) {
5381 Perl_warn_nocontext("Can't find DLL name for the module `%s' by the handle %d, rc=%lu=%#lx",
5382 dlls->modname, (int)dlls->handle, rc, rc);
5383 dlls++;
5384 continue;
5385 }
5386 rc = DosLoadModule(fail, sizeof fail, dllname, &dlls->handle);
5387 if (rc)
5388 Perl_warn_nocontext("Can't load DLL `%s', possible problematic module `%s'",
5389 dllname, fail);
5390 dlls++;
5391 }
5392 }
5393
5394 { /* Support message queue etc. */
5395 os2_mytype = my_type();
5396 /* Apparently, subprocesses (in particular, fork()) do not
5397 inherit the morphed state, so os2_mytype is the same as
5398 os2_mytype_ini. */
5399
5400 if (Perl_os2_initial_mode != -1
5401 && Perl_os2_initial_mode != os2_mytype) {
5402 /* XXXX ??? */
5403 }
5404 }
5405 if (Perl_HAB_set)
5406 (void)_obtain_Perl_HAB;
5407 if (Perl_hmq_refcnt) {
5408 if (my_type() != 3)
5409 my_type_set(3);
5410 Create_HMQ(Perl_hmq_servers != 0,
5411 "Cannot create a message queue on fork");
5412 }
5413
5414 /* We may have loaded some modules */
5415 _control87(fpflag, MCW_EM); /* Some modules reset FP flags on (un)load */
5416 }
5417 return rc;
5418}
5419
5420/* APIRET APIENTRY DosGetInfoSeg(PSEL pselGlobal, PSEL pselLocal); */
5421
5422ULONG _THUNK_FUNCTION(Dos16GetInfoSeg)(USHORT *pGlobal, USHORT *pLocal);
5423
5424APIRET APIENTRY
5425myDosGetInfoSeg(PGINFOSEG *pGlobal, PLINFOSEG *pLocal)
5426{
5427 APIRET rc;
5428 USHORT gSel, lSel; /* Will not cross 64K boundary */
5429
5430 rc = ((USHORT)
5431 (_THUNK_PROLOG (4+4);
5432 _THUNK_FLAT (&gSel);
5433 _THUNK_FLAT (&lSel);
5434 _THUNK_CALL (Dos16GetInfoSeg)));
5435 if (rc)
5436 return rc;
5437 *pGlobal = MAKEPGINFOSEG(gSel);
5438 *pLocal = MAKEPLINFOSEG(lSel);
5439 return rc;
5440}
5441
5442static void
5443GetInfoTables(void)
5444{
5445 ULONG rc = 0;
5446
5447 MUTEX_LOCK(&perlos2_state_mutex);
5448 if (!gTable)
5449 rc = myDosGetInfoSeg(&gTable, &lTable);
5450 MUTEX_UNLOCK(&perlos2_state_mutex);
5451 os2cp_croak(rc, "Dos16GetInfoSeg");
5452}
5453
5454ULONG
5455msCounter(void)
5456{ /* XXXX Is not lTable thread-specific? */
5457 if (!gTable)
5458 GetInfoTables();
5459 return gTable->SIS_MsCount;
5460}
5461
5462ULONG
5463InfoTable(int local)
5464{
5465 if (!gTable)
5466 GetInfoTables();
5467 return local ? (ULONG)lTable : (ULONG)gTable;
5468}