Implement win32_isatty()
[perl.git] / win32 / win32.c
1 /* WIN32.C
2  *
3  * (c) 1995 Microsoft Corporation. All rights reserved.
4  *              Developed by hip communications inc.
5  * Portions (c) 1993 Intergraph Corporation. All rights reserved.
6  *
7  *    You may distribute under the terms of either the GNU General Public
8  *    License or the Artistic License, as specified in the README file.
9  */
10 #define PERLIO_NOT_STDIO 0
11 #define WIN32_LEAN_AND_MEAN
12 #define WIN32IO_IS_STDIO
13 #include <tchar.h>
14 #ifdef __GNUC__
15 #define Win32_Winsock
16 #endif
17 #include <windows.h>
18 #ifndef HWND_MESSAGE
19 #  define HWND_MESSAGE     ((HWND)-3)
20 #endif
21 #ifndef WC_NO_BEST_FIT_CHARS
22 #  define WC_NO_BEST_FIT_CHARS 0x00000400 /* requires Windows 2000 or later */
23 #endif
24 #include <winnt.h>
25 #include <commctrl.h>
26 #include <tlhelp32.h>
27 #include <io.h>
28 #include <signal.h>
29
30 #define SystemProcessesAndThreadsInformation 5
31
32 /* Inline some definitions from the DDK */
33 typedef struct {
34     USHORT          Length;
35     USHORT          MaximumLength;
36     PWSTR           Buffer;
37 }   UNICODE_STRING;
38
39 typedef struct {
40     ULONG           NextEntryDelta;
41     ULONG           ThreadCount;
42     ULONG           Reserved1[6];
43     LARGE_INTEGER   CreateTime;
44     LARGE_INTEGER   UserTime;
45     LARGE_INTEGER   KernelTime;
46     UNICODE_STRING  ProcessName;
47     LONG            BasePriority;
48     ULONG           ProcessId;
49     ULONG           InheritedFromProcessId;
50     /* Remainder of the structure depends on the Windows version,
51      * but we don't need those additional fields anyways... */
52 }   SYSTEM_PROCESSES;
53
54 /* #include "config.h" */
55
56 #if !defined(PERLIO_IS_STDIO) && !defined(USE_SFIO)
57 #define PerlIO FILE
58 #endif
59
60 #include <sys/stat.h>
61 #include "EXTERN.h"
62 #include "perl.h"
63
64 #define NO_XSLOCKS
65 #define PERL_NO_GET_CONTEXT
66 #include "XSUB.h"
67
68 #include <fcntl.h>
69 #ifndef __GNUC__
70 /* assert.h conflicts with #define of assert in perl.h */
71 #include <assert.h>
72 #endif
73 #include <string.h>
74 #include <stdarg.h>
75 #include <float.h>
76 #include <time.h>
77 #if defined(_MSC_VER) || defined(__MINGW32__)
78 #include <sys/utime.h>
79 #else
80 #include <utime.h>
81 #endif
82 #ifdef __GNUC__
83 /* Mingw32 defaults to globing command line
84  * So we turn it off like this:
85  */
86 int _CRT_glob = 0;
87 #endif
88
89 #if defined(__MINGW32__) && (__MINGW32_MAJOR_VERSION==1)        
90 /* Mingw32-1.1 is missing some prototypes */
91 START_EXTERN_C
92 FILE * _wfopen(LPCWSTR wszFileName, LPCWSTR wszMode);
93 FILE * _wfdopen(int nFd, LPCWSTR wszMode);
94 FILE * _freopen(LPCWSTR wszFileName, LPCWSTR wszMode, FILE * pOldStream);
95 int _flushall();
96 int _fcloseall();
97 END_EXTERN_C
98 #endif
99
100 #if defined(__BORLANDC__)
101 #  define _stat stat
102 #  define _utimbuf utimbuf
103 #endif
104
105 #define EXECF_EXEC 1
106 #define EXECF_SPAWN 2
107 #define EXECF_SPAWN_NOWAIT 3
108
109 #if defined(PERL_IMPLICIT_SYS)
110 #  undef win32_get_privlib
111 #  define win32_get_privlib g_win32_get_privlib
112 #  undef win32_get_sitelib
113 #  define win32_get_sitelib g_win32_get_sitelib
114 #  undef win32_get_vendorlib
115 #  define win32_get_vendorlib g_win32_get_vendorlib
116 #  undef getlogin
117 #  define getlogin g_getlogin
118 #endif
119
120 static void             get_shell(void);
121 static long             tokenize(const char *str, char **dest, char ***destv);
122 static int              do_spawn2(pTHX_ const char *cmd, int exectype);
123 static BOOL             has_shell_metachars(const char *ptr);
124 static long             filetime_to_clock(PFILETIME ft);
125 static BOOL             filetime_from_time(PFILETIME ft, time_t t);
126 static char *           get_emd_part(SV **leading, STRLEN *const len,
127                                      char *trailing, ...);
128 static void             remove_dead_process(long deceased);
129 static long             find_pid(int pid);
130 static char *           qualified_path(const char *cmd);
131 static char *           win32_get_xlib(const char *pl, const char *xlib,
132                                        const char *libname, STRLEN *const len);
133 static LRESULT  win32_process_message(HWND hwnd, UINT msg,
134                        WPARAM wParam, LPARAM lParam);
135
136 #ifdef USE_ITHREADS
137 static void             remove_dead_pseudo_process(long child);
138 static long             find_pseudo_pid(int pid);
139 #endif
140
141 START_EXTERN_C
142 HANDLE  w32_perldll_handle = INVALID_HANDLE_VALUE;
143 char    w32_module_name[MAX_PATH+1];
144 END_EXTERN_C
145
146 static OSVERSIONINFO g_osver = {0, 0, 0, 0, 0, ""};
147
148 static HANDLE (WINAPI *pfnCreateToolhelp32Snapshot)(DWORD, DWORD) = NULL;
149 static BOOL   (WINAPI *pfnProcess32First)(HANDLE, PROCESSENTRY32*) = NULL;
150 static BOOL   (WINAPI *pfnProcess32Next)(HANDLE, PROCESSENTRY32*) = NULL;
151 static LONG   (WINAPI *pfnZwQuerySystemInformation)(UINT, PVOID, ULONG, PULONG);
152
153 #ifdef __BORLANDC__
154 /* Silence STDERR grumblings from Borland's math library. */
155 DllExport int
156 _matherr(struct _exception *a)
157 {
158     PERL_UNUSED_VAR(a);
159     return 1;
160 }
161 #endif
162
163 /* VS2005 (MSC version 14) provides a mechanism to set an invalid
164  * parameter handler.  This functionality is not available in the
165  * 64-bit compiler from the Platform SDK, which unfortunately also
166  * believes itself to be MSC version 14.
167  *
168  * There is no #define related to _set_invalid_parameter_handler(),
169  * but we can check for one of the constants defined for
170  * _set_abort_behavior(), which was introduced into stdlib.h at
171  * the same time.
172  */
173
174 #if _MSC_VER >= 1400 && defined(_WRITE_ABORT_MSG)
175 #  define SET_INVALID_PARAMETER_HANDLER
176 #endif
177
178 #ifdef SET_INVALID_PARAMETER_HANDLER
179 void my_invalid_parameter_handler(const wchar_t* expression,
180     const wchar_t* function, 
181     const wchar_t* file, 
182     unsigned int line, 
183     uintptr_t pReserved)
184 {
185 #  ifdef _DEBUG
186     wprintf(L"Invalid parameter detected in function %s."
187             L" File: %s Line: %d\n", function, file, line);
188     wprintf(L"Expression: %s\n", expression);
189 #  endif
190 }
191 #endif
192
193 int
194 IsWin95(void)
195 {
196     return (g_osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS);
197 }
198
199 int
200 IsWinNT(void)
201 {
202     return (g_osver.dwPlatformId == VER_PLATFORM_WIN32_NT);
203 }
204
205 int
206 IsWin2000(void)
207 {
208     return (g_osver.dwMajorVersion > 4);
209 }
210
211 EXTERN_C void
212 set_w32_module_name(void)
213 {
214     /* this function may be called at DLL_PROCESS_ATTACH time */
215     char* ptr;
216     HMODULE module = (HMODULE)((w32_perldll_handle == INVALID_HANDLE_VALUE)
217                                ? GetModuleHandle(NULL)
218                                : w32_perldll_handle);
219
220     OSVERSIONINFO osver; /* g_osver may not yet be initialized */
221     osver.dwOSVersionInfoSize = sizeof(osver);
222     GetVersionEx(&osver);
223
224     if (osver.dwMajorVersion > 4) {
225         WCHAR modulename[MAX_PATH];
226         WCHAR fullname[MAX_PATH];
227         char *ansi;
228
229         DWORD (__stdcall *pfnGetLongPathNameW)(LPCWSTR, LPWSTR, DWORD) =
230             (DWORD (__stdcall *)(LPCWSTR, LPWSTR, DWORD))
231             GetProcAddress(GetModuleHandle("kernel32.dll"), "GetLongPathNameW");
232
233         GetModuleFileNameW(module, modulename, sizeof(modulename)/sizeof(WCHAR));
234
235         /* Make sure we get an absolute pathname in case the module was loaded
236          * explicitly by LoadLibrary() with a relative path. */
237         GetFullPathNameW(modulename, sizeof(fullname)/sizeof(WCHAR), fullname, NULL);
238
239         /* Make sure we start with the long path name of the module because we
240          * later scan for pathname components to match "5.xx" to locate
241          * compatible sitelib directories, and the short pathname might mangle
242          * this path segment (e.g. by removing the dot on NTFS to something
243          * like "5xx~1.yy") */
244         if (pfnGetLongPathNameW)
245             pfnGetLongPathNameW(fullname, fullname, sizeof(fullname)/sizeof(WCHAR));
246
247         /* remove \\?\ prefix */
248         if (memcmp(fullname, L"\\\\?\\", 4*sizeof(WCHAR)) == 0)
249             memmove(fullname, fullname+4, (wcslen(fullname+4)+1)*sizeof(WCHAR));
250
251         ansi = win32_ansipath(fullname);
252         my_strlcpy(w32_module_name, ansi, sizeof(w32_module_name));
253         win32_free(ansi);
254     }
255     else {
256         GetModuleFileName(module, w32_module_name, sizeof(w32_module_name));
257
258         /* remove \\?\ prefix */
259         if (memcmp(w32_module_name, "\\\\?\\", 4) == 0)
260             memmove(w32_module_name, w32_module_name+4, strlen(w32_module_name+4)+1);
261
262         /* try to get full path to binary (which may be mangled when perl is
263          * run from a 16-bit app) */
264         /*PerlIO_printf(Perl_debug_log, "Before %s\n", w32_module_name);*/
265         win32_longpath(w32_module_name);
266         /*PerlIO_printf(Perl_debug_log, "After  %s\n", w32_module_name);*/
267     }
268
269     /* normalize to forward slashes */
270     ptr = w32_module_name;
271     while (*ptr) {
272         if (*ptr == '\\')
273             *ptr = '/';
274         ++ptr;
275     }
276 }
277
278 /* *svp (if non-NULL) is expected to be POK (valid allocated SvPVX(*svp)) */
279 static char*
280 get_regstr_from(HKEY hkey, const char *valuename, SV **svp)
281 {
282     /* Retrieve a REG_SZ or REG_EXPAND_SZ from the registry */
283     HKEY handle;
284     DWORD type;
285     const char *subkey = "Software\\Perl";
286     char *str = NULL;
287     long retval;
288
289     retval = RegOpenKeyEx(hkey, subkey, 0, KEY_READ, &handle);
290     if (retval == ERROR_SUCCESS) {
291         DWORD datalen;
292         retval = RegQueryValueEx(handle, valuename, 0, &type, NULL, &datalen);
293         if (retval == ERROR_SUCCESS
294             && (type == REG_SZ || type == REG_EXPAND_SZ))
295         {
296             dTHX;
297             if (!*svp)
298                 *svp = sv_2mortal(newSVpvn("",0));
299             SvGROW(*svp, datalen);
300             retval = RegQueryValueEx(handle, valuename, 0, NULL,
301                                      (PBYTE)SvPVX(*svp), &datalen);
302             if (retval == ERROR_SUCCESS) {
303                 str = SvPVX(*svp);
304                 SvCUR_set(*svp,datalen-1);
305             }
306         }
307         RegCloseKey(handle);
308     }
309     return str;
310 }
311
312 /* *svp (if non-NULL) is expected to be POK (valid allocated SvPVX(*svp)) */
313 static char*
314 get_regstr(const char *valuename, SV **svp)
315 {
316     char *str = get_regstr_from(HKEY_CURRENT_USER, valuename, svp);
317     if (!str)
318         str = get_regstr_from(HKEY_LOCAL_MACHINE, valuename, svp);
319     return str;
320 }
321
322 /* *prev_pathp (if non-NULL) is expected to be POK (valid allocated SvPVX(sv)) */
323 static char *
324 get_emd_part(SV **prev_pathp, STRLEN *const len, char *trailing_path, ...)
325 {
326     char base[10];
327     va_list ap;
328     char mod_name[MAX_PATH+1];
329     char *ptr;
330     char *optr;
331     char *strip;
332     STRLEN baselen;
333
334     va_start(ap, trailing_path);
335     strip = va_arg(ap, char *);
336
337     sprintf(base, "%d.%d", (int)PERL_REVISION, (int)PERL_VERSION);
338     baselen = strlen(base);
339
340     if (!*w32_module_name) {
341         set_w32_module_name();
342     }
343     strcpy(mod_name, w32_module_name);
344     ptr = strrchr(mod_name, '/');
345     while (ptr && strip) {
346         /* look for directories to skip back */
347         optr = ptr;
348         *ptr = '\0';
349         ptr = strrchr(mod_name, '/');
350         /* avoid stripping component if there is no slash,
351          * or it doesn't match ... */
352         if (!ptr || stricmp(ptr+1, strip) != 0) {
353             /* ... but not if component matches m|5\.$patchlevel.*| */
354             if (!ptr || !(*strip == '5' && *(ptr+1) == '5'
355                           && strncmp(strip, base, baselen) == 0
356                           && strncmp(ptr+1, base, baselen) == 0))
357             {
358                 *optr = '/';
359                 ptr = optr;
360             }
361         }
362         strip = va_arg(ap, char *);
363     }
364     if (!ptr) {
365         ptr = mod_name;
366         *ptr++ = '.';
367         *ptr = '/';
368     }
369     va_end(ap);
370     strcpy(++ptr, trailing_path);
371
372     /* only add directory if it exists */
373     if (GetFileAttributes(mod_name) != (DWORD) -1) {
374         /* directory exists */
375         dTHX;
376         if (!*prev_pathp)
377             *prev_pathp = sv_2mortal(newSVpvn("",0));
378         else if (SvPVX(*prev_pathp))
379             sv_catpvn(*prev_pathp, ";", 1);
380         sv_catpv(*prev_pathp, mod_name);
381         if(len)
382             *len = SvCUR(*prev_pathp);
383         return SvPVX(*prev_pathp);
384     }
385
386     return NULL;
387 }
388
389 char *
390 win32_get_privlib(const char *pl, STRLEN *const len)
391 {
392     dTHX;
393     char *stdlib = "lib";
394     char buffer[MAX_PATH+1];
395     SV *sv = NULL;
396
397     /* $stdlib = $HKCU{"lib-$]"} || $HKLM{"lib-$]"} || $HKCU{"lib"} || $HKLM{"lib"} || "";  */
398     sprintf(buffer, "%s-%s", stdlib, pl);
399     if (!get_regstr(buffer, &sv))
400         (void)get_regstr(stdlib, &sv);
401
402     /* $stdlib .= ";$EMD/../../lib" */
403     return get_emd_part(&sv, len, stdlib, ARCHNAME, "bin", NULL);
404 }
405
406 static char *
407 win32_get_xlib(const char *pl, const char *xlib, const char *libname,
408                STRLEN *const len)
409 {
410     dTHX;
411     char regstr[40];
412     char pathstr[MAX_PATH+1];
413     SV *sv1 = NULL;
414     SV *sv2 = NULL;
415
416     /* $HKCU{"$xlib-$]"} || $HKLM{"$xlib-$]"} . ---; */
417     sprintf(regstr, "%s-%s", xlib, pl);
418     (void)get_regstr(regstr, &sv1);
419
420     /* $xlib .=
421      * ";$EMD/" . ((-d $EMD/../../../$]) ? "../../.." : "../.."). "/$libname/$]/lib";  */
422     sprintf(pathstr, "%s/%s/lib", libname, pl);
423     (void)get_emd_part(&sv1, NULL, pathstr, ARCHNAME, "bin", pl, NULL);
424
425     /* $HKCU{$xlib} || $HKLM{$xlib} . ---; */
426     (void)get_regstr(xlib, &sv2);
427
428     /* $xlib .=
429      * ";$EMD/" . ((-d $EMD/../../../$]) ? "../../.." : "../.."). "/$libname/lib";  */
430     sprintf(pathstr, "%s/lib", libname);
431     (void)get_emd_part(&sv2, NULL, pathstr, ARCHNAME, "bin", pl, NULL);
432
433     if (!sv1 && !sv2)
434         return NULL;
435     if (!sv1) {
436         sv1 = sv2;
437     } else if (sv2) {
438         sv_catpvn(sv1, ";", 1);
439         sv_catsv(sv1, sv2);
440     }
441
442     if (len)
443         *len = SvCUR(sv1);
444     return SvPVX(sv1);
445 }
446
447 char *
448 win32_get_sitelib(const char *pl, STRLEN *const len)
449 {
450     return win32_get_xlib(pl, "sitelib", "site", len);
451 }
452
453 #ifndef PERL_VENDORLIB_NAME
454 #  define PERL_VENDORLIB_NAME   "vendor"
455 #endif
456
457 char *
458 win32_get_vendorlib(const char *pl, STRLEN *const len)
459 {
460     return win32_get_xlib(pl, "vendorlib", PERL_VENDORLIB_NAME, len);
461 }
462
463 static BOOL
464 has_shell_metachars(const char *ptr)
465 {
466     int inquote = 0;
467     char quote = '\0';
468
469     /*
470      * Scan string looking for redirection (< or >) or pipe
471      * characters (|) that are not in a quoted string.
472      * Shell variable interpolation (%VAR%) can also happen inside strings.
473      */
474     while (*ptr) {
475         switch(*ptr) {
476         case '%':
477             return TRUE;
478         case '\'':
479         case '\"':
480             if (inquote) {
481                 if (quote == *ptr) {
482                     inquote = 0;
483                     quote = '\0';
484                 }
485             }
486             else {
487                 quote = *ptr;
488                 inquote++;
489             }
490             break;
491         case '>':
492         case '<':
493         case '|':
494             if (!inquote)
495                 return TRUE;
496         default:
497             break;
498         }
499         ++ptr;
500     }
501     return FALSE;
502 }
503
504 #if !defined(PERL_IMPLICIT_SYS)
505 /* since the current process environment is being updated in util.c
506  * the library functions will get the correct environment
507  */
508 PerlIO *
509 Perl_my_popen(pTHX_ const char *cmd, const char *mode)
510 {
511 #ifdef FIXCMD
512 #define fixcmd(x)   {                                   \
513                         char *pspace = strchr((x),' '); \
514                         if (pspace) {                   \
515                             char *p = (x);              \
516                             while (p < pspace) {        \
517                                 if (*p == '/')          \
518                                     *p = '\\';          \
519                                 p++;                    \
520                             }                           \
521                         }                               \
522                     }
523 #else
524 #define fixcmd(x)
525 #endif
526     fixcmd(cmd);
527     PERL_FLUSHALL_FOR_CHILD;
528     return win32_popen(cmd, mode);
529 }
530
531 long
532 Perl_my_pclose(pTHX_ PerlIO *fp)
533 {
534     return win32_pclose(fp);
535 }
536 #endif
537
538 DllExport unsigned long
539 win32_os_id(void)
540 {
541     return (unsigned long)g_osver.dwPlatformId;
542 }
543
544 DllExport int
545 win32_getpid(void)
546 {
547     int pid;
548 #ifdef USE_ITHREADS
549     dTHX;
550     if (w32_pseudo_id)
551         return -((int)w32_pseudo_id);
552 #endif
553     pid = _getpid();
554     /* Windows 9x appears to always reports a pid for threads and processes
555      * that has the high bit set. So we treat the lower 31 bits as the
556      * "real" PID for Perl's purposes. */
557     if (IsWin95() && pid < 0)
558         pid = -pid;
559     return pid;
560 }
561
562 /* Tokenize a string.  Words are null-separated, and the list
563  * ends with a doubled null.  Any character (except null and
564  * including backslash) may be escaped by preceding it with a
565  * backslash (the backslash will be stripped).
566  * Returns number of words in result buffer.
567  */
568 static long
569 tokenize(const char *str, char **dest, char ***destv)
570 {
571     char *retstart = NULL;
572     char **retvstart = 0;
573     int items = -1;
574     if (str) {
575         dTHX;
576         int slen = strlen(str);
577         register char *ret;
578         register char **retv;
579         Newx(ret, slen+2, char);
580         Newx(retv, (slen+3)/2, char*);
581
582         retstart = ret;
583         retvstart = retv;
584         *retv = ret;
585         items = 0;
586         while (*str) {
587             *ret = *str++;
588             if (*ret == '\\' && *str)
589                 *ret = *str++;
590             else if (*ret == ' ') {
591                 while (*str == ' ')
592                     str++;
593                 if (ret == retstart)
594                     ret--;
595                 else {
596                     *ret = '\0';
597                     ++items;
598                     if (*str)
599                         *++retv = ret+1;
600                 }
601             }
602             else if (!*str)
603                 ++items;
604             ret++;
605         }
606         retvstart[items] = NULL;
607         *ret++ = '\0';
608         *ret = '\0';
609     }
610     *dest = retstart;
611     *destv = retvstart;
612     return items;
613 }
614
615 static void
616 get_shell(void)
617 {
618     dTHX;
619     if (!w32_perlshell_tokens) {
620         /* we don't use COMSPEC here for two reasons:
621          *  1. the same reason perl on UNIX doesn't use SHELL--rampant and
622          *     uncontrolled unportability of the ensuing scripts.
623          *  2. PERL5SHELL could be set to a shell that may not be fit for
624          *     interactive use (which is what most programs look in COMSPEC
625          *     for).
626          */
627         const char* defaultshell = (IsWinNT()
628                                     ? "cmd.exe /x/d/c" : "command.com /c");
629         const char *usershell = PerlEnv_getenv("PERL5SHELL");
630         w32_perlshell_items = tokenize(usershell ? usershell : defaultshell,
631                                        &w32_perlshell_tokens,
632                                        &w32_perlshell_vec);
633     }
634 }
635
636 int
637 Perl_do_aspawn(pTHX_ SV *really, SV **mark, SV **sp)
638 {
639     char **argv;
640     char *str;
641     int status;
642     int flag = P_WAIT;
643     int index = 0;
644
645     PERL_ARGS_ASSERT_DO_ASPAWN;
646
647     if (sp <= mark)
648         return -1;
649
650     get_shell();
651     Newx(argv, (sp - mark) + w32_perlshell_items + 2, char*);
652
653     if (SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
654         ++mark;
655         flag = SvIVx(*mark);
656     }
657
658     while (++mark <= sp) {
659         if (*mark && (str = SvPV_nolen(*mark)))
660             argv[index++] = str;
661         else
662             argv[index++] = "";
663     }
664     argv[index++] = 0;
665
666     status = win32_spawnvp(flag,
667                            (const char*)(really ? SvPV_nolen(really) : argv[0]),
668                            (const char* const*)argv);
669
670     if (status < 0 && (errno == ENOEXEC || errno == ENOENT)) {
671         /* possible shell-builtin, invoke with shell */
672         int sh_items;
673         sh_items = w32_perlshell_items;
674         while (--index >= 0)
675             argv[index+sh_items] = argv[index];
676         while (--sh_items >= 0)
677             argv[sh_items] = w32_perlshell_vec[sh_items];
678
679         status = win32_spawnvp(flag,
680                                (const char*)(really ? SvPV_nolen(really) : argv[0]),
681                                (const char* const*)argv);
682     }
683
684     if (flag == P_NOWAIT) {
685         PL_statusvalue = -1;    /* >16bits hint for pp_system() */
686     }
687     else {
688         if (status < 0) {
689             if (ckWARN(WARN_EXEC))
690                 Perl_warner(aTHX_ packWARN(WARN_EXEC), "Can't spawn \"%s\": %s", argv[0], strerror(errno));
691             status = 255 * 256;
692         }
693         else
694             status *= 256;
695         PL_statusvalue = status;
696     }
697     Safefree(argv);
698     return (status);
699 }
700
701 /* returns pointer to the next unquoted space or the end of the string */
702 static char*
703 find_next_space(const char *s)
704 {
705     bool in_quotes = FALSE;
706     while (*s) {
707         /* ignore doubled backslashes, or backslash+quote */
708         if (*s == '\\' && (s[1] == '\\' || s[1] == '"')) {
709             s += 2;
710         }
711         /* keep track of when we're within quotes */
712         else if (*s == '"') {
713             s++;
714             in_quotes = !in_quotes;
715         }
716         /* break it up only at spaces that aren't in quotes */
717         else if (!in_quotes && isSPACE(*s))
718             return (char*)s;
719         else
720             s++;
721     }
722     return (char*)s;
723 }
724
725 static int
726 do_spawn2(pTHX_ const char *cmd, int exectype)
727 {
728     char **a;
729     char *s;
730     char **argv;
731     int status = -1;
732     BOOL needToTry = TRUE;
733     char *cmd2;
734
735     /* Save an extra exec if possible. See if there are shell
736      * metacharacters in it */
737     if (!has_shell_metachars(cmd)) {
738         Newx(argv, strlen(cmd) / 2 + 2, char*);
739         Newx(cmd2, strlen(cmd) + 1, char);
740         strcpy(cmd2, cmd);
741         a = argv;
742         for (s = cmd2; *s;) {
743             while (*s && isSPACE(*s))
744                 s++;
745             if (*s)
746                 *(a++) = s;
747             s = find_next_space(s);
748             if (*s)
749                 *s++ = '\0';
750         }
751         *a = NULL;
752         if (argv[0]) {
753             switch (exectype) {
754             case EXECF_SPAWN:
755                 status = win32_spawnvp(P_WAIT, argv[0],
756                                        (const char* const*)argv);
757                 break;
758             case EXECF_SPAWN_NOWAIT:
759                 status = win32_spawnvp(P_NOWAIT, argv[0],
760                                        (const char* const*)argv);
761                 break;
762             case EXECF_EXEC:
763                 status = win32_execvp(argv[0], (const char* const*)argv);
764                 break;
765             }
766             if (status != -1 || errno == 0)
767                 needToTry = FALSE;
768         }
769         Safefree(argv);
770         Safefree(cmd2);
771     }
772     if (needToTry) {
773         char **argv;
774         int i = -1;
775         get_shell();
776         Newx(argv, w32_perlshell_items + 2, char*);
777         while (++i < w32_perlshell_items)
778             argv[i] = w32_perlshell_vec[i];
779         argv[i++] = (char *)cmd;
780         argv[i] = NULL;
781         switch (exectype) {
782         case EXECF_SPAWN:
783             status = win32_spawnvp(P_WAIT, argv[0],
784                                    (const char* const*)argv);
785             break;
786         case EXECF_SPAWN_NOWAIT:
787             status = win32_spawnvp(P_NOWAIT, argv[0],
788                                    (const char* const*)argv);
789             break;
790         case EXECF_EXEC:
791             status = win32_execvp(argv[0], (const char* const*)argv);
792             break;
793         }
794         cmd = argv[0];
795         Safefree(argv);
796     }
797     if (exectype == EXECF_SPAWN_NOWAIT) {
798         PL_statusvalue = -1;    /* >16bits hint for pp_system() */
799     }
800     else {
801         if (status < 0) {
802             if (ckWARN(WARN_EXEC))
803                 Perl_warner(aTHX_ packWARN(WARN_EXEC), "Can't %s \"%s\": %s",
804                      (exectype == EXECF_EXEC ? "exec" : "spawn"),
805                      cmd, strerror(errno));
806             status = 255 * 256;
807         }
808         else
809             status *= 256;
810         PL_statusvalue = status;
811     }
812     return (status);
813 }
814
815 int
816 Perl_do_spawn(pTHX_ char *cmd)
817 {
818     PERL_ARGS_ASSERT_DO_SPAWN;
819
820     return do_spawn2(aTHX_ cmd, EXECF_SPAWN);
821 }
822
823 int
824 Perl_do_spawn_nowait(pTHX_ char *cmd)
825 {
826     PERL_ARGS_ASSERT_DO_SPAWN_NOWAIT;
827
828     return do_spawn2(aTHX_ cmd, EXECF_SPAWN_NOWAIT);
829 }
830
831 bool
832 Perl_do_exec(pTHX_ const char *cmd)
833 {
834     PERL_ARGS_ASSERT_DO_EXEC;
835
836     do_spawn2(aTHX_ cmd, EXECF_EXEC);
837     return FALSE;
838 }
839
840 /* The idea here is to read all the directory names into a string table
841  * (separated by nulls) and when one of the other dir functions is called
842  * return the pointer to the current file name.
843  */
844 DllExport DIR *
845 win32_opendir(const char *filename)
846 {
847     dTHX;
848     DIR                 *dirp;
849     long                len;
850     long                idx;
851     char                scanname[MAX_PATH+3];
852     Stat_t              sbuf;
853     WIN32_FIND_DATAA    aFindData;
854     WIN32_FIND_DATAW    wFindData;
855     bool                using_wide;
856     char                buffer[MAX_PATH*2];
857     char                *ptr;
858
859     len = strlen(filename);
860     if (len > MAX_PATH)
861         return NULL;
862
863     /* check to see if filename is a directory */
864     if (win32_stat(filename, &sbuf) < 0 || !S_ISDIR(sbuf.st_mode))
865         return NULL;
866
867     /* Get us a DIR structure */
868     Newxz(dirp, 1, DIR);
869
870     /* Create the search pattern */
871     strcpy(scanname, filename);
872
873     /* bare drive name means look in cwd for drive */
874     if (len == 2 && isALPHA(scanname[0]) && scanname[1] == ':') {
875         scanname[len++] = '.';
876         scanname[len++] = '/';
877     }
878     else if (scanname[len-1] != '/' && scanname[len-1] != '\\') {
879         scanname[len++] = '/';
880     }
881     scanname[len++] = '*';
882     scanname[len] = '\0';
883
884     /* do the FindFirstFile call */
885     if (IsWin2000()) {
886         WCHAR wscanname[sizeof(scanname)];
887         MultiByteToWideChar(CP_ACP, 0, scanname, -1, wscanname, sizeof(wscanname)/sizeof(WCHAR));
888         dirp->handle = FindFirstFileW(PerlDir_mapW(wscanname), &wFindData);
889         using_wide = TRUE;
890     }
891     else {
892         dirp->handle = FindFirstFileA(PerlDir_mapA(scanname), &aFindData);
893     }
894     if (dirp->handle == INVALID_HANDLE_VALUE) {
895         DWORD err = GetLastError();
896         /* FindFirstFile() fails on empty drives! */
897         switch (err) {
898         case ERROR_FILE_NOT_FOUND:
899             return dirp;
900         case ERROR_NO_MORE_FILES:
901         case ERROR_PATH_NOT_FOUND:
902             errno = ENOENT;
903             break;
904         case ERROR_NOT_ENOUGH_MEMORY:
905             errno = ENOMEM;
906             break;
907         default:
908             errno = EINVAL;
909             break;
910         }
911         Safefree(dirp);
912         return NULL;
913     }
914
915     if (using_wide) {
916         BOOL use_default = FALSE;
917         WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS,
918                             wFindData.cFileName, -1,
919                             buffer, sizeof(buffer), NULL, &use_default);
920         if (use_default && *wFindData.cAlternateFileName) {
921             WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS,
922                                 wFindData.cAlternateFileName, -1,
923                                 buffer, sizeof(buffer), NULL, NULL);
924         }
925         ptr = buffer;
926     }
927     else {
928         ptr = aFindData.cFileName;
929     }
930     /* now allocate the first part of the string table for
931      * the filenames that we find.
932      */
933     idx = strlen(ptr)+1;
934     if (idx < 256)
935         dirp->size = 256;
936     else
937         dirp->size = idx;
938     Newx(dirp->start, dirp->size, char);
939     strcpy(dirp->start, ptr);
940     dirp->nfiles++;
941     dirp->end = dirp->curr = dirp->start;
942     dirp->end += idx;
943     return dirp;
944 }
945
946
947 /* Readdir just returns the current string pointer and bumps the
948  * string pointer to the nDllExport entry.
949  */
950 DllExport struct direct *
951 win32_readdir(DIR *dirp)
952 {
953     long         len;
954
955     if (dirp->curr) {
956         /* first set up the structure to return */
957         len = strlen(dirp->curr);
958         strcpy(dirp->dirstr.d_name, dirp->curr);
959         dirp->dirstr.d_namlen = len;
960
961         /* Fake an inode */
962         dirp->dirstr.d_ino = dirp->curr - dirp->start;
963
964         /* Now set up for the next call to readdir */
965         dirp->curr += len + 1;
966         if (dirp->curr >= dirp->end) {
967             dTHX;
968             BOOL res;
969             WIN32_FIND_DATAA aFindData;
970             char buffer[MAX_PATH*2];
971             char *ptr;
972
973             /* finding the next file that matches the wildcard
974              * (which should be all of them in this directory!).
975              */
976             if (IsWin2000()) {
977                 WIN32_FIND_DATAW wFindData;
978                 res = FindNextFileW(dirp->handle, &wFindData);
979                 if (res) {
980                     BOOL use_default = FALSE;
981                     WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS,
982                                         wFindData.cFileName, -1,
983                                         buffer, sizeof(buffer), NULL, &use_default);
984                     if (use_default && *wFindData.cAlternateFileName) {
985                         WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS,
986                                             wFindData.cAlternateFileName, -1,
987                                             buffer, sizeof(buffer), NULL, NULL);
988                     }
989                     ptr = buffer;
990                 }
991             }
992             else {
993                 res = FindNextFileA(dirp->handle, &aFindData);
994                 ptr = aFindData.cFileName;
995             }
996             if (res) {
997                 long endpos = dirp->end - dirp->start;
998                 long newsize = endpos + strlen(ptr) + 1;
999                 /* bump the string table size by enough for the
1000                  * new name and its null terminator */
1001                 while (newsize > dirp->size) {
1002                     long curpos = dirp->curr - dirp->start;
1003                     dirp->size *= 2;
1004                     Renew(dirp->start, dirp->size, char);
1005                     dirp->curr = dirp->start + curpos;
1006                 }
1007                 strcpy(dirp->start + endpos, ptr);
1008                 dirp->end = dirp->start + newsize;
1009                 dirp->nfiles++;
1010             }
1011             else
1012                 dirp->curr = NULL;
1013         }
1014         return &(dirp->dirstr);
1015     }
1016     else
1017         return NULL;
1018 }
1019
1020 /* Telldir returns the current string pointer position */
1021 DllExport long
1022 win32_telldir(DIR *dirp)
1023 {
1024     return (dirp->curr - dirp->start);
1025 }
1026
1027
1028 /* Seekdir moves the string pointer to a previously saved position
1029  * (returned by telldir).
1030  */
1031 DllExport void
1032 win32_seekdir(DIR *dirp, long loc)
1033 {
1034     dirp->curr = dirp->start + loc;
1035 }
1036
1037 /* Rewinddir resets the string pointer to the start */
1038 DllExport void
1039 win32_rewinddir(DIR *dirp)
1040 {
1041     dirp->curr = dirp->start;
1042 }
1043
1044 /* free the memory allocated by opendir */
1045 DllExport int
1046 win32_closedir(DIR *dirp)
1047 {
1048     dTHX;
1049     if (dirp->handle != INVALID_HANDLE_VALUE)
1050         FindClose(dirp->handle);
1051     Safefree(dirp->start);
1052     Safefree(dirp);
1053     return 1;
1054 }
1055
1056
1057 /*
1058  * various stubs
1059  */
1060
1061
1062 /* Ownership
1063  *
1064  * Just pretend that everyone is a superuser. NT will let us know if
1065  * we don\'t really have permission to do something.
1066  */
1067
1068 #define ROOT_UID    ((uid_t)0)
1069 #define ROOT_GID    ((gid_t)0)
1070
1071 uid_t
1072 getuid(void)
1073 {
1074     return ROOT_UID;
1075 }
1076
1077 uid_t
1078 geteuid(void)
1079 {
1080     return ROOT_UID;
1081 }
1082
1083 gid_t
1084 getgid(void)
1085 {
1086     return ROOT_GID;
1087 }
1088
1089 gid_t
1090 getegid(void)
1091 {
1092     return ROOT_GID;
1093 }
1094
1095 int
1096 setuid(uid_t auid)
1097 {
1098     return (auid == ROOT_UID ? 0 : -1);
1099 }
1100
1101 int
1102 setgid(gid_t agid)
1103 {
1104     return (agid == ROOT_GID ? 0 : -1);
1105 }
1106
1107 char *
1108 getlogin(void)
1109 {
1110     dTHX;
1111     char *buf = w32_getlogin_buffer;
1112     DWORD size = sizeof(w32_getlogin_buffer);
1113     if (GetUserName(buf,&size))
1114         return buf;
1115     return (char*)NULL;
1116 }
1117
1118 int
1119 chown(const char *path, uid_t owner, gid_t group)
1120 {
1121     /* XXX noop */
1122     return 0;
1123 }
1124
1125 /*
1126  * XXX this needs strengthening  (for PerlIO)
1127  *   -- BKS, 11-11-200
1128 */
1129 int mkstemp(const char *path)
1130 {
1131     dTHX;
1132     char buf[MAX_PATH+1];
1133     int i = 0, fd = -1;
1134
1135 retry:
1136     if (i++ > 10) { /* give up */
1137         errno = ENOENT;
1138         return -1;
1139     }
1140     if (!GetTempFileNameA((LPCSTR)path, "plr", 1, buf)) {
1141         errno = ENOENT;
1142         return -1;
1143     }
1144     fd = PerlLIO_open3(buf, O_CREAT|O_RDWR|O_EXCL, 0600);
1145     if (fd == -1)
1146         goto retry;
1147     return fd;
1148 }
1149
1150 static long
1151 find_pid(int pid)
1152 {
1153     dTHX;
1154     long child = w32_num_children;
1155     while (--child >= 0) {
1156         if ((int)w32_child_pids[child] == pid)
1157             return child;
1158     }
1159     return -1;
1160 }
1161
1162 static void
1163 remove_dead_process(long child)
1164 {
1165     if (child >= 0) {
1166         dTHX;
1167         CloseHandle(w32_child_handles[child]);
1168         Move(&w32_child_handles[child+1], &w32_child_handles[child],
1169              (w32_num_children-child-1), HANDLE);
1170         Move(&w32_child_pids[child+1], &w32_child_pids[child],
1171              (w32_num_children-child-1), DWORD);
1172         w32_num_children--;
1173     }
1174 }
1175
1176 #ifdef USE_ITHREADS
1177 static long
1178 find_pseudo_pid(int pid)
1179 {
1180     dTHX;
1181     long child = w32_num_pseudo_children;
1182     while (--child >= 0) {
1183         if ((int)w32_pseudo_child_pids[child] == pid)
1184             return child;
1185     }
1186     return -1;
1187 }
1188
1189 static void
1190 remove_dead_pseudo_process(long child)
1191 {
1192     if (child >= 0) {
1193         dTHX;
1194         CloseHandle(w32_pseudo_child_handles[child]);
1195         Move(&w32_pseudo_child_handles[child+1], &w32_pseudo_child_handles[child],
1196              (w32_num_pseudo_children-child-1), HANDLE);
1197         Move(&w32_pseudo_child_pids[child+1], &w32_pseudo_child_pids[child],
1198              (w32_num_pseudo_children-child-1), DWORD);
1199         Move(&w32_pseudo_child_message_hwnds[child+1], &w32_pseudo_child_message_hwnds[child],
1200              (w32_num_pseudo_children-child-1), HWND);
1201         w32_num_pseudo_children--;
1202     }
1203 }
1204 #endif
1205
1206 static int
1207 terminate_process(DWORD pid, HANDLE process_handle, int sig)
1208 {
1209     switch(sig) {
1210     case 0:
1211         /* "Does process exist?" use of kill */
1212         return 1;
1213     case 2:
1214         if (GenerateConsoleCtrlEvent(CTRL_C_EVENT, pid))
1215             return 1;
1216         break;
1217     case SIGBREAK:
1218     case SIGTERM:
1219         if (GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, pid))
1220             return 1;
1221         break;
1222     default: /* For now be backwards compatible with perl 5.6 */
1223     case 9:
1224         /* Note that we will only be able to kill processes owned by the
1225          * current process owner, even when we are running as an administrator.
1226          * To kill processes of other owners we would need to set the
1227          * 'SeDebugPrivilege' privilege before obtaining the process handle.
1228          */
1229         if (TerminateProcess(process_handle, sig))
1230             return 1;
1231         break;
1232     }
1233     return 0;
1234 }
1235
1236 /* Traverse process tree using ToolHelp functions */
1237 static int
1238 kill_process_tree_toolhelp(DWORD pid, int sig)
1239 {
1240     HANDLE process_handle;
1241     HANDLE snapshot_handle;
1242     int killed = 0;
1243
1244     process_handle = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
1245     if (process_handle == NULL)
1246         return 0;
1247
1248     killed += terminate_process(pid, process_handle, sig);
1249
1250     snapshot_handle = pfnCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
1251     if (snapshot_handle != INVALID_HANDLE_VALUE) {
1252         PROCESSENTRY32 entry;
1253
1254         entry.dwSize = sizeof(entry);
1255         if (pfnProcess32First(snapshot_handle, &entry)) {
1256             do {
1257                 if (entry.th32ParentProcessID == pid)
1258                     killed += kill_process_tree_toolhelp(entry.th32ProcessID, sig);
1259                 entry.dwSize = sizeof(entry);
1260             }
1261             while (pfnProcess32Next(snapshot_handle, &entry));
1262         }
1263         CloseHandle(snapshot_handle);
1264     }
1265     CloseHandle(process_handle);
1266     return killed;
1267 }
1268
1269 /* Traverse process tree using undocumented system information structures.
1270  * This is only necessary on Windows NT, which lacks the ToolHelp functions.
1271  */
1272 static int
1273 kill_process_tree_sysinfo(SYSTEM_PROCESSES *process_info, DWORD pid, int sig)
1274 {
1275     HANDLE process_handle;
1276     SYSTEM_PROCESSES *p = process_info;
1277     int killed = 0;
1278
1279     process_handle = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
1280     if (process_handle == NULL)
1281         return 0;
1282
1283     killed += terminate_process(pid, process_handle, sig);
1284
1285     while (1) {
1286         if (p->InheritedFromProcessId == (DWORD)pid)
1287             killed += kill_process_tree_sysinfo(process_info, p->ProcessId, sig);
1288
1289         if (p->NextEntryDelta == 0)
1290             break;
1291
1292         p = (SYSTEM_PROCESSES*)((char*)p + p->NextEntryDelta);
1293     }
1294
1295     CloseHandle(process_handle);
1296     return killed;
1297 }
1298
1299 int
1300 killpg(int pid, int sig)
1301 {
1302     /* Use "documented" method whenever available */
1303     if (pfnCreateToolhelp32Snapshot && pfnProcess32First && pfnProcess32Next) {
1304         return kill_process_tree_toolhelp((DWORD)pid, sig);
1305     }
1306
1307     /* Fall back to undocumented Windows internals on Windows NT */
1308     if (pfnZwQuerySystemInformation) {
1309         dTHX;
1310         char *buffer;
1311         DWORD size = 0;
1312
1313         pfnZwQuerySystemInformation(SystemProcessesAndThreadsInformation, NULL, 0, &size);
1314         Newx(buffer, size, char);
1315
1316         if (pfnZwQuerySystemInformation(SystemProcessesAndThreadsInformation, buffer, size, NULL) >= 0) {
1317             int killed = kill_process_tree_sysinfo((SYSTEM_PROCESSES*)buffer, (DWORD)pid, sig);
1318             Safefree(buffer);
1319             return killed;
1320         }
1321     }
1322     return 0;
1323 }
1324
1325 static int
1326 my_kill(int pid, int sig)
1327 {
1328     int retval = 0;
1329     HANDLE process_handle;
1330
1331     if (sig < 0)
1332         return killpg(pid, -sig);
1333
1334     process_handle = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
1335     /* OpenProcess() returns NULL on error, *not* INVALID_HANDLE_VALUE */
1336     if (process_handle != NULL) {
1337         retval = terminate_process(pid, process_handle, sig);
1338         CloseHandle(process_handle);
1339     }
1340     return retval;
1341 }
1342
1343 DllExport int
1344 win32_kill(int pid, int sig)
1345 {
1346     dTHX;
1347     long child;
1348 #ifdef USE_ITHREADS
1349     if (pid < 0) {
1350         /* it is a pseudo-forked child */
1351         child = find_pseudo_pid(-pid);
1352         if (child >= 0) {
1353             HWND hwnd = w32_pseudo_child_message_hwnds[child];
1354             HANDLE hProcess = w32_pseudo_child_handles[child];
1355             switch (sig) {
1356             case 0:
1357                 /* "Does process exist?" use of kill */
1358                 return 0;
1359
1360             case 9:
1361                 /* kill -9 style un-graceful exit */
1362                 if (TerminateThread(hProcess, sig)) {
1363                     remove_dead_pseudo_process(child);
1364                     return 0;
1365                 }
1366                 break;
1367
1368             default: {
1369                 int count = 0;
1370                 /* pseudo-process has not yet properly initialized if hwnd isn't set */
1371                 while (hwnd == INVALID_HANDLE_VALUE && count < 5) {
1372                     /* Yield and wait for the other thread to send us its message_hwnd */
1373                     Sleep(0);
1374                     win32_async_check(aTHX);
1375                     hwnd = w32_pseudo_child_message_hwnds[child];
1376                     ++count;
1377                 }
1378                 if (hwnd != INVALID_HANDLE_VALUE) {
1379                     /* We fake signals to pseudo-processes using Win32
1380                      * message queue.  In Win9X the pids are negative already. */
1381                     if ((hwnd != NULL && PostMessage(hwnd, WM_USER_KILL, sig, 0)) ||
1382                         PostThreadMessage(IsWin95() ? pid : -pid, WM_USER_KILL, sig, 0))
1383                     {
1384                         /* It might be us ... */
1385                         PERL_ASYNC_CHECK();
1386                         return 0;
1387                     }
1388                 }
1389                 break;
1390             }
1391             } /* switch */
1392         }
1393         else if (IsWin95()) {
1394             pid = -pid;
1395             goto alien_process;
1396         }
1397     }
1398     else
1399 #endif
1400     {
1401         child = find_pid(pid);
1402         if (child >= 0) {
1403             if (my_kill(pid, sig)) {
1404                 DWORD exitcode = 0;
1405                 if (GetExitCodeProcess(w32_child_handles[child], &exitcode) &&
1406                     exitcode != STILL_ACTIVE)
1407                 {
1408                     remove_dead_process(child);
1409                 }
1410                 return 0;
1411             }
1412         }
1413         else {
1414 alien_process:
1415             if (my_kill((IsWin95() ? -pid : pid), sig))
1416                 return 0;
1417         }
1418     }
1419     errno = EINVAL;
1420     return -1;
1421 }
1422
1423 DllExport int
1424 win32_stat(const char *path, Stat_t *sbuf)
1425 {
1426     dTHX;
1427     char        buffer[MAX_PATH+1];
1428     int         l = strlen(path);
1429     int         res;
1430     int         nlink = 1;
1431     BOOL        expect_dir = FALSE;
1432
1433     GV          *gv_sloppy = gv_fetchpvs("\027IN32_SLOPPY_STAT",
1434                                          GV_NOTQUAL, SVt_PV);
1435     BOOL        sloppy = gv_sloppy && SvTRUE(GvSV(gv_sloppy));
1436
1437     if (l > 1) {
1438         switch(path[l - 1]) {
1439         /* FindFirstFile() and stat() are buggy with a trailing
1440          * slashes, except for the root directory of a drive */
1441         case '\\':
1442         case '/':
1443             if (l > sizeof(buffer)) {
1444                 errno = ENAMETOOLONG;
1445                 return -1;
1446             }
1447             --l;
1448             strncpy(buffer, path, l);
1449             /* remove additional trailing slashes */
1450             while (l > 1 && (buffer[l-1] == '/' || buffer[l-1] == '\\'))
1451                 --l;
1452             /* add back slash if we otherwise end up with just a drive letter */
1453             if (l == 2 && isALPHA(buffer[0]) && buffer[1] == ':')
1454                 buffer[l++] = '\\';
1455             buffer[l] = '\0';
1456             path = buffer;
1457             expect_dir = TRUE;
1458             break;
1459
1460         /* FindFirstFile() is buggy with "x:", so add a dot :-( */
1461         case ':':
1462             if (l == 2 && isALPHA(path[0])) {
1463                 buffer[0] = path[0];
1464                 buffer[1] = ':';
1465                 buffer[2] = '.';
1466                 buffer[3] = '\0';
1467                 l = 3;
1468                 path = buffer;
1469             }
1470             break;
1471         }
1472     }
1473
1474     path = PerlDir_mapA(path);
1475     l = strlen(path);
1476
1477     if (!sloppy) {
1478         /* We must open & close the file once; otherwise file attribute changes  */
1479         /* might not yet have propagated to "other" hard links of the same file. */
1480         /* This also gives us an opportunity to determine the number of links.   */
1481         HANDLE handle = CreateFileA(path, 0, 0, NULL, OPEN_EXISTING, 0, NULL);
1482         if (handle != INVALID_HANDLE_VALUE) {
1483             BY_HANDLE_FILE_INFORMATION bhi;
1484             if (GetFileInformationByHandle(handle, &bhi))
1485                 nlink = bhi.nNumberOfLinks;
1486             CloseHandle(handle);
1487         }
1488     }
1489
1490     /* path will be mapped correctly above */
1491 #if defined(WIN64) || defined(USE_LARGE_FILES)
1492     res = _stati64(path, sbuf);
1493 #else
1494     res = stat(path, sbuf);
1495 #endif
1496     sbuf->st_nlink = nlink;
1497
1498     if (res < 0) {
1499         /* CRT is buggy on sharenames, so make sure it really isn't.
1500          * XXX using GetFileAttributesEx() will enable us to set
1501          * sbuf->st_*time (but note that's not available on the
1502          * Windows of 1995) */
1503         DWORD r = GetFileAttributesA(path);
1504         if (r != 0xffffffff && (r & FILE_ATTRIBUTE_DIRECTORY)) {
1505             /* sbuf may still contain old garbage since stat() failed */
1506             Zero(sbuf, 1, Stat_t);
1507             sbuf->st_mode = S_IFDIR | S_IREAD;
1508             errno = 0;
1509             if (!(r & FILE_ATTRIBUTE_READONLY))
1510                 sbuf->st_mode |= S_IWRITE | S_IEXEC;
1511             return 0;
1512         }
1513     }
1514     else {
1515         if (l == 3 && isALPHA(path[0]) && path[1] == ':'
1516             && (path[2] == '\\' || path[2] == '/'))
1517         {
1518             /* The drive can be inaccessible, some _stat()s are buggy */
1519             if (!GetVolumeInformationA(path,NULL,0,NULL,NULL,NULL,NULL,0)) {
1520                 errno = ENOENT;
1521                 return -1;
1522             }
1523         }
1524         if (expect_dir && !S_ISDIR(sbuf->st_mode)) {
1525             errno = ENOTDIR;
1526             return -1;
1527         }
1528         if (S_ISDIR(sbuf->st_mode)) {
1529             /* Ensure the "write" bit is switched off in the mode for
1530              * directories with the read-only attribute set. Borland (at least)
1531              * switches it on for directories, which is technically correct
1532              * (directories are indeed always writable unless denied by DACLs),
1533              * but we want stat() and -w to reflect the state of the read-only
1534              * attribute for symmetry with chmod(). */
1535             DWORD r = GetFileAttributesA(path);
1536             if (r != 0xffffffff && (r & FILE_ATTRIBUTE_READONLY)) {
1537                 sbuf->st_mode &= ~S_IWRITE;
1538             }
1539         }
1540 #ifdef __BORLANDC__
1541         if (S_ISDIR(sbuf->st_mode)) {
1542             sbuf->st_mode |= S_IEXEC;
1543         }
1544         else if (S_ISREG(sbuf->st_mode)) {
1545             int perms;
1546             if (l >= 4 && path[l-4] == '.') {
1547                 const char *e = path + l - 3;
1548                 if (strnicmp(e,"exe",3)
1549                     && strnicmp(e,"bat",3)
1550                     && strnicmp(e,"com",3)
1551                     && (IsWin95() || strnicmp(e,"cmd",3)))
1552                     sbuf->st_mode &= ~S_IEXEC;
1553                 else
1554                     sbuf->st_mode |= S_IEXEC;
1555             }
1556             else
1557                 sbuf->st_mode &= ~S_IEXEC;
1558             /* Propagate permissions to _group_ and _others_ */
1559             perms = sbuf->st_mode & (S_IREAD|S_IWRITE|S_IEXEC);
1560             sbuf->st_mode |= (perms>>3) | (perms>>6);
1561         }
1562 #endif
1563     }
1564     return res;
1565 }
1566
1567 #define isSLASH(c) ((c) == '/' || (c) == '\\')
1568 #define SKIP_SLASHES(s) \
1569     STMT_START {                                \
1570         while (*(s) && isSLASH(*(s)))           \
1571             ++(s);                              \
1572     } STMT_END
1573 #define COPY_NONSLASHES(d,s) \
1574     STMT_START {                                \
1575         while (*(s) && !isSLASH(*(s)))          \
1576             *(d)++ = *(s)++;                    \
1577     } STMT_END
1578
1579 /* Find the longname of a given path.  path is destructively modified.
1580  * It should have space for at least MAX_PATH characters. */
1581 DllExport char *
1582 win32_longpath(char *path)
1583 {
1584     WIN32_FIND_DATA fdata;
1585     HANDLE fhand;
1586     char tmpbuf[MAX_PATH+1];
1587     char *tmpstart = tmpbuf;
1588     char *start = path;
1589     char sep;
1590     if (!path)
1591         return NULL;
1592
1593     /* drive prefix */
1594     if (isALPHA(path[0]) && path[1] == ':') {
1595         start = path + 2;
1596         *tmpstart++ = path[0];
1597         *tmpstart++ = ':';
1598     }
1599     /* UNC prefix */
1600     else if (isSLASH(path[0]) && isSLASH(path[1])) {
1601         start = path + 2;
1602         *tmpstart++ = path[0];
1603         *tmpstart++ = path[1];
1604         SKIP_SLASHES(start);
1605         COPY_NONSLASHES(tmpstart,start);        /* copy machine name */
1606         if (*start) {
1607             *tmpstart++ = *start++;
1608             SKIP_SLASHES(start);
1609             COPY_NONSLASHES(tmpstart,start);    /* copy share name */
1610         }
1611     }
1612     *tmpstart = '\0';
1613     while (*start) {
1614         /* copy initial slash, if any */
1615         if (isSLASH(*start)) {
1616             *tmpstart++ = *start++;
1617             *tmpstart = '\0';
1618             SKIP_SLASHES(start);
1619         }
1620
1621         /* FindFirstFile() expands "." and "..", so we need to pass
1622          * those through unmolested */
1623         if (*start == '.'
1624             && (!start[1] || isSLASH(start[1])
1625                 || (start[1] == '.' && (!start[2] || isSLASH(start[2])))))
1626         {
1627             COPY_NONSLASHES(tmpstart,start);    /* copy "." or ".." */
1628             *tmpstart = '\0';
1629             continue;
1630         }
1631
1632         /* if this is the end, bust outta here */
1633         if (!*start)
1634             break;
1635
1636         /* now we're at a non-slash; walk up to next slash */
1637         while (*start && !isSLASH(*start))
1638             ++start;
1639
1640         /* stop and find full name of component */
1641         sep = *start;
1642         *start = '\0';
1643         fhand = FindFirstFile(path,&fdata);
1644         *start = sep;
1645         if (fhand != INVALID_HANDLE_VALUE) {
1646             STRLEN len = strlen(fdata.cFileName);
1647             if ((STRLEN)(tmpbuf + sizeof(tmpbuf) - tmpstart) > len) {
1648                 strcpy(tmpstart, fdata.cFileName);
1649                 tmpstart += len;
1650                 FindClose(fhand);
1651             }
1652             else {
1653                 FindClose(fhand);
1654                 errno = ERANGE;
1655                 return NULL;
1656             }
1657         }
1658         else {
1659             /* failed a step, just return without side effects */
1660             /*PerlIO_printf(Perl_debug_log, "Failed to find %s\n", path);*/
1661             errno = EINVAL;
1662             return NULL;
1663         }
1664     }
1665     strcpy(path,tmpbuf);
1666     return path;
1667 }
1668
1669 static void
1670 out_of_memory(void)
1671 {
1672     if (PL_curinterp) {
1673         dTHX;
1674         /* Can't use PerlIO to write as it allocates memory */
1675         PerlLIO_write(PerlIO_fileno(Perl_error_log),
1676                       PL_no_mem, strlen(PL_no_mem));
1677         my_exit(1);
1678     }
1679     exit(1);
1680 }
1681
1682 /* The win32_ansipath() function takes a Unicode filename and converts it
1683  * into the current Windows codepage. If some characters cannot be mapped,
1684  * then it will convert the short name instead.
1685  *
1686  * The buffer to the ansi pathname must be freed with win32_free() when it
1687  * it no longer needed.
1688  *
1689  * The argument to win32_ansipath() must exist before this function is
1690  * called; otherwise there is no way to determine the short path name.
1691  *
1692  * Ideas for future refinement:
1693  * - Only convert those segments of the path that are not in the current
1694  *   codepage, but leave the other segments in their long form.
1695  * - If the resulting name is longer than MAX_PATH, start converting
1696  *   additional path segments into short names until the full name
1697  *   is shorter than MAX_PATH.  Shorten the filename part last!
1698  */
1699 DllExport char *
1700 win32_ansipath(const WCHAR *widename)
1701 {
1702     char *name;
1703     BOOL use_default = FALSE;
1704     size_t widelen = wcslen(widename)+1;
1705     int len = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, widename, widelen,
1706                                   NULL, 0, NULL, NULL);
1707     name = win32_malloc(len);
1708     if (!name)
1709         out_of_memory();
1710
1711     WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, widename, widelen,
1712                         name, len, NULL, &use_default);
1713     if (use_default) {
1714         DWORD shortlen = GetShortPathNameW(widename, NULL, 0);
1715         if (shortlen) {
1716             WCHAR *shortname = win32_malloc(shortlen*sizeof(WCHAR));
1717             if (!shortname)
1718                 out_of_memory();
1719             shortlen = GetShortPathNameW(widename, shortname, shortlen)+1;
1720
1721             len = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, shortname, shortlen,
1722                                       NULL, 0, NULL, NULL);
1723             name = win32_realloc(name, len);
1724             if (!name)
1725                 out_of_memory();
1726             WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, shortname, shortlen,
1727                                 name, len, NULL, NULL);
1728             win32_free(shortname);
1729         }
1730     }
1731     return name;
1732 }
1733
1734 DllExport char *
1735 win32_getenv(const char *name)
1736 {
1737     dTHX;
1738     DWORD needlen;
1739     SV *curitem = NULL;
1740
1741     needlen = GetEnvironmentVariableA(name,NULL,0);
1742     if (needlen != 0) {
1743         curitem = sv_2mortal(newSVpvn("", 0));
1744         do {
1745             SvGROW(curitem, needlen+1);
1746             needlen = GetEnvironmentVariableA(name,SvPVX(curitem),
1747                                               needlen);
1748         } while (needlen >= SvLEN(curitem));
1749         SvCUR_set(curitem, needlen);
1750     }
1751     else {
1752         /* allow any environment variables that begin with 'PERL'
1753            to be stored in the registry */
1754         if (strncmp(name, "PERL", 4) == 0)
1755             (void)get_regstr(name, &curitem);
1756     }
1757     if (curitem && SvCUR(curitem))
1758         return SvPVX(curitem);
1759
1760     return NULL;
1761 }
1762
1763 DllExport int
1764 win32_putenv(const char *name)
1765 {
1766     dTHX;
1767     char* curitem;
1768     char* val;
1769     int relval = -1;
1770
1771     if (name) {
1772         Newx(curitem,strlen(name)+1,char);
1773         strcpy(curitem, name);
1774         val = strchr(curitem, '=');
1775         if (val) {
1776             /* The sane way to deal with the environment.
1777              * Has these advantages over putenv() & co.:
1778              *  * enables us to store a truly empty value in the
1779              *    environment (like in UNIX).
1780              *  * we don't have to deal with RTL globals, bugs and leaks
1781              *    (specifically, see http://support.microsoft.com/kb/235601).
1782              *  * Much faster.
1783              * Why you may want to use the RTL environment handling
1784              * (previously enabled by USE_WIN32_RTL_ENV):
1785              *  * environ[] and RTL functions will not reflect changes,
1786              *    which might be an issue if extensions want to access
1787              *    the env. via RTL.  This cuts both ways, since RTL will
1788              *    not see changes made by extensions that call the Win32
1789              *    functions directly, either.
1790              * GSAR 97-06-07
1791              */
1792             *val++ = '\0';
1793             if (SetEnvironmentVariableA(curitem, *val ? val : NULL))
1794                 relval = 0;
1795         }
1796         Safefree(curitem);
1797     }
1798     return relval;
1799 }
1800
1801 static long
1802 filetime_to_clock(PFILETIME ft)
1803 {
1804     __int64 qw = ft->dwHighDateTime;
1805     qw <<= 32;
1806     qw |= ft->dwLowDateTime;
1807     qw /= 10000;  /* File time ticks at 0.1uS, clock at 1mS */
1808     return (long) qw;
1809 }
1810
1811 DllExport int
1812 win32_times(struct tms *timebuf)
1813 {
1814     FILETIME user;
1815     FILETIME kernel;
1816     FILETIME dummy;
1817     clock_t process_time_so_far = clock();
1818     if (GetProcessTimes(GetCurrentProcess(), &dummy, &dummy,
1819                         &kernel,&user)) {
1820         timebuf->tms_utime = filetime_to_clock(&user);
1821         timebuf->tms_stime = filetime_to_clock(&kernel);
1822         timebuf->tms_cutime = 0;
1823         timebuf->tms_cstime = 0;
1824     } else {
1825         /* That failed - e.g. Win95 fallback to clock() */
1826         timebuf->tms_utime = process_time_so_far;
1827         timebuf->tms_stime = 0;
1828         timebuf->tms_cutime = 0;
1829         timebuf->tms_cstime = 0;
1830     }
1831     return process_time_so_far;
1832 }
1833
1834 /* fix utime() so it works on directories in NT */
1835 static BOOL
1836 filetime_from_time(PFILETIME pFileTime, time_t Time)
1837 {
1838     struct tm *pTM = localtime(&Time);
1839     SYSTEMTIME SystemTime;
1840     FILETIME LocalTime;
1841
1842     if (pTM == NULL)
1843         return FALSE;
1844
1845     SystemTime.wYear   = pTM->tm_year + 1900;
1846     SystemTime.wMonth  = pTM->tm_mon + 1;
1847     SystemTime.wDay    = pTM->tm_mday;
1848     SystemTime.wHour   = pTM->tm_hour;
1849     SystemTime.wMinute = pTM->tm_min;
1850     SystemTime.wSecond = pTM->tm_sec;
1851     SystemTime.wMilliseconds = 0;
1852
1853     return SystemTimeToFileTime(&SystemTime, &LocalTime) &&
1854            LocalFileTimeToFileTime(&LocalTime, pFileTime);
1855 }
1856
1857 DllExport int
1858 win32_unlink(const char *filename)
1859 {
1860     dTHX;
1861     int ret;
1862     DWORD attrs;
1863
1864     filename = PerlDir_mapA(filename);
1865     attrs = GetFileAttributesA(filename);
1866     if (attrs == 0xFFFFFFFF) {
1867         errno = ENOENT;
1868         return -1;
1869     }
1870     if (attrs & FILE_ATTRIBUTE_READONLY) {
1871         (void)SetFileAttributesA(filename, attrs & ~FILE_ATTRIBUTE_READONLY);
1872         ret = unlink(filename);
1873         if (ret == -1)
1874             (void)SetFileAttributesA(filename, attrs);
1875     }
1876     else
1877         ret = unlink(filename);
1878     return ret;
1879 }
1880
1881 DllExport int
1882 win32_utime(const char *filename, struct utimbuf *times)
1883 {
1884     dTHX;
1885     HANDLE handle;
1886     FILETIME ftCreate;
1887     FILETIME ftAccess;
1888     FILETIME ftWrite;
1889     struct utimbuf TimeBuffer;
1890     int rc;
1891
1892     filename = PerlDir_mapA(filename);
1893     rc = utime(filename, times);
1894
1895     /* EACCES: path specifies directory or readonly file */
1896     if (rc == 0 || errno != EACCES /* || !IsWinNT() */)
1897         return rc;
1898
1899     if (times == NULL) {
1900         times = &TimeBuffer;
1901         time(&times->actime);
1902         times->modtime = times->actime;
1903     }
1904
1905     /* This will (and should) still fail on readonly files */
1906     handle = CreateFileA(filename, GENERIC_READ | GENERIC_WRITE,
1907                          FILE_SHARE_READ | FILE_SHARE_DELETE, NULL,
1908                          OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
1909     if (handle == INVALID_HANDLE_VALUE)
1910         return rc;
1911
1912     if (GetFileTime(handle, &ftCreate, &ftAccess, &ftWrite) &&
1913         filetime_from_time(&ftAccess, times->actime) &&
1914         filetime_from_time(&ftWrite, times->modtime) &&
1915         SetFileTime(handle, &ftCreate, &ftAccess, &ftWrite))
1916     {
1917         rc = 0;
1918     }
1919
1920     CloseHandle(handle);
1921     return rc;
1922 }
1923
1924 typedef union {
1925     unsigned __int64    ft_i64;
1926     FILETIME            ft_val;
1927 } FT_t;
1928
1929 #ifdef __GNUC__
1930 #define Const64(x) x##LL
1931 #else
1932 #define Const64(x) x##i64
1933 #endif
1934 /* Number of 100 nanosecond units from 1/1/1601 to 1/1/1970 */
1935 #define EPOCH_BIAS  Const64(116444736000000000)
1936
1937 /* NOTE: This does not compute the timezone info (doing so can be expensive,
1938  * and appears to be unsupported even by glibc) */
1939 DllExport int
1940 win32_gettimeofday(struct timeval *tp, void *not_used)
1941 {
1942     FT_t ft;
1943
1944     /* this returns time in 100-nanosecond units  (i.e. tens of usecs) */
1945     GetSystemTimeAsFileTime(&ft.ft_val);
1946
1947     /* seconds since epoch */
1948     tp->tv_sec = (long)((ft.ft_i64 - EPOCH_BIAS) / Const64(10000000));
1949
1950     /* microseconds remaining */
1951     tp->tv_usec = (long)((ft.ft_i64 / Const64(10)) % Const64(1000000));
1952
1953     return 0;
1954 }
1955
1956 DllExport int
1957 win32_uname(struct utsname *name)
1958 {
1959     struct hostent *hep;
1960     STRLEN nodemax = sizeof(name->nodename)-1;
1961
1962     /* sysname */
1963     switch (g_osver.dwPlatformId) {
1964     case VER_PLATFORM_WIN32_WINDOWS:
1965         strcpy(name->sysname, "Windows");
1966         break;
1967     case VER_PLATFORM_WIN32_NT:
1968         strcpy(name->sysname, "Windows NT");
1969         break;
1970     case VER_PLATFORM_WIN32s:
1971         strcpy(name->sysname, "Win32s");
1972         break;
1973     default:
1974         strcpy(name->sysname, "Win32 Unknown");
1975         break;
1976     }
1977
1978     /* release */
1979     sprintf(name->release, "%d.%d",
1980             g_osver.dwMajorVersion, g_osver.dwMinorVersion);
1981
1982     /* version */
1983     sprintf(name->version, "Build %d",
1984             g_osver.dwPlatformId == VER_PLATFORM_WIN32_NT
1985             ? g_osver.dwBuildNumber : (g_osver.dwBuildNumber & 0xffff));
1986     if (g_osver.szCSDVersion[0]) {
1987         char *buf = name->version + strlen(name->version);
1988         sprintf(buf, " (%s)", g_osver.szCSDVersion);
1989     }
1990
1991     /* nodename */
1992     hep = win32_gethostbyname("localhost");
1993     if (hep) {
1994         STRLEN len = strlen(hep->h_name);
1995         if (len <= nodemax) {
1996             strcpy(name->nodename, hep->h_name);
1997         }
1998         else {
1999             strncpy(name->nodename, hep->h_name, nodemax);
2000             name->nodename[nodemax] = '\0';
2001         }
2002     }
2003     else {
2004         DWORD sz = nodemax;
2005         if (!GetComputerName(name->nodename, &sz))
2006             *name->nodename = '\0';
2007     }
2008
2009     /* machine (architecture) */
2010     {
2011         SYSTEM_INFO info;
2012         DWORD procarch;
2013         char *arch;
2014         GetSystemInfo(&info);
2015
2016 #if (defined(__BORLANDC__)&&(__BORLANDC__<=0x520)) \
2017  || (defined(__MINGW32__) && !defined(_ANONYMOUS_UNION) && !defined(__MINGW_EXTENSION))
2018         procarch = info.u.s.wProcessorArchitecture;
2019 #else
2020         procarch = info.wProcessorArchitecture;
2021 #endif
2022         switch (procarch) {
2023         case PROCESSOR_ARCHITECTURE_INTEL:
2024             arch = "x86"; break;
2025         case PROCESSOR_ARCHITECTURE_MIPS:
2026             arch = "mips"; break;
2027         case PROCESSOR_ARCHITECTURE_ALPHA:
2028             arch = "alpha"; break;
2029         case PROCESSOR_ARCHITECTURE_PPC:
2030             arch = "ppc"; break;
2031 #ifdef PROCESSOR_ARCHITECTURE_SHX
2032         case PROCESSOR_ARCHITECTURE_SHX:
2033             arch = "shx"; break;
2034 #endif
2035 #ifdef PROCESSOR_ARCHITECTURE_ARM
2036         case PROCESSOR_ARCHITECTURE_ARM:
2037             arch = "arm"; break;
2038 #endif
2039 #ifdef PROCESSOR_ARCHITECTURE_IA64
2040         case PROCESSOR_ARCHITECTURE_IA64:
2041             arch = "ia64"; break;
2042 #endif
2043 #ifdef PROCESSOR_ARCHITECTURE_ALPHA64
2044         case PROCESSOR_ARCHITECTURE_ALPHA64:
2045             arch = "alpha64"; break;
2046 #endif
2047 #ifdef PROCESSOR_ARCHITECTURE_MSIL
2048         case PROCESSOR_ARCHITECTURE_MSIL:
2049             arch = "msil"; break;
2050 #endif
2051 #ifdef PROCESSOR_ARCHITECTURE_AMD64
2052         case PROCESSOR_ARCHITECTURE_AMD64:
2053             arch = "amd64"; break;
2054 #endif
2055 #ifdef PROCESSOR_ARCHITECTURE_IA32_ON_WIN64
2056         case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64:
2057             arch = "ia32-64"; break;
2058 #endif
2059 #ifdef PROCESSOR_ARCHITECTURE_UNKNOWN
2060         case PROCESSOR_ARCHITECTURE_UNKNOWN:
2061             arch = "unknown"; break;
2062 #endif
2063         default:
2064             sprintf(name->machine, "unknown(0x%x)", procarch);
2065             arch = name->machine;
2066             break;
2067         }
2068         if (name->machine != arch)
2069             strcpy(name->machine, arch);
2070     }
2071     return 0;
2072 }
2073
2074 /* Timing related stuff */
2075
2076 int
2077 do_raise(pTHX_ int sig) 
2078 {
2079     if (sig < SIG_SIZE) {
2080         Sighandler_t handler = w32_sighandler[sig];
2081         if (handler == SIG_IGN) {
2082             return 0;
2083         }
2084         else if (handler != SIG_DFL) {
2085             (*handler)(sig);
2086             return 0;
2087         }
2088         else {
2089             /* Choose correct default behaviour */
2090             switch (sig) {
2091 #ifdef SIGCLD
2092                 case SIGCLD:
2093 #endif
2094 #ifdef SIGCHLD
2095                 case SIGCHLD:
2096 #endif
2097                 case 0:
2098                     return 0;
2099                 case SIGTERM:
2100                 default:
2101                     break;
2102             }
2103         }
2104     }
2105     /* Tell caller to exit thread/process as approriate */
2106     return 1;
2107 }
2108
2109 void
2110 sig_terminate(pTHX_ int sig)
2111 {
2112     Perl_warn(aTHX_ "Terminating on signal SIG%s(%d)\n",PL_sig_name[sig], sig);
2113     /* exit() seems to be safe, my_exit() or die() is a problem in ^C 
2114        thread 
2115      */
2116     exit(sig);
2117 }
2118
2119 DllExport int
2120 win32_async_check(pTHX)
2121 {
2122     MSG msg;
2123     HWND hwnd = w32_message_hwnd;
2124
2125     /* Reset w32_poll_count before doing anything else, incase we dispatch
2126      * messages that end up calling back into perl */
2127     w32_poll_count = 0;
2128
2129     if (hwnd != INVALID_HANDLE_VALUE) {
2130         /* Passing PeekMessage -1 as HWND (2nd arg) only gets PostThreadMessage() messages
2131         * and ignores window messages - should co-exist better with windows apps e.g. Tk
2132         */
2133         if (hwnd == NULL)
2134             hwnd = (HWND)-1;
2135
2136         while (PeekMessage(&msg, hwnd, WM_TIMER,    WM_TIMER,    PM_REMOVE|PM_NOYIELD) ||
2137                PeekMessage(&msg, hwnd, WM_USER_MIN, WM_USER_MAX, PM_REMOVE|PM_NOYIELD))
2138         {
2139             /* re-post a WM_QUIT message (we'll mark it as read later) */
2140             if(msg.message == WM_QUIT) {
2141                 PostQuitMessage((int)msg.wParam);
2142                 break;
2143             }
2144
2145             if(!CallMsgFilter(&msg, MSGF_USER))
2146             {
2147                 TranslateMessage(&msg);
2148                 DispatchMessage(&msg);
2149             }
2150         }
2151     }
2152
2153     /* Call PeekMessage() to mark all pending messages in the queue as "old".
2154      * This is necessary when we are being called by win32_msgwait() to
2155      * make sure MsgWaitForMultipleObjects() stops reporting the same waiting
2156      * message over and over.  An example how this can happen is when
2157      * Perl is calling win32_waitpid() inside a GUI application and the GUI
2158      * is generating messages before the process terminated.
2159      */
2160     PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE|PM_NOYIELD);
2161
2162     /* Above or other stuff may have set a signal flag */
2163     if (PL_sig_pending)
2164         despatch_signals();
2165     
2166     return 1;
2167 }
2168
2169 /* This function will not return until the timeout has elapsed, or until
2170  * one of the handles is ready. */
2171 DllExport DWORD
2172 win32_msgwait(pTHX_ DWORD count, LPHANDLE handles, DWORD timeout, LPDWORD resultp)
2173 {
2174     /* We may need several goes at this - so compute when we stop */
2175     DWORD ticks = 0;
2176     if (timeout != INFINITE) {
2177         ticks = GetTickCount();
2178         timeout += ticks;
2179     }
2180     while (1) {
2181         DWORD result = MsgWaitForMultipleObjects(count,handles,FALSE,timeout-ticks, QS_POSTMESSAGE|QS_TIMER|QS_SENDMESSAGE);
2182         if (resultp)
2183            *resultp = result;
2184         if (result == WAIT_TIMEOUT) {
2185             /* Ran out of time - explicit return of zero to avoid -ve if we
2186                have scheduling issues
2187              */
2188             return 0;
2189         }
2190         if (timeout != INFINITE) {
2191             ticks = GetTickCount();
2192         }
2193         if (result == WAIT_OBJECT_0 + count) {
2194             /* Message has arrived - check it */
2195             (void)win32_async_check(aTHX);
2196         }
2197         else {
2198            /* Not timeout or message - one of handles is ready */
2199            break;
2200         }
2201     }
2202     /* compute time left to wait */
2203     ticks = timeout - ticks;
2204     /* If we are past the end say zero */
2205     return (ticks > 0) ? ticks : 0;
2206 }
2207
2208 int
2209 win32_internal_wait(int *status, DWORD timeout)
2210 {
2211     /* XXX this wait emulation only knows about processes
2212      * spawned via win32_spawnvp(P_NOWAIT, ...).
2213      */
2214     dTHX;
2215     int i, retval;
2216     DWORD exitcode, waitcode;
2217
2218 #ifdef USE_ITHREADS
2219     if (w32_num_pseudo_children) {
2220         win32_msgwait(aTHX_ w32_num_pseudo_children, w32_pseudo_child_handles,
2221                       timeout, &waitcode);
2222         /* Time out here if there are no other children to wait for. */
2223         if (waitcode == WAIT_TIMEOUT) {
2224             if (!w32_num_children) {
2225                 return 0;
2226             }
2227         }
2228         else if (waitcode != WAIT_FAILED) {
2229             if (waitcode >= WAIT_ABANDONED_0
2230                 && waitcode < WAIT_ABANDONED_0 + w32_num_pseudo_children)
2231                 i = waitcode - WAIT_ABANDONED_0;
2232             else
2233                 i = waitcode - WAIT_OBJECT_0;
2234             if (GetExitCodeThread(w32_pseudo_child_handles[i], &exitcode)) {
2235                 *status = (int)((exitcode & 0xff) << 8);
2236                 retval = (int)w32_pseudo_child_pids[i];
2237                 remove_dead_pseudo_process(i);
2238                 return -retval;
2239             }
2240         }
2241     }
2242 #endif
2243
2244     if (!w32_num_children) {
2245         errno = ECHILD;
2246         return -1;
2247     }
2248
2249     /* if a child exists, wait for it to die */
2250     win32_msgwait(aTHX_ w32_num_children, w32_child_handles, timeout, &waitcode);
2251     if (waitcode == WAIT_TIMEOUT) {
2252         return 0;
2253     }
2254     if (waitcode != WAIT_FAILED) {
2255         if (waitcode >= WAIT_ABANDONED_0
2256             && waitcode < WAIT_ABANDONED_0 + w32_num_children)
2257             i = waitcode - WAIT_ABANDONED_0;
2258         else
2259             i = waitcode - WAIT_OBJECT_0;
2260         if (GetExitCodeProcess(w32_child_handles[i], &exitcode) ) {
2261             *status = (int)((exitcode & 0xff) << 8);
2262             retval = (int)w32_child_pids[i];
2263             remove_dead_process(i);
2264             return retval;
2265         }
2266     }
2267
2268     errno = GetLastError();
2269     return -1;
2270 }
2271
2272 DllExport int
2273 win32_waitpid(int pid, int *status, int flags)
2274 {
2275     dTHX;
2276     DWORD timeout = (flags & WNOHANG) ? 0 : INFINITE;
2277     int retval = -1;
2278     long child;
2279     if (pid == -1)                              /* XXX threadid == 1 ? */
2280         return win32_internal_wait(status, timeout);
2281 #ifdef USE_ITHREADS
2282     else if (pid < 0) {
2283         child = find_pseudo_pid(-pid);
2284         if (child >= 0) {
2285             HANDLE hThread = w32_pseudo_child_handles[child];
2286             DWORD waitcode;
2287             win32_msgwait(aTHX_ 1, &hThread, timeout, &waitcode);
2288             if (waitcode == WAIT_TIMEOUT) {
2289                 return 0;
2290             }
2291             else if (waitcode == WAIT_OBJECT_0) {
2292                 if (GetExitCodeThread(hThread, &waitcode)) {
2293                     *status = (int)((waitcode & 0xff) << 8);
2294                     retval = (int)w32_pseudo_child_pids[child];
2295                     remove_dead_pseudo_process(child);
2296                     return -retval;
2297                 }
2298             }
2299             else
2300                 errno = ECHILD;
2301         }
2302         else if (IsWin95()) {
2303             pid = -pid;
2304             goto alien_process;
2305         }
2306     }
2307 #endif
2308     else {
2309         HANDLE hProcess;
2310         DWORD waitcode;
2311         child = find_pid(pid);
2312         if (child >= 0) {
2313             hProcess = w32_child_handles[child];
2314             win32_msgwait(aTHX_ 1, &hProcess, timeout, &waitcode);
2315             if (waitcode == WAIT_TIMEOUT) {
2316                 return 0;
2317             }
2318             else if (waitcode == WAIT_OBJECT_0) {
2319                 if (GetExitCodeProcess(hProcess, &waitcode)) {
2320                     *status = (int)((waitcode & 0xff) << 8);
2321                     retval = (int)w32_child_pids[child];
2322                     remove_dead_process(child);
2323                     return retval;
2324                 }
2325             }
2326             else
2327                 errno = ECHILD;
2328         }
2329         else {
2330 alien_process:
2331             hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE,
2332                                    (IsWin95() ? -pid : pid));
2333             if (hProcess) {
2334                 win32_msgwait(aTHX_ 1, &hProcess, timeout, &waitcode);
2335                 if (waitcode == WAIT_TIMEOUT) {
2336                     CloseHandle(hProcess);
2337                     return 0;
2338                 }
2339                 else if (waitcode == WAIT_OBJECT_0) {
2340                     if (GetExitCodeProcess(hProcess, &waitcode)) {
2341                         *status = (int)((waitcode & 0xff) << 8);
2342                         CloseHandle(hProcess);
2343                         return pid;
2344                     }
2345                 }
2346                 CloseHandle(hProcess);
2347             }
2348             else
2349                 errno = ECHILD;
2350         }
2351     }
2352     return retval >= 0 ? pid : retval;
2353 }
2354
2355 DllExport int
2356 win32_wait(int *status)
2357 {
2358     return win32_internal_wait(status, INFINITE);
2359 }
2360
2361 DllExport unsigned int
2362 win32_sleep(unsigned int t)
2363 {
2364     dTHX;
2365     /* Win32 times are in ms so *1000 in and /1000 out */
2366     return win32_msgwait(aTHX_ 0, NULL, t*1000, NULL)/1000;
2367 }
2368
2369 DllExport unsigned int
2370 win32_alarm(unsigned int sec)
2371 {
2372     /*
2373      * the 'obvious' implentation is SetTimer() with a callback
2374      * which does whatever receiving SIGALRM would do
2375      * we cannot use SIGALRM even via raise() as it is not
2376      * one of the supported codes in <signal.h>
2377      */
2378     dTHX;
2379
2380     if (w32_message_hwnd == INVALID_HANDLE_VALUE)
2381         w32_message_hwnd = win32_create_message_window();
2382
2383     if (sec) {
2384         if (w32_message_hwnd == NULL)
2385             w32_timerid = SetTimer(NULL, w32_timerid, sec*1000, NULL);
2386         else {
2387             w32_timerid = 1;
2388             SetTimer(w32_message_hwnd, w32_timerid, sec*1000, NULL);
2389         }
2390     }
2391     else {
2392         if (w32_timerid) {
2393             KillTimer(w32_message_hwnd, w32_timerid);
2394             w32_timerid = 0;
2395         }
2396     }
2397     return 0;
2398 }
2399
2400 #ifdef HAVE_DES_FCRYPT
2401 extern char *   des_fcrypt(const char *txt, const char *salt, char *cbuf);
2402 #endif
2403
2404 DllExport char *
2405 win32_crypt(const char *txt, const char *salt)
2406 {
2407     dTHX;
2408 #ifdef HAVE_DES_FCRYPT
2409     return des_fcrypt(txt, salt, w32_crypt_buffer);
2410 #else
2411     Perl_croak(aTHX_ "The crypt() function is unimplemented due to excessive paranoia.");
2412     return NULL;
2413 #endif
2414 }
2415
2416 #ifdef USE_FIXED_OSFHANDLE
2417
2418 #define FOPEN                   0x01    /* file handle open */
2419 #define FNOINHERIT              0x10    /* file handle opened O_NOINHERIT */
2420 #define FAPPEND                 0x20    /* file handle opened O_APPEND */
2421 #define FDEV                    0x40    /* file handle refers to device */
2422 #define FTEXT                   0x80    /* file handle is in text mode */
2423
2424 /***
2425 *int my_open_osfhandle(intptr_t osfhandle, int flags) - open C Runtime file handle
2426 *
2427 *Purpose:
2428 *       This function allocates a free C Runtime file handle and associates
2429 *       it with the Win32 HANDLE specified by the first parameter. This is a
2430 *       temperary fix for WIN95's brain damage GetFileType() error on socket
2431 *       we just bypass that call for socket
2432 *
2433 *       This works with MSVC++ 4.0+ or GCC/Mingw32
2434 *
2435 *Entry:
2436 *       intptr_t osfhandle - Win32 HANDLE to associate with C Runtime file handle.
2437 *       int flags      - flags to associate with C Runtime file handle.
2438 *
2439 *Exit:
2440 *       returns index of entry in fh, if successful
2441 *       return -1, if no free entry is found
2442 *
2443 *Exceptions:
2444 *
2445 *******************************************************************************/
2446
2447 /*
2448  * we fake up some parts of the CRT that aren't exported by MSVCRT.dll
2449  * this lets sockets work on Win9X with GCC and should fix the problems
2450  * with perl95.exe
2451  *      -- BKS, 1-23-2000
2452 */
2453
2454 /* create an ioinfo entry, kill its handle, and steal the entry */
2455
2456 static int
2457 _alloc_osfhnd(void)
2458 {
2459     HANDLE hF = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
2460     int fh = _open_osfhandle((intptr_t)hF, 0);
2461     CloseHandle(hF);
2462     if (fh == -1)
2463         return fh;
2464     EnterCriticalSection(&(_pioinfo(fh)->lock));
2465     return fh;
2466 }
2467
2468 static int
2469 my_open_osfhandle(intptr_t osfhandle, int flags)
2470 {
2471     int fh;
2472     char fileflags;             /* _osfile flags */
2473
2474     /* copy relevant flags from second parameter */
2475     fileflags = FDEV;
2476
2477     if (flags & O_APPEND)
2478         fileflags |= FAPPEND;
2479
2480     if (flags & O_TEXT)
2481         fileflags |= FTEXT;
2482
2483     if (flags & O_NOINHERIT)
2484         fileflags |= FNOINHERIT;
2485
2486     /* attempt to allocate a C Runtime file handle */
2487     if ((fh = _alloc_osfhnd()) == -1) {
2488         errno = EMFILE;         /* too many open files */
2489         _doserrno = 0L;         /* not an OS error */
2490         return -1;              /* return error to caller */
2491     }
2492
2493     /* the file is open. now, set the info in _osfhnd array */
2494     _set_osfhnd(fh, osfhandle);
2495
2496     fileflags |= FOPEN;         /* mark as open */
2497
2498     _osfile(fh) = fileflags;    /* set osfile entry */
2499     LeaveCriticalSection(&_pioinfo(fh)->lock);
2500
2501     return fh;                  /* return handle */
2502 }
2503
2504 #endif  /* USE_FIXED_OSFHANDLE */
2505
2506 /* simulate flock by locking a range on the file */
2507
2508 #define LK_LEN          0xffff0000
2509
2510 DllExport int
2511 win32_flock(int fd, int oper)
2512 {
2513     OVERLAPPED o;
2514     int i = -1;
2515     HANDLE fh;
2516
2517     if (!IsWinNT()) {
2518         dTHX;
2519         Perl_croak_nocontext("flock() unimplemented on this platform");
2520         return -1;
2521     }
2522     fh = (HANDLE)_get_osfhandle(fd);
2523     if (fh == (HANDLE)-1)  /* _get_osfhandle() already sets errno to EBADF */
2524         return -1;
2525
2526     memset(&o, 0, sizeof(o));
2527
2528     switch(oper) {
2529     case LOCK_SH:               /* shared lock */
2530         if (LockFileEx(fh, 0, 0, LK_LEN, 0, &o))
2531             i = 0;
2532         break;
2533     case LOCK_EX:               /* exclusive lock */
2534         if (LockFileEx(fh, LOCKFILE_EXCLUSIVE_LOCK, 0, LK_LEN, 0, &o))
2535             i = 0;
2536         break;
2537     case LOCK_SH|LOCK_NB:       /* non-blocking shared lock */
2538         if (LockFileEx(fh, LOCKFILE_FAIL_IMMEDIATELY, 0, LK_LEN, 0, &o))
2539             i = 0;
2540         break;
2541     case LOCK_EX|LOCK_NB:       /* non-blocking exclusive lock */
2542         if (LockFileEx(fh, LOCKFILE_EXCLUSIVE_LOCK|LOCKFILE_FAIL_IMMEDIATELY,
2543                        0, LK_LEN, 0, &o))
2544             i = 0;
2545         break;
2546     case LOCK_UN:               /* unlock lock */
2547         if (UnlockFileEx(fh, 0, LK_LEN, 0, &o))
2548             i = 0;
2549         break;
2550     default:                    /* unknown */
2551         errno = EINVAL;
2552         return -1;
2553     }
2554     if (i == -1) {
2555         if (GetLastError() == ERROR_LOCK_VIOLATION)
2556             errno = WSAEWOULDBLOCK;
2557         else
2558             errno = EINVAL;
2559     }
2560     return i;
2561 }
2562
2563 #undef LK_LEN
2564
2565 /*
2566  *  redirected io subsystem for all XS modules
2567  *
2568  */
2569
2570 DllExport int *
2571 win32_errno(void)
2572 {
2573     return (&errno);
2574 }
2575
2576 DllExport char ***
2577 win32_environ(void)
2578 {
2579     return (&(_environ));
2580 }
2581
2582 /* the rest are the remapped stdio routines */
2583 DllExport FILE *
2584 win32_stderr(void)
2585 {
2586     return (stderr);
2587 }
2588
2589 DllExport FILE *
2590 win32_stdin(void)
2591 {
2592     return (stdin);
2593 }
2594
2595 DllExport FILE *
2596 win32_stdout(void)
2597 {
2598     return (stdout);
2599 }
2600
2601 DllExport int
2602 win32_ferror(FILE *fp)
2603 {
2604     return (ferror(fp));
2605 }
2606
2607
2608 DllExport int
2609 win32_feof(FILE *fp)
2610 {
2611     return (feof(fp));
2612 }
2613
2614 /*
2615  * Since the errors returned by the socket error function
2616  * WSAGetLastError() are not known by the library routine strerror
2617  * we have to roll our own.
2618  */
2619
2620 DllExport char *
2621 win32_strerror(int e)
2622 {
2623 #if !defined __BORLANDC__ && !defined __MINGW32__      /* compiler intolerance */
2624     extern int sys_nerr;
2625 #endif
2626
2627     if (e < 0 || e > sys_nerr) {
2628         dTHX;
2629         if (e < 0)
2630             e = GetLastError();
2631
2632         if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
2633                          |FORMAT_MESSAGE_IGNORE_INSERTS, NULL, e, 0,
2634                           w32_strerror_buffer, sizeof(w32_strerror_buffer),
2635                           NULL) == 0)
2636         {
2637             strcpy(w32_strerror_buffer, "Unknown Error");
2638         }
2639         return w32_strerror_buffer;
2640     }
2641 #undef strerror
2642     return strerror(e);
2643 #define strerror win32_strerror
2644 }
2645
2646 DllExport void
2647 win32_str_os_error(void *sv, DWORD dwErr)
2648 {
2649     DWORD dwLen;
2650     char *sMsg;
2651     dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER
2652                           |FORMAT_MESSAGE_IGNORE_INSERTS
2653                           |FORMAT_MESSAGE_FROM_SYSTEM, NULL,
2654                            dwErr, 0, (char *)&sMsg, 1, NULL);
2655     /* strip trailing whitespace and period */
2656     if (0 < dwLen) {
2657         do {
2658             --dwLen;    /* dwLen doesn't include trailing null */
2659         } while (0 < dwLen && isSPACE(sMsg[dwLen]));
2660         if ('.' != sMsg[dwLen])
2661             dwLen++;
2662         sMsg[dwLen] = '\0';
2663     }
2664     if (0 == dwLen) {
2665         sMsg = (char*)LocalAlloc(0, 64/**sizeof(TCHAR)*/);
2666         if (sMsg)
2667             dwLen = sprintf(sMsg,
2668                             "Unknown error #0x%lX (lookup 0x%lX)",
2669                             dwErr, GetLastError());
2670     }
2671     if (sMsg) {
2672         dTHX;
2673         sv_setpvn((SV*)sv, sMsg, dwLen);
2674         LocalFree(sMsg);
2675     }
2676 }
2677
2678 DllExport int
2679 win32_fprintf(FILE *fp, const char *format, ...)
2680 {
2681     va_list marker;
2682     va_start(marker, format);     /* Initialize variable arguments. */
2683
2684     return (vfprintf(fp, format, marker));
2685 }
2686
2687 DllExport int
2688 win32_printf(const char *format, ...)
2689 {
2690     va_list marker;
2691     va_start(marker, format);     /* Initialize variable arguments. */
2692
2693     return (vprintf(format, marker));
2694 }
2695
2696 DllExport int
2697 win32_vfprintf(FILE *fp, const char *format, va_list args)
2698 {
2699     return (vfprintf(fp, format, args));
2700 }
2701
2702 DllExport int
2703 win32_vprintf(const char *format, va_list args)
2704 {
2705     return (vprintf(format, args));
2706 }
2707
2708 DllExport size_t
2709 win32_fread(void *buf, size_t size, size_t count, FILE *fp)
2710 {
2711     return fread(buf, size, count, fp);
2712 }
2713
2714 DllExport size_t
2715 win32_fwrite(const void *buf, size_t size, size_t count, FILE *fp)
2716 {
2717     return fwrite(buf, size, count, fp);
2718 }
2719
2720 #define MODE_SIZE 10
2721
2722 DllExport FILE *
2723 win32_fopen(const char *filename, const char *mode)
2724 {
2725     dTHX;
2726     FILE *f;
2727
2728     if (!*filename)
2729         return NULL;
2730
2731     if (stricmp(filename, "/dev/null")==0)
2732         filename = "NUL";
2733
2734     f = fopen(PerlDir_mapA(filename), mode);
2735     /* avoid buffering headaches for child processes */
2736     if (f && *mode == 'a')
2737         win32_fseek(f, 0, SEEK_END);
2738     return f;
2739 }
2740
2741 #ifndef USE_SOCKETS_AS_HANDLES
2742 #undef fdopen
2743 #define fdopen my_fdopen
2744 #endif
2745
2746 DllExport FILE *
2747 win32_fdopen(int handle, const char *mode)
2748 {
2749     dTHX;
2750     FILE *f;
2751     f = fdopen(handle, (char *) mode);
2752     /* avoid buffering headaches for child processes */
2753     if (f && *mode == 'a')
2754         win32_fseek(f, 0, SEEK_END);
2755     return f;
2756 }
2757
2758 DllExport FILE *
2759 win32_freopen(const char *path, const char *mode, FILE *stream)
2760 {
2761     dTHX;
2762     if (stricmp(path, "/dev/null")==0)
2763         path = "NUL";
2764
2765     return freopen(PerlDir_mapA(path), mode, stream);
2766 }
2767
2768 DllExport int
2769 win32_fclose(FILE *pf)
2770 {
2771     return my_fclose(pf);       /* defined in win32sck.c */
2772 }
2773
2774 DllExport int
2775 win32_fputs(const char *s,FILE *pf)
2776 {
2777     return fputs(s, pf);
2778 }
2779
2780 DllExport int
2781 win32_fputc(int c,FILE *pf)
2782 {
2783     return fputc(c,pf);
2784 }
2785
2786 DllExport int
2787 win32_ungetc(int c,FILE *pf)
2788 {
2789     return ungetc(c,pf);
2790 }
2791
2792 DllExport int
2793 win32_getc(FILE *pf)
2794 {
2795     return getc(pf);
2796 }
2797
2798 DllExport int
2799 win32_fileno(FILE *pf)
2800 {
2801     return fileno(pf);
2802 }
2803
2804 DllExport void
2805 win32_clearerr(FILE *pf)
2806 {
2807     clearerr(pf);
2808     return;
2809 }
2810
2811 DllExport int
2812 win32_fflush(FILE *pf)
2813 {
2814     return fflush(pf);
2815 }
2816
2817 DllExport Off_t
2818 win32_ftell(FILE *pf)
2819 {
2820 #if defined(WIN64) || defined(USE_LARGE_FILES)
2821 #if defined(__BORLANDC__) /* buk */
2822     return win32_tell( fileno( pf ) );
2823 #else
2824     fpos_t pos;
2825     if (fgetpos(pf, &pos))
2826         return -1;
2827     return (Off_t)pos;
2828 #endif
2829 #else
2830     return ftell(pf);
2831 #endif
2832 }
2833
2834 DllExport int
2835 win32_fseek(FILE *pf, Off_t offset,int origin)
2836 {
2837 #if defined(WIN64) || defined(USE_LARGE_FILES)
2838 #if defined(__BORLANDC__) /* buk */
2839     return win32_lseek(
2840         fileno(pf),
2841         offset,
2842         origin
2843         );
2844 #else
2845     fpos_t pos;
2846     switch (origin) {
2847     case SEEK_CUR:
2848         if (fgetpos(pf, &pos))
2849             return -1;
2850         offset += pos;
2851         break;
2852     case SEEK_END:
2853         fseek(pf, 0, SEEK_END);
2854         pos = _telli64(fileno(pf));
2855         offset += pos;
2856         break;
2857     case SEEK_SET:
2858         break;
2859     default:
2860         errno = EINVAL;
2861         return -1;
2862     }
2863     return fsetpos(pf, &offset);
2864 #endif
2865 #else
2866     return fseek(pf, (long)offset, origin);
2867 #endif
2868 }
2869
2870 DllExport int
2871 win32_fgetpos(FILE *pf,fpos_t *p)
2872 {
2873 #if defined(__BORLANDC__) && defined(USE_LARGE_FILES) /* buk */
2874     if( win32_tell(fileno(pf)) == -1L ) {
2875         errno = EBADF;
2876         return -1;
2877     }
2878     return 0;
2879 #else
2880     return fgetpos(pf, p);
2881 #endif
2882 }
2883
2884 DllExport int
2885 win32_fsetpos(FILE *pf,const fpos_t *p)
2886 {
2887 #if defined(__BORLANDC__) && defined(USE_LARGE_FILES) /* buk */
2888     return win32_lseek(fileno(pf), *p, SEEK_CUR);
2889 #else
2890     return fsetpos(pf, p);
2891 #endif
2892 }
2893
2894 DllExport void
2895 win32_rewind(FILE *pf)
2896 {
2897     rewind(pf);
2898     return;
2899 }
2900
2901 DllExport int
2902 win32_tmpfd(void)
2903 {
2904     dTHX;
2905     char prefix[MAX_PATH+1];
2906     char filename[MAX_PATH+1];
2907     DWORD len = GetTempPath(MAX_PATH, prefix);
2908     if (len && len < MAX_PATH) {
2909         if (GetTempFileName(prefix, "plx", 0, filename)) {
2910             HANDLE fh = CreateFile(filename,
2911                                    DELETE | GENERIC_READ | GENERIC_WRITE,
2912                                    0,
2913                                    NULL,
2914                                    CREATE_ALWAYS,
2915                                    FILE_ATTRIBUTE_NORMAL
2916                                    | FILE_FLAG_DELETE_ON_CLOSE,
2917                                    NULL);
2918             if (fh != INVALID_HANDLE_VALUE) {
2919                 int fd = win32_open_osfhandle((intptr_t)fh, 0);
2920                 if (fd >= 0) {
2921 #if defined(__BORLANDC__)
2922                     setmode(fd,O_BINARY);
2923 #endif
2924                     DEBUG_p(PerlIO_printf(Perl_debug_log,
2925                                           "Created tmpfile=%s\n",filename));
2926                     return fd;
2927                 }
2928             }
2929         }
2930     }
2931     return -1;
2932 }
2933
2934 DllExport FILE*
2935 win32_tmpfile(void)
2936 {
2937     int fd = win32_tmpfd();
2938     if (fd >= 0)
2939         return win32_fdopen(fd, "w+b");
2940     return NULL;
2941 }
2942
2943 DllExport void
2944 win32_abort(void)
2945 {
2946     abort();
2947     return;
2948 }
2949
2950 DllExport int
2951 win32_fstat(int fd, Stat_t *sbufptr)
2952 {
2953 #ifdef __BORLANDC__
2954     /* A file designated by filehandle is not shown as accessible
2955      * for write operations, probably because it is opened for reading.
2956      * --Vadim Konovalov
2957      */
2958     BY_HANDLE_FILE_INFORMATION bhfi;
2959 #if defined(WIN64) || defined(USE_LARGE_FILES)    
2960     /* Borland 5.5.1 has a 64-bit stat, but only a 32-bit fstat */
2961     struct stat tmp;
2962     int rc = fstat(fd,&tmp);
2963    
2964     sbufptr->st_dev   = tmp.st_dev;
2965     sbufptr->st_ino   = tmp.st_ino;
2966     sbufptr->st_mode  = tmp.st_mode;
2967     sbufptr->st_nlink = tmp.st_nlink;
2968     sbufptr->st_uid   = tmp.st_uid;
2969     sbufptr->st_gid   = tmp.st_gid;
2970     sbufptr->st_rdev  = tmp.st_rdev;
2971     sbufptr->st_size  = tmp.st_size;
2972     sbufptr->st_atime = tmp.st_atime;
2973     sbufptr->st_mtime = tmp.st_mtime;
2974     sbufptr->st_ctime = tmp.st_ctime;
2975 #else
2976     int rc = fstat(fd,sbufptr);
2977 #endif       
2978
2979     if (GetFileInformationByHandle((HANDLE)_get_osfhandle(fd), &bhfi)) {
2980 #if defined(WIN64) || defined(USE_LARGE_FILES)    
2981         sbufptr->st_size = ((__int64)bhfi.nFileSizeHigh << 32) | bhfi.nFileSizeLow ;
2982 #endif
2983         sbufptr->st_mode &= 0xFE00;
2984         if (bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
2985             sbufptr->st_mode |= (S_IREAD + (S_IREAD >> 3) + (S_IREAD >> 6));
2986         else
2987             sbufptr->st_mode |= ((S_IREAD|S_IWRITE) + ((S_IREAD|S_IWRITE) >> 3)
2988               + ((S_IREAD|S_IWRITE) >> 6));
2989     }
2990     return rc;
2991 #else
2992     return my_fstat(fd,sbufptr);
2993 #endif
2994 }
2995
2996 DllExport int
2997 win32_pipe(int *pfd, unsigned int size, int mode)
2998 {
2999     return _pipe(pfd, size, mode);
3000 }
3001
3002 DllExport PerlIO*
3003 win32_popenlist(const char *mode, IV narg, SV **args)
3004 {
3005  dTHX;
3006  Perl_croak(aTHX_ "List form of pipe open not implemented");
3007  return NULL;
3008 }
3009
3010 /*
3011  * a popen() clone that respects PERL5SHELL
3012  *
3013  * changed to return PerlIO* rather than FILE * by BKS, 11-11-2000
3014  */
3015
3016 DllExport PerlIO*
3017 win32_popen(const char *command, const char *mode)
3018 {
3019 #ifdef USE_RTL_POPEN
3020     return _popen(command, mode);
3021 #else
3022     dTHX;
3023     int p[2];
3024     int parent, child;
3025     int stdfd, oldfd;
3026     int ourmode;
3027     int childpid;
3028     DWORD nhandle;
3029     HANDLE old_h;
3030     int lock_held = 0;
3031
3032     /* establish which ends read and write */
3033     if (strchr(mode,'w')) {
3034         stdfd = 0;              /* stdin */
3035         parent = 1;
3036         child = 0;
3037         nhandle = STD_INPUT_HANDLE;
3038     }
3039     else if (strchr(mode,'r')) {
3040         stdfd = 1;              /* stdout */
3041         parent = 0;
3042         child = 1;
3043         nhandle = STD_OUTPUT_HANDLE;
3044     }
3045     else
3046         return NULL;
3047
3048     /* set the correct mode */
3049     if (strchr(mode,'b'))
3050         ourmode = O_BINARY;
3051     else if (strchr(mode,'t'))
3052         ourmode = O_TEXT;
3053     else
3054         ourmode = _fmode & (O_TEXT | O_BINARY);
3055
3056     /* the child doesn't inherit handles */
3057     ourmode |= O_NOINHERIT;
3058
3059     if (win32_pipe(p, 512, ourmode) == -1)
3060         return NULL;
3061
3062     /* save the old std handle (this needs to happen before the
3063      * dup2(), since that might call SetStdHandle() too) */
3064     OP_REFCNT_LOCK;
3065     lock_held = 1;
3066     old_h = GetStdHandle(nhandle);
3067
3068     /* save current stdfd */
3069     if ((oldfd = win32_dup(stdfd)) == -1)
3070         goto cleanup;
3071
3072     /* make stdfd go to child end of pipe (implicitly closes stdfd) */
3073     /* stdfd will be inherited by the child */
3074     if (win32_dup2(p[child], stdfd) == -1)
3075         goto cleanup;
3076
3077     /* close the child end in parent */
3078     win32_close(p[child]);
3079
3080     /* set the new std handle (in case dup2() above didn't) */
3081     SetStdHandle(nhandle, (HANDLE)_get_osfhandle(stdfd));
3082
3083     /* start the child */
3084     {
3085         dTHX;
3086         if ((childpid = do_spawn_nowait((char*)command)) == -1)
3087             goto cleanup;
3088
3089         /* revert stdfd to whatever it was before */
3090         if (win32_dup2(oldfd, stdfd) == -1)
3091             goto cleanup;
3092
3093         /* close saved handle */
3094         win32_close(oldfd);
3095
3096         /* restore the old std handle (this needs to happen after the
3097          * dup2(), since that might call SetStdHandle() too */
3098         if (lock_held) {
3099             SetStdHandle(nhandle, old_h);
3100             OP_REFCNT_UNLOCK;
3101             lock_held = 0;
3102         }
3103
3104         sv_setiv(*av_fetch(w32_fdpid, p[parent], TRUE), childpid);
3105
3106         /* set process id so that it can be returned by perl's open() */
3107         PL_forkprocess = childpid;
3108     }
3109
3110     /* we have an fd, return a file stream */
3111     return (PerlIO_fdopen(p[parent], (char *)mode));
3112
3113 cleanup:
3114     /* we don't need to check for errors here */
3115     win32_close(p[0]);
3116     win32_close(p[1]);
3117     if (oldfd != -1) {
3118         win32_dup2(oldfd, stdfd);
3119         win32_close(oldfd);
3120     }
3121     if (lock_held) {
3122         SetStdHandle(nhandle, old_h);
3123         OP_REFCNT_UNLOCK;
3124         lock_held = 0;
3125     }
3126     return (NULL);
3127
3128 #endif /* USE_RTL_POPEN */
3129 }
3130
3131 /*
3132  * pclose() clone
3133  */
3134
3135 DllExport int
3136 win32_pclose(PerlIO *pf)
3137 {
3138 #ifdef USE_RTL_POPEN
3139     return _pclose(pf);
3140 #else
3141     dTHX;
3142     int childpid, status;
3143     SV *sv;
3144
3145     sv = *av_fetch(w32_fdpid, PerlIO_fileno(pf), TRUE);
3146
3147     if (SvIOK(sv))
3148         childpid = SvIVX(sv);
3149     else
3150         childpid = 0;
3151
3152     if (!childpid) {
3153         errno = EBADF;
3154         return -1;
3155     }
3156
3157 #ifdef USE_PERLIO
3158     PerlIO_close(pf);
3159 #else
3160     fclose(pf);
3161 #endif
3162     SvIVX(sv) = 0;
3163
3164     if (win32_waitpid(childpid, &status, 0) == -1)
3165         return -1;
3166
3167     return status;
3168
3169 #endif /* USE_RTL_POPEN */
3170 }
3171
3172 static BOOL WINAPI
3173 Nt4CreateHardLinkW(
3174     LPCWSTR lpFileName,
3175     LPCWSTR lpExistingFileName,
3176     LPSECURITY_ATTRIBUTES lpSecurityAttributes)
3177 {
3178     HANDLE handle;
3179     WCHAR wFullName[MAX_PATH+1];
3180     LPVOID lpContext = NULL;
3181     WIN32_STREAM_ID StreamId;
3182     DWORD dwSize = (char*)&StreamId.cStreamName - (char*)&StreamId;
3183     DWORD dwWritten;
3184     DWORD dwLen;
3185     BOOL bSuccess;
3186
3187     BOOL (__stdcall *pfnBackupWrite)(HANDLE, LPBYTE, DWORD, LPDWORD,
3188                                      BOOL, BOOL, LPVOID*) =
3189         (BOOL (__stdcall *)(HANDLE, LPBYTE, DWORD, LPDWORD,
3190                             BOOL, BOOL, LPVOID*))
3191         GetProcAddress(GetModuleHandle("kernel32.dll"), "BackupWrite");
3192     if (pfnBackupWrite == NULL)
3193         return 0;
3194
3195     dwLen = GetFullPathNameW(lpFileName, MAX_PATH, wFullName, NULL);
3196     if (dwLen == 0)
3197         return 0;
3198     dwLen = (dwLen+1)*sizeof(WCHAR);
3199
3200     handle = CreateFileW(lpExistingFileName, FILE_WRITE_ATTRIBUTES,
3201                          FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
3202                          NULL, OPEN_EXISTING, 0, NULL);
3203     if (handle == INVALID_HANDLE_VALUE)
3204         return 0;
3205
3206     StreamId.dwStreamId = BACKUP_LINK;
3207     StreamId.dwStreamAttributes = 0;
3208     StreamId.dwStreamNameSize = 0;
3209 #if defined(__BORLANDC__) \
3210  ||(defined(__MINGW32__) && !defined(_ANONYMOUS_UNION))
3211     StreamId.Size.u.HighPart = 0;
3212     StreamId.Size.u.LowPart = dwLen;
3213 #else
3214     StreamId.Size.HighPart = 0;
3215     StreamId.Size.LowPart = dwLen;
3216 #endif
3217
3218     bSuccess = pfnBackupWrite(handle, (LPBYTE)&StreamId, dwSize, &dwWritten,
3219                               FALSE, FALSE, &lpContext);
3220     if (bSuccess) {
3221         bSuccess = pfnBackupWrite(handle, (LPBYTE)wFullName, dwLen, &dwWritten,
3222                                   FALSE, FALSE, &lpContext);
3223         pfnBackupWrite(handle, NULL, 0, &dwWritten, TRUE, FALSE, &lpContext);
3224     }
3225
3226     CloseHandle(handle);
3227     return bSuccess;
3228 }
3229
3230 DllExport int
3231 win32_link(const char *oldname, const char *newname)
3232 {
3233     dTHX;
3234     BOOL (__stdcall *pfnCreateHardLinkW)(LPCWSTR,LPCWSTR,LPSECURITY_ATTRIBUTES);
3235     WCHAR wOldName[MAX_PATH+1];
3236     WCHAR wNewName[MAX_PATH+1];
3237
3238     if (IsWin95())
3239         Perl_croak(aTHX_ PL_no_func, "link");
3240
3241     pfnCreateHardLinkW =
3242         (BOOL (__stdcall *)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES))
3243         GetProcAddress(GetModuleHandle("kernel32.dll"), "CreateHardLinkW");
3244     if (pfnCreateHardLinkW == NULL)
3245         pfnCreateHardLinkW = Nt4CreateHardLinkW;
3246
3247     if (MultiByteToWideChar(CP_ACP, 0, oldname, -1, wOldName, MAX_PATH+1) &&
3248         MultiByteToWideChar(CP_ACP, 0, newname, -1, wNewName, MAX_PATH+1) &&
3249         (wcscpy(wOldName, PerlDir_mapW(wOldName)),
3250         pfnCreateHardLinkW(PerlDir_mapW(wNewName), wOldName, NULL)))
3251     {
3252         return 0;
3253     }
3254     errno = (GetLastError() == ERROR_FILE_NOT_FOUND) ? ENOENT : EINVAL;
3255     return -1;
3256 }
3257
3258 DllExport int
3259 win32_rename(const char *oname, const char *newname)
3260 {
3261     char szOldName[MAX_PATH+1];
3262     char szNewName[MAX_PATH+1];
3263     BOOL bResult;
3264     dTHX;
3265
3266     /* XXX despite what the documentation says about MoveFileEx(),
3267      * it doesn't work under Windows95!
3268      */
3269     if (IsWinNT()) {
3270         DWORD dwFlags = MOVEFILE_COPY_ALLOWED;
3271         if (stricmp(newname, oname))
3272             dwFlags |= MOVEFILE_REPLACE_EXISTING;
3273         strcpy(szOldName, PerlDir_mapA(oname));
3274         bResult = MoveFileExA(szOldName,PerlDir_mapA(newname), dwFlags);
3275         if (!bResult) {
3276             DWORD err = GetLastError();
3277             switch (err) {
3278             case ERROR_BAD_NET_NAME:
3279             case ERROR_BAD_NETPATH:
3280             case ERROR_BAD_PATHNAME:
3281             case ERROR_FILE_NOT_FOUND:
3282             case ERROR_FILENAME_EXCED_RANGE:
3283             case ERROR_INVALID_DRIVE:
3284             case ERROR_NO_MORE_FILES:
3285             case ERROR_PATH_NOT_FOUND:
3286                 errno = ENOENT;
3287                 break;
3288             default:
3289                 errno = EACCES;
3290                 break;
3291             }
3292             return -1;
3293         }
3294         return 0;
3295     }
3296     else {
3297         int retval = 0;
3298         char szTmpName[MAX_PATH+1];
3299         char dname[MAX_PATH+1];
3300         char *endname = NULL;
3301         STRLEN tmplen = 0;
3302         DWORD from_attr, to_attr;
3303
3304         strcpy(szOldName, PerlDir_mapA(oname));
3305         strcpy(szNewName, PerlDir_mapA(newname));
3306
3307         /* if oname doesn't exist, do nothing */
3308         from_attr = GetFileAttributes(szOldName);
3309         if (from_attr == 0xFFFFFFFF) {
3310             errno = ENOENT;
3311             return -1;
3312         }
3313
3314         /* if newname exists, rename it to a temporary name so that we
3315          * don't delete it in case oname happens to be the same file
3316          * (but perhaps accessed via a different path)
3317          */
3318         to_attr = GetFileAttributes(szNewName);
3319         if (to_attr != 0xFFFFFFFF) {
3320             /* if newname is a directory, we fail
3321              * XXX could overcome this with yet more convoluted logic */
3322             if (to_attr & FILE_ATTRIBUTE_DIRECTORY) {
3323                 errno = EACCES;
3324                 return -1;
3325             }
3326             tmplen = strlen(szNewName);
3327             strcpy(szTmpName,szNewName);
3328             endname = szTmpName+tmplen;
3329             for (; endname > szTmpName ; --endname) {
3330                 if (*endname == '/' || *endname == '\\') {
3331                     *endname = '\0';
3332                     break;
3333                 }
3334             }
3335             if (endname > szTmpName)
3336                 endname = strcpy(dname,szTmpName);
3337             else
3338                 endname = ".";
3339
3340             /* get a temporary filename in same directory
3341              * XXX is this really the best we can do? */
3342             if (!GetTempFileName((LPCTSTR)endname, "plr", 0, szTmpName)) {
3343                 errno = ENOENT;
3344                 return -1;
3345             }
3346             DeleteFile(szTmpName);
3347
3348             retval = rename(szNewName, szTmpName);
3349             if (retval != 0) {
3350                 errno = EACCES;
3351                 return retval;
3352             }
3353         }
3354
3355         /* rename oname to newname */
3356         retval = rename(szOldName, szNewName);
3357
3358         /* if we created a temporary file before ... */
3359         if (endname != NULL) {
3360             /* ...and rename succeeded, delete temporary file/directory */
3361             if (retval == 0)
3362                 DeleteFile(szTmpName);
3363             /* else restore it to what it was */
3364             else
3365                 (void)rename(szTmpName, szNewName);
3366         }
3367         return retval;
3368     }
3369 }
3370
3371 DllExport int
3372 win32_setmode(int fd, int mode)
3373 {
3374     return setmode(fd, mode);
3375 }
3376
3377 DllExport int
3378 win32_chsize(int fd, Off_t size)
3379 {
3380 #if defined(WIN64) || defined(USE_LARGE_FILES)
3381     int retval = 0;
3382     Off_t cur, end, extend;
3383
3384     cur = win32_tell(fd);
3385     if (cur < 0)
3386         return -1;
3387     end = win32_lseek(fd, 0, SEEK_END);
3388     if (end < 0)
3389         return -1;
3390     extend = size - end;
3391     if (extend == 0) {
3392         /* do nothing */
3393     }
3394     else if (extend > 0) {
3395         /* must grow the file, padding with nulls */
3396         char b[4096];
3397         int oldmode = win32_setmode(fd, O_BINARY);
3398         size_t count;
3399         memset(b, '\0', sizeof(b));
3400         do {
3401             count = extend >= sizeof(b) ? sizeof(b) : (size_t)extend;
3402             count = win32_write(fd, b, count);
3403             if ((int)count < 0) {
3404                 retval = -1;
3405                 break;
3406             }
3407         } while ((extend -= count) > 0);
3408         win32_setmode(fd, oldmode);
3409     }
3410     else {
3411         /* shrink the file */
3412         win32_lseek(fd, size, SEEK_SET);
3413         if (!SetEndOfFile((HANDLE)_get_osfhandle(fd))) {
3414             errno = EACCES;
3415             retval = -1;
3416         }
3417     }
3418 finish:
3419     win32_lseek(fd, cur, SEEK_SET);
3420     return retval;
3421 #else
3422     return chsize(fd, (long)size);
3423 #endif
3424 }
3425
3426 DllExport Off_t
3427 win32_lseek(int fd, Off_t offset, int origin)
3428 {
3429 #if defined(WIN64) || defined(USE_LARGE_FILES)
3430 #if defined(__BORLANDC__) /* buk */
3431     LARGE_INTEGER pos;
3432     pos.QuadPart = offset;
3433     pos.LowPart = SetFilePointer(
3434         (HANDLE)_get_osfhandle(fd),
3435         pos.LowPart,
3436         &pos.HighPart,
3437         origin
3438     );
3439     if (pos.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) {
3440         pos.QuadPart = -1;
3441     }
3442
3443     return pos.QuadPart;
3444 #else
3445     return _lseeki64(fd, offset, origin);
3446 #endif
3447 #else
3448     return lseek(fd, (long)offset, origin);
3449 #endif
3450 }
3451
3452 DllExport Off_t
3453 win32_tell(int fd)
3454 {
3455 #if defined(WIN64) || defined(USE_LARGE_FILES)
3456 #if defined(__BORLANDC__) /* buk */
3457     LARGE_INTEGER pos;
3458     pos.QuadPart = 0;
3459     pos.LowPart = SetFilePointer(
3460         (HANDLE)_get_osfhandle(fd),
3461         pos.LowPart,
3462         &pos.HighPart,
3463         FILE_CURRENT
3464     );
3465     if (pos.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) {
3466         pos.QuadPart = -1;
3467     }
3468
3469     return pos.QuadPart;
3470     /* return tell(fd); */
3471 #else
3472     return _telli64(fd);
3473 #endif
3474 #else
3475     return tell(fd);
3476 #endif
3477 }
3478
3479 DllExport int
3480 win32_open(const char *path, int flag, ...)
3481 {
3482     dTHX;
3483     va_list ap;
3484     int pmode;
3485
3486     va_start(ap, flag);
3487     pmode = va_arg(ap, int);
3488     va_end(ap);
3489
3490     if (stricmp(path, "/dev/null")==0)
3491         path = "NUL";
3492
3493     return open(PerlDir_mapA(path), flag, pmode);
3494 }
3495
3496 /* close() that understands socket */
3497 extern int my_close(int);       /* in win32sck.c */
3498
3499 DllExport int
3500 win32_close(int fd)
3501 {
3502     return my_close(fd);
3503 }
3504
3505 DllExport int
3506 win32_eof(int fd)
3507 {
3508     return eof(fd);
3509 }
3510
3511 DllExport int
3512 win32_isatty(int fd)
3513 {
3514     /* The Microsoft isatty() function returns true for *all*
3515      * character mode devices, including "nul".  Our implementation
3516      * should only return true if the handle has a console buffer.
3517      */
3518     DWORD mode;
3519     HANDLE fh = (HANDLE)_get_osfhandle(fd);
3520     if (fh == (HANDLE)-1) {
3521         /* errno is already set to EBADF */
3522         return 0;
3523     }
3524
3525     if (GetConsoleMode(fh, &mode))
3526         return 1;
3527
3528     errno = ENOTTY;
3529     return 0;
3530 }
3531
3532 DllExport int
3533 win32_dup(int fd)
3534 {
3535     return dup(fd);
3536 }
3537
3538 DllExport int
3539 win32_dup2(int fd1,int fd2)
3540 {
3541     return dup2(fd1,fd2);
3542 }
3543
3544 #ifdef PERL_MSVCRT_READFIX
3545
3546 #define LF              10      /* line feed */
3547 #define CR              13      /* carriage return */
3548 #define CTRLZ           26      /* ctrl-z means eof for text */
3549 #define FOPEN           0x01    /* file handle open */
3550 #define FEOFLAG         0x02    /* end of file has been encountered */
3551 #define FCRLF           0x04    /* CR-LF across read buffer (in text mode) */
3552 #define FPIPE           0x08    /* file handle refers to a pipe */
3553 #define FAPPEND         0x20    /* file handle opened O_APPEND */
3554 #define FDEV            0x40    /* file handle refers to device */
3555 #define FTEXT           0x80    /* file handle is in text mode */
3556 #define MAX_DESCRIPTOR_COUNT    (64*32) /* this is the maximun that MSVCRT can handle */
3557
3558 int __cdecl
3559 _fixed_read(int fh, void *buf, unsigned cnt)
3560 {
3561     int bytes_read;                 /* number of bytes read */
3562     char *buffer;                   /* buffer to read to */
3563     int os_read;                    /* bytes read on OS call */
3564     char *p, *q;                    /* pointers into buffer */
3565     char peekchr;                   /* peek-ahead character */
3566     ULONG filepos;                  /* file position after seek */
3567     ULONG dosretval;                /* o.s. return value */
3568
3569     /* validate handle */
3570     if (((unsigned)fh >= (unsigned)MAX_DESCRIPTOR_COUNT) ||
3571          !(_osfile(fh) & FOPEN))
3572     {
3573         /* out of range -- return error */
3574         errno = EBADF;
3575         _doserrno = 0;  /* not o.s. error */
3576         return -1;
3577     }
3578
3579     /*
3580      * If lockinitflag is FALSE, assume fd is device
3581      * lockinitflag is set to TRUE by open.
3582      */
3583     if (_pioinfo(fh)->lockinitflag)
3584         EnterCriticalSection(&(_pioinfo(fh)->lock));  /* lock file */
3585
3586     bytes_read = 0;                 /* nothing read yet */
3587     buffer = (char*)buf;
3588
3589     if (cnt == 0 || (_osfile(fh) & FEOFLAG)) {
3590         /* nothing to read or at EOF, so return 0 read */
3591         goto functionexit;
3592     }
3593
3594     if ((_osfile(fh) & (FPIPE|FDEV)) && _pipech(fh) != LF) {
3595         /* a pipe/device and pipe lookahead non-empty: read the lookahead
3596          * char */
3597         *buffer++ = _pipech(fh);
3598         ++bytes_read;
3599         --cnt;
3600         _pipech(fh) = LF;           /* mark as empty */
3601     }
3602
3603     /* read the data */
3604
3605     if (!ReadFile((HANDLE)_osfhnd(fh), buffer, cnt, (LPDWORD)&os_read, NULL))
3606     {
3607         /* ReadFile has reported an error. recognize two special cases.
3608          *
3609          *      1. map ERROR_ACCESS_DENIED to EBADF
3610          *
3611          *      2. just return 0 if ERROR_BROKEN_PIPE has occurred. it
3612          *         means the handle is a read-handle on a pipe for which
3613          *         all write-handles have been closed and all data has been
3614          *         read. */
3615
3616         if ((dosretval = GetLastError()) == ERROR_ACCESS_DENIED) {
3617             /* wrong read/write mode should return EBADF, not EACCES */
3618             errno = EBADF;
3619             _doserrno = dosretval;
3620             bytes_read = -1;
3621             goto functionexit;
3622         }
3623         else if (dosretval == ERROR_BROKEN_PIPE) {
3624             bytes_read = 0;
3625             goto functionexit;
3626         }
3627         else {
3628             bytes_read = -1;
3629             goto functionexit;
3630         }
3631     }
3632
3633     bytes_read += os_read;          /* update bytes read */
3634
3635     if (_osfile(fh) & FTEXT) {
3636         /* now must translate CR-LFs to LFs in the buffer */
3637
3638         /* set CRLF flag to indicate LF at beginning of buffer */
3639         /* if ((os_read != 0) && (*(char *)buf == LF))   */
3640         /*    _osfile(fh) |= FCRLF;                      */
3641         /* else                                          */
3642         /*    _osfile(fh) &= ~FCRLF;                     */
3643
3644         _osfile(fh) &= ~FCRLF;
3645
3646         /* convert chars in the buffer: p is src, q is dest */
3647         p = q = (char*)buf;
3648         while (p < (char *)buf + bytes_read) {
3649             if (*p == CTRLZ) {
3650                 /* if fh is not a device, set ctrl-z flag */
3651                 if (!(_osfile(fh) & FDEV))
3652                     _osfile(fh) |= FEOFLAG;
3653                 break;              /* stop translating */
3654             }
3655             else if (*p != CR)
3656                 *q++ = *p++;
3657             else {
3658                 /* *p is CR, so must check next char for LF */
3659                 if (p < (char *)buf + bytes_read - 1) {
3660                     if (*(p+1) == LF) {
3661                         p += 2;
3662                         *q++ = LF;  /* convert CR-LF to LF */
3663                     }
3664                     else
3665                         *q++ = *p++;    /* store char normally */
3666                 }
3667                 else {
3668                     /* This is the hard part.  We found a CR at end of
3669                        buffer.  We must peek ahead to see if next char
3670                        is an LF. */
3671                     ++p;
3672
3673                     dosretval = 0;
3674                     if (!ReadFile((HANDLE)_osfhnd(fh), &peekchr, 1,
3675                                     (LPDWORD)&os_read, NULL))
3676                         dosretval = GetLastError();
3677
3678                     if (dosretval != 0 || os_read == 0) {
3679                         /* couldn't read ahead, store CR */
3680                         *q++ = CR;
3681                     }
3682                     else {
3683                         /* peekchr now has the extra character -- we now
3684                            have several possibilities:
3685                            1. disk file and char is not LF; just seek back
3686                               and copy CR
3687                            2. disk file and char is LF; store LF, don't seek back
3688                            3. pipe/device and char is LF; store LF.
3689                            4. pipe/device and char isn't LF, store CR and
3690                               put char in pipe lookahead buffer. */
3691                         if (_osfile(fh) & (FDEV|FPIPE)) {
3692                             /* non-seekable device */
3693                             if (peekchr == LF)
3694                                 *q++ = LF;
3695                             else {
3696                                 *q++ = CR;
3697                                 _pipech(fh) = peekchr;
3698                             }
3699                         }
3700                         else {
3701                             /* disk file */
3702                             if (peekchr == LF) {
3703                                 /* nothing read yet; must make some
3704                                    progress */
3705                                 *q++ = LF;
3706                                 /* turn on this flag for tell routine */
3707                                 _osfile(fh) |= FCRLF;
3708                             }
3709                             else {
3710                                 HANDLE osHandle;        /* o.s. handle value */
3711                                 /* seek back */
3712                                 if ((osHandle = (HANDLE)_get_osfhandle(fh)) != (HANDLE)-1)
3713                                 {
3714                                     if ((filepos = SetFilePointer(osHandle, -1, NULL, FILE_CURRENT)) == -1)
3715                                         dosretval = GetLastError();
3716                                 }
3717                                 if (peekchr != LF)
3718                                     *q++ = CR;
3719                             }
3720                         }
3721                     }
3722                 }
3723             }
3724         }
3725
3726         /* we now change bytes_read to reflect the true number of chars
3727            in the buffer */
3728         bytes_read = q - (char *)buf;
3729     }
3730
3731 functionexit:
3732     if (_pioinfo(fh)->lockinitflag)
3733         LeaveCriticalSection(&(_pioinfo(fh)->lock));    /* unlock file */
3734
3735     return bytes_read;
3736 }
3737
3738 #endif  /* PERL_MSVCRT_READFIX */
3739
3740 DllExport int
3741 win32_read(int fd, void *buf, unsigned int cnt)
3742 {
3743 #ifdef PERL_MSVCRT_READFIX
3744     return _fixed_read(fd, buf, cnt);
3745 #else
3746     return read(fd, buf, cnt);
3747 #endif
3748 }
3749
3750 DllExport int
3751 win32_write(int fd, const void *buf, unsigned int cnt)
3752 {
3753     return write(fd, buf, cnt);
3754 }
3755
3756 DllExport int
3757 win32_mkdir(const char *dir, int mode)
3758 {
3759     dTHX;
3760     return mkdir(PerlDir_mapA(dir)); /* just ignore mode */
3761 }
3762
3763 DllExport int
3764 win32_rmdir(const char *dir)
3765 {
3766     dTHX;
3767     return rmdir(PerlDir_mapA(dir));
3768 }
3769
3770 DllExport int
3771 win32_chdir(const char *dir)
3772 {
3773     dTHX;
3774     if (!dir) {
3775         errno = ENOENT;
3776         return -1;
3777     }
3778     return chdir(dir);
3779 }
3780
3781 DllExport  int
3782 win32_access(const char *path, int mode)
3783 {
3784     dTHX;
3785     return access(PerlDir_mapA(path), mode);
3786 }
3787
3788 DllExport  int
3789 win32_chmod(const char *path, int mode)
3790 {
3791     dTHX;
3792     return chmod(PerlDir_mapA(path), mode);
3793 }
3794
3795
3796 static char *
3797 create_command_line(char *cname, STRLEN clen, const char * const *args)
3798 {
3799     dTHX;
3800     int index, argc;
3801     char *cmd, *ptr;
3802     const char *arg;
3803     STRLEN len = 0;
3804     bool bat_file = FALSE;
3805     bool cmd_shell = FALSE;
3806     bool dumb_shell = FALSE;
3807     bool extra_quotes = FALSE;
3808     bool quote_next = FALSE;
3809
3810     if (!cname)
3811         cname = (char*)args[0];
3812
3813     /* The NT cmd.exe shell has the following peculiarity that needs to be
3814      * worked around.  It strips a leading and trailing dquote when any
3815      * of the following is true:
3816      *    1. the /S switch was used
3817      *    2. there are more than two dquotes
3818      *    3. there is a special character from this set: &<>()@^|
3819      *    4. no whitespace characters within the two dquotes
3820      *    5. string between two dquotes isn't an executable file
3821      * To work around this, we always add a leading and trailing dquote
3822      * to the string, if the first argument is either "cmd.exe" or "cmd",
3823      * and there were at least two or more arguments passed to cmd.exe
3824      * (not including switches).
3825      * XXX the above rules (from "cmd /?") don't seem to be applied
3826      * always, making for the convolutions below :-(
3827      */
3828     if (cname) {
3829         if (!clen)
3830             clen = strlen(cname);
3831
3832         if (clen > 4
3833             && (stricmp(&cname[clen-4], ".bat") == 0
3834                 || (IsWinNT() && stricmp(&cname[clen-4], ".cmd") == 0)))
3835         {
3836             bat_file = TRUE;
3837             if (!IsWin95())
3838                 len += 3;
3839         }
3840         else {
3841             char *exe = strrchr(cname, '/');
3842             char *exe2 = strrchr(cname, '\\');
3843             if (exe2 > exe)
3844                 exe = exe2;
3845             if (exe)
3846                 ++exe;
3847             else
3848                 exe = cname;
3849             if (stricmp(exe, "cmd.exe") == 0 || stricmp(exe, "cmd") == 0) {
3850                 cmd_shell = TRUE;
3851                 len += 3;
3852             }
3853             else if (stricmp(exe, "command.com") == 0
3854                      || stricmp(exe, "command") == 0)
3855             {
3856                 dumb_shell = TRUE;
3857             }
3858         }
3859     }
3860
3861     DEBUG_p(PerlIO_printf(Perl_debug_log, "Args "));
3862     for (index = 0; (arg = (char*)args[index]) != NULL; ++index) {
3863         STRLEN curlen = strlen(arg);
3864         if (!(arg[0] == '"' && arg[curlen-1] == '"'))
3865             len += 2;   /* assume quoting needed (worst case) */
3866         len += curlen + 1;
3867         DEBUG_p(PerlIO_printf(Perl_debug_log, "[%s]",arg));
3868     }
3869     DEBUG_p(PerlIO_printf(Perl_debug_log, "\n"));
3870
3871     argc = index;
3872     Newx(cmd, len, char);
3873     ptr = cmd;
3874
3875     if (bat_file && !IsWin95()) {
3876         *ptr++ = '"';
3877         extra_quotes = TRUE;
3878     }
3879
3880     for (index = 0; (arg = (char*)args[index]) != NULL; ++index) {
3881         bool do_quote = 0;
3882         STRLEN curlen = strlen(arg);
3883
3884         /* we want to protect empty arguments and ones with spaces with
3885          * dquotes, but only if they aren't already there */
3886         if (!dumb_shell) {
3887             if (!curlen) {
3888                 do_quote = 1;
3889             }
3890             else if (quote_next) {
3891                 /* see if it really is multiple arguments pretending to
3892                  * be one and force a set of quotes around it */
3893                 if (*find_next_space(arg))
3894                     do_quote = 1;
3895             }
3896             else if (!(arg[0] == '"' && curlen > 1 && arg[curlen-1] == '"')) {
3897                 STRLEN i = 0;
3898                 while (i < curlen) {
3899                     if (isSPACE(arg[i])) {
3900                         do_quote = 1;
3901                     }
3902                     else if (arg[i] == '"') {
3903                         do_quote = 0;
3904                         break;
3905                     }
3906                     i++;
3907                 }
3908             }
3909         }
3910
3911         if (do_quote)
3912             *ptr++ = '"';
3913
3914         strcpy(ptr, arg);
3915         ptr += curlen;
3916
3917         if (do_quote)
3918             *ptr++ = '"';
3919
3920         if (args[index+1])
3921             *ptr++ = ' ';
3922
3923         if (!extra_quotes
3924             && cmd_shell
3925             && curlen >= 2
3926             && *arg  == '/'     /* see if arg is "/c", "/x/c", "/x/d/c" etc. */
3927             && stricmp(arg+curlen-2, "/c") == 0)
3928         {
3929             /* is there a next argument? */
3930             if (args[index+1]) {
3931                 /* are there two or more next arguments? */
3932                 if (args[index+2]) {
3933                     *ptr++ = '"';
3934                     extra_quotes = TRUE;
3935                 }
3936                 else {
3937                     /* single argument, force quoting if it has spaces */
3938                     quote_next = TRUE;
3939                 }
3940             }
3941         }
3942     }
3943
3944     if (extra_quotes)
3945         *ptr++ = '"';
3946
3947     *ptr = '\0';
3948
3949     return cmd;
3950 }
3951
3952 static char *
3953 qualified_path(const char *cmd)
3954 {
3955     dTHX;
3956     char *pathstr;
3957     char *fullcmd, *curfullcmd;
3958     STRLEN cmdlen = 0;
3959     int has_slash = 0;
3960
3961     if (!cmd)
3962         return NULL;
3963     fullcmd = (char*)cmd;
3964     while (*fullcmd) {
3965         if (*fullcmd == '/' || *fullcmd == '\\')
3966             has_slash++;
3967         fullcmd++;
3968         cmdlen++;
3969     }
3970
3971     /* look in PATH */
3972     pathstr = PerlEnv_getenv("PATH");
3973
3974     /* worst case: PATH is a single directory; we need additional space
3975      * to append "/", ".exe" and trailing "\0" */
3976     Newx(fullcmd, (pathstr ? strlen(pathstr) : 0) + cmdlen + 6, char);
3977     curfullcmd = fullcmd;
3978
3979     while (1) {
3980         DWORD res;
3981
3982         /* start by appending the name to the current prefix */
3983         strcpy(curfullcmd, cmd);
3984         curfullcmd += cmdlen;
3985
3986         /* if it doesn't end with '.', or has no extension, try adding
3987          * a trailing .exe first */
3988         if (cmd[cmdlen-1] != '.'
3989             && (cmdlen < 4 || cmd[cmdlen-4] != '.'))
3990         {
3991             strcpy(curfullcmd, ".exe");
3992             res = GetFileAttributes(fullcmd);
3993             if (res != 0xFFFFFFFF && !(res & FILE_ATTRIBUTE_DIRECTORY))
3994                 return fullcmd;
3995             *curfullcmd = '\0';
3996         }
3997
3998         /* that failed, try the bare name */
3999         res = GetFileAttributes(fullcmd);
4000         if (res != 0xFFFFFFFF && !(res & FILE_ATTRIBUTE_DIRECTORY))
4001             return fullcmd;
4002
4003         /* quit if no other path exists, or if cmd already has path */
4004         if (!pathstr || !*pathstr || has_slash)
4005             break;
4006
4007         /* skip leading semis */
4008         while (*pathstr == ';')
4009             pathstr++;
4010
4011         /* build a new prefix from scratch */
4012         curfullcmd = fullcmd;
4013         while (*pathstr && *pathstr != ';') {
4014             if (*pathstr == '"') {      /* foo;"baz;etc";bar */
4015                 pathstr++;              /* skip initial '"' */
4016                 while (*pathstr && *pathstr != '"') {
4017                     *curfullcmd++ = *pathstr++;
4018                 }
4019                 if (*pathstr)
4020                     pathstr++;          /* skip trailing '"' */
4021             }
4022             else {
4023                 *curfullcmd++ = *pathstr++;
4024             }
4025         }
4026         if (*pathstr)
4027             pathstr++;                  /* skip trailing semi */
4028         if (curfullcmd > fullcmd        /* append a dir separator */
4029             && curfullcmd[-1] != '/' && curfullcmd[-1] != '\\')
4030         {
4031             *curfullcmd++ = '\\';
4032         }
4033     }
4034
4035     Safefree(fullcmd);
4036     return NULL;
4037 }
4038
4039 /* The following are just place holders.
4040  * Some hosts may provide and environment that the OS is
4041  * not tracking, therefore, these host must provide that
4042  * environment and the current directory to CreateProcess
4043  */
4044
4045 DllExport void*
4046 win32_get_childenv(void)
4047 {
4048     return NULL;
4049 }
4050
4051 DllExport void
4052 win32_free_childenv(void* d)
4053 {
4054 }
4055
4056 DllExport void
4057 win32_clearenv(void)
4058 {
4059     char *envv = GetEnvironmentStrings();
4060     char *cur = envv;
4061     STRLEN len;
4062     while (*cur) {
4063         char *end = strchr(cur,'=');
4064         if (end && end != cur) {
4065             *end = '\0';
4066             SetEnvironmentVariable(cur, NULL);
4067             *end = '=';
4068             cur = end + strlen(end+1)+2;
4069         }
4070         else if ((len = strlen(cur)))
4071             cur += len+1;
4072     }
4073     FreeEnvironmentStrings(envv);
4074 }
4075
4076 DllExport char*
4077 win32_get_childdir(void)
4078 {
4079     dTHX;
4080     char* ptr;
4081     char szfilename[MAX_PATH+1];
4082
4083     GetCurrentDirectoryA(MAX_PATH+1, szfilename);
4084     Newx(ptr, strlen(szfilename)+1, char);
4085     strcpy(ptr, szfilename);
4086     return ptr;
4087 }
4088
4089 DllExport void
4090 win32_free_childdir(char* d)
4091 {
4092     dTHX;
4093     Safefree(d);
4094 }
4095
4096
4097 /* XXX this needs to be made more compatible with the spawnvp()
4098  * provided by the various RTLs.  In particular, searching for
4099  * *.{com,bat,cmd} files (as done by the RTLs) is unimplemented.
4100  * This doesn't significantly affect perl itself, because we
4101  * always invoke things using PERL5SHELL if a direct attempt to
4102  * spawn the executable fails.
4103  *
4104  * XXX splitting and rejoining the commandline between do_aspawn()
4105  * and win32_spawnvp() could also be avoided.
4106  */
4107
4108 DllExport int
4109 win32_spawnvp(int mode, const char *cmdname, const char *const *argv)
4110 {
4111 #ifdef USE_RTL_SPAWNVP
4112     return spawnvp(mode, cmdname, (char * const *)argv);
4113 #else
4114     dTHX;
4115     int ret;
4116     void* env;
4117     char* dir;
4118     child_IO_table tbl;
4119     STARTUPINFO StartupInfo;
4120     PROCESS_INFORMATION ProcessInformation;
4121     DWORD create = 0;
4122     char *cmd;
4123     char *fullcmd = NULL;
4124     char *cname = (char *)cmdname;
4125     STRLEN clen = 0;
4126
4127     if (cname) {
4128         clen = strlen(cname);
4129         /* if command name contains dquotes, must remove them */
4130         if (strchr(cname, '"')) {
4131             cmd = cname;
4132             Newx(cname,clen+1,char);
4133             clen = 0;
4134             while (*cmd) {
4135                 if (*cmd != '"') {
4136                     cname[clen] = *cmd;
4137                     ++clen;
4138                 }
4139                 ++cmd;
4140             }
4141             cname[clen] = '\0';
4142         }
4143     }
4144
4145     cmd = create_command_line(cname, clen, argv);
4146
4147     env = PerlEnv_get_childenv();
4148     dir = PerlEnv_get_childdir();
4149
4150     switch(mode) {
4151     case P_NOWAIT:      /* asynch + remember result */
4152         if (w32_num_children >= MAXIMUM_WAIT_OBJECTS) {
4153             errno = EAGAIN;
4154             ret = -1;
4155             goto RETVAL;
4156         }
4157         /* Create a new process group so we can use GenerateConsoleCtrlEvent()
4158          * in win32_kill()
4159          */
4160         create |= CREATE_NEW_PROCESS_GROUP;
4161         /* FALL THROUGH */
4162
4163     case P_WAIT:        /* synchronous execution */
4164         break;
4165     default:            /* invalid mode */
4166         errno = EINVAL;
4167         ret = -1;
4168         goto RETVAL;
4169     }
4170     memset(&StartupInfo,0,sizeof(StartupInfo));
4171     StartupInfo.cb = sizeof(StartupInfo);
4172     memset(&tbl,0,sizeof(tbl));
4173     PerlEnv_get_child_IO(&tbl);
4174     StartupInfo.dwFlags         = tbl.dwFlags;
4175     StartupInfo.dwX             = tbl.dwX;
4176     StartupInfo.dwY             = tbl.dwY;
4177     StartupInfo.dwXSize         = tbl.dwXSize;
4178     StartupInfo.dwYSize         = tbl.dwYSize;
4179     StartupInfo.dwXCountChars   = tbl.dwXCountChars;
4180     StartupInfo.dwYCountChars   = tbl.dwYCountChars;
4181     StartupInfo.dwFillAttribute = tbl.dwFillAttribute;
4182     StartupInfo.wShowWindow     = tbl.wShowWindow;
4183     StartupInfo.hStdInput       = tbl.childStdIn;
4184     StartupInfo.hStdOutput      = tbl.childStdOut;
4185     StartupInfo.hStdError       = tbl.childStdErr;
4186     if (StartupInfo.hStdInput == INVALID_HANDLE_VALUE &&
4187         StartupInfo.hStdOutput == INVALID_HANDLE_VALUE &&
4188         StartupInfo.hStdError == INVALID_HANDLE_VALUE)
4189     {
4190         create |= CREATE_NEW_CONSOLE;
4191     }
4192     else {
4193         StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
4194     }
4195     if (w32_use_showwindow) {
4196         StartupInfo.dwFlags |= STARTF_USESHOWWINDOW;
4197         StartupInfo.wShowWindow = w32_showwindow;
4198     }
4199
4200     DEBUG_p(PerlIO_printf(Perl_debug_log, "Spawning [%s] with [%s]\n",
4201                           cname,cmd));
4202 RETRY:
4203     if (!CreateProcess(cname,           /* search PATH to find executable */
4204                        cmd,             /* executable, and its arguments */
4205                        NULL,            /* process attributes */
4206                        NULL,            /* thread attributes */
4207                        TRUE,            /* inherit handles */
4208                        create,          /* creation flags */
4209                        (LPVOID)env,     /* inherit environment */
4210                        dir,             /* inherit cwd */
4211                        &StartupInfo,
4212                        &ProcessInformation))
4213     {
4214         /* initial NULL argument to CreateProcess() does a PATH
4215          * search, but it always first looks in the directory
4216          * where the current process was started, which behavior
4217          * is undesirable for backward compatibility.  So we
4218          * jump through our own hoops by picking out the path
4219          * we really want it to use. */
4220         if (!fullcmd) {
4221             fullcmd = qualified_path(cname);
4222             if (fullcmd) {
4223                 if (cname != cmdname)
4224                     Safefree(cname);
4225                 cname = fullcmd;
4226                 DEBUG_p(PerlIO_printf(Perl_debug_log,
4227                                       "Retrying [%s] with same args\n",
4228                                       cname));
4229                 goto RETRY;
4230             }
4231         }
4232         errno = ENOENT;
4233         ret = -1;
4234         goto RETVAL;
4235     }
4236
4237     if (mode == P_NOWAIT) {
4238         /* asynchronous spawn -- store handle, return PID */
4239         ret = (int)ProcessInformation.dwProcessId;
4240         if (IsWin95() && ret < 0)
4241             ret = -ret;
4242
4243         w32_child_handles[w32_num_children] = ProcessInformation.hProcess;
4244         w32_child_pids[w32_num_children] = (DWORD)ret;
4245         ++w32_num_children;
4246     }
4247     else  {
4248         DWORD status;
4249         win32_msgwait(aTHX_ 1, &ProcessInformation.hProcess, INFINITE, NULL);
4250         /* FIXME: if msgwait returned due to message perhaps forward the
4251            "signal" to the process
4252          */
4253         GetExitCodeProcess(ProcessInformation.hProcess, &status);
4254         ret = (int)status;
4255         CloseHandle(ProcessInformation.hProcess);
4256     }
4257
4258     CloseHandle(ProcessInformation.hThread);
4259
4260 RETVAL:
4261     PerlEnv_free_childenv(env);
4262     PerlEnv_free_childdir(dir);
4263     Safefree(cmd);
4264     if (cname != cmdname)
4265         Safefree(cname);
4266     return ret;
4267 #endif
4268 }
4269
4270 DllExport int
4271 win32_execv(const char *cmdname, const char *const *argv)
4272 {
4273 #ifdef USE_ITHREADS
4274     dTHX;
4275     /* if this is a pseudo-forked child, we just want to spawn
4276      * the new program, and return */
4277     if (w32_pseudo_id)
4278 #  ifdef __BORLANDC__
4279         return spawnv(P_WAIT, cmdname, (char *const *)argv);
4280 #  else
4281         return spawnv(P_WAIT, cmdname, argv);
4282 #  endif
4283 #endif
4284 #ifdef __BORLANDC__
4285     return execv(cmdname, (char *const *)argv);
4286 #else
4287     return execv(cmdname, argv);
4288 #endif
4289 }
4290
4291 DllExport int
4292 win32_execvp(const char *cmdname, const char *const *argv)
4293 {
4294 #ifdef USE_ITHREADS
4295     dTHX;
4296     /* if this is a pseudo-forked child, we just want to spawn
4297      * the new program, and return */
4298     if (w32_pseudo_id) {
4299         int status = win32_spawnvp(P_WAIT, cmdname, (const char *const *)argv);
4300         if (status != -1) {
4301             my_exit(status);
4302             return 0;
4303         }
4304         else
4305             return status;
4306     }
4307 #endif
4308 #ifdef __BORLANDC__
4309     return execvp(cmdname, (char *const *)argv);
4310 #else
4311     return execvp(cmdname, argv);
4312 #endif
4313 }
4314
4315 DllExport void
4316 win32_perror(const char *str)
4317 {
4318     perror(str);
4319 }
4320
4321 DllExport void
4322 win32_setbuf(FILE *pf, char *buf)
4323 {
4324     setbuf(pf, buf);
4325 }
4326
4327 DllExport int
4328 win32_setvbuf(FILE *pf, char *buf, int type, size_t size)
4329 {
4330     return setvbuf(pf, buf, type, size);
4331 }
4332
4333 DllExport int
4334 win32_flushall(void)
4335 {
4336     return flushall();
4337 }
4338
4339 DllExport int
4340 win32_fcloseall(void)
4341 {
4342     return fcloseall();
4343 }
4344
4345 DllExport char*
4346 win32_fgets(char *s, int n, FILE *pf)
4347 {
4348     return fgets(s, n, pf);
4349 }
4350
4351 DllExport char*
4352 win32_gets(char *s)
4353 {
4354     return gets(s);
4355 }
4356
4357 DllExport int
4358 win32_fgetc(FILE *pf)
4359 {
4360     return fgetc(pf);
4361 }
4362
4363 DllExport int
4364 win32_putc(int c, FILE *pf)
4365 {
4366     return putc(c,pf);
4367 }
4368
4369 DllExport int
4370 win32_puts(const char *s)
4371 {
4372     return puts(s);
4373 }
4374
4375 DllExport int
4376 win32_getchar(void)
4377 {
4378     return getchar();
4379 }
4380
4381 DllExport int
4382 win32_putchar(int c)
4383 {
4384     return putchar(c);
4385 }
4386
4387 #ifdef MYMALLOC
4388
4389 #ifndef USE_PERL_SBRK
4390
4391 static char *committed = NULL;          /* XXX threadead */
4392 static char *base      = NULL;          /* XXX threadead */
4393 static char *reserved  = NULL;          /* XXX threadead */
4394 static char *brk       = NULL;          /* XXX threadead */
4395 static DWORD pagesize  = 0;             /* XXX threadead */
4396
4397 void *
4398 sbrk(ptrdiff_t need)
4399 {
4400  void *result;
4401  if (!pagesize)
4402   {SYSTEM_INFO info;
4403    GetSystemInfo(&info);
4404    /* Pretend page size is larger so we don't perpetually
4405     * call the OS to commit just one page ...
4406     */
4407    pagesize = info.dwPageSize << 3;
4408   }
4409  if (brk+need >= reserved)
4410   {
4411    DWORD size = brk+need-reserved;
4412    char *addr;
4413    char *prev_committed = NULL;
4414    if (committed && reserved && committed < reserved)
4415     {
4416      /* Commit last of previous chunk cannot span allocations */
4417      addr = (char *) VirtualAlloc(committed,reserved-committed,MEM_COMMIT,PAGE_READWRITE);
4418      if (addr)
4419       {
4420       /* Remember where we committed from in case we want to decommit later */
4421       prev_committed = committed;
4422       committed = reserved;
4423       }
4424     }
4425    /* Reserve some (more) space
4426     * Contiguous blocks give us greater efficiency, so reserve big blocks -
4427     * this is only address space not memory...
4428     * Note this is a little sneaky, 1st call passes NULL as reserved
4429     * so lets system choose where we start, subsequent calls pass
4430     * the old end address so ask for a contiguous block
4431     */
4432 sbrk_reserve:
4433    if (size < 64*1024*1024)
4434     size = 64*1024*1024;
4435    size = ((size + pagesize - 1) / pagesize) * pagesize;
4436    addr  = (char *) VirtualAlloc(reserved,size,MEM_RESERVE,PAGE_NOACCESS);
4437    if (addr)
4438     {
4439      reserved = addr+size;
4440      if (!base)
4441       base = addr;
4442      if (!committed)
4443       committed = base;
4444      if (!brk)
4445       brk = committed;
4446     }
4447    else if (reserved)
4448     {
4449       /* The existing block could not be extended far enough, so decommit
4450        * anything that was just committed above and start anew */
4451       if (prev_committed)
4452        {
4453        if (!VirtualFree(prev_committed,reserved-prev_committed,MEM_DECOMMIT))
4454         return (void *) -1;
4455        }
4456       reserved = base = committed = brk = NULL;
4457       size = need;
4458       goto sbrk_reserve;
4459     }
4460    else
4461     {
4462      return (void *) -1;
4463     }
4464   }
4465  result = brk;
4466  brk += need;
4467  if (brk > committed)
4468   {
4469    DWORD size = ((brk-committed + pagesize -1)/pagesize) * pagesize;
4470    char *addr;
4471    if (committed+size > reserved)
4472     size = reserved-committed;
4473    addr = (char *) VirtualAlloc(committed,size,MEM_COMMIT,PAGE_READWRITE);
4474    if (addr)
4475     committed += size;
4476    else
4477     return (void *) -1;
4478   }
4479  return result;
4480 }
4481
4482 #endif
4483 #endif
4484
4485 DllExport void*
4486 win32_malloc(size_t size)
4487 {
4488     return malloc(size);
4489 }
4490
4491 DllExport void*
4492 win32_calloc(size_t numitems, size_t size)
4493 {
4494     return calloc(numitems,size);
4495 }
4496
4497 DllExport void*
4498 win32_realloc(void *block, size_t size)
4499 {
4500     return realloc(block,size);
4501 }
4502
4503 DllExport void
4504 win32_free(void *block)
4505 {
4506     free(block);
4507 }
4508
4509
4510 DllExport int
4511 win32_open_osfhandle(intptr_t handle, int flags)
4512 {
4513 #ifdef USE_FIXED_OSFHANDLE
4514     if (IsWin95())
4515         return my_open_osfhandle(handle, flags);
4516 #endif
4517     return _open_osfhandle(handle, flags);
4518 }
4519
4520 DllExport intptr_t
4521 win32_get_osfhandle(int fd)
4522 {
4523     return (intptr_t)_get_osfhandle(fd);
4524 }
4525
4526 DllExport FILE *
4527 win32_fdupopen(FILE *pf)
4528 {
4529     FILE* pfdup;
4530     fpos_t pos;
4531     char mode[3];
4532     int fileno = win32_dup(win32_fileno(pf));
4533
4534     /* open the file in the same mode */
4535 #ifdef __BORLANDC__
4536     if((pf)->flags & _F_READ) {
4537         mode[0] = 'r';
4538         mode[1] = 0;
4539     }
4540     else if((pf)->flags & _F_WRIT) {
4541         mode[0] = 'a';
4542         mode[1] = 0;
4543     }
4544     else if((pf)->flags & _F_RDWR) {
4545         mode[0] = 'r';
4546         mode[1] = '+';
4547         mode[2] = 0;
4548     }
4549 #else
4550     if((pf)->_flag & _IOREAD) {
4551         mode[0] = 'r';
4552         mode[1] = 0;
4553     }
4554     else if((pf)->_flag & _IOWRT) {
4555         mode[0] = 'a';
4556         mode[1] = 0;
4557     }
4558     else if((pf)->_flag & _IORW) {
4559         mode[0] = 'r';
4560         mode[1] = '+';
4561         mode[2] = 0;
4562     }
4563 #endif
4564
4565     /* it appears that the binmode is attached to the
4566      * file descriptor so binmode files will be handled
4567      * correctly
4568      */
4569     pfdup = win32_fdopen(fileno, mode);
4570
4571     /* move the file pointer to the same position */
4572     if (!fgetpos(pf, &pos)) {
4573         fsetpos(pfdup, &pos);
4574     }
4575     return pfdup;
4576 }
4577
4578 DllExport void*
4579 win32_dynaload(const char* filename)
4580 {
4581     dTHX;
4582     char buf[MAX_PATH+1];
4583     char *first;
4584
4585     /* LoadLibrary() doesn't recognize forward slashes correctly,
4586      * so turn 'em back. */
4587     first = strchr(filename, '/');
4588     if (first) {
4589         STRLEN len = strlen(filename);
4590         if (len <= MAX_PATH) {
4591             strcpy(buf, filename);
4592             filename = &buf[first - filename];
4593             while (*filename) {
4594                 if (*filename == '/')
4595                     *(char*)filename = '\\';
4596                 ++filename;
4597             }
4598             filename = buf;
4599         }
4600     }
4601     return LoadLibraryExA(PerlDir_mapA(filename), NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
4602 }
4603
4604 XS(w32_SetChildShowWindow)
4605 {
4606     dXSARGS;
4607     BOOL use_showwindow = w32_use_showwindow;
4608     /* use "unsigned short" because Perl has redefined "WORD" */
4609     unsigned short showwindow = w32_showwindow;
4610
4611     if (items > 1)
4612         Perl_croak(aTHX_ "usage: Win32::SetChildShowWindow($showwindow)");
4613
4614     if (items == 0 || !SvOK(ST(0)))
4615         w32_use_showwindow = FALSE;
4616     else {
4617         w32_use_showwindow = TRUE;
4618         w32_showwindow = (unsigned short)SvIV(ST(0));
4619     }
4620
4621     EXTEND(SP, 1);
4622     if (use_showwindow)
4623         ST(0) = sv_2mortal(newSViv(showwindow));
4624     else
4625         ST(0) = &PL_sv_undef;
4626     XSRETURN(1);
4627 }
4628
4629 void
4630 Perl_init_os_extras(void)
4631 {
4632     dTHX;
4633     char *file = __FILE__;
4634
4635     /* Initialize Win32CORE if it has been statically linked. */
4636     void (*pfn_init)(pTHX);
4637 #if defined(__BORLANDC__)
4638     /* makedef.pl seems to have given up on fixing this issue in the .def file */
4639     pfn_init = (void (*)(pTHX))GetProcAddress((HMODULE)w32_perldll_handle, "_init_Win32CORE");
4640 #else
4641     pfn_init = (void (*)(pTHX))GetProcAddress((HMODULE)w32_perldll_handle, "init_Win32CORE");
4642 #endif
4643     if (pfn_init)
4644         pfn_init(aTHX);
4645
4646     newXS("Win32::SetChildShowWindow", w32_SetChildShowWindow, file);
4647 }
4648
4649 void *
4650 win32_signal_context(void)
4651 {
4652     dTHX;
4653 #ifdef MULTIPLICITY
4654     if (!my_perl) {
4655         my_perl = PL_curinterp;
4656         PERL_SET_THX(my_perl);
4657     }
4658     return my_perl;
4659 #else
4660     return PL_curinterp;
4661 #endif
4662 }
4663
4664
4665 BOOL WINAPI
4666 win32_ctrlhandler(DWORD dwCtrlType)
4667 {
4668 #ifdef MULTIPLICITY
4669     dTHXa(PERL_GET_SIG_CONTEXT);
4670
4671     if (!my_perl)
4672         return FALSE;
4673 #endif
4674
4675     switch(dwCtrlType) {
4676     case CTRL_CLOSE_EVENT:
4677      /*  A signal that the system sends to all processes attached to a console when
4678          the user closes the console (either by choosing the Close command from the
4679          console window's System menu, or by choosing the End Task command from the
4680          Task List
4681       */
4682         if (do_raise(aTHX_ 1))        /* SIGHUP */
4683             sig_terminate(aTHX_ 1);
4684         return TRUE;
4685
4686     case CTRL_C_EVENT:
4687         /*  A CTRL+c signal was received */
4688         if (do_raise(aTHX_ SIGINT))
4689             sig_terminate(aTHX_ SIGINT);
4690         return TRUE;
4691
4692     case CTRL_BREAK_EVENT:
4693         /*  A CTRL+BREAK signal was received */
4694         if (do_raise(aTHX_ SIGBREAK))
4695             sig_terminate(aTHX_ SIGBREAK);
4696         return TRUE;
4697
4698     case CTRL_LOGOFF_EVENT:
4699       /*  A signal that the system sends to all console processes when a user is logging
4700           off. This signal does not indicate which user is logging off, so no
4701           assumptions can be made.
4702        */
4703         break;
4704     case CTRL_SHUTDOWN_EVENT:
4705       /*  A signal that the system sends to all console processes when the system is
4706           shutting down.
4707        */
4708         if (do_raise(aTHX_ SIGTERM))
4709             sig_terminate(aTHX_ SIGTERM);
4710         return TRUE;
4711     default:
4712         break;
4713     }
4714     return FALSE;
4715 }
4716
4717
4718 #ifdef SET_INVALID_PARAMETER_HANDLER
4719 #  include <crtdbg.h>
4720 #endif
4721
4722 static void
4723 ansify_path(void)
4724 {
4725     size_t len;
4726     char *ansi_path;
4727     WCHAR *wide_path;
4728     WCHAR *wide_dir;
4729
4730     /* win32_ansipath() requires Windows 2000 or later */
4731     if (!IsWin2000())
4732         return;
4733
4734     /* fetch Unicode version of PATH */
4735     len = 2000;
4736     wide_path = win32_malloc(len*sizeof(WCHAR));
4737     while (wide_path) {
4738         size_t newlen = GetEnvironmentVariableW(L"PATH", wide_path, len);
4739         if (newlen < len)
4740             break;
4741         len = newlen;
4742         wide_path = win32_realloc(wide_path, len*sizeof(WCHAR));
4743     }
4744     if (!wide_path)
4745         return;
4746
4747     /* convert to ANSI pathnames */
4748     wide_dir = wide_path;
4749     ansi_path = NULL;
4750     while (wide_dir) {
4751         WCHAR *sep = wcschr(wide_dir, ';');
4752         char *ansi_dir;
4753         size_t ansi_len;
4754         size_t wide_len;
4755
4756         if (sep)
4757             *sep++ = '\0';
4758
4759         /* remove quotes around pathname */
4760         if (*wide_dir == '"')
4761             ++wide_dir;
4762         wide_len = wcslen(wide_dir);
4763         if (wide_len && wide_dir[wide_len-1] == '"')
4764             wide_dir[wide_len-1] = '\0';
4765
4766         /* append ansi_dir to ansi_path */
4767         ansi_dir = win32_ansipath(wide_dir);
4768         ansi_len = strlen(ansi_dir);
4769         if (ansi_path) {
4770             size_t newlen = len + 1 + ansi_len;
4771             ansi_path = win32_realloc(ansi_path, newlen+1);
4772             if (!ansi_path)
4773                 break;
4774             ansi_path[len] = ';';
4775             memcpy(ansi_path+len+1, ansi_dir, ansi_len+1);
4776             len = newlen;
4777         }
4778         else {
4779             len = ansi_len;
4780             ansi_path = win32_malloc(5+len+1);
4781             if (!ansi_path)
4782                 break;
4783             memcpy(ansi_path, "PATH=", 5);
4784             memcpy(ansi_path+5, ansi_dir, len+1);
4785             len += 5;
4786         }
4787         win32_free(ansi_dir);
4788         wide_dir = sep;
4789     }
4790
4791     if (ansi_path) {
4792         /* Update C RTL environ array.  This will only have full effect if
4793          * perl_parse() is later called with `environ` as the `env` argument.
4794          * Otherwise S_init_postdump_symbols() will overwrite PATH again.
4795          *
4796          * We do have to ansify() the PATH before Perl has been fully
4797          * initialized because S_find_script() uses the PATH when perl
4798          * is being invoked with the -S option.  This happens before %ENV
4799          * is initialized in S_init_postdump_symbols().
4800          *
4801          * XXX Is this a bug? Should S_find_script() use the environment
4802          * XXX passed in the `env` arg to parse_perl()?
4803          */
4804         putenv(ansi_path);
4805         /* Keep system environment in sync because S_init_postdump_symbols()
4806          * will not call mg_set() if it initializes %ENV from `environ`.
4807          */
4808         SetEnvironmentVariableA("PATH", ansi_path+5);
4809         /* We are intentionally leaking the ansi_path string here because
4810          * the Borland runtime library puts it directly into the environ
4811          * array.  The Microsoft runtime library seems to make a copy,
4812          * but will leak the copy should it be replaced again later.
4813          * Since this code is only called once during PERL_SYS_INIT this
4814          * shouldn't really matter.
4815          */
4816     }
4817     win32_free(wide_path);
4818 }
4819
4820 void
4821 Perl_win32_init(int *argcp, char ***argvp)
4822 {
4823     HMODULE module;
4824
4825 #ifdef SET_INVALID_PARAMETER_HANDLER
4826     _invalid_parameter_handler oldHandler, newHandler;
4827     newHandler = my_invalid_parameter_handler;
4828     oldHandler = _set_invalid_parameter_handler(newHandler);
4829     _CrtSetReportMode(_CRT_ASSERT, 0);
4830 #endif
4831     /* Disable floating point errors, Perl will trap the ones we
4832      * care about.  VC++ RTL defaults to switching these off
4833      * already, but the Borland RTL doesn't.  Since we don't
4834      * want to be at the vendor's whim on the default, we set
4835      * it explicitly here.
4836      */
4837 #if !defined(_ALPHA_) && !defined(__GNUC__)
4838     _control87(MCW_EM, MCW_EM);
4839 #endif
4840     MALLOC_INIT;
4841
4842     /* When the manifest resource requests Common-Controls v6 then
4843      * user32.dll no longer registers all the Windows classes used for
4844      * standard controls but leaves some of them to be registered by
4845      * comctl32.dll.  InitCommonControls() doesn't do anything but calling
4846      * it makes sure comctl32.dll gets loaded into the process and registers
4847      * the standard control classes.  Without this even normal Windows APIs
4848      * like MessageBox() can fail under some versions of Windows XP.
4849      */
4850     InitCommonControls();
4851
4852     module = GetModuleHandle("ntdll.dll");
4853     if (module) {
4854         *(FARPROC*)&pfnZwQuerySystemInformation = GetProcAddress(module, "ZwQuerySystemInformation");
4855     }
4856
4857     module = GetModuleHandle("kernel32.dll");
4858     if (module) {
4859         *(FARPROC*)&pfnCreateToolhelp32Snapshot = GetProcAddress(module, "CreateToolhelp32Snapshot");
4860         *(FARPROC*)&pfnProcess32First           = GetProcAddress(module, "Process32First");
4861         *(FARPROC*)&pfnProcess32Next            = GetProcAddress(module, "Process32Next");
4862     }
4863
4864     g_osver.dwOSVersionInfoSize = sizeof(g_osver);
4865     GetVersionEx(&g_osver);
4866
4867     ansify_path();
4868 }
4869
4870 void
4871 Perl_win32_term(void)
4872 {
4873     dTHX;
4874     HINTS_REFCNT_TERM;
4875     OP_REFCNT_TERM;
4876     PERLIO_TERM;
4877     MALLOC_TERM;
4878 }
4879
4880 void
4881 win32_get_child_IO(child_IO_table* ptbl)
4882 {
4883     ptbl->childStdIn    = GetStdHandle(STD_INPUT_HANDLE);
4884     ptbl->childStdOut   = GetStdHandle(STD_OUTPUT_HANDLE);
4885     ptbl->childStdErr   = GetStdHandle(STD_ERROR_HANDLE);
4886 }
4887
4888 Sighandler_t
4889 win32_signal(int sig, Sighandler_t subcode)
4890 {
4891     dTHX;
4892     if (sig < SIG_SIZE) {
4893         int save_errno = errno;
4894         Sighandler_t result = signal(sig, subcode);
4895         if (result == SIG_ERR) {
4896             result = w32_sighandler[sig];
4897             errno = save_errno;
4898         }
4899         w32_sighandler[sig] = subcode;
4900         return result;
4901     }
4902     else {
4903         errno = EINVAL;
4904         return SIG_ERR;
4905     }
4906 }
4907
4908 /* The PerlMessageWindowClass's WindowProc */
4909 LRESULT CALLBACK
4910 win32_message_window_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
4911 {
4912     return win32_process_message(hwnd, msg, wParam, lParam) ?
4913         0 : DefWindowProc(hwnd, msg, wParam, lParam);
4914 }
4915
4916 /* we use a message filter hook to process thread messages, passing any
4917  * messages that we don't process on to the rest of the hook chain
4918  * Anyone else writing a message loop that wants to play nicely with perl
4919  * should do
4920  *   CallMsgFilter(&msg, MSGF_***);
4921  * between their GetMessage and DispatchMessage calls.  */
4922 LRESULT CALLBACK
4923 win32_message_filter_proc(int code, WPARAM wParam, LPARAM lParam) {
4924     LPMSG pmsg = (LPMSG)lParam;
4925
4926     /* we'll process it if code says we're allowed, and it's a thread message */
4927     if (code >= 0 && pmsg->hwnd == NULL
4928             && win32_process_message(pmsg->hwnd, pmsg->message,
4929                                      pmsg->wParam, pmsg->lParam))
4930     {
4931             return TRUE;
4932     }
4933
4934     /* XXX: MSDN says that hhk is ignored, but we should really use the
4935      * return value from SetWindowsHookEx() in win32_create_message_window().  */
4936     return CallNextHookEx(NULL, code, wParam, lParam);
4937 }
4938
4939 /* The real message handler. Can be called with
4940  * hwnd == NULL to process our thread messages. Returns TRUE for any messages
4941  * that it processes */
4942 static LRESULT
4943 win32_process_message(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
4944 {
4945     /* BEWARE. The context retrieved using dTHX; is the context of the
4946      * 'parent' thread during the CreateWindow() phase - i.e. for all messages
4947      * up to and including WM_CREATE.  If it ever happens that you need the
4948      * 'child' context before this, then it needs to be passed into
4949      * win32_create_message_window(), and passed to the WM_NCCREATE handler
4950      * from the lparam of CreateWindow().  It could then be stored/retrieved
4951      * using [GS]etWindowLongPtr(... GWLP_USERDATA ...), possibly eliminating
4952      * the dTHX calls here. */
4953     /* XXX For now it is assumed that the overhead of the dTHX; for what
4954      * are relativley infrequent code-paths, is better than the added
4955      * complexity of getting the correct context passed into
4956      * win32_create_message_window() */
4957
4958     switch(msg) {
4959
4960 #ifdef USE_ITHREADS
4961         case WM_USER_MESSAGE: {
4962             long child = find_pseudo_pid((int)wParam);
4963             if (child >= 0) {
4964                 dTHX;
4965                 w32_pseudo_child_message_hwnds[child] = (HWND)lParam;
4966                 return 1;
4967             }
4968             break;
4969         }
4970 #endif
4971
4972         case WM_USER_KILL: {
4973             dTHX;
4974             /* We use WM_USER_KILL to fake kill() with other signals */
4975             int sig = (int)wParam;
4976             if (do_raise(aTHX_ sig))
4977                 sig_terminate(aTHX_ sig);
4978
4979             return 1;
4980         }
4981
4982         case WM_TIMER: {
4983             dTHX;
4984             /* alarm() is a one-shot but SetTimer() repeats so kill it */
4985             if (w32_timerid && w32_timerid==(UINT)wParam) {
4986                 KillTimer(w32_message_hwnd, w32_timerid);
4987                 w32_timerid=0;
4988
4989                 /* Now fake a call to signal handler */
4990                 if (do_raise(aTHX_ 14))
4991                     sig_terminate(aTHX_ 14);
4992
4993                 return 1;
4994             }
4995             break;
4996         }
4997
4998         default:
4999             break;
5000
5001     } /* switch */
5002
5003     /* Above or other stuff may have set a signal flag, and we may not have
5004      * been called from win32_async_check() (e.g. some other GUI's message
5005      * loop.  BUT DON'T dispatch signals here: If someone has set a SIGALRM
5006      * handler that die's, and the message loop that calls here is wrapped
5007      * in an eval, then you may well end up with orphaned windows - signals
5008      * are dispatched by win32_async_check() */
5009
5010     return 0;
5011 }
5012
5013 void
5014 win32_create_message_window_class(void)
5015 {
5016     /* create the window class for "message only" windows */
5017     WNDCLASS wc;
5018
5019     Zero(&wc, 1, wc);
5020     wc.lpfnWndProc = win32_message_window_proc;
5021     wc.hInstance = (HINSTANCE)GetModuleHandle(NULL);
5022     wc.lpszClassName = "PerlMessageWindowClass";
5023
5024     /* second and subsequent calls will fail, but class
5025      * will already be registered */
5026     RegisterClass(&wc);
5027 }
5028
5029 HWND
5030 win32_create_message_window(void)
5031 {
5032     HWND hwnd = NULL;
5033
5034     /* "message-only" windows have been implemented in Windows 2000 and later.
5035      * On earlier versions we'll continue to post messages to a specific
5036      * thread and use hwnd==NULL.  This is brittle when either an embedding
5037      * application or an XS module is also posting messages to hwnd=NULL
5038      * because once removed from the queue they cannot be delivered to the
5039      * "right" place with DispatchMessage() anymore, as there is no WindowProc
5040      * if there is no window handle.
5041      */
5042     /* Using HWND_MESSAGE appears to work under Win98, despite MSDN
5043      * documentation to the contrary, however, there is some evidence that
5044      * there may be problems with the implementation on Win98. As it is not
5045      * officially supported we take the cautious route and stick with thread
5046      * messages (hwnd == NULL) on platforms prior to Win2k.
5047      */
5048     if (IsWin2000()) {
5049         win32_create_message_window_class();
5050
5051         hwnd = CreateWindow("PerlMessageWindowClass", "PerlMessageWindow",
5052                 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
5053     }
5054
5055     /* If we din't create a window for any reason, then we'll use thread
5056      * messages for our signalling, so we install a hook which
5057      * is called by CallMsgFilter in win32_async_check(), or any other
5058      * modal loop (e.g. Win32::MsgBox or any other GUI extention, or anything
5059      * that use OLE, etc. */
5060     if(!hwnd) {
5061         SetWindowsHookEx(WH_MSGFILTER, win32_message_filter_proc,
5062                 NULL, GetCurrentThreadId());
5063     }
5064   
5065     return hwnd;
5066 }
5067
5068 #ifdef HAVE_INTERP_INTERN
5069
5070 static void
5071 win32_csighandler(int sig)
5072 {
5073 #if 0
5074     dTHXa(PERL_GET_SIG_CONTEXT);
5075     Perl_warn(aTHX_ "Got signal %d",sig);
5076 #endif
5077     /* Does nothing */
5078 }
5079
5080 #if defined(__MINGW32__) && defined(__cplusplus)
5081 #define CAST_HWND__(x) (HWND__*)(x)
5082 #else
5083 #define CAST_HWND__(x) x
5084 #endif
5085
5086 void
5087 Perl_sys_intern_init(pTHX)
5088 {
5089     int i;
5090
5091     w32_perlshell_tokens        = NULL;
5092     w32_perlshell_vec           = (char**)NULL;
5093     w32_perlshell_items         = 0;
5094     w32_fdpid                   = newAV();
5095     Newx(w32_children, 1, child_tab);
5096     w32_num_children            = 0;
5097 #  ifdef USE_ITHREADS
5098     w32_pseudo_id               = 0;
5099     Newx(w32_pseudo_children, 1, pseudo_child_tab);
5100     w32_num_pseudo_children     = 0;
5101 #  endif
5102     w32_timerid                 = 0;
5103     w32_message_hwnd            = CAST_HWND__(INVALID_HANDLE_VALUE);
5104     w32_poll_count              = 0;
5105     for (i=0; i < SIG_SIZE; i++) {
5106         w32_sighandler[i] = SIG_DFL;
5107     }
5108 #  ifdef MULTIPLICITY
5109     if (my_perl == PL_curinterp) {
5110 #  else
5111     {
5112 #  endif
5113         /* Force C runtime signal stuff to set its console handler */
5114         signal(SIGINT,win32_csighandler);
5115         signal(SIGBREAK,win32_csighandler);
5116
5117         /* We spawn asynchronous processes with the CREATE_NEW_PROCESS_GROUP
5118          * flag.  This has the side-effect of disabling Ctrl-C events in all
5119          * processes in this group.  At least on Windows NT and later we
5120          * can re-enable Ctrl-C handling by calling SetConsoleCtrlHandler()
5121          * with a NULL handler.  This is not valid on Windows 9X.
5122          */
5123         if (IsWinNT())
5124             SetConsoleCtrlHandler(NULL,FALSE);
5125
5126         /* Push our handler on top */
5127         SetConsoleCtrlHandler(win32_ctrlhandler,TRUE);
5128     }
5129 }
5130
5131 void
5132 Perl_sys_intern_clear(pTHX)
5133 {
5134     Safefree(w32_perlshell_tokens);
5135     Safefree(w32_perlshell_vec);
5136     /* NOTE: w32_fdpid is freed by sv_clean_all() */
5137     Safefree(w32_children);
5138     if (w32_timerid) {
5139         KillTimer(w32_message_hwnd, w32_timerid);
5140         w32_timerid = 0;
5141     }
5142     if (w32_message_hwnd != NULL && w32_message_hwnd != INVALID_HANDLE_VALUE)
5143         DestroyWindow(w32_message_hwnd);
5144 #  ifdef MULTIPLICITY
5145     if (my_perl == PL_curinterp) {
5146 #  else
5147     {
5148 #  endif
5149         SetConsoleCtrlHandler(win32_ctrlhandler,FALSE);
5150     }
5151 #  ifdef USE_ITHREADS
5152     Safefree(w32_pseudo_children);
5153 #  endif
5154 }
5155
5156 #  ifdef USE_ITHREADS
5157
5158 void
5159 Perl_sys_intern_dup(pTHX_ struct interp_intern *src, struct interp_intern *dst)
5160 {
5161     PERL_ARGS_ASSERT_SYS_INTERN_DUP;
5162
5163     dst->perlshell_tokens       = NULL;
5164     dst->perlshell_vec          = (char**)NULL;
5165     dst->perlshell_items        = 0;
5166     dst->fdpid                  = newAV();
5167     Newxz(dst->children, 1, child_tab);
5168     dst->pseudo_id              = 0;
5169     Newxz(dst->pseudo_children, 1, pseudo_child_tab);
5170     dst->timerid                = 0;
5171     dst->message_hwnd           = CAST_HWND__(INVALID_HANDLE_VALUE);
5172     dst->poll_count             = 0;
5173     Copy(src->sigtable,dst->sigtable,SIG_SIZE,Sighandler_t);
5174 }
5175 #  endif /* USE_ITHREADS */
5176 #endif /* HAVE_INTERP_INTERN */