[PATCH] improve WinCE port
[p5sagit/p5-mst-13.2.git] / wince / wince.c
1 /*  WINCE.C - stuff for Windows CE
2  *
3  *  Time-stamp: <26/10/01 15:25:20 keuchel@keuchelnt>
4  *
5  *  You may distribute under the terms of either the GNU General Public
6  *  License or the Artistic License, as specified in the README file.
7  */
8
9 #define WIN32_LEAN_AND_MEAN
10 #define WIN32IO_IS_STDIO
11 #include <windows.h>
12 #include <signal.h>
13
14 #define PERLIO_NOT_STDIO 0 
15
16 #if !defined(PERLIO_IS_STDIO) && !defined(USE_SFIO)
17 #define PerlIO FILE
18 #endif
19
20 #define wince_private
21 #include "errno.h"
22
23 #include "EXTERN.h"
24 #include "perl.h"
25
26 #define NO_XSLOCKS
27 #define PERL_NO_GET_CONTEXT
28 #include "XSUB.h"
29
30 #include "win32iop.h"
31 #include <string.h>
32 #include <stdarg.h>
33 #include <float.h>
34 #include <shellapi.h>
35
36 #define perl
37 #include "celib_defs.h"
38 #include "cewin32.h"
39 #include "cecrt.h"
40 #include "cewin32_defs.h"
41 #include "cecrt_defs.h"
42
43 #ifdef PALM_SIZE
44 #include "stdio-palmsize.h"
45 #endif
46
47 #define EXECF_EXEC 1
48 #define EXECF_SPAWN 2
49 #define EXECF_SPAWN_NOWAIT 3
50
51 #if defined(PERL_IMPLICIT_SYS)
52 #  undef win32_get_privlib
53 #  define win32_get_privlib g_win32_get_privlib
54 #  undef win32_get_sitelib
55 #  define win32_get_sitelib g_win32_get_sitelib
56 #  undef win32_get_vendorlib
57 #  define win32_get_vendorlib g_win32_get_vendorlib
58 #  undef do_spawn
59 #  define do_spawn g_do_spawn
60 #  undef getlogin
61 #  define getlogin g_getlogin
62 #endif
63
64 static long             filetime_to_clock(PFILETIME ft);
65 static BOOL             filetime_from_time(PFILETIME ft, time_t t);
66 static char *           get_emd_part(SV **leading, char *trailing, ...);
67 static char *           win32_get_xlib(const char *pl, const char *xlib,
68                                        const char *libname);
69
70 START_EXTERN_C
71 HANDLE  w32_perldll_handle = INVALID_HANDLE_VALUE;
72 char    w32_module_name[MAX_PATH+1];
73 END_EXTERN_C
74
75 static DWORD    w32_platform = (DWORD)-1;
76
77 int 
78 IsWin95(void)
79 {
80   return (win32_os_id() == VER_PLATFORM_WIN32_WINDOWS);
81 }
82
83 int
84 IsWinNT(void)
85 {
86   return (win32_os_id() == VER_PLATFORM_WIN32_NT);
87 }
88
89 int
90 IsWinCE(void)
91 {
92   return (win32_os_id() == VER_PLATFORM_WIN32_CE);
93 }
94
95 EXTERN_C void
96 set_w32_module_name(void)
97 {
98   char* ptr;
99   XCEGetModuleFileNameA((HMODULE)((w32_perldll_handle == INVALID_HANDLE_VALUE)
100                                   ? XCEGetModuleHandleA(NULL)
101                                   : w32_perldll_handle),
102                         w32_module_name, sizeof(w32_module_name));
103
104   /* normalize to forward slashes */
105   ptr = w32_module_name;
106   while (*ptr) {
107     if (*ptr == '\\')
108       *ptr = '/';
109     ++ptr;
110   }
111 }
112
113 /* *svp (if non-NULL) is expected to be POK (valid allocated SvPVX(*svp)) */
114 static char*
115 get_regstr_from(HKEY hkey, const char *valuename, SV **svp)
116 {
117     /* Retrieve a REG_SZ or REG_EXPAND_SZ from the registry */
118     HKEY handle;
119     DWORD type;
120     const char *subkey = "Software\\Perl";
121     char *str = Nullch;
122     long retval;
123
124     retval = XCERegOpenKeyExA(hkey, subkey, 0, KEY_READ, &handle);
125     if (retval == ERROR_SUCCESS) {
126         DWORD datalen;
127         retval = XCERegQueryValueExA(handle, valuename, 0, &type, NULL, &datalen);
128         if (retval == ERROR_SUCCESS && type == REG_SZ) {
129             dTHX;
130             if (!*svp)
131                 *svp = sv_2mortal(newSVpvn("",0));
132             SvGROW(*svp, datalen);
133             retval = XCERegQueryValueExA(handle, valuename, 0, NULL,
134                                      (PBYTE)SvPVX(*svp), &datalen);
135             if (retval == ERROR_SUCCESS) {
136                 str = SvPVX(*svp);
137                 SvCUR_set(*svp,datalen-1);
138             }
139         }
140         RegCloseKey(handle);
141     }
142     return str;
143 }
144
145 /* *svp (if non-NULL) is expected to be POK (valid allocated SvPVX(*svp)) */
146 static char*
147 get_regstr(const char *valuename, SV **svp)
148 {
149     char *str = get_regstr_from(HKEY_CURRENT_USER, valuename, svp);
150     if (!str)
151         str = get_regstr_from(HKEY_LOCAL_MACHINE, valuename, svp);
152     return str;
153 }
154
155 /* *prev_pathp (if non-NULL) is expected to be POK (valid allocated SvPVX(sv)) */
156 static char *
157 get_emd_part(SV **prev_pathp, char *trailing_path, ...)
158 {
159     char base[10];
160     va_list ap;
161     char mod_name[MAX_PATH+1];
162     char *ptr;
163     char *optr;
164     char *strip;
165     int oldsize, newsize;
166     STRLEN baselen;
167
168     va_start(ap, trailing_path);
169     strip = va_arg(ap, char *);
170
171     sprintf(base, "%d.%d", (int)PERL_REVISION, (int)PERL_VERSION);
172     baselen = strlen(base);
173
174     if (!*w32_module_name) {
175         set_w32_module_name();
176     }
177     strcpy(mod_name, w32_module_name);
178     ptr = strrchr(mod_name, '/');
179     while (ptr && strip) {
180         /* look for directories to skip back */
181         optr = ptr;
182         *ptr = '\0';
183         ptr = strrchr(mod_name, '/');
184         /* avoid stripping component if there is no slash,
185          * or it doesn't match ... */
186         if (!ptr || stricmp(ptr+1, strip) != 0) {
187             /* ... but not if component matches m|5\.$patchlevel.*| */
188             if (!ptr || !(*strip == '5' && *(ptr+1) == '5'
189                           && strncmp(strip, base, baselen) == 0
190                           && strncmp(ptr+1, base, baselen) == 0))
191             {
192                 *optr = '/';
193                 ptr = optr;
194             }
195         }
196         strip = va_arg(ap, char *);
197     }
198     if (!ptr) {
199         ptr = mod_name;
200         *ptr++ = '.';
201         *ptr = '/';
202     }
203     va_end(ap);
204     strcpy(++ptr, trailing_path);
205
206     /* only add directory if it exists */
207     if (XCEGetFileAttributesA(mod_name) != (DWORD) -1) {
208         /* directory exists */
209         dTHX;
210         if (!*prev_pathp)
211             *prev_pathp = sv_2mortal(newSVpvn("",0));
212         sv_catpvn(*prev_pathp, ";", 1);
213         sv_catpv(*prev_pathp, mod_name);
214         return SvPVX(*prev_pathp);
215     }
216
217     return Nullch;
218 }
219
220 char *
221 win32_get_privlib(const char *pl)
222 {
223     dTHX;
224     char *stdlib = "lib";
225     char buffer[MAX_PATH+1];
226     SV *sv = Nullsv;
227
228     /* $stdlib = $HKCU{"lib-$]"} || $HKLM{"lib-$]"} || $HKCU{"lib"} || $HKLM{"lib"} || "";  */
229     sprintf(buffer, "%s-%s", stdlib, pl);
230     if (!get_regstr(buffer, &sv))
231         (void)get_regstr(stdlib, &sv);
232
233     /* $stdlib .= ";$EMD/../../lib" */
234     return get_emd_part(&sv, stdlib, ARCHNAME, "bin", Nullch);
235 }
236
237 static char *
238 win32_get_xlib(const char *pl, const char *xlib, const char *libname)
239 {
240     dTHX;
241     char regstr[40];
242     char pathstr[MAX_PATH+1];
243     DWORD datalen;
244     int len, newsize;
245     SV *sv1 = Nullsv;
246     SV *sv2 = Nullsv;
247
248     /* $HKCU{"$xlib-$]"} || $HKLM{"$xlib-$]"} . ---; */
249     sprintf(regstr, "%s-%s", xlib, pl);
250     (void)get_regstr(regstr, &sv1);
251
252     /* $xlib .=
253      * ";$EMD/" . ((-d $EMD/../../../$]) ? "../../.." : "../.."). "/$libname/$]/lib";  */
254     sprintf(pathstr, "%s/%s/lib", libname, pl);
255     (void)get_emd_part(&sv1, pathstr, ARCHNAME, "bin", pl, Nullch);
256
257     /* $HKCU{$xlib} || $HKLM{$xlib} . ---; */
258     (void)get_regstr(xlib, &sv2);
259
260     /* $xlib .=
261      * ";$EMD/" . ((-d $EMD/../../../$]) ? "../../.." : "../.."). "/$libname/lib";  */
262     sprintf(pathstr, "%s/lib", libname);
263     (void)get_emd_part(&sv2, pathstr, ARCHNAME, "bin", pl, Nullch);
264
265     if (!sv1 && !sv2)
266         return Nullch;
267     if (!sv1)
268         return SvPVX(sv2);
269     if (!sv2)
270         return SvPVX(sv1);
271
272     sv_catpvn(sv1, ";", 1);
273     sv_catsv(sv1, sv2);
274
275     return SvPVX(sv1);
276 }
277
278 char *
279 win32_get_sitelib(const char *pl)
280 {
281     return win32_get_xlib(pl, "sitelib", "site");
282 }
283
284 #ifndef PERL_VENDORLIB_NAME
285 #  define PERL_VENDORLIB_NAME   "vendor"
286 #endif
287
288 char *
289 win32_get_vendorlib(const char *pl)
290 {
291     return win32_get_xlib(pl, "vendorlib", PERL_VENDORLIB_NAME);
292 }
293
294 #if !defined(PERL_IMPLICIT_SYS)
295 /* since the current process environment is being updated in util.c
296  * the library functions will get the correct environment
297  */
298 PerlIO *
299 Perl_my_popen(pTHX_ char *cmd, char *mode)
300 {
301   printf("popen(%s)\n", cmd);
302
303   Perl_croak(aTHX_ PL_no_func, "popen");
304   return NULL;
305 }
306
307 long
308 Perl_my_pclose(pTHX_ PerlIO *fp)
309 {
310   Perl_croak(aTHX_ PL_no_func, "pclose");
311   return -1;
312 }
313 #endif
314
315 DllExport unsigned long
316 win32_os_id(void)
317 {
318     static OSVERSIONINFOA osver;
319
320     if (osver.dwPlatformId != w32_platform) {
321         memset(&osver, 0, sizeof(OSVERSIONINFOA));
322         osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
323         XCEGetVersionExA(&osver);
324         w32_platform = osver.dwPlatformId;
325     }
326     return (unsigned long)w32_platform;
327 }
328
329 DllExport int
330 win32_getpid(void)
331 {
332   return xcegetpid();
333 }
334
335 bool
336 Perl_do_exec(pTHX_ char *cmd)
337 {
338   Perl_croak_nocontext("exec() unimplemented on this platform");
339   return FALSE;
340 }
341
342 DllExport int
343 win32_pipe(int *pfd, unsigned int size, int mode)
344 {
345   dTHX;
346   Perl_croak(aTHX_ PL_no_func, "pipe");
347   return -1;
348 }
349
350 DllExport int
351 win32_times(struct tms *timebuf)
352 {
353   dTHX;
354   Perl_croak(aTHX_ PL_no_func, "times");
355   return -1;
356 }
357
358 /* TODO */
359 Sighandler_t
360 win32_signal(int sig, Sighandler_t subcode)
361 {
362   dTHX;
363   Perl_croak_nocontext("signal() TBD on this platform");
364   return FALSE;
365 }
366
367 DllExport void
368 win32_clearenv()
369 {
370   return;
371 }
372
373
374 DllExport char ***
375 win32_environ(void)
376 {
377   return (&(environ));
378 }
379
380 DllExport DIR *
381 win32_opendir(char *filename)
382 {
383   return opendir(filename);
384 }
385
386 DllExport struct direct *
387 win32_readdir(DIR *dirp)
388 {
389   return readdir(dirp);
390 }
391
392 DllExport long
393 win32_telldir(DIR *dirp)
394 {
395   dTHX;
396   Perl_croak(aTHX_ PL_no_func, "telldir");
397   return -1;
398 }
399
400 DllExport void
401 win32_seekdir(DIR *dirp, long loc)
402 {
403   dTHX;
404   Perl_croak(aTHX_ PL_no_func, "seekdir");
405 }
406
407 DllExport void
408 win32_rewinddir(DIR *dirp)
409 {
410   dTHX;
411   Perl_croak(aTHX_ PL_no_func, "rewinddir");
412 }
413
414 DllExport int
415 win32_closedir(DIR *dirp)
416 {
417   closedir(dirp);
418   return 0;
419 }
420
421 DllExport int
422 win32_kill(int pid, int sig)
423 {
424   dTHX;
425   Perl_croak(aTHX_ PL_no_func, "kill");
426   return -1;
427 }
428
429 DllExport unsigned int
430 win32_sleep(unsigned int t)
431 {
432   return xcesleep(t);
433 }
434
435 DllExport int
436 win32_stat(const char *path, struct stat *sbuf)
437 {
438   return xcestat(path, sbuf);
439 }
440
441 DllExport char *
442 win32_longpath(char *path)
443 {
444   return path;
445 }
446
447 #ifndef USE_WIN32_RTL_ENV
448
449 DllExport char *
450 win32_getenv(const char *name)
451 {
452   return xcegetenv(name);
453 }
454
455 DllExport int
456 win32_putenv(const char *name)
457 {
458   return xceputenv(name);
459 }
460
461 #endif
462
463 static long
464 filetime_to_clock(PFILETIME ft)
465 {
466     __int64 qw = ft->dwHighDateTime;
467     qw <<= 32;
468     qw |= ft->dwLowDateTime;
469     qw /= 10000;  /* File time ticks at 0.1uS, clock at 1mS */
470     return (long) qw;
471 }
472
473 /* fix utime() so it works on directories in NT */
474 static BOOL
475 filetime_from_time(PFILETIME pFileTime, time_t Time)
476 {
477     struct tm *pTM = localtime(&Time);
478     SYSTEMTIME SystemTime;
479     FILETIME LocalTime;
480
481     if (pTM == NULL)
482         return FALSE;
483
484     SystemTime.wYear   = pTM->tm_year + 1900;
485     SystemTime.wMonth  = pTM->tm_mon + 1;
486     SystemTime.wDay    = pTM->tm_mday;
487     SystemTime.wHour   = pTM->tm_hour;
488     SystemTime.wMinute = pTM->tm_min;
489     SystemTime.wSecond = pTM->tm_sec;
490     SystemTime.wMilliseconds = 0;
491
492     return SystemTimeToFileTime(&SystemTime, &LocalTime) &&
493            LocalFileTimeToFileTime(&LocalTime, pFileTime);
494 }
495
496 DllExport int
497 win32_unlink(const char *filename)
498 {
499   return xceunlink(filename);
500 }
501
502 DllExport int
503 win32_utime(const char *filename, struct utimbuf *times)
504 {
505   return xceutime(filename, (struct _utimbuf *) times);
506 }
507
508 DllExport int
509 win32_gettimeofday(struct timeval *tp, void *not_used)
510 {
511     return xcegettimeofday(tp,not_used);
512 }
513
514 DllExport int
515 win32_uname(struct utsname *name)
516 {
517     struct hostent *hep;
518     STRLEN nodemax = sizeof(name->nodename)-1;
519     OSVERSIONINFOA osver;
520
521     memset(&osver, 0, sizeof(OSVERSIONINFOA));
522     osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
523     if (XCEGetVersionExA(&osver)) {
524         /* sysname */
525         switch (osver.dwPlatformId) {
526         case VER_PLATFORM_WIN32_CE:
527             strcpy(name->sysname, "Windows CE");
528             break;
529         case VER_PLATFORM_WIN32_WINDOWS:
530             strcpy(name->sysname, "Windows");
531             break;
532         case VER_PLATFORM_WIN32_NT:
533             strcpy(name->sysname, "Windows NT");
534             break;
535         case VER_PLATFORM_WIN32s:
536             strcpy(name->sysname, "Win32s");
537             break;
538         default:
539             strcpy(name->sysname, "Win32 Unknown");
540             break;
541         }
542
543         /* release */
544         sprintf(name->release, "%d.%d",
545                 osver.dwMajorVersion, osver.dwMinorVersion);
546
547         /* version */
548         sprintf(name->version, "Build %d",
549                 osver.dwPlatformId == VER_PLATFORM_WIN32_NT
550                 ? osver.dwBuildNumber : (osver.dwBuildNumber & 0xffff));
551         if (osver.szCSDVersion[0]) {
552             char *buf = name->version + strlen(name->version);
553             sprintf(buf, " (%s)", osver.szCSDVersion);
554         }
555     }
556     else {
557         *name->sysname = '\0';
558         *name->version = '\0';
559         *name->release = '\0';
560     }
561
562     /* nodename */
563     hep = win32_gethostbyname("localhost");
564     if (hep) {
565         STRLEN len = strlen(hep->h_name);
566         if (len <= nodemax) {
567             strcpy(name->nodename, hep->h_name);
568         }
569         else {
570             strncpy(name->nodename, hep->h_name, nodemax);
571             name->nodename[nodemax] = '\0';
572         }
573     }
574     else {
575         DWORD sz = nodemax;
576         if (!XCEGetComputerNameA(name->nodename, &sz))
577             *name->nodename = '\0';
578     }
579
580     /* machine (architecture) */
581     {
582         SYSTEM_INFO info;
583         char *arch;
584         GetSystemInfo(&info);
585
586         switch (info.wProcessorArchitecture) {
587         case PROCESSOR_ARCHITECTURE_INTEL:
588             arch = "x86"; break;
589         case PROCESSOR_ARCHITECTURE_MIPS:
590             arch = "mips"; break;
591         case PROCESSOR_ARCHITECTURE_ALPHA:
592             arch = "alpha"; break;
593         case PROCESSOR_ARCHITECTURE_PPC:
594             arch = "ppc"; break;
595         case PROCESSOR_ARCHITECTURE_ARM:
596             arch = "arm"; break;
597         case PROCESSOR_HITACHI_SH3:
598             arch = "sh3"; break;
599         case PROCESSOR_SHx_SH3:
600             arch = "sh3"; break;
601
602         default:
603             arch = "unknown"; break;
604         }
605         strcpy(name->machine, arch);
606     }
607     return 0;
608 }
609
610 void
611 sig_terminate(pTHX_ int sig)
612 {
613     Perl_warn(aTHX_ "Terminating on signal SIG%s(%d)\n",PL_sig_name[sig], sig);
614     /* exit() seems to be safe, my_exit() or die() is a problem in ^C 
615        thread 
616      */
617     exit(sig);
618 }
619
620 DllExport int
621 win32_async_check(pTHX)
622 {
623     MSG msg;
624     int ours = 1;
625     /* Passing PeekMessage -1 as HWND (2nd arg) only get PostThreadMessage() messages
626      * and ignores window messages - should co-exist better with windows apps e.g. Tk
627      */
628     while (PeekMessage(&msg, (HWND)-1, 0, 0, PM_REMOVE|PM_NOYIELD)) {
629         int sig;
630         switch(msg.message) {
631
632 #if 0
633     /* Perhaps some other messages could map to signals ? ... */
634         case WM_CLOSE:
635         case WM_QUIT:
636             /* Treat WM_QUIT like SIGHUP?  */
637             sig = SIGHUP;
638             goto Raise;
639             break;
640 #endif
641
642         /* We use WM_USER to fake kill() with other signals */
643         case WM_USER: {
644             sig = msg.wParam;
645         Raise:
646             if (do_raise(aTHX_ sig)) {
647                    sig_terminate(aTHX_ sig);
648             }
649             break;
650         }
651
652         case WM_TIMER: {
653             /* alarm() is a one-shot but SetTimer() repeats so kill it */
654             if (w32_timerid) {
655                 KillTimer(NULL,w32_timerid);
656                 w32_timerid=0;
657             }
658             /* Now fake a call to signal handler */
659             if (do_raise(aTHX_ 14)) {
660                 sig_terminate(aTHX_ 14);
661             }
662             break;
663         }
664
665         /* Otherwise do normal Win32 thing - in case it is useful */
666         default:
667             TranslateMessage(&msg);
668             DispatchMessage(&msg);
669             ours = 0;
670             break;
671         }
672     }
673     w32_poll_count = 0;
674
675     /* Above or other stuff may have set a signal flag */
676     if (PL_sig_pending) {
677         despatch_signals();
678     }
679     return ours;
680 }
681
682 /* This function will not return until the timeout has elapsed, or until
683  * one of the handles is ready. */
684 DllExport DWORD
685 win32_msgwait(pTHX_ DWORD count, LPHANDLE handles, DWORD timeout, LPDWORD resultp)
686 {
687     /* We may need several goes at this - so compute when we stop */
688     DWORD ticks = 0;
689     if (timeout != INFINITE) {
690         ticks = GetTickCount();
691         timeout += ticks;
692     }
693     while (1) {
694         DWORD result = MsgWaitForMultipleObjects(count,handles,FALSE,timeout-ticks, QS_ALLEVENTS);
695         if (resultp)
696            *resultp = result;
697         if (result == WAIT_TIMEOUT) {
698             /* Ran out of time - explicit return of zero to avoid -ve if we
699                have scheduling issues
700              */
701             return 0;
702         }
703         if (timeout != INFINITE) {
704             ticks = GetTickCount();
705         }
706         if (result == WAIT_OBJECT_0 + count) {
707             /* Message has arrived - check it */
708             (void)win32_async_check(aTHX);
709         }
710         else {
711            /* Not timeout or message - one of handles is ready */
712            break;
713         }
714     }
715     /* compute time left to wait */
716     ticks = timeout - ticks;
717     /* If we are past the end say zero */
718     return (ticks > 0) ? ticks : 0;
719 }
720
721 /* Timing related stuff */
722
723 int
724 do_raise(pTHX_ int sig) 
725 {
726     if (sig < SIG_SIZE) {
727         Sighandler_t handler = w32_sighandler[sig];
728         if (handler == SIG_IGN) {
729             return 0;
730         }
731         else if (handler != SIG_DFL) {
732             (*handler)(sig);
733             return 0;
734         }
735         else {
736             /* Choose correct default behaviour */
737             switch (sig) {
738 #ifdef SIGCLD
739                 case SIGCLD:
740 #endif
741 #ifdef SIGCHLD
742                 case SIGCHLD:
743 #endif
744                 case 0:
745                     return 0;
746                 case SIGTERM:
747                 default:
748                     break;
749             }
750         }
751     }
752     /* Tell caller to exit thread/process as approriate */
753     return 1;
754 }
755
756 static UINT timerid = 0;
757
758 static VOID CALLBACK TimerProc(HWND win, UINT msg, UINT id, DWORD time)
759 {
760     dTHX;
761     KillTimer(NULL,timerid);
762     timerid=0;  
763     sighandler(14);
764 }
765
766 DllExport unsigned int
767 win32_alarm(unsigned int sec)
768 {
769     /* 
770      * the 'obvious' implentation is SetTimer() with a callback
771      * which does whatever receiving SIGALRM would do 
772      * we cannot use SIGALRM even via raise() as it is not 
773      * one of the supported codes in <signal.h>
774      *
775      * Snag is unless something is looking at the message queue
776      * nothing happens :-(
777      */ 
778     dTHX;
779     if (sec)
780      {
781       timerid = SetTimer(NULL,timerid,sec*1000,(TIMERPROC)TimerProc);
782       if (!timerid)
783        Perl_croak_nocontext("Cannot set timer");
784      } 
785     else
786      {
787       if (timerid)
788        {
789         KillTimer(NULL,timerid);
790         timerid=0;  
791        }
792      }
793     return 0;
794 }
795
796 #ifdef HAVE_DES_FCRYPT
797 extern char *   des_fcrypt(const char *txt, const char *salt, char *cbuf);
798 #endif
799
800 DllExport char *
801 win32_crypt(const char *txt, const char *salt)
802 {
803     dTHX;
804 #ifdef HAVE_DES_FCRYPT
805     dTHR;
806     return des_fcrypt(txt, salt, w32_crypt_buffer);
807 #else
808     Perl_croak(aTHX_ "The crypt() function is unimplemented due to excessive paranoia.");
809     return Nullch;
810 #endif
811 }
812
813 /* C doesn't like repeat struct definitions */
814
815 #if defined(USE_FIXED_OSFHANDLE) || defined(PERL_MSVCRT_READFIX)
816
817 #ifndef _CRTIMP
818 #define _CRTIMP __declspec(dllimport)
819 #endif
820
821 /*
822  * Control structure for lowio file handles
823  */
824 typedef struct {
825     long osfhnd;    /* underlying OS file HANDLE */
826     char osfile;    /* attributes of file (e.g., open in text mode?) */
827     char pipech;    /* one char buffer for handles opened on pipes */
828     int lockinitflag;
829     CRITICAL_SECTION lock;
830 } ioinfo;
831
832
833 /*
834  * Array of arrays of control structures for lowio files.
835  */
836 EXTERN_C _CRTIMP ioinfo* __pioinfo[];
837
838 /*
839  * Definition of IOINFO_L2E, the log base 2 of the number of elements in each
840  * array of ioinfo structs.
841  */
842 #define IOINFO_L2E          5
843
844 /*
845  * Definition of IOINFO_ARRAY_ELTS, the number of elements in ioinfo array
846  */
847 #define IOINFO_ARRAY_ELTS   (1 << IOINFO_L2E)
848
849 /*
850  * Access macros for getting at an ioinfo struct and its fields from a
851  * file handle
852  */
853 #define _pioinfo(i) (__pioinfo[(i) >> IOINFO_L2E] + ((i) & (IOINFO_ARRAY_ELTS - 1)))
854 #define _osfhnd(i)  (_pioinfo(i)->osfhnd)
855 #define _osfile(i)  (_pioinfo(i)->osfile)
856 #define _pipech(i)  (_pioinfo(i)->pipech)
857
858 #endif
859
860 /*
861  *  redirected io subsystem for all XS modules
862  *
863  */
864
865 DllExport int *
866 win32_errno(void)
867 {
868     return (&errno);
869 }
870
871 /* the rest are the remapped stdio routines */
872 DllExport FILE *
873 win32_stderr(void)
874 {
875     return (stderr);
876 }
877
878 char *g_getlogin() {
879     return "no-getlogin";
880 }
881
882 DllExport FILE *
883 win32_stdin(void)
884 {
885     return (stdin);
886 }
887
888 DllExport FILE *
889 win32_stdout()
890 {
891     return (stdout);
892 }
893
894 DllExport int
895 win32_ferror(FILE *fp)
896 {
897     return (ferror(fp));
898 }
899
900
901 DllExport int
902 win32_feof(FILE *fp)
903 {
904     return (feof(fp));
905 }
906
907 /*
908  * Since the errors returned by the socket error function 
909  * WSAGetLastError() are not known by the library routine strerror
910  * we have to roll our own.
911  */
912
913 DllExport char *
914 win32_strerror(int e) 
915 {
916   return xcestrerror(e);
917 }
918
919 DllExport void
920 win32_str_os_error(void *sv, DWORD dwErr)
921 {
922   dTHX;
923
924   sv_setpvn((SV*)sv, "Error", 5);
925 }
926
927
928 DllExport int
929 win32_fprintf(FILE *fp, const char *format, ...)
930 {
931     va_list marker;
932     va_start(marker, format);     /* Initialize variable arguments. */
933
934     return (vfprintf(fp, format, marker));
935 }
936
937 DllExport int
938 win32_printf(const char *format, ...)
939 {
940     va_list marker;
941     va_start(marker, format);     /* Initialize variable arguments. */
942
943     return (vprintf(format, marker));
944 }
945
946 DllExport int
947 win32_vfprintf(FILE *fp, const char *format, va_list args)
948 {
949     return (vfprintf(fp, format, args));
950 }
951
952 DllExport int
953 win32_vprintf(const char *format, va_list args)
954 {
955     return (vprintf(format, args));
956 }
957
958 DllExport size_t
959 win32_fread(void *buf, size_t size, size_t count, FILE *fp)
960 {
961   return fread(buf, size, count, fp);
962 }
963
964 DllExport size_t
965 win32_fwrite(const void *buf, size_t size, size_t count, FILE *fp)
966 {
967   return fwrite(buf, size, count, fp);
968 }
969
970 DllExport FILE *
971 win32_fopen(const char *filename, const char *mode)
972 {
973   return xcefopen(filename, mode);
974 }
975
976 DllExport FILE *
977 win32_fdopen(int handle, const char *mode)
978 {
979   return palm_fdopen(handle, mode);
980 }
981
982 DllExport FILE *
983 win32_freopen(const char *path, const char *mode, FILE *stream)
984 {
985   return xcefreopen(path, mode, stream);
986 }
987
988 DllExport int
989 win32_fclose(FILE *pf)
990 {
991   return xcefclose(pf);
992 }
993
994 DllExport int
995 win32_fputs(const char *s,FILE *pf)
996 {
997   return fputs(s, pf);
998 }
999
1000 DllExport int
1001 win32_fputc(int c,FILE *pf)
1002 {
1003   return fputc(c,pf);
1004 }
1005
1006 DllExport int
1007 win32_ungetc(int c,FILE *pf)
1008 {
1009   return ungetc(c,pf);
1010 }
1011
1012 DllExport int
1013 win32_getc(FILE *pf)
1014 {
1015   return getc(pf);
1016 }
1017
1018 DllExport int
1019 win32_fileno(FILE *pf)
1020 {
1021   return palm_fileno(pf);
1022 }
1023
1024 DllExport void
1025 win32_clearerr(FILE *pf)
1026 {
1027   clearerr(pf);
1028   return;
1029 }
1030
1031 DllExport int
1032 win32_fflush(FILE *pf)
1033 {
1034   return fflush(pf);
1035 }
1036
1037 DllExport long
1038 win32_ftell(FILE *pf)
1039 {
1040   return ftell(pf);
1041 }
1042
1043 DllExport int
1044 win32_fseek(FILE *pf,long offset,int origin)
1045 {
1046   return fseek(pf, offset, origin);
1047 }
1048
1049 /* fpos_t seems to be int64 on hpc pro! Really stupid. */
1050 /* But maybe someday there will be such large disks in a hpc... */
1051 DllExport int
1052 win32_fgetpos(FILE *pf, fpos_t *p)
1053 {
1054   return fgetpos(pf, p);
1055 }
1056
1057 DllExport int
1058 win32_fsetpos(FILE *pf, const fpos_t *p)
1059 {
1060   return fsetpos(pf, p);
1061 }
1062
1063 DllExport void
1064 win32_rewind(FILE *pf)
1065 {
1066   fseek(pf, 0, SEEK_SET);
1067   return;
1068 }
1069
1070 DllExport int
1071 win32_tmpfd(void)
1072 {
1073     dTHX;
1074     char prefix[MAX_PATH+1];
1075     char filename[MAX_PATH+1];
1076     DWORD len = GetTempPath(MAX_PATH, prefix);
1077     if (len && len < MAX_PATH) {
1078         if (GetTempFileName(prefix, "plx", 0, filename)) {
1079             HANDLE fh = CreateFile(filename,
1080                                    DELETE | GENERIC_READ | GENERIC_WRITE,
1081                                    0,
1082                                    NULL,
1083                                    CREATE_ALWAYS,
1084                                    FILE_ATTRIBUTE_NORMAL
1085                                    | FILE_FLAG_DELETE_ON_CLOSE,
1086                                    NULL);
1087             if (fh != INVALID_HANDLE_VALUE) {
1088                 int fd = win32_open_osfhandle((intptr_t)fh, 0);
1089                 if (fd >= 0) {
1090 #if defined(__BORLANDC__)
1091                     setmode(fd,O_BINARY);
1092 #endif
1093                     DEBUG_p(PerlIO_printf(Perl_debug_log,
1094                                           "Created tmpfile=%s\n",filename));
1095                     return fd;
1096                 }
1097             }
1098         }
1099     }
1100     return -1;
1101 }
1102
1103 DllExport FILE*
1104 win32_tmpfile(void)
1105 {
1106     int fd = win32_tmpfd();
1107     if (fd >= 0)
1108         return win32_fdopen(fd, "w+b");
1109     return NULL;
1110 }
1111
1112 DllExport void
1113 win32_abort(void)
1114 {
1115   xceabort();
1116
1117   return;
1118 }
1119
1120 DllExport int
1121 win32_fstat(int fd, struct stat *sbufptr)
1122 {
1123   return xcefstat(fd, sbufptr);
1124 }
1125
1126 DllExport int
1127 win32_link(const char *oldname, const char *newname)
1128 {
1129   dTHX;
1130   Perl_croak(aTHX_ PL_no_func, "link");
1131
1132   return -1;
1133 }
1134
1135 DllExport int
1136 win32_rename(const char *oname, const char *newname)
1137 {
1138   return xcerename(oname, newname);
1139 }
1140
1141 DllExport int
1142 win32_setmode(int fd, int mode)
1143 {
1144     /* currently 'celib' seem to have this function in src, but not
1145      * exported. When it will be, we'll uncomment following line.
1146      */
1147     /* return xcesetmode(fd, mode); */
1148     return 0;
1149 }
1150
1151 DllExport int
1152 win32_chsize(int fd, Off_t size)
1153 {
1154     return chsize(fd, size);
1155 }
1156
1157 DllExport long
1158 win32_lseek(int fd, long offset, int origin)
1159 {
1160   return xcelseek(fd, offset, origin);
1161 }
1162
1163 DllExport long
1164 win32_tell(int fd)
1165 {
1166   return xcelseek(fd, 0, SEEK_CUR);
1167 }
1168
1169 DllExport int
1170 win32_open(const char *path, int flag, ...)
1171 {
1172   int pmode;
1173   va_list ap;
1174
1175   va_start(ap, flag);
1176   pmode = va_arg(ap, int);
1177   va_end(ap);
1178
1179   return xceopen(path, flag, pmode);
1180 }
1181
1182 DllExport int
1183 win32_close(int fd)
1184 {
1185   return xceclose(fd);
1186 }
1187
1188 DllExport int
1189 win32_eof(int fd)
1190 {
1191   dTHX;
1192   Perl_croak(aTHX_ PL_no_func, "eof");
1193   return -1;
1194 }
1195
1196 DllExport int
1197 win32_dup(int fd)
1198 {
1199   return xcedup(fd); /* from celib/ceio.c; requires some more work on it */
1200 }
1201
1202 DllExport int
1203 win32_dup2(int fd1,int fd2)
1204 {
1205   return xcedup2(fd1,fd2);
1206 }
1207
1208 DllExport int
1209 win32_read(int fd, void *buf, unsigned int cnt)
1210 {
1211   return xceread(fd, buf, cnt);
1212 }
1213
1214 DllExport int
1215 win32_write(int fd, const void *buf, unsigned int cnt)
1216 {
1217   return xcewrite(fd, (void *) buf, cnt);
1218 }
1219
1220 DllExport int
1221 win32_mkdir(const char *dir, int mode)
1222 {
1223   return xcemkdir(dir);
1224 }
1225
1226 DllExport int
1227 win32_rmdir(const char *dir)
1228 {
1229   return xcermdir(dir);
1230 }
1231
1232 DllExport int
1233 win32_chdir(const char *dir)
1234 {
1235   return xcechdir(dir);
1236 }
1237
1238 DllExport  int
1239 win32_access(const char *path, int mode)
1240 {
1241   return xceaccess(path, mode);
1242 }
1243
1244 DllExport  int
1245 win32_chmod(const char *path, int mode)
1246 {
1247   return xcechmod(path, mode);
1248 }
1249
1250 DllExport void
1251 win32_perror(const char *str)
1252 {
1253   xceperror(str);
1254 }
1255
1256 DllExport void
1257 win32_setbuf(FILE *pf, char *buf)
1258 {
1259   dTHX;
1260   Perl_croak(aTHX_ PL_no_func, "setbuf");
1261 }
1262
1263 DllExport int
1264 win32_setvbuf(FILE *pf, char *buf, int type, size_t size)
1265 {
1266   return setvbuf(pf, buf, type, size);
1267 }
1268
1269 DllExport int
1270 win32_flushall(void)
1271 {
1272   return flushall();
1273 }
1274
1275 DllExport int
1276 win32_fcloseall(void)
1277 {
1278   return fcloseall();
1279 }
1280
1281 DllExport char*
1282 win32_fgets(char *s, int n, FILE *pf)
1283 {
1284   return fgets(s, n, pf);
1285 }
1286
1287 DllExport char*
1288 win32_gets(char *s)
1289 {
1290   return gets(s);
1291 }
1292
1293 DllExport int
1294 win32_fgetc(FILE *pf)
1295 {
1296   return fgetc(pf);
1297 }
1298
1299 DllExport int
1300 win32_putc(int c, FILE *pf)
1301 {
1302   return putc(c,pf);
1303 }
1304
1305 DllExport int
1306 win32_puts(const char *s)
1307 {
1308   return puts(s);
1309 }
1310
1311 DllExport int
1312 win32_getchar(void)
1313 {
1314   return getchar();
1315 }
1316
1317 DllExport int
1318 win32_putchar(int c)
1319 {
1320   return putchar(c);
1321 }
1322
1323 #ifdef MYMALLOC
1324
1325 #ifndef USE_PERL_SBRK
1326
1327 static char *committed = NULL;
1328 static char *base      = NULL;
1329 static char *reserved  = NULL;
1330 static char *brk       = NULL;
1331 static DWORD pagesize  = 0;
1332 static DWORD allocsize = 0;
1333
1334 void *
1335 sbrk(int need)
1336 {
1337  void *result;
1338  if (!pagesize)
1339   {SYSTEM_INFO info;
1340    GetSystemInfo(&info);
1341    /* Pretend page size is larger so we don't perpetually
1342     * call the OS to commit just one page ...
1343     */
1344    pagesize = info.dwPageSize << 3;
1345    allocsize = info.dwAllocationGranularity;
1346   }
1347  /* This scheme fails eventually if request for contiguous
1348   * block is denied so reserve big blocks - this is only 
1349   * address space not memory ...
1350   */
1351  if (brk+need >= reserved)
1352   {
1353    DWORD size = 64*1024*1024;
1354    char *addr;
1355    if (committed && reserved && committed < reserved)
1356     {
1357      /* Commit last of previous chunk cannot span allocations */
1358      addr = (char *) VirtualAlloc(committed,reserved-committed,MEM_COMMIT,PAGE_READWRITE);
1359      if (addr)
1360       committed = reserved;
1361     }
1362    /* Reserve some (more) space 
1363     * Note this is a little sneaky, 1st call passes NULL as reserved
1364     * so lets system choose where we start, subsequent calls pass
1365     * the old end address so ask for a contiguous block
1366     */
1367    addr  = (char *) VirtualAlloc(reserved,size,MEM_RESERVE,PAGE_NOACCESS);
1368    if (addr)
1369     {
1370      reserved = addr+size;
1371      if (!base)
1372       base = addr;
1373      if (!committed)
1374       committed = base;
1375      if (!brk)
1376       brk = committed;
1377     }
1378    else
1379     {
1380      return (void *) -1;
1381     }
1382   }
1383  result = brk;
1384  brk += need;
1385  if (brk > committed)
1386   {
1387    DWORD size = ((brk-committed + pagesize -1)/pagesize) * pagesize;
1388    char *addr = (char *) VirtualAlloc(committed,size,MEM_COMMIT,PAGE_READWRITE);
1389    if (addr)
1390     {
1391      committed += size;
1392     }
1393    else
1394     return (void *) -1;
1395   }
1396  return result;
1397 }
1398
1399 #endif
1400 #endif
1401
1402 DllExport void*
1403 win32_malloc(size_t size)
1404 {
1405     return malloc(size);
1406 }
1407
1408 DllExport void*
1409 win32_calloc(size_t numitems, size_t size)
1410 {
1411     return calloc(numitems,size);
1412 }
1413
1414 DllExport void*
1415 win32_realloc(void *block, size_t size)
1416 {
1417     return realloc(block,size);
1418 }
1419
1420 DllExport void
1421 win32_free(void *block)
1422 {
1423     free(block);
1424 }
1425
1426 /* returns pointer to the next unquoted space or the end of the string */
1427 static char*
1428 find_next_space(const char *s)
1429 {
1430     bool in_quotes = FALSE;
1431     while (*s) {
1432         /* ignore doubled backslashes, or backslash+quote */
1433         if (*s == '\\' && (s[1] == '\\' || s[1] == '"')) {
1434             s += 2;
1435         }
1436         /* keep track of when we're within quotes */
1437         else if (*s == '"') {
1438             s++;
1439             in_quotes = !in_quotes;
1440         }
1441         /* break it up only at spaces that aren't in quotes */
1442         else if (!in_quotes && isSPACE(*s))
1443             return (char*)s;
1444         else
1445             s++;
1446     }
1447     return (char*)s;
1448 }
1449
1450 static char *
1451 create_command_line(char *cname, STRLEN clen, const char * const *args)
1452 {
1453     dTHX;
1454     int index, argc;
1455     char *cmd, *ptr;
1456     const char *arg;
1457     STRLEN len = 0;
1458     bool bat_file = FALSE;
1459     bool cmd_shell = FALSE;
1460     bool dumb_shell = FALSE;
1461     bool extra_quotes = FALSE;
1462     bool quote_next = FALSE;
1463
1464     if (!cname)
1465         cname = (char*)args[0];
1466
1467     /* The NT cmd.exe shell has the following peculiarity that needs to be
1468      * worked around.  It strips a leading and trailing dquote when any
1469      * of the following is true:
1470      *    1. the /S switch was used
1471      *    2. there are more than two dquotes
1472      *    3. there is a special character from this set: &<>()@^|
1473      *    4. no whitespace characters within the two dquotes
1474      *    5. string between two dquotes isn't an executable file
1475      * To work around this, we always add a leading and trailing dquote
1476      * to the string, if the first argument is either "cmd.exe" or "cmd",
1477      * and there were at least two or more arguments passed to cmd.exe
1478      * (not including switches).
1479      * XXX the above rules (from "cmd /?") don't seem to be applied
1480      * always, making for the convolutions below :-(
1481      */
1482     if (cname) {
1483         if (!clen)
1484             clen = strlen(cname);
1485
1486         if (clen > 4
1487             && (stricmp(&cname[clen-4], ".bat") == 0
1488                 || (IsWinNT() && stricmp(&cname[clen-4], ".cmd") == 0)))
1489         {
1490             bat_file = TRUE;
1491             len += 3;
1492         }
1493         else {
1494             char *exe = strrchr(cname, '/');
1495             char *exe2 = strrchr(cname, '\\');
1496             if (exe2 > exe)
1497                 exe = exe2;
1498             if (exe)
1499                 ++exe;
1500             else
1501                 exe = cname;
1502             if (stricmp(exe, "cmd.exe") == 0 || stricmp(exe, "cmd") == 0) {
1503                 cmd_shell = TRUE;
1504                 len += 3;
1505             }
1506             else if (stricmp(exe, "command.com") == 0
1507                      || stricmp(exe, "command") == 0)
1508             {
1509                 dumb_shell = TRUE;
1510             }
1511         }
1512     }
1513
1514     DEBUG_p(PerlIO_printf(Perl_debug_log, "Args "));
1515     for (index = 0; (arg = (char*)args[index]) != NULL; ++index) {
1516         STRLEN curlen = strlen(arg);
1517         if (!(arg[0] == '"' && arg[curlen-1] == '"'))
1518             len += 2;   /* assume quoting needed (worst case) */
1519         len += curlen + 1;
1520         DEBUG_p(PerlIO_printf(Perl_debug_log, "[%s]",arg));
1521     }
1522     DEBUG_p(PerlIO_printf(Perl_debug_log, "\n"));
1523
1524     argc = index;
1525     New(1310, cmd, len, char);
1526     ptr = cmd;
1527
1528     if (bat_file) {
1529         *ptr++ = '"';
1530         extra_quotes = TRUE;
1531     }
1532
1533     for (index = 0; (arg = (char*)args[index]) != NULL; ++index) {
1534         bool do_quote = 0;
1535         STRLEN curlen = strlen(arg);
1536
1537         /* we want to protect empty arguments and ones with spaces with
1538          * dquotes, but only if they aren't already there */
1539         if (!dumb_shell) {
1540             if (!curlen) {
1541                 do_quote = 1;
1542             }
1543             else if (quote_next) {
1544                 /* see if it really is multiple arguments pretending to
1545                  * be one and force a set of quotes around it */
1546                 if (*find_next_space(arg))
1547                     do_quote = 1;
1548             }
1549             else if (!(arg[0] == '"' && curlen > 1 && arg[curlen-1] == '"')) {
1550                 STRLEN i = 0;
1551                 while (i < curlen) {
1552                     if (isSPACE(arg[i])) {
1553                         do_quote = 1;
1554                     }
1555                     else if (arg[i] == '"') {
1556                         do_quote = 0;
1557                         break;
1558                     }
1559                     i++;
1560                 }
1561             }
1562         }
1563
1564         if (do_quote)
1565             *ptr++ = '"';
1566
1567         strcpy(ptr, arg);
1568         ptr += curlen;
1569
1570         if (do_quote)
1571             *ptr++ = '"';
1572
1573         if (args[index+1])
1574             *ptr++ = ' ';
1575
1576         if (!extra_quotes
1577             && cmd_shell
1578             && curlen >= 2
1579             && *arg  == '/'     /* see if arg is "/c", "/x/c", "/x/d/c" etc. */
1580             && stricmp(arg+curlen-2, "/c") == 0)
1581         {
1582             /* is there a next argument? */
1583             if (args[index+1]) {
1584                 /* are there two or more next arguments? */
1585                 if (args[index+2]) {
1586                     *ptr++ = '"';
1587                     extra_quotes = TRUE;
1588                 }
1589                 else {
1590                     /* single argument, force quoting if it has spaces */
1591                     quote_next = TRUE;
1592                 }
1593             }
1594         }
1595     }
1596
1597     if (extra_quotes)
1598         *ptr++ = '"';
1599
1600     *ptr = '\0';
1601
1602     return cmd;
1603 }
1604
1605 static char *
1606 qualified_path(const char *cmd)
1607 {
1608     dTHX;
1609     char *pathstr;
1610     char *fullcmd, *curfullcmd;
1611     STRLEN cmdlen = 0;
1612     int has_slash = 0;
1613
1614     if (!cmd)
1615         return Nullch;
1616     fullcmd = (char*)cmd;
1617     while (*fullcmd) {
1618         if (*fullcmd == '/' || *fullcmd == '\\')
1619             has_slash++;
1620         fullcmd++;
1621         cmdlen++;
1622     }
1623
1624     /* look in PATH */
1625     pathstr = PerlEnv_getenv("PATH");
1626     New(0, fullcmd, MAX_PATH+1, char);
1627     curfullcmd = fullcmd;
1628
1629     while (1) {
1630         DWORD res;
1631
1632         /* start by appending the name to the current prefix */
1633         strcpy(curfullcmd, cmd);
1634         curfullcmd += cmdlen;
1635
1636         /* if it doesn't end with '.', or has no extension, try adding
1637          * a trailing .exe first */
1638         if (cmd[cmdlen-1] != '.'
1639             && (cmdlen < 4 || cmd[cmdlen-4] != '.'))
1640         {
1641             strcpy(curfullcmd, ".exe");
1642             res = GetFileAttributes(fullcmd);
1643             if (res != 0xFFFFFFFF && !(res & FILE_ATTRIBUTE_DIRECTORY))
1644                 return fullcmd;
1645             *curfullcmd = '\0';
1646         }
1647
1648         /* that failed, try the bare name */
1649         res = GetFileAttributes(fullcmd);
1650         if (res != 0xFFFFFFFF && !(res & FILE_ATTRIBUTE_DIRECTORY))
1651             return fullcmd;
1652
1653         /* quit if no other path exists, or if cmd already has path */
1654         if (!pathstr || !*pathstr || has_slash)
1655             break;
1656
1657         /* skip leading semis */
1658         while (*pathstr == ';')
1659             pathstr++;
1660
1661         /* build a new prefix from scratch */
1662         curfullcmd = fullcmd;
1663         while (*pathstr && *pathstr != ';') {
1664             if (*pathstr == '"') {      /* foo;"baz;etc";bar */
1665                 pathstr++;              /* skip initial '"' */
1666                 while (*pathstr && *pathstr != '"') {
1667                     if ((STRLEN)(curfullcmd-fullcmd) < MAX_PATH-cmdlen-5)
1668                         *curfullcmd++ = *pathstr;
1669                     pathstr++;
1670                 }
1671                 if (*pathstr)
1672                     pathstr++;          /* skip trailing '"' */
1673             }
1674             else {
1675                 if ((STRLEN)(curfullcmd-fullcmd) < MAX_PATH-cmdlen-5)
1676                     *curfullcmd++ = *pathstr;
1677                 pathstr++;
1678             }
1679         }
1680         if (*pathstr)
1681             pathstr++;                  /* skip trailing semi */
1682         if (curfullcmd > fullcmd        /* append a dir separator */
1683             && curfullcmd[-1] != '/' && curfullcmd[-1] != '\\')
1684         {
1685             *curfullcmd++ = '\\';
1686         }
1687     }
1688
1689     Safefree(fullcmd);
1690     return Nullch;
1691 }
1692
1693 /* XXX this needs to be made more compatible with the spawnvp()
1694  * provided by the various RTLs.  In particular, searching for
1695  * *.{com,bat,cmd} files (as done by the RTLs) is unimplemented.
1696  * This doesn't significantly affect perl itself, because we
1697  * always invoke things using PERL5SHELL if a direct attempt to
1698  * spawn the executable fails.
1699  *
1700  * XXX splitting and rejoining the commandline between do_aspawn()
1701  * and win32_spawnvp() could also be avoided.
1702  */
1703
1704 #define P_WAIT         0
1705 #define P_NOWAIT       1
1706 DllExport int
1707 win32_spawnvp(int mode, const char *cmdname, const char *const *argv)
1708 {
1709 #ifdef USE_RTL_SPAWNVP
1710     return spawnvp(mode, cmdname, (char * const *)argv);
1711 #else
1712     dTHX;
1713     int ret;
1714     void* env;
1715     char* dir;
1716     child_IO_table tbl;
1717     STARTUPINFO StartupInfo;
1718     PROCESS_INFORMATION ProcessInformation;
1719     DWORD create = 0;
1720     char *cmd;
1721     char *fullcmd = Nullch;
1722     char *cname = (char *)cmdname;
1723     STRLEN clen = 0;
1724
1725     if (cname) {
1726         clen = strlen(cname);
1727         /* if command name contains dquotes, must remove them */
1728         if (strchr(cname, '"')) {
1729             cmd = cname;
1730             New(0,cname,clen+1,char);
1731             clen = 0;
1732             while (*cmd) {
1733                 if (*cmd != '"') {
1734                     cname[clen] = *cmd;
1735                     ++clen;
1736                 }
1737                 ++cmd;
1738             }
1739             cname[clen] = '\0';
1740         }
1741     }
1742
1743     cmd = create_command_line(cname, clen, argv);
1744
1745     env = PerlEnv_get_childenv();
1746     dir = PerlEnv_get_childdir();
1747
1748     switch(mode) {
1749     case P_NOWAIT:      /* asynch + remember result */
1750         if (w32_num_children >= MAXIMUM_WAIT_OBJECTS) {
1751             errno = EAGAIN;
1752             ret = -1;
1753             goto RETVAL;
1754         }
1755         /* Create a new process group so we can use GenerateConsoleCtrlEvent()
1756          * in win32_kill()
1757          */
1758         /* not supported on CE create |= CREATE_NEW_PROCESS_GROUP; */
1759         /* FALL THROUGH */
1760
1761     case P_WAIT:        /* synchronous execution */
1762         break;
1763     default:            /* invalid mode */
1764         errno = EINVAL;
1765         ret = -1;
1766         goto RETVAL;
1767     }
1768     memset(&StartupInfo,0,sizeof(StartupInfo));
1769     StartupInfo.cb = sizeof(StartupInfo);
1770     memset(&tbl,0,sizeof(tbl));
1771     PerlEnv_get_child_IO(&tbl);
1772     StartupInfo.dwFlags         = tbl.dwFlags;
1773     StartupInfo.dwX             = tbl.dwX;
1774     StartupInfo.dwY             = tbl.dwY;
1775     StartupInfo.dwXSize         = tbl.dwXSize;
1776     StartupInfo.dwYSize         = tbl.dwYSize;
1777     StartupInfo.dwXCountChars   = tbl.dwXCountChars;
1778     StartupInfo.dwYCountChars   = tbl.dwYCountChars;
1779     StartupInfo.dwFillAttribute = tbl.dwFillAttribute;
1780     StartupInfo.wShowWindow     = tbl.wShowWindow;
1781     StartupInfo.hStdInput       = tbl.childStdIn;
1782     StartupInfo.hStdOutput      = tbl.childStdOut;
1783     StartupInfo.hStdError       = tbl.childStdErr;
1784     if (StartupInfo.hStdInput == INVALID_HANDLE_VALUE &&
1785         StartupInfo.hStdOutput == INVALID_HANDLE_VALUE &&
1786         StartupInfo.hStdError == INVALID_HANDLE_VALUE)
1787     {
1788         create |= CREATE_NEW_CONSOLE;
1789     }
1790     else {
1791         StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
1792     }
1793     if (w32_use_showwindow) {
1794         StartupInfo.dwFlags |= STARTF_USESHOWWINDOW;
1795         StartupInfo.wShowWindow = w32_showwindow;
1796     }
1797
1798     DEBUG_p(PerlIO_printf(Perl_debug_log, "Spawning [%s] with [%s]\n",
1799                           cname,cmd));
1800 RETRY:
1801     if (!CreateProcess(cname,           /* search PATH to find executable */
1802                        cmd,             /* executable, and its arguments */
1803                        NULL,            /* process attributes */
1804                        NULL,            /* thread attributes */
1805                        TRUE,            /* inherit handles */
1806                        create,          /* creation flags */
1807                        (LPVOID)env,     /* inherit environment */
1808                        dir,             /* inherit cwd */
1809                        &StartupInfo,
1810                        &ProcessInformation))
1811     {
1812         /* initial NULL argument to CreateProcess() does a PATH
1813          * search, but it always first looks in the directory
1814          * where the current process was started, which behavior
1815          * is undesirable for backward compatibility.  So we
1816          * jump through our own hoops by picking out the path
1817          * we really want it to use. */
1818         if (!fullcmd) {
1819             fullcmd = qualified_path(cname);
1820             if (fullcmd) {
1821                 if (cname != cmdname)
1822                     Safefree(cname);
1823                 cname = fullcmd;
1824                 DEBUG_p(PerlIO_printf(Perl_debug_log,
1825                                       "Retrying [%s] with same args\n",
1826                                       cname));
1827                 goto RETRY;
1828             }
1829         }
1830         errno = ENOENT;
1831         ret = -1;
1832         goto RETVAL;
1833     }
1834
1835     if (mode == P_NOWAIT) {
1836         /* asynchronous spawn -- store handle, return PID */
1837         ret = (int)ProcessInformation.dwProcessId;
1838         if (IsWin95() && ret < 0)
1839             ret = -ret;
1840
1841         w32_child_handles[w32_num_children] = ProcessInformation.hProcess;
1842         w32_child_pids[w32_num_children] = (DWORD)ret;
1843         ++w32_num_children;
1844     }
1845     else  {
1846         DWORD status;
1847         win32_msgwait(aTHX_ 1, &ProcessInformation.hProcess, INFINITE, NULL);
1848         /* FIXME: if msgwait returned due to message perhaps forward the
1849            "signal" to the process
1850          */
1851         GetExitCodeProcess(ProcessInformation.hProcess, &status);
1852         ret = (int)status;
1853         CloseHandle(ProcessInformation.hProcess);
1854     }
1855
1856     CloseHandle(ProcessInformation.hThread);
1857
1858 RETVAL:
1859     PerlEnv_free_childenv(env);
1860     PerlEnv_free_childdir(dir);
1861     Safefree(cmd);
1862     if (cname != cmdname)
1863         Safefree(cname);
1864     return ret;
1865 #endif
1866 }
1867
1868 DllExport int
1869 win32_execv(const char *cmdname, const char *const *argv)
1870 {
1871   dTHX;
1872   Perl_croak(aTHX_ PL_no_func, "execv");
1873   return -1;
1874 }
1875
1876 DllExport int
1877 win32_execvp(const char *cmdname, const char *const *argv)
1878 {
1879   dTHX;
1880   Perl_croak(aTHX_ PL_no_func, "execvp");
1881   return -1;
1882 }
1883
1884 DllExport void*
1885 win32_dynaload(const char* filename)
1886 {
1887     dTHX;
1888     HMODULE hModule;
1889
1890     hModule = XCELoadLibraryA(filename);
1891
1892     return hModule;
1893 }
1894
1895 /* this is needed by Cwd.pm... */
1896
1897 static
1898 XS(w32_GetCwd)
1899 {
1900   dXSARGS;
1901   char buf[MAX_PATH];
1902   SV *sv = sv_newmortal();
1903
1904   xcegetcwd(buf, sizeof(buf));
1905
1906   sv_setpv(sv, xcestrdup(buf));
1907   EXTEND(SP,1);
1908   SvPOK_on(sv);
1909   ST(0) = sv;
1910 #ifndef INCOMPLETE_TAINTS
1911   SvTAINTED_on(ST(0));
1912 #endif
1913   XSRETURN(1);
1914 }
1915
1916 static
1917 XS(w32_SetCwd)
1918 {
1919   dXSARGS;
1920
1921   if (items != 1)
1922     Perl_croak(aTHX_ "usage: Win32::SetCwd($cwd)");
1923
1924   if (!xcechdir(SvPV_nolen(ST(0))))
1925     XSRETURN_YES;
1926
1927   XSRETURN_NO;
1928 }
1929
1930 static
1931 XS(w32_GetTickCount)
1932 {
1933     dXSARGS;
1934     DWORD msec = GetTickCount();
1935     EXTEND(SP,1);
1936     if ((IV)msec > 0)
1937         XSRETURN_IV(msec);
1938     XSRETURN_NV(msec);
1939 }
1940
1941 static
1942 XS(w32_GetOSVersion)
1943 {
1944     dXSARGS;
1945     OSVERSIONINFOA osver;
1946
1947     osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
1948     if (!XCEGetVersionExA(&osver)) {
1949       XSRETURN_EMPTY;
1950     }
1951     XPUSHs(newSVpvn(osver.szCSDVersion, strlen(osver.szCSDVersion)));
1952     XPUSHs(newSViv(osver.dwMajorVersion));
1953     XPUSHs(newSViv(osver.dwMinorVersion));
1954     XPUSHs(newSViv(osver.dwBuildNumber));
1955     /* WINCE = 3 */
1956     XPUSHs(newSViv(osver.dwPlatformId));
1957     PUTBACK;
1958 }
1959
1960 static
1961 XS(w32_IsWinNT)
1962 {
1963     dXSARGS;
1964     EXTEND(SP,1);
1965     XSRETURN_IV(IsWinNT());
1966 }
1967
1968 static
1969 XS(w32_IsWin95)
1970 {
1971     dXSARGS;
1972     EXTEND(SP,1);
1973     XSRETURN_IV(IsWin95());
1974 }
1975
1976 static
1977 XS(w32_IsWinCE)
1978 {
1979     dXSARGS;
1980     EXTEND(SP,1);
1981     XSRETURN_IV(IsWinCE());
1982 }
1983
1984 static
1985 XS(w32_GetOemInfo)
1986 {
1987   dXSARGS;
1988   wchar_t wbuf[126];
1989   char buf[126];
1990
1991   if(SystemParametersInfoW(SPI_GETOEMINFO, sizeof(wbuf), wbuf, FALSE))
1992     WideCharToMultiByte(CP_ACP, 0, wbuf, -1, buf, sizeof(buf), 0, 0);
1993   else
1994     sprintf(buf, "SystemParametersInfo failed: %d", GetLastError());
1995
1996   EXTEND(SP,1);
1997   XSRETURN_PV(buf);
1998 }
1999
2000 static
2001 XS(w32_Sleep)
2002 {
2003     dXSARGS;
2004     if (items != 1)
2005         Perl_croak(aTHX_ "usage: Win32::Sleep($milliseconds)");
2006     Sleep(SvIV(ST(0)));
2007     XSRETURN_YES;
2008 }
2009
2010 static
2011 XS(w32_CopyFile)
2012 {
2013     dXSARGS;
2014     BOOL bResult;
2015     if (items != 3)
2016         Perl_croak(aTHX_ "usage: Win32::CopyFile($from, $to, $overwrite)");
2017
2018     {
2019       char szSourceFile[MAX_PATH+1];
2020       strcpy(szSourceFile, PerlDir_mapA(SvPV_nolen(ST(0))));
2021       bResult = XCECopyFileA(szSourceFile, SvPV_nolen(ST(1)), 
2022                              !SvTRUE(ST(2)));
2023     }
2024
2025     if (bResult)
2026         XSRETURN_YES;
2027
2028     XSRETURN_NO;
2029 }
2030
2031 static
2032 XS(w32_MessageBox)
2033 {
2034     dXSARGS;
2035
2036     char *txt;
2037     unsigned int res;
2038     unsigned int flags = MB_OK;
2039
2040     txt = SvPV_nolen(ST(0));
2041     
2042     if (items < 1 || items > 2)
2043         Perl_croak(aTHX_ "usage: Win32::MessageBox($txt, [$flags])");
2044
2045     if(items == 2)
2046       flags = SvIV(ST(1));
2047
2048     res = XCEMessageBoxA(NULL, txt, "Perl", flags);
2049
2050     XSRETURN_IV(res);
2051 }
2052
2053 static
2054 XS(w32_GetPowerStatus)
2055 {
2056   dXSARGS;
2057
2058   SYSTEM_POWER_STATUS_EX sps;
2059
2060   if(GetSystemPowerStatusEx(&sps, TRUE) == FALSE)
2061     {
2062       XSRETURN_EMPTY;
2063     }
2064
2065   XPUSHs(newSViv(sps.ACLineStatus));
2066   XPUSHs(newSViv(sps.BatteryFlag));
2067   XPUSHs(newSViv(sps.BatteryLifePercent));
2068   XPUSHs(newSViv(sps.BatteryLifeTime));
2069   XPUSHs(newSViv(sps.BatteryFullLifeTime));
2070   XPUSHs(newSViv(sps.BackupBatteryFlag));
2071   XPUSHs(newSViv(sps.BackupBatteryLifePercent));
2072   XPUSHs(newSViv(sps.BackupBatteryLifeTime));
2073   XPUSHs(newSViv(sps.BackupBatteryFullLifeTime));
2074
2075   PUTBACK;
2076 }
2077
2078 #if UNDER_CE > 200
2079 static
2080 XS(w32_ShellEx)
2081 {
2082   dXSARGS;
2083
2084   char buf[126];
2085   SHELLEXECUTEINFO si;
2086   char *file, *verb;
2087   wchar_t wfile[MAX_PATH];
2088   wchar_t wverb[20];
2089
2090   if (items != 2)
2091     Perl_croak(aTHX_ "usage: Win32::ShellEx($file, $verb)");
2092
2093   file = SvPV_nolen(ST(0));
2094   verb = SvPV_nolen(ST(1));
2095
2096   memset(&si, 0, sizeof(si));
2097   si.cbSize = sizeof(si);
2098   si.fMask = SEE_MASK_FLAG_NO_UI;
2099
2100   MultiByteToWideChar(CP_ACP, 0, verb, -1, 
2101                       wverb, sizeof(wverb)/2);
2102   si.lpVerb = (TCHAR *)wverb;
2103
2104   MultiByteToWideChar(CP_ACP, 0, file, -1, 
2105                       wfile, sizeof(wfile)/2);
2106   si.lpFile = (TCHAR *)wfile;
2107
2108   if(ShellExecuteEx(&si) == FALSE)
2109     {
2110       XSRETURN_NO;
2111     }
2112   XSRETURN_YES;
2113 }
2114 #endif
2115
2116 void
2117 Perl_init_os_extras(void)
2118 {
2119     dTHX;
2120     char *file = __FILE__;
2121     dXSUB_SYS;
2122
2123     w32_perlshell_tokens = Nullch;
2124     w32_perlshell_items = -1;
2125     w32_fdpid = newAV(); /* XX needs to be in Perl_win32_init()? */
2126     New(1313, w32_children, 1, child_tab);
2127     w32_num_children = 0;
2128
2129     newXS("Win32::GetCwd", w32_GetCwd, file);
2130     newXS("Win32::SetCwd", w32_SetCwd, file);
2131     newXS("Win32::GetTickCount", w32_GetTickCount, file);
2132     newXS("Win32::GetOSVersion", w32_GetOSVersion, file);
2133 #if UNDER_CE > 200
2134     newXS("Win32::ShellEx", w32_ShellEx, file);
2135 #endif
2136     newXS("Win32::IsWinNT", w32_IsWinNT, file);
2137     newXS("Win32::IsWin95", w32_IsWin95, file);
2138     newXS("Win32::IsWinCE", w32_IsWinCE, file);
2139     newXS("Win32::CopyFile", w32_CopyFile, file);
2140     newXS("Win32::Sleep", w32_Sleep, file);
2141     newXS("Win32::MessageBox", w32_MessageBox, file);
2142     newXS("Win32::GetPowerStatus", w32_GetPowerStatus, file);
2143     newXS("Win32::GetOemInfo", w32_GetOemInfo, file);
2144 }
2145
2146 void
2147 myexit(void)
2148 {
2149   char buf[126];
2150
2151   puts("Hit return");
2152   fgets(buf, sizeof(buf), stdin);
2153 }
2154
2155 void
2156 Perl_win32_init(int *argcp, char ***argvp)
2157 {
2158 #ifdef UNDER_CE
2159   char *p;
2160
2161   if((p = xcegetenv("PERLDEBUG")) && (p[0] == 'y' || p[0] == 'Y'))
2162     atexit(myexit);
2163 #endif
2164
2165   MALLOC_INIT;
2166 }
2167
2168 void
2169 Perl_win32_term(void)
2170 {
2171     OP_REFCNT_TERM;
2172     MALLOC_TERM;
2173 }
2174
2175 DllExport void
2176 Perl_win32_term(void)
2177 {
2178     OP_REFCNT_TERM;
2179     MALLOC_TERM;
2180 }
2181
2182 void
2183 win32_get_child_IO(child_IO_table* ptbl)
2184 {
2185     ptbl->childStdIn    = GetStdHandle(STD_INPUT_HANDLE);
2186     ptbl->childStdOut   = GetStdHandle(STD_OUTPUT_HANDLE);
2187     ptbl->childStdErr   = GetStdHandle(STD_ERROR_HANDLE);
2188 }
2189
2190 win32_flock(int fd, int oper)
2191 {
2192   dTHX;
2193   Perl_croak(aTHX_ PL_no_func, "flock");
2194   return -1;
2195 }
2196
2197 DllExport int
2198 win32_waitpid(int pid, int *status, int flags)
2199 {
2200   dTHX;
2201   Perl_croak(aTHX_ PL_no_func, "waitpid");
2202   return -1;
2203 }
2204
2205 DllExport int
2206 win32_wait(int *status)
2207 {
2208   dTHX;
2209   Perl_croak(aTHX_ PL_no_func, "wait");
2210   return -1;
2211 }
2212
2213 int
2214 do_spawn(char *cmd)
2215 {
2216   return xcesystem(cmd);
2217 }
2218
2219 int
2220 Perl_do_spawn(pTHX_ char *cmd)
2221 {
2222     return do_spawn(aTHX_ cmd);
2223 }
2224
2225 int
2226 Perl_do_aspawn(pTHX_ SV *really, SV **mark, SV **sp)
2227 {
2228   Perl_croak(aTHX_ PL_no_func, "aspawn");
2229   return -1;
2230 }
2231
2232 int
2233 wce_reopen_stdout(char *fname)
2234 {     
2235   if(xcefreopen(fname, "w", stdout) == NULL)
2236     return -1;
2237
2238   return 0;
2239 }
2240
2241 void
2242 wce_hitreturn()
2243 {
2244   char buf[126];
2245
2246   printf("Hit RETURN");
2247   fflush(stdout);
2248   fgets(buf, sizeof(buf), stdin);
2249   return;
2250 }
2251
2252 /* //////////////////////////////////////////////////////////////////// */
2253
2254 #undef getcwd
2255
2256 char *
2257 getcwd(char *buf, size_t size)
2258 {
2259   return xcegetcwd(buf, size);
2260 }
2261
2262 int 
2263 isnan(double d)
2264 {
2265   return _isnan(d);
2266 }
2267
2268 int
2269 win32_open_osfhandle(intptr_t osfhandle, int flags)
2270 {
2271     int fh;
2272     char fileflags=0;           /* _osfile flags */
2273
2274     Perl_croak_nocontext("win32_open_osfhandle() TBD on this platform");
2275     return 0;
2276 }
2277
2278 int
2279 win32_get_osfhandle(int fd)
2280 {
2281     int fh;
2282     char fileflags=0;           /* _osfile flags */
2283
2284     Perl_croak_nocontext("win32_get_osfhandle() TBD on this platform");
2285     return 0;
2286 }
2287
2288 /*
2289  * a popen() clone that respects PERL5SHELL
2290  *
2291  * changed to return PerlIO* rather than FILE * by BKS, 11-11-2000
2292  */
2293
2294 DllExport PerlIO*
2295 win32_popen(const char *command, const char *mode)
2296 {
2297     Perl_croak_nocontext("win32_popen() TBD on this platform");
2298 }
2299
2300 DllExport PerlIO*
2301 win32_popenlist(const char *mode, IV narg, SV **args)
2302 {
2303  dTHX;
2304  Perl_croak(aTHX_ "List form of pipe open not implemented");
2305  return NULL;
2306 }
2307
2308 /*
2309  * pclose() clone
2310  */
2311
2312 DllExport int
2313 win32_pclose(PerlIO *pf)
2314 {
2315 #ifdef USE_RTL_POPEN
2316     return _pclose(pf);
2317 #else
2318     dTHX;
2319     int childpid, status;
2320     SV *sv;
2321
2322     LOCK_FDPID_MUTEX;
2323     sv = *av_fetch(w32_fdpid, PerlIO_fileno(pf), TRUE);
2324
2325     if (SvIOK(sv))
2326         childpid = SvIVX(sv);
2327     else
2328         childpid = 0;
2329
2330     if (!childpid) {
2331         errno = EBADF;
2332         return -1;
2333     }
2334
2335 #ifdef USE_PERLIO
2336     PerlIO_close(pf);
2337 #else
2338     fclose(pf);
2339 #endif
2340     SvIVX(sv) = 0;
2341     UNLOCK_FDPID_MUTEX;
2342
2343     if (win32_waitpid(childpid, &status, 0) == -1)
2344         return -1;
2345
2346     return status;
2347
2348 #endif /* USE_RTL_POPEN */
2349 }
2350
2351 FILE *
2352 win32_fdupopen(FILE *pf)
2353 {
2354     FILE* pfdup;
2355     fpos_t pos;
2356     char mode[3];
2357     int fileno = win32_dup(win32_fileno(pf));
2358
2359     XCEMessageBoxA(NULL, "NEED TO IMPLEMENT a place in .../wince/wince.c(win32_fdupopen)", "Perl(developer)", 0);
2360     Perl_croak_nocontext("win32_fdupopen() TBD on this platform");
2361
2362 #if 0
2363     /* open the file in the same mode */
2364     if((pf)->_flag & _IOREAD) {
2365         mode[0] = 'r';
2366         mode[1] = 0;
2367     }
2368     else if((pf)->_flag & _IOWRT) {
2369         mode[0] = 'a';
2370         mode[1] = 0;
2371     }
2372     else if((pf)->_flag & _IORW) {
2373         mode[0] = 'r';
2374         mode[1] = '+';
2375         mode[2] = 0;
2376     }
2377
2378     /* it appears that the binmode is attached to the
2379      * file descriptor so binmode files will be handled
2380      * correctly
2381      */
2382     pfdup = win32_fdopen(fileno, mode);
2383
2384     /* move the file pointer to the same position */
2385     if (!fgetpos(pf, &pos)) {
2386         fsetpos(pfdup, &pos);
2387     }
2388 #endif
2389     return pfdup;
2390 }
2391
2392 #ifdef HAVE_INTERP_INTERN
2393
2394
2395 static void
2396 win32_csighandler(int sig)
2397 {
2398 #if 0
2399     dTHXa(PERL_GET_SIG_CONTEXT);
2400     Perl_warn(aTHX_ "Got signal %d",sig);
2401 #endif
2402     /* Does nothing */
2403 }
2404
2405 void
2406 Perl_sys_intern_init(pTHX)
2407 {
2408     int i;
2409     w32_perlshell_tokens        = Nullch;
2410     w32_perlshell_vec           = (char**)NULL;
2411     w32_perlshell_items         = 0;
2412     w32_fdpid                   = newAV();
2413     New(1313, w32_children, 1, child_tab);
2414     w32_num_children            = 0;
2415 #  ifdef USE_ITHREADS
2416     w32_pseudo_id               = 0;
2417     New(1313, w32_pseudo_children, 1, child_tab);
2418     w32_num_pseudo_children     = 0;
2419 #  endif
2420     w32_init_socktype           = 0;
2421     w32_timerid                 = 0;
2422     w32_poll_count              = 0;
2423 }
2424
2425 void
2426 Perl_sys_intern_clear(pTHX)
2427 {
2428     Safefree(w32_perlshell_tokens);
2429     Safefree(w32_perlshell_vec);
2430     /* NOTE: w32_fdpid is freed by sv_clean_all() */
2431     Safefree(w32_children);
2432     if (w32_timerid) {
2433         KillTimer(NULL,w32_timerid);
2434         w32_timerid=0;
2435     }
2436 #  ifdef USE_ITHREADS
2437     Safefree(w32_pseudo_children);
2438 #  endif
2439 }
2440
2441 #  ifdef USE_ITHREADS
2442
2443 void
2444 Perl_sys_intern_dup(pTHX_ struct interp_intern *src, struct interp_intern *dst)
2445 {
2446     dst->perlshell_tokens       = Nullch;
2447     dst->perlshell_vec          = (char**)NULL;
2448     dst->perlshell_items        = 0;
2449     dst->fdpid                  = newAV();
2450     Newz(1313, dst->children, 1, child_tab);
2451     dst->pseudo_id              = 0;
2452     Newz(1313, dst->pseudo_children, 1, child_tab);
2453     dst->thr_intern.Winit_socktype = 0;
2454     dst->timerid                 = 0;
2455     dst->poll_count              = 0;
2456     Copy(src->sigtable,dst->sigtable,SIG_SIZE,Sighandler_t);
2457 }
2458 #  endif /* USE_ITHREADS */
2459 #endif /* HAVE_INTERP_INTERN */
2460
2461 static void
2462 win32_free_argvw(pTHX_ void *ptr)
2463 {
2464     char** argv = (char**)ptr;
2465     while(*argv) {
2466         Safefree(*argv);
2467         *argv++ = Nullch;
2468     }
2469 }
2470
2471 void
2472 win32_argv2utf8(int argc, char** argv)
2473 {
2474   /* do nothing, since we're not aware of command line arguments
2475    * currently ...
2476    */
2477 }
2478
2479 #if 0
2480 void
2481 Perl_sys_intern_clear(pTHX)
2482 {
2483     Safefree(w32_perlshell_tokens);
2484     Safefree(w32_perlshell_vec);
2485     /* NOTE: w32_fdpid is freed by sv_clean_all() */
2486     Safefree(w32_children);
2487 #  ifdef USE_ITHREADS
2488     Safefree(w32_pseudo_children);
2489 #  endif
2490 }
2491
2492 #endif
2493 // added to remove undefied symbol error in CodeWarrior compilation
2494 int
2495 Perl_Ireentrant_buffer_ptr(aTHX)
2496 {
2497         return 0;
2498 }