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