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