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