e58af529e8be21f9ec423ab4735bcb0502ad8ce9
[p5sagit/p5-mst-13.2.git] / win32 / win32.c
1 /* WIN32.C
2  *
3  * (c) 1995 Microsoft Corporation. All rights reserved. 
4  *              Developed by hip communications inc., http://info.hip.com/info/
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
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
19 #ifndef __MINGW32__
20 #include <lmcons.h>
21 #include <lmerr.h>
22 /* ugliness to work around a buggy struct definition in lmwksta.h */
23 #undef LPTSTR
24 #define LPTSTR LPWSTR
25 #include <lmwksta.h>
26 #undef LPTSTR
27 #define LPTSTR LPSTR
28 #include <lmapibuf.h>
29 #endif /* __MINGW32__ */
30
31 /* #include "config.h" */
32
33 #define PERLIO_NOT_STDIO 0 
34 #if !defined(PERLIO_IS_STDIO) && !defined(USE_SFIO)
35 #define PerlIO FILE
36 #endif
37
38 #include "EXTERN.h"
39 #include "perl.h"
40
41 #define NO_XSLOCKS
42 #ifdef PERL_OBJECT
43 extern CPerlObj* pPerl;
44 #endif
45 #include "XSUB.h"
46
47 #include "Win32iop.h"
48 #include <fcntl.h>
49 #include <sys/stat.h>
50 #ifndef __GNUC__
51 /* assert.h conflicts with #define of assert in perl.h */
52 #include <assert.h>
53 #endif
54 #include <string.h>
55 #include <stdarg.h>
56 #include <float.h>
57 #include <time.h>
58 #if defined(_MSC_VER) || defined(__MINGW32__)
59 #include <sys/utime.h>
60 #else
61 #include <utime.h>
62 #endif
63
64 #ifdef __GNUC__
65 /* Mingw32 defaults to globing command line 
66  * So we turn it off like this:
67  */
68 int _CRT_glob = 0;
69 #endif
70
71 #define EXECF_EXEC 1
72 #define EXECF_SPAWN 2
73 #define EXECF_SPAWN_NOWAIT 3
74
75 #if defined(PERL_OBJECT)
76 #undef win32_get_privlib
77 #define win32_get_privlib g_win32_get_privlib
78 #undef win32_get_sitelib
79 #define win32_get_sitelib g_win32_get_sitelib
80 #undef do_aspawn
81 #define do_aspawn g_do_aspawn
82 #undef do_spawn
83 #define do_spawn g_do_spawn
84 #undef do_exec
85 #define do_exec g_do_exec
86 #undef getlogin
87 #define getlogin g_getlogin
88 #endif
89
90 static DWORD            os_id(void);
91 static void             get_shell(void);
92 static long             tokenize(char *str, char **dest, char ***destv);
93         int             do_spawn2(char *cmd, int exectype);
94 static BOOL             has_redirection(char *ptr);
95 static long             filetime_to_clock(PFILETIME ft);
96 static BOOL             filetime_from_time(PFILETIME ft, time_t t);
97 static char *           get_emd_part(char *leading, char *trailing, ...);
98 static void             remove_dead_process(HANDLE deceased);
99
100 HANDLE  w32_perldll_handle = INVALID_HANDLE_VALUE;
101 static DWORD    w32_platform = (DWORD)-1;
102
103 #ifdef USE_THREADS
104 #  ifdef USE_DECLSPEC_THREAD
105 __declspec(thread) char strerror_buffer[512];
106 __declspec(thread) char getlogin_buffer[128];
107 __declspec(thread) char w32_perllib_root[MAX_PATH+1];
108 #    ifdef HAVE_DES_FCRYPT
109 __declspec(thread) char crypt_buffer[30];
110 #    endif
111 #  else
112 #    define strerror_buffer     (thr->i.Wstrerror_buffer)
113 #    define getlogin_buffer     (thr->i.Wgetlogin_buffer)
114 #    define w32_perllib_root    (thr->i.Ww32_perllib_root)
115 #    define crypt_buffer        (thr->i.Wcrypt_buffer)
116 #  endif
117 #else
118 static char     strerror_buffer[512];
119 static char     getlogin_buffer[128];
120 static char     w32_perllib_root[MAX_PATH+1];
121 #  ifdef HAVE_DES_FCRYPT
122 static char     crypt_buffer[30];
123 #  endif
124 #endif
125
126 int 
127 IsWin95(void) {
128     return (os_id() == VER_PLATFORM_WIN32_WINDOWS);
129 }
130
131 int
132 IsWinNT(void) {
133     return (os_id() == VER_PLATFORM_WIN32_NT);
134 }
135
136 char*
137 GetRegStrFromKey(HKEY hkey, const char *lpszValueName, char** ptr, DWORD* lpDataLen)
138 {   /* Retrieve a REG_SZ or REG_EXPAND_SZ from the registry */
139     HKEY handle;
140     DWORD type;
141     const char *subkey = "Software\\Perl";
142     long retval;
143
144     retval = RegOpenKeyEx(hkey, subkey, 0, KEY_READ, &handle);
145     if (retval == ERROR_SUCCESS){
146         retval = RegQueryValueEx(handle, lpszValueName, 0, &type, NULL, lpDataLen);
147         if (retval == ERROR_SUCCESS && type == REG_SZ) {
148             if (*ptr) {
149                 Renew(*ptr, *lpDataLen, char);
150             }
151             else {
152                 New(1312, *ptr, *lpDataLen, char);
153             }
154             retval = RegQueryValueEx(handle, lpszValueName, 0, NULL, (PBYTE)*ptr, lpDataLen);
155             if (retval != ERROR_SUCCESS) {
156                 Safefree(*ptr);
157                 *ptr = Nullch;
158             }
159         }
160         RegCloseKey(handle);
161     }
162     return *ptr;
163 }
164
165 char*
166 GetRegStr(const char *lpszValueName, char** ptr, DWORD* lpDataLen)
167 {
168     *ptr = GetRegStrFromKey(HKEY_CURRENT_USER, lpszValueName, ptr, lpDataLen);
169     if (*ptr == Nullch)
170     {
171         *ptr = GetRegStrFromKey(HKEY_LOCAL_MACHINE, lpszValueName, ptr, lpDataLen);
172     }
173     return *ptr;
174 }
175
176 static char *
177 get_emd_part(char *prev_path, char *trailing_path, ...)
178 {
179     va_list ap;
180     char mod_name[MAX_PATH+1];
181     char *ptr;
182     char *optr;
183     char *strip;
184     int oldsize, newsize;
185
186     va_start(ap, trailing_path);
187     strip = va_arg(ap, char *);
188
189     GetModuleFileName((w32_perldll_handle == INVALID_HANDLE_VALUE)
190                       ? GetModuleHandle(NULL)
191                       : w32_perldll_handle, mod_name, sizeof(mod_name));
192     ptr = strrchr(mod_name, '\\');
193     while (ptr && strip) {
194         /* look for directories to skip back */
195         optr = ptr;
196         *ptr = '\0';
197         ptr = strrchr(mod_name, '\\');
198         if (!ptr || stricmp(ptr+1, strip) != 0) {
199             *optr = '\\';
200             ptr = optr;
201         }
202         strip = va_arg(ap, char *);
203     }
204     if (!ptr) {
205         ptr = mod_name;
206         *ptr++ = '.';
207         *ptr = '\\';
208     }
209     va_end(ap);
210     strcpy(++ptr, trailing_path);
211
212     newsize = strlen(mod_name) + 1;
213     if (prev_path) {
214         oldsize = strlen(prev_path) + 1;
215         newsize += oldsize;                     /* includes plus 1 for ';' */
216         Renew(prev_path, newsize, char);
217         prev_path[oldsize-1] = ';';
218         strcpy(&prev_path[oldsize], mod_name);
219     }
220     else {
221         New(1311, prev_path, newsize, char);
222         strcpy(prev_path, mod_name);
223     }
224
225     return prev_path;
226 }
227
228 char *
229 win32_get_privlib(char *pl)
230 {
231     char *stdlib = "lib";
232     char buffer[MAX_PATH+1];
233     char *path = Nullch;
234     DWORD datalen;
235
236     /* $stdlib = $HKCU{"lib-$]"} || $HKLM{"lib-$]"} || $HKCU{"lib"} || $HKLM{"lib"} || "";  */
237     sprintf(buffer, "%s-%s", stdlib, pl);
238     path = GetRegStr(buffer, &path, &datalen);
239     if (!path)
240         path = GetRegStr(stdlib, &path, &datalen);
241
242     /* $stdlib .= ";$EMD/../../lib" */
243     return get_emd_part(path, stdlib, ARCHNAME, "bin", Nullch);
244 }
245
246 char *
247 win32_get_sitelib(char *pl)
248 {
249     char *sitelib = "sitelib";
250     char regstr[40];
251     char pathstr[MAX_PATH+1];
252     DWORD datalen;
253     char *path1 = Nullch;
254     char *path2 = Nullch;
255     int len, newsize;
256
257     /* $HKCU{"sitelib-$]"} || $HKLM{"sitelib-$]"} . ---; */
258     sprintf(regstr, "%s-%s", sitelib, pl);
259     path1 = GetRegStr(regstr, &path1, &datalen);
260
261     /* $sitelib .=
262      * ";$EMD/" . ((-d $EMD/../../../$]) ? "../../.." : "../.."). "/site/$]/lib";  */
263     sprintf(pathstr, "site\\%s\\lib", pl);
264     path1 = get_emd_part(path1, pathstr, ARCHNAME, "bin", pl, Nullch);
265
266     /* $HKCU{'sitelib'} || $HKLM{'sitelib'} . ---; */
267     path2 = GetRegStr(sitelib, &path2, &datalen);
268
269     /* $sitelib .=
270      * ";$EMD/" . ((-d $EMD/../../../$]) ? "../../.." : "../.."). "/site/lib";  */
271     path2 = get_emd_part(path2, "site\\lib", ARCHNAME, "bin", pl, Nullch);
272
273     if (!path1)
274         return path2;
275
276     if (!path2)
277         return path1;
278
279     len = strlen(path1);
280     newsize = len + strlen(path2) + 2; /* plus one for ';' */
281
282     Renew(path1, newsize, char);
283     path1[len++] = ';';
284     strcpy(&path1[len], path2);
285
286     Safefree(path2);
287     return path1;
288 }
289
290
291 static BOOL
292 has_redirection(char *ptr)
293 {
294     int inquote = 0;
295     char quote = '\0';
296
297     /*
298      * Scan string looking for redirection (< or >) or pipe
299      * characters (|) that are not in a quoted string
300      */
301     while (*ptr) {
302         switch(*ptr) {
303         case '\'':
304         case '\"':
305             if (inquote) {
306                 if (quote == *ptr) {
307                     inquote = 0;
308                     quote = '\0';
309                 }
310             }
311             else {
312                 quote = *ptr;
313                 inquote++;
314             }
315             break;
316         case '>':
317         case '<':
318         case '|':
319             if (!inquote)
320                 return TRUE;
321         default:
322             break;
323         }
324         ++ptr;
325     }
326     return FALSE;
327 }
328
329 #if !defined(PERL_OBJECT)
330 /* since the current process environment is being updated in util.c
331  * the library functions will get the correct environment
332  */
333 PerlIO *
334 my_popen(char *cmd, char *mode)
335 {
336 #ifdef FIXCMD
337 #define fixcmd(x)       {                                       \
338                             char *pspace = strchr((x),' ');     \
339                             if (pspace) {                       \
340                                 char *p = (x);                  \
341                                 while (p < pspace) {            \
342                                     if (*p == '/')              \
343                                         *p = '\\';              \
344                                     p++;                        \
345                                 }                               \
346                             }                                   \
347                         }
348 #else
349 #define fixcmd(x)
350 #endif
351     fixcmd(cmd);
352     win32_fflush(stdout);
353     win32_fflush(stderr);
354     return win32_popen(cmd, mode);
355 }
356
357 long
358 my_pclose(PerlIO *fp)
359 {
360     return win32_pclose(fp);
361 }
362 #endif
363
364 static DWORD
365 os_id(void)
366 {
367     static OSVERSIONINFO osver;
368
369     if (osver.dwPlatformId != w32_platform) {
370         memset(&osver, 0, sizeof(OSVERSIONINFO));
371         osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
372         GetVersionEx(&osver);
373         w32_platform = osver.dwPlatformId;
374     }
375     return (w32_platform);
376 }
377
378 /* Tokenize a string.  Words are null-separated, and the list
379  * ends with a doubled null.  Any character (except null and
380  * including backslash) may be escaped by preceding it with a
381  * backslash (the backslash will be stripped).
382  * Returns number of words in result buffer.
383  */
384 static long
385 tokenize(char *str, char **dest, char ***destv)
386 {
387     char *retstart = Nullch;
388     char **retvstart = 0;
389     int items = -1;
390     if (str) {
391         int slen = strlen(str);
392         register char *ret;
393         register char **retv;
394         New(1307, ret, slen+2, char);
395         New(1308, retv, (slen+3)/2, char*);
396
397         retstart = ret;
398         retvstart = retv;
399         *retv = ret;
400         items = 0;
401         while (*str) {
402             *ret = *str++;
403             if (*ret == '\\' && *str)
404                 *ret = *str++;
405             else if (*ret == ' ') {
406                 while (*str == ' ')
407                     str++;
408                 if (ret == retstart)
409                     ret--;
410                 else {
411                     *ret = '\0';
412                     ++items;
413                     if (*str)
414                         *++retv = ret+1;
415                 }
416             }
417             else if (!*str)
418                 ++items;
419             ret++;
420         }
421         retvstart[items] = Nullch;
422         *ret++ = '\0';
423         *ret = '\0';
424     }
425     *dest = retstart;
426     *destv = retvstart;
427     return items;
428 }
429
430 static void
431 get_shell(void)
432 {
433     if (!w32_perlshell_tokens) {
434         /* we don't use COMSPEC here for two reasons:
435          *  1. the same reason perl on UNIX doesn't use SHELL--rampant and
436          *     uncontrolled unportability of the ensuing scripts.
437          *  2. PERL5SHELL could be set to a shell that may not be fit for
438          *     interactive use (which is what most programs look in COMSPEC
439          *     for).
440          */
441         char* defaultshell = (IsWinNT() ? "cmd.exe /x/c" : "command.com /c");
442         char *usershell = getenv("PERL5SHELL");
443         w32_perlshell_items = tokenize(usershell ? usershell : defaultshell,
444                                        &w32_perlshell_tokens,
445                                        &w32_perlshell_vec);
446     }
447 }
448
449 int
450 do_aspawn(void *vreally, void **vmark, void **vsp)
451 {
452     SV *really = (SV*)vreally;
453     SV **mark = (SV**)vmark;
454     SV **sp = (SV**)vsp;
455     char **argv;
456     char *str;
457     int status;
458     int flag = P_WAIT;
459     int index = 0;
460
461     if (sp <= mark)
462         return -1;
463
464     get_shell();
465     New(1306, argv, (sp - mark) + w32_perlshell_items + 2, char*);
466
467     if (SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
468         ++mark;
469         flag = SvIVx(*mark);
470     }
471
472     while (++mark <= sp) {
473         if (*mark && (str = SvPV(*mark, PL_na)))
474             argv[index++] = str;
475         else
476             argv[index++] = "";
477     }
478     argv[index++] = 0;
479    
480     status = win32_spawnvp(flag,
481                            (const char*)(really ? SvPV(really,PL_na) : argv[0]),
482                            (const char* const*)argv);
483
484     if (status < 0 && errno == ENOEXEC) {
485         /* possible shell-builtin, invoke with shell */
486         int sh_items;
487         sh_items = w32_perlshell_items;
488         while (--index >= 0)
489             argv[index+sh_items] = argv[index];
490         while (--sh_items >= 0)
491             argv[sh_items] = w32_perlshell_vec[sh_items];
492    
493         status = win32_spawnvp(flag,
494                                (const char*)(really ? SvPV(really,PL_na) : argv[0]),
495                                (const char* const*)argv);
496     }
497
498     if (flag != P_NOWAIT) {
499         if (status < 0) {
500             if (PL_dowarn)
501                 warn("Can't spawn \"%s\": %s", argv[0], strerror(errno));
502             status = 255 * 256;
503         }
504         else
505             status *= 256;
506         PL_statusvalue = status;
507     }
508     Safefree(argv);
509     return (status);
510 }
511
512 int
513 do_spawn2(char *cmd, int exectype)
514 {
515     char **a;
516     char *s;
517     char **argv;
518     int status = -1;
519     BOOL needToTry = TRUE;
520     char *cmd2;
521
522     /* Save an extra exec if possible. See if there are shell
523      * metacharacters in it */
524     if (!has_redirection(cmd)) {
525         New(1301,argv, strlen(cmd) / 2 + 2, char*);
526         New(1302,cmd2, strlen(cmd) + 1, char);
527         strcpy(cmd2, cmd);
528         a = argv;
529         for (s = cmd2; *s;) {
530             while (*s && isspace(*s))
531                 s++;
532             if (*s)
533                 *(a++) = s;
534             while (*s && !isspace(*s))
535                 s++;
536             if (*s)
537                 *s++ = '\0';
538         }
539         *a = Nullch;
540         if (argv[0]) {
541             switch (exectype) {
542             case EXECF_SPAWN:
543                 status = win32_spawnvp(P_WAIT, argv[0],
544                                        (const char* const*)argv);
545                 break;
546             case EXECF_SPAWN_NOWAIT:
547                 status = win32_spawnvp(P_NOWAIT, argv[0],
548                                        (const char* const*)argv);
549                 break;
550             case EXECF_EXEC:
551                 status = win32_execvp(argv[0], (const char* const*)argv);
552                 break;
553             }
554             if (status != -1 || errno == 0)
555                 needToTry = FALSE;
556         }
557         Safefree(argv);
558         Safefree(cmd2);
559     }
560     if (needToTry) {
561         char **argv;
562         int i = -1;
563         get_shell();
564         New(1306, argv, w32_perlshell_items + 2, char*);
565         while (++i < w32_perlshell_items)
566             argv[i] = w32_perlshell_vec[i];
567         argv[i++] = cmd;
568         argv[i] = Nullch;
569         switch (exectype) {
570         case EXECF_SPAWN:
571             status = win32_spawnvp(P_WAIT, argv[0],
572                                    (const char* const*)argv);
573             break;
574         case EXECF_SPAWN_NOWAIT:
575             status = win32_spawnvp(P_NOWAIT, argv[0],
576                                    (const char* const*)argv);
577             break;
578         case EXECF_EXEC:
579             status = win32_execvp(argv[0], (const char* const*)argv);
580             break;
581         }
582         cmd = argv[0];
583         Safefree(argv);
584     }
585     if (exectype != EXECF_SPAWN_NOWAIT) {
586         if (status < 0) {
587             if (PL_dowarn)
588                 warn("Can't %s \"%s\": %s",
589                      (exectype == EXECF_EXEC ? "exec" : "spawn"),
590                      cmd, strerror(errno));
591             status = 255 * 256;
592         }
593         else
594             status *= 256;
595         PL_statusvalue = status;
596     }
597     return (status);
598 }
599
600 int
601 do_spawn(char *cmd)
602 {
603     return do_spawn2(cmd, EXECF_SPAWN);
604 }
605
606 int
607 do_spawn_nowait(char *cmd)
608 {
609     return do_spawn2(cmd, EXECF_SPAWN_NOWAIT);
610 }
611
612 bool
613 do_exec(char *cmd)
614 {
615     do_spawn2(cmd, EXECF_EXEC);
616     return FALSE;
617 }
618
619 /* The idea here is to read all the directory names into a string table
620  * (separated by nulls) and when one of the other dir functions is called
621  * return the pointer to the current file name.
622  */
623 DIR *
624 win32_opendir(char *filename)
625 {
626     DIR                 *p;
627     long                len;
628     long                idx;
629     char                scanname[MAX_PATH+3];
630     struct stat         sbuf;
631     WIN32_FIND_DATA     FindData;
632     HANDLE              fh;
633
634     len = strlen(filename);
635     if (len > MAX_PATH)
636         return NULL;
637
638     /* check to see if filename is a directory */
639     if (win32_stat(filename, &sbuf) < 0 || (sbuf.st_mode & S_IFDIR) == 0) {
640         /* CRT is buggy on sharenames, so make sure it really isn't */
641         DWORD r = GetFileAttributes(filename);
642         if (r == 0xffffffff || !(r & FILE_ATTRIBUTE_DIRECTORY))
643             return NULL;
644     }
645
646     /* Get us a DIR structure */
647     Newz(1303, p, 1, DIR);
648     if (p == NULL)
649         return NULL;
650
651     /* Create the search pattern */
652     strcpy(scanname, filename);
653     if (scanname[len-1] != '/' && scanname[len-1] != '\\')
654         scanname[len++] = '/';
655     scanname[len++] = '*';
656     scanname[len] = '\0';
657
658     /* do the FindFirstFile call */
659     fh = FindFirstFile(scanname, &FindData);
660     if (fh == INVALID_HANDLE_VALUE) {
661         return NULL;
662     }
663
664     /* now allocate the first part of the string table for
665      * the filenames that we find.
666      */
667     idx = strlen(FindData.cFileName)+1;
668     New(1304, p->start, idx, char);
669     if (p->start == NULL)
670         croak("opendir: malloc failed!\n");
671     strcpy(p->start, FindData.cFileName);
672     p->nfiles++;
673
674     /* loop finding all the files that match the wildcard
675      * (which should be all of them in this directory!).
676      * the variable idx should point one past the null terminator
677      * of the previous string found.
678      */
679     while (FindNextFile(fh, &FindData)) {
680         len = strlen(FindData.cFileName);
681         /* bump the string table size by enough for the
682          * new name and it's null terminator
683          */
684         Renew(p->start, idx+len+1, char);
685         if (p->start == NULL)
686             croak("opendir: malloc failed!\n");
687         strcpy(&p->start[idx], FindData.cFileName);
688         p->nfiles++;
689         idx += len+1;
690     }
691     FindClose(fh);
692     p->size = idx;
693     p->curr = p->start;
694     return p;
695 }
696
697
698 /* Readdir just returns the current string pointer and bumps the
699  * string pointer to the nDllExport entry.
700  */
701 struct direct *
702 win32_readdir(DIR *dirp)
703 {
704     int         len;
705     static int  dummy = 0;
706
707     if (dirp->curr) {
708         /* first set up the structure to return */
709         len = strlen(dirp->curr);
710         strcpy(dirp->dirstr.d_name, dirp->curr);
711         dirp->dirstr.d_namlen = len;
712
713         /* Fake an inode */
714         dirp->dirstr.d_ino = dummy++;
715
716         /* Now set up for the nDllExport call to readdir */
717         dirp->curr += len + 1;
718         if (dirp->curr >= (dirp->start + dirp->size)) {
719             dirp->curr = NULL;
720         }
721
722         return &(dirp->dirstr);
723     } 
724     else
725         return NULL;
726 }
727
728 /* Telldir returns the current string pointer position */
729 long
730 win32_telldir(DIR *dirp)
731 {
732     return (long) dirp->curr;
733 }
734
735
736 /* Seekdir moves the string pointer to a previously saved position
737  *(Saved by telldir).
738  */
739 void
740 win32_seekdir(DIR *dirp, long loc)
741 {
742     dirp->curr = (char *)loc;
743 }
744
745 /* Rewinddir resets the string pointer to the start */
746 void
747 win32_rewinddir(DIR *dirp)
748 {
749     dirp->curr = dirp->start;
750 }
751
752 /* free the memory allocated by opendir */
753 int
754 win32_closedir(DIR *dirp)
755 {
756     Safefree(dirp->start);
757     Safefree(dirp);
758     return 1;
759 }
760
761
762 /*
763  * various stubs
764  */
765
766
767 /* Ownership
768  *
769  * Just pretend that everyone is a superuser. NT will let us know if
770  * we don\'t really have permission to do something.
771  */
772
773 #define ROOT_UID    ((uid_t)0)
774 #define ROOT_GID    ((gid_t)0)
775
776 uid_t
777 getuid(void)
778 {
779     return ROOT_UID;
780 }
781
782 uid_t
783 geteuid(void)
784 {
785     return ROOT_UID;
786 }
787
788 gid_t
789 getgid(void)
790 {
791     return ROOT_GID;
792 }
793
794 gid_t
795 getegid(void)
796 {
797     return ROOT_GID;
798 }
799
800 int
801 setuid(uid_t auid)
802
803     return (auid == ROOT_UID ? 0 : -1);
804 }
805
806 int
807 setgid(gid_t agid)
808 {
809     return (agid == ROOT_GID ? 0 : -1);
810 }
811
812 char *
813 getlogin(void)
814 {
815     dTHR;
816     char *buf = getlogin_buffer;
817     DWORD size = sizeof(getlogin_buffer);
818     if (GetUserName(buf,&size))
819         return buf;
820     return (char*)NULL;
821 }
822
823 int
824 chown(const char *path, uid_t owner, gid_t group)
825 {
826     /* XXX noop */
827     return 0;
828 }
829
830 static void
831 remove_dead_process(HANDLE deceased)
832 {
833 #ifndef USE_RTL_WAIT
834     int child;
835     for (child = 0 ; child < w32_num_children ; ++child) {
836         if (w32_child_pids[child] == deceased) {
837             Copy(&w32_child_pids[child+1], &w32_child_pids[child],
838                  (w32_num_children-child-1), HANDLE);
839             w32_num_children--;
840             break;
841         }
842     }
843 #endif
844 }
845
846 DllExport int
847 win32_kill(int pid, int sig)
848 {
849 #ifdef USE_RTL_WAIT
850     HANDLE hProcess= OpenProcess(PROCESS_ALL_ACCESS, TRUE, pid);
851 #else
852     HANDLE hProcess = (HANDLE) pid;
853 #endif
854
855     if (hProcess == NULL) {
856         croak("kill process failed!\n");
857     }
858     else {
859         if (!TerminateProcess(hProcess, sig))
860             croak("kill process failed!\n");
861         CloseHandle(hProcess);
862
863         /* WaitForMultipleObjects() on a pid that was killed returns error
864          * so if we know the pid is gone we remove it from process list */
865         remove_dead_process(hProcess);
866     }
867     return 0;
868 }
869
870 /*
871  * File system stuff
872  */
873
874 DllExport unsigned int
875 win32_sleep(unsigned int t)
876 {
877     Sleep(t*1000);
878     return 0;
879 }
880
881 DllExport int
882 win32_stat(const char *path, struct stat *buffer)
883 {
884     char                t[MAX_PATH+1]; 
885     const char  *p = path;
886     int         l = strlen(path);
887     int         res;
888
889     if (l > 1) {
890         switch(path[l - 1]) {
891         case '\\':
892         case '/':
893             if (path[l - 2] != ':') {
894                 strncpy(t, path, l - 1);
895                 t[l - 1] = 0;
896                 p = t;
897             };
898         }
899     }
900     res = stat(p,buffer);
901 #ifdef __BORLANDC__
902     if (res == 0) {
903         if (S_ISDIR(buffer->st_mode))
904             buffer->st_mode |= S_IWRITE | S_IEXEC;
905         else if (S_ISREG(buffer->st_mode)) {
906             if (l >= 4 && path[l-4] == '.') {
907                 const char *e = path + l - 3;
908                 if (strnicmp(e,"exe",3)
909                     && strnicmp(e,"bat",3)
910                     && strnicmp(e,"com",3)
911                     && (IsWin95() || strnicmp(e,"cmd",3)))
912                     buffer->st_mode &= ~S_IEXEC;
913                 else
914                     buffer->st_mode |= S_IEXEC;
915             }
916             else
917                 buffer->st_mode &= ~S_IEXEC;
918         }
919     }
920 #endif
921     return res;
922 }
923
924 #ifndef USE_WIN32_RTL_ENV
925
926 DllExport char *
927 win32_getenv(const char *name)
928 {
929     static char *curitem = Nullch;      /* XXX threadead */
930     static DWORD curlen = 0;            /* XXX threadead */
931     DWORD needlen;
932     if (!curitem) {
933         curlen = 512;
934         New(1305,curitem,curlen,char);
935     }
936
937     needlen = GetEnvironmentVariable(name,curitem,curlen);
938     if (needlen != 0) {
939         while (needlen > curlen) {
940             Renew(curitem,needlen,char);
941             curlen = needlen;
942             needlen = GetEnvironmentVariable(name,curitem,curlen);
943         }
944     }
945     else {
946         /* allow any environment variables that begin with 'PERL'
947            to be stored in the registry */
948         if (curitem)
949             *curitem = '\0';
950
951         if (strncmp(name, "PERL", 4) == 0) {
952             if (curitem) {
953                 Safefree(curitem);
954                 curitem = Nullch;
955                 curlen = 0;
956             }
957             curitem = GetRegStr(name, &curitem, &curlen);
958         }
959     }
960     if (curitem && *curitem == '\0')
961         return Nullch;
962
963     return curitem;
964 }
965
966 #endif
967
968 static long
969 filetime_to_clock(PFILETIME ft)
970 {
971  __int64 qw = ft->dwHighDateTime;
972  qw <<= 32;
973  qw |= ft->dwLowDateTime;
974  qw /= 10000;  /* File time ticks at 0.1uS, clock at 1mS */
975  return (long) qw;
976 }
977
978 DllExport int
979 win32_times(struct tms *timebuf)
980 {
981     FILETIME user;
982     FILETIME kernel;
983     FILETIME dummy;
984     if (GetProcessTimes(GetCurrentProcess(), &dummy, &dummy, 
985                         &kernel,&user)) {
986         timebuf->tms_utime = filetime_to_clock(&user);
987         timebuf->tms_stime = filetime_to_clock(&kernel);
988         timebuf->tms_cutime = 0;
989         timebuf->tms_cstime = 0;
990         
991     } else { 
992         /* That failed - e.g. Win95 fallback to clock() */
993         clock_t t = clock();
994         timebuf->tms_utime = t;
995         timebuf->tms_stime = 0;
996         timebuf->tms_cutime = 0;
997         timebuf->tms_cstime = 0;
998     }
999     return 0;
1000 }
1001
1002 /* fix utime() so it works on directories in NT
1003  * thanks to Jan Dubois <jan.dubois@ibm.net>
1004  */
1005 static BOOL
1006 filetime_from_time(PFILETIME pFileTime, time_t Time)
1007 {
1008     struct tm *pTM = gmtime(&Time);
1009     SYSTEMTIME SystemTime;
1010
1011     if (pTM == NULL)
1012         return FALSE;
1013
1014     SystemTime.wYear   = pTM->tm_year + 1900;
1015     SystemTime.wMonth  = pTM->tm_mon + 1;
1016     SystemTime.wDay    = pTM->tm_mday;
1017     SystemTime.wHour   = pTM->tm_hour;
1018     SystemTime.wMinute = pTM->tm_min;
1019     SystemTime.wSecond = pTM->tm_sec;
1020     SystemTime.wMilliseconds = 0;
1021
1022     return SystemTimeToFileTime(&SystemTime, pFileTime);
1023 }
1024
1025 DllExport int
1026 win32_utime(const char *filename, struct utimbuf *times)
1027 {
1028     HANDLE handle;
1029     FILETIME ftCreate;
1030     FILETIME ftAccess;
1031     FILETIME ftWrite;
1032     struct utimbuf TimeBuffer;
1033
1034     int rc = utime(filename,times);
1035     /* EACCES: path specifies directory or readonly file */
1036     if (rc == 0 || errno != EACCES /* || !IsWinNT() */)
1037         return rc;
1038
1039     if (times == NULL) {
1040         times = &TimeBuffer;
1041         time(&times->actime);
1042         times->modtime = times->actime;
1043     }
1044
1045     /* This will (and should) still fail on readonly files */
1046     handle = CreateFile(filename, GENERIC_READ | GENERIC_WRITE,
1047                         FILE_SHARE_READ | FILE_SHARE_DELETE, NULL,
1048                         OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
1049     if (handle == INVALID_HANDLE_VALUE)
1050         return rc;
1051
1052     if (GetFileTime(handle, &ftCreate, &ftAccess, &ftWrite) &&
1053         filetime_from_time(&ftAccess, times->actime) &&
1054         filetime_from_time(&ftWrite, times->modtime) &&
1055         SetFileTime(handle, &ftCreate, &ftAccess, &ftWrite))
1056     {
1057         rc = 0;
1058     }
1059
1060     CloseHandle(handle);
1061     return rc;
1062 }
1063
1064 DllExport int
1065 win32_waitpid(int pid, int *status, int flags)
1066 {
1067     int rc;
1068     if (pid == -1) 
1069       return win32_wait(status);
1070     else {
1071       rc = cwait(status, pid, WAIT_CHILD);
1072     /* cwait() returns differently on Borland */
1073 #ifdef __BORLANDC__
1074     if (status)
1075         *status =  (((*status >> 8) & 0xff) | ((*status << 8) & 0xff00));
1076 #endif
1077       remove_dead_process((HANDLE)pid);
1078     }
1079     return rc >= 0 ? pid : rc;                
1080 }
1081
1082 DllExport int
1083 win32_wait(int *status)
1084 {
1085 #ifdef USE_RTL_WAIT
1086     return wait(status);
1087 #else
1088     /* XXX this wait emulation only knows about processes
1089      * spawned via win32_spawnvp(P_NOWAIT, ...).
1090      */
1091     int i, retval;
1092     DWORD exitcode, waitcode;
1093
1094     if (!w32_num_children) {
1095         errno = ECHILD;
1096         return -1;
1097     }
1098
1099     /* if a child exists, wait for it to die */
1100     waitcode = WaitForMultipleObjects(w32_num_children,
1101                                       w32_child_pids,
1102                                       FALSE,
1103                                       INFINITE);
1104     if (waitcode != WAIT_FAILED) {
1105         if (waitcode >= WAIT_ABANDONED_0
1106             && waitcode < WAIT_ABANDONED_0 + w32_num_children)
1107             i = waitcode - WAIT_ABANDONED_0;
1108         else
1109             i = waitcode - WAIT_OBJECT_0;
1110         if (GetExitCodeProcess(w32_child_pids[i], &exitcode) ) {
1111             CloseHandle(w32_child_pids[i]);
1112             *status = (int)((exitcode & 0xff) << 8);
1113             retval = (int)w32_child_pids[i];
1114             Copy(&w32_child_pids[i+1], &w32_child_pids[i],
1115                  (w32_num_children-i-1), HANDLE);
1116             w32_num_children--;
1117             return retval;
1118         }
1119     }
1120
1121 FAILED:
1122     errno = GetLastError();
1123     return -1;
1124
1125 #endif
1126 }
1127
1128 static UINT timerid = 0;
1129
1130 static VOID CALLBACK TimerProc(HWND win, UINT msg, UINT id, DWORD time)
1131 {
1132  KillTimer(NULL,timerid);
1133  timerid=0;  
1134  sighandler(14);
1135 }
1136
1137 DllExport unsigned int
1138 win32_alarm(unsigned int sec)
1139 {
1140     /* 
1141      * the 'obvious' implentation is SetTimer() with a callback
1142      * which does whatever receiving SIGALRM would do 
1143      * we cannot use SIGALRM even via raise() as it is not 
1144      * one of the supported codes in <signal.h>
1145      *
1146      * Snag is unless something is looking at the message queue
1147      * nothing happens :-(
1148      */ 
1149     if (sec)
1150      {
1151       timerid = SetTimer(NULL,timerid,sec*1000,(TIMERPROC)TimerProc);
1152       if (!timerid)
1153        croak("Cannot set timer");
1154      } 
1155     else
1156      {
1157       if (timerid)
1158        {
1159         KillTimer(NULL,timerid);
1160         timerid=0;  
1161        }
1162      }
1163     return 0;
1164 }
1165
1166 #if defined(HAVE_DES_FCRYPT) || defined(PERL_OBJECT)
1167 #ifdef HAVE_DES_FCRYPT
1168 extern char *   des_fcrypt(const char *txt, const char *salt, char *cbuf);
1169 #endif
1170
1171 DllExport char *
1172 win32_crypt(const char *txt, const char *salt)
1173 {
1174 #ifdef HAVE_DES_FCRYPT
1175     dTHR;
1176     return des_fcrypt(txt, salt, crypt_buffer);
1177 #else
1178     die("The crypt() function is unimplemented due to excessive paranoia.");
1179     return Nullch;
1180 #endif
1181 }
1182 #endif
1183
1184 #ifdef USE_FIXED_OSFHANDLE
1185
1186 EXTERN_C int __cdecl _alloc_osfhnd(void);
1187 EXTERN_C int __cdecl _set_osfhnd(int fh, long value);
1188 EXTERN_C void __cdecl _lock_fhandle(int);
1189 EXTERN_C void __cdecl _unlock_fhandle(int);
1190 EXTERN_C void __cdecl _unlock(int);
1191
1192 #if     (_MSC_VER >= 1000)
1193 typedef struct  {
1194     long osfhnd;    /* underlying OS file HANDLE */
1195     char osfile;    /* attributes of file (e.g., open in text mode?) */
1196     char pipech;    /* one char buffer for handles opened on pipes */
1197 #if defined (_MT) && !defined (DLL_FOR_WIN32S)
1198     int lockinitflag;
1199     CRITICAL_SECTION lock;
1200 #endif  /* defined (_MT) && !defined (DLL_FOR_WIN32S) */
1201 }       ioinfo;
1202
1203 EXTERN_C ioinfo * __pioinfo[];
1204
1205 #define IOINFO_L2E                      5
1206 #define IOINFO_ARRAY_ELTS       (1 << IOINFO_L2E)
1207 #define _pioinfo(i)     (__pioinfo[i >> IOINFO_L2E] + (i & (IOINFO_ARRAY_ELTS - 1)))
1208 #define _osfile(i)      (_pioinfo(i)->osfile)
1209
1210 #else   /* (_MSC_VER >= 1000) */
1211 extern char _osfile[];
1212 #endif  /* (_MSC_VER >= 1000) */
1213
1214 #define FOPEN                   0x01    /* file handle open */
1215 #define FAPPEND                 0x20    /* file handle opened O_APPEND */
1216 #define FDEV                    0x40    /* file handle refers to device */
1217 #define FTEXT                   0x80    /* file handle is in text mode */
1218
1219 #define _STREAM_LOCKS   26              /* Table of stream locks */
1220 #define _LAST_STREAM_LOCK  (_STREAM_LOCKS+_NSTREAM_-1)  /* Last stream lock */
1221 #define _FH_LOCKS          (_LAST_STREAM_LOCK+1)        /* Table of fh locks */
1222
1223 /***
1224 *int my_open_osfhandle(long osfhandle, int flags) - open C Runtime file handle
1225 *
1226 *Purpose:
1227 *       This function allocates a free C Runtime file handle and associates
1228 *       it with the Win32 HANDLE specified by the first parameter. This is a
1229 *               temperary fix for WIN95's brain damage GetFileType() error on socket
1230 *               we just bypass that call for socket
1231 *
1232 *Entry:
1233 *       long osfhandle - Win32 HANDLE to associate with C Runtime file handle.
1234 *       int flags      - flags to associate with C Runtime file handle.
1235 *
1236 *Exit:
1237 *       returns index of entry in fh, if successful
1238 *       return -1, if no free entry is found
1239 *
1240 *Exceptions:
1241 *
1242 *******************************************************************************/
1243
1244 static int
1245 my_open_osfhandle(long osfhandle, int flags)
1246 {
1247     int fh;
1248     char fileflags;             /* _osfile flags */
1249
1250     /* copy relevant flags from second parameter */
1251     fileflags = FDEV;
1252
1253     if (flags & O_APPEND)
1254         fileflags |= FAPPEND;
1255
1256     if (flags & O_TEXT)
1257         fileflags |= FTEXT;
1258
1259     /* attempt to allocate a C Runtime file handle */
1260     if ((fh = _alloc_osfhnd()) == -1) {
1261         errno = EMFILE;         /* too many open files */
1262         _doserrno = 0L;         /* not an OS error */
1263         return -1;              /* return error to caller */
1264     }
1265
1266     /* the file is open. now, set the info in _osfhnd array */
1267     _set_osfhnd(fh, osfhandle);
1268
1269     fileflags |= FOPEN;         /* mark as open */
1270
1271 #if (_MSC_VER >= 1000)
1272     _osfile(fh) = fileflags;    /* set osfile entry */
1273     _unlock_fhandle(fh);
1274 #else
1275     _osfile[fh] = fileflags;    /* set osfile entry */
1276     _unlock(fh+_FH_LOCKS);              /* unlock handle */
1277 #endif
1278
1279     return fh;                  /* return handle */
1280 }
1281
1282 #define _open_osfhandle my_open_osfhandle
1283 #endif  /* USE_FIXED_OSFHANDLE */
1284
1285 /* simulate flock by locking a range on the file */
1286
1287 #define LK_ERR(f,i)     ((f) ? (i = 0) : (errno = GetLastError()))
1288 #define LK_LEN          0xffff0000
1289
1290 DllExport int
1291 win32_flock(int fd, int oper)
1292 {
1293     OVERLAPPED o;
1294     int i = -1;
1295     HANDLE fh;
1296
1297     if (!IsWinNT()) {
1298         croak("flock() unimplemented on this platform");
1299         return -1;
1300     }
1301     fh = (HANDLE)_get_osfhandle(fd);
1302     memset(&o, 0, sizeof(o));
1303
1304     switch(oper) {
1305     case LOCK_SH:               /* shared lock */
1306         LK_ERR(LockFileEx(fh, 0, 0, LK_LEN, 0, &o),i);
1307         break;
1308     case LOCK_EX:               /* exclusive lock */
1309         LK_ERR(LockFileEx(fh, LOCKFILE_EXCLUSIVE_LOCK, 0, LK_LEN, 0, &o),i);
1310         break;
1311     case LOCK_SH|LOCK_NB:       /* non-blocking shared lock */
1312         LK_ERR(LockFileEx(fh, LOCKFILE_FAIL_IMMEDIATELY, 0, LK_LEN, 0, &o),i);
1313         break;
1314     case LOCK_EX|LOCK_NB:       /* non-blocking exclusive lock */
1315         LK_ERR(LockFileEx(fh,
1316                        LOCKFILE_EXCLUSIVE_LOCK|LOCKFILE_FAIL_IMMEDIATELY,
1317                        0, LK_LEN, 0, &o),i);
1318         break;
1319     case LOCK_UN:               /* unlock lock */
1320         LK_ERR(UnlockFileEx(fh, 0, LK_LEN, 0, &o),i);
1321         break;
1322     default:                    /* unknown */
1323         errno = EINVAL;
1324         break;
1325     }
1326     return i;
1327 }
1328
1329 #undef LK_ERR
1330 #undef LK_LEN
1331
1332 /*
1333  *  redirected io subsystem for all XS modules
1334  *
1335  */
1336
1337 DllExport int *
1338 win32_errno(void)
1339 {
1340     return (&errno);
1341 }
1342
1343 DllExport char ***
1344 win32_environ(void)
1345 {
1346     return (&(_environ));
1347 }
1348
1349 /* the rest are the remapped stdio routines */
1350 DllExport FILE *
1351 win32_stderr(void)
1352 {
1353     return (stderr);
1354 }
1355
1356 DllExport FILE *
1357 win32_stdin(void)
1358 {
1359     return (stdin);
1360 }
1361
1362 DllExport FILE *
1363 win32_stdout()
1364 {
1365     return (stdout);
1366 }
1367
1368 DllExport int
1369 win32_ferror(FILE *fp)
1370 {
1371     return (ferror(fp));
1372 }
1373
1374
1375 DllExport int
1376 win32_feof(FILE *fp)
1377 {
1378     return (feof(fp));
1379 }
1380
1381 /*
1382  * Since the errors returned by the socket error function 
1383  * WSAGetLastError() are not known by the library routine strerror
1384  * we have to roll our own.
1385  */
1386
1387 DllExport char *
1388 win32_strerror(int e) 
1389 {
1390 #ifndef __BORLANDC__            /* Borland intolerance */
1391     extern int sys_nerr;
1392 #endif
1393     DWORD source = 0;
1394
1395     if (e < 0 || e > sys_nerr) {
1396         dTHR;
1397         if (e < 0)
1398             e = GetLastError();
1399
1400         if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, &source, e, 0,
1401                          strerror_buffer, sizeof(strerror_buffer), NULL) == 0) 
1402             strcpy(strerror_buffer, "Unknown Error");
1403
1404         return strerror_buffer;
1405     }
1406     return strerror(e);
1407 }
1408
1409 DllExport void
1410 win32_str_os_error(void *sv, DWORD dwErr)
1411 {
1412     DWORD dwLen;
1413     char *sMsg;
1414     dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER
1415                           |FORMAT_MESSAGE_IGNORE_INSERTS
1416                           |FORMAT_MESSAGE_FROM_SYSTEM, NULL,
1417                            dwErr, 0, (char *)&sMsg, 1, NULL);
1418     if (0 < dwLen) {
1419         while (0 < dwLen  &&  isspace(sMsg[--dwLen]))
1420             ;
1421         if ('.' != sMsg[dwLen])
1422             dwLen++;
1423         sMsg[dwLen]= '\0';
1424     }
1425     if (0 == dwLen) {
1426         sMsg = (char*)LocalAlloc(0, 64/**sizeof(TCHAR)*/);
1427         dwLen = sprintf(sMsg,
1428                         "Unknown error #0x%lX (lookup 0x%lX)",
1429                         dwErr, GetLastError());
1430     }
1431     sv_setpvn((SV*)sv, sMsg, dwLen);
1432     LocalFree(sMsg);
1433 }
1434
1435
1436 DllExport int
1437 win32_fprintf(FILE *fp, const char *format, ...)
1438 {
1439     va_list marker;
1440     va_start(marker, format);     /* Initialize variable arguments. */
1441
1442     return (vfprintf(fp, format, marker));
1443 }
1444
1445 DllExport int
1446 win32_printf(const char *format, ...)
1447 {
1448     va_list marker;
1449     va_start(marker, format);     /* Initialize variable arguments. */
1450
1451     return (vprintf(format, marker));
1452 }
1453
1454 DllExport int
1455 win32_vfprintf(FILE *fp, const char *format, va_list args)
1456 {
1457     return (vfprintf(fp, format, args));
1458 }
1459
1460 DllExport int
1461 win32_vprintf(const char *format, va_list args)
1462 {
1463     return (vprintf(format, args));
1464 }
1465
1466 DllExport size_t
1467 win32_fread(void *buf, size_t size, size_t count, FILE *fp)
1468 {
1469     return fread(buf, size, count, fp);
1470 }
1471
1472 DllExport size_t
1473 win32_fwrite(const void *buf, size_t size, size_t count, FILE *fp)
1474 {
1475     return fwrite(buf, size, count, fp);
1476 }
1477
1478 DllExport FILE *
1479 win32_fopen(const char *filename, const char *mode)
1480 {
1481     if (stricmp(filename, "/dev/null")==0)
1482         return fopen("NUL", mode);
1483     return fopen(filename, mode);
1484 }
1485
1486 #ifndef USE_SOCKETS_AS_HANDLES
1487 #undef fdopen
1488 #define fdopen my_fdopen
1489 #endif
1490
1491 DllExport FILE *
1492 win32_fdopen( int handle, const char *mode)
1493 {
1494     return fdopen(handle, (char *) mode);
1495 }
1496
1497 DllExport FILE *
1498 win32_freopen( const char *path, const char *mode, FILE *stream)
1499 {
1500     if (stricmp(path, "/dev/null")==0)
1501         return freopen("NUL", mode, stream);
1502     return freopen(path, mode, stream);
1503 }
1504
1505 DllExport int
1506 win32_fclose(FILE *pf)
1507 {
1508     return my_fclose(pf);       /* defined in win32sck.c */
1509 }
1510
1511 DllExport int
1512 win32_fputs(const char *s,FILE *pf)
1513 {
1514     return fputs(s, pf);
1515 }
1516
1517 DllExport int
1518 win32_fputc(int c,FILE *pf)
1519 {
1520     return fputc(c,pf);
1521 }
1522
1523 DllExport int
1524 win32_ungetc(int c,FILE *pf)
1525 {
1526     return ungetc(c,pf);
1527 }
1528
1529 DllExport int
1530 win32_getc(FILE *pf)
1531 {
1532     return getc(pf);
1533 }
1534
1535 DllExport int
1536 win32_fileno(FILE *pf)
1537 {
1538     return fileno(pf);
1539 }
1540
1541 DllExport void
1542 win32_clearerr(FILE *pf)
1543 {
1544     clearerr(pf);
1545     return;
1546 }
1547
1548 DllExport int
1549 win32_fflush(FILE *pf)
1550 {
1551     return fflush(pf);
1552 }
1553
1554 DllExport long
1555 win32_ftell(FILE *pf)
1556 {
1557     return ftell(pf);
1558 }
1559
1560 DllExport int
1561 win32_fseek(FILE *pf,long offset,int origin)
1562 {
1563     return fseek(pf, offset, origin);
1564 }
1565
1566 DllExport int
1567 win32_fgetpos(FILE *pf,fpos_t *p)
1568 {
1569     return fgetpos(pf, p);
1570 }
1571
1572 DllExport int
1573 win32_fsetpos(FILE *pf,const fpos_t *p)
1574 {
1575     return fsetpos(pf, p);
1576 }
1577
1578 DllExport void
1579 win32_rewind(FILE *pf)
1580 {
1581     rewind(pf);
1582     return;
1583 }
1584
1585 DllExport FILE*
1586 win32_tmpfile(void)
1587 {
1588     return tmpfile();
1589 }
1590
1591 DllExport void
1592 win32_abort(void)
1593 {
1594     abort();
1595     return;
1596 }
1597
1598 DllExport int
1599 win32_fstat(int fd,struct stat *sbufptr)
1600 {
1601     return fstat(fd,sbufptr);
1602 }
1603
1604 DllExport int
1605 win32_pipe(int *pfd, unsigned int size, int mode)
1606 {
1607     return _pipe(pfd, size, mode);
1608 }
1609
1610 /*
1611  * a popen() clone that respects PERL5SHELL
1612  */
1613
1614 DllExport FILE*
1615 win32_popen(const char *command, const char *mode)
1616 {
1617 #ifdef USE_RTL_POPEN
1618     return _popen(command, mode);
1619 #else
1620     int p[2];
1621     int parent, child;
1622     int stdfd, oldfd;
1623     int ourmode;
1624     int childpid;
1625
1626     /* establish which ends read and write */
1627     if (strchr(mode,'w')) {
1628         stdfd = 0;              /* stdin */
1629         parent = 1;
1630         child = 0;
1631     }
1632     else if (strchr(mode,'r')) {
1633         stdfd = 1;              /* stdout */
1634         parent = 0;
1635         child = 1;
1636     }
1637     else
1638         return NULL;
1639
1640     /* set the correct mode */
1641     if (strchr(mode,'b'))
1642         ourmode = O_BINARY;
1643     else if (strchr(mode,'t'))
1644         ourmode = O_TEXT;
1645     else
1646         ourmode = _fmode & (O_TEXT | O_BINARY);
1647
1648     /* the child doesn't inherit handles */
1649     ourmode |= O_NOINHERIT;
1650
1651     if (win32_pipe( p, 512, ourmode) == -1)
1652         return NULL;
1653
1654     /* save current stdfd */
1655     if ((oldfd = win32_dup(stdfd)) == -1)
1656         goto cleanup;
1657
1658     /* make stdfd go to child end of pipe (implicitly closes stdfd) */
1659     /* stdfd will be inherited by the child */
1660     if (win32_dup2(p[child], stdfd) == -1)
1661         goto cleanup;
1662
1663     /* close the child end in parent */
1664     win32_close(p[child]);
1665
1666     /* start the child */
1667     if ((childpid = do_spawn_nowait((char*)command)) == -1)
1668         goto cleanup;
1669
1670     /* revert stdfd to whatever it was before */
1671     if (win32_dup2(oldfd, stdfd) == -1)
1672         goto cleanup;
1673
1674     /* close saved handle */
1675     win32_close(oldfd);
1676
1677     sv_setiv(*av_fetch(w32_fdpid, p[parent], TRUE), childpid);
1678
1679     /* we have an fd, return a file stream */
1680     return (win32_fdopen(p[parent], (char *)mode));
1681
1682 cleanup:
1683     /* we don't need to check for errors here */
1684     win32_close(p[0]);
1685     win32_close(p[1]);
1686     if (oldfd != -1) {
1687         win32_dup2(oldfd, stdfd);
1688         win32_close(oldfd);
1689     }
1690     return (NULL);
1691
1692 #endif /* USE_RTL_POPEN */
1693 }
1694
1695 /*
1696  * pclose() clone
1697  */
1698
1699 DllExport int
1700 win32_pclose(FILE *pf)
1701 {
1702 #ifdef USE_RTL_POPEN
1703     return _pclose(pf);
1704 #else
1705
1706     int childpid, status;
1707     SV *sv;
1708
1709     sv = *av_fetch(w32_fdpid, win32_fileno(pf), TRUE);
1710     if (SvIOK(sv))
1711         childpid = SvIVX(sv);
1712     else
1713         childpid = 0;
1714
1715     if (!childpid) {
1716         errno = EBADF;
1717         return -1;
1718     }
1719
1720     win32_fclose(pf);
1721     SvIVX(sv) = 0;
1722
1723     remove_dead_process((HANDLE)childpid);
1724
1725     /* wait for the child */
1726     if (cwait(&status, childpid, WAIT_CHILD) == -1)
1727         return (-1);
1728     /* cwait() returns differently on Borland */
1729 #ifdef __BORLANDC__
1730     return (((status >> 8) & 0xff) | ((status << 8) & 0xff00));
1731 #else
1732     return (status);
1733 #endif
1734
1735 #endif /* USE_RTL_POPEN */
1736 }
1737
1738 DllExport int
1739 win32_rename(const char *oname, const char *newname)
1740 {
1741     char szNewWorkName[MAX_PATH+1];
1742     WIN32_FIND_DATA fdOldFile, fdNewFile;
1743     HANDLE handle;
1744     char *ptr;
1745
1746     if ((strchr(oname, '\\') || strchr(oname, '/'))
1747         && strchr(newname, '\\') == NULL
1748         && strchr(newname, '/') == NULL)
1749     {
1750         strcpy(szNewWorkName, oname);
1751         if ((ptr = strrchr(szNewWorkName, '\\')) == NULL)
1752             ptr = strrchr(szNewWorkName, '/');
1753         strcpy(++ptr, newname);
1754     }
1755     else
1756         strcpy(szNewWorkName, newname);
1757
1758     if (stricmp(oname, szNewWorkName) != 0) {
1759         // check that we're not being fooled by relative paths
1760         // and only delete the new file
1761         //  1) if it exists
1762         //  2) it is not the same file as the old file
1763         //  3) old file exist
1764         // GetFullPathName does not return the long file name on some systems
1765         handle = FindFirstFile(oname, &fdOldFile);
1766         if (handle != INVALID_HANDLE_VALUE) {
1767             FindClose(handle);
1768     
1769             handle = FindFirstFile(szNewWorkName, &fdNewFile);
1770     
1771             if (handle != INVALID_HANDLE_VALUE)
1772                 FindClose(handle);
1773             else
1774                 fdNewFile.cFileName[0] = '\0';
1775
1776             if (strcmp(fdOldFile.cAlternateFileName,
1777                        fdNewFile.cAlternateFileName) != 0
1778                 && strcmp(fdOldFile.cFileName, fdNewFile.cFileName) != 0)
1779             {
1780                 // file exists and not same file
1781                 DeleteFile(szNewWorkName);
1782             }
1783         }
1784     }
1785     return rename(oname, newname);
1786 }
1787
1788 DllExport int
1789 win32_setmode(int fd, int mode)
1790 {
1791     return setmode(fd, mode);
1792 }
1793
1794 DllExport long
1795 win32_lseek(int fd, long offset, int origin)
1796 {
1797     return lseek(fd, offset, origin);
1798 }
1799
1800 DllExport long
1801 win32_tell(int fd)
1802 {
1803     return tell(fd);
1804 }
1805
1806 DllExport int
1807 win32_open(const char *path, int flag, ...)
1808 {
1809     va_list ap;
1810     int pmode;
1811
1812     va_start(ap, flag);
1813     pmode = va_arg(ap, int);
1814     va_end(ap);
1815
1816     if (stricmp(path, "/dev/null")==0)
1817         return open("NUL", flag, pmode);
1818     return open(path,flag,pmode);
1819 }
1820
1821 DllExport int
1822 win32_close(int fd)
1823 {
1824     return close(fd);
1825 }
1826
1827 DllExport int
1828 win32_eof(int fd)
1829 {
1830     return eof(fd);
1831 }
1832
1833 DllExport int
1834 win32_dup(int fd)
1835 {
1836     return dup(fd);
1837 }
1838
1839 DllExport int
1840 win32_dup2(int fd1,int fd2)
1841 {
1842     return dup2(fd1,fd2);
1843 }
1844
1845 DllExport int
1846 win32_read(int fd, void *buf, unsigned int cnt)
1847 {
1848     return read(fd, buf, cnt);
1849 }
1850
1851 DllExport int
1852 win32_write(int fd, const void *buf, unsigned int cnt)
1853 {
1854     return write(fd, buf, cnt);
1855 }
1856
1857 DllExport int
1858 win32_mkdir(const char *dir, int mode)
1859 {
1860     return mkdir(dir); /* just ignore mode */
1861 }
1862
1863 DllExport int
1864 win32_rmdir(const char *dir)
1865 {
1866     return rmdir(dir);
1867 }
1868
1869 DllExport int
1870 win32_chdir(const char *dir)
1871 {
1872     return chdir(dir);
1873 }
1874
1875 DllExport int
1876 win32_spawnvp(int mode, const char *cmdname, const char *const *argv)
1877 {
1878     int status;
1879
1880 #ifndef USE_RTL_WAIT
1881     if (mode == P_NOWAIT && w32_num_children >= MAXIMUM_WAIT_OBJECTS)
1882         return -1;
1883 #endif
1884
1885     status = spawnvp(mode, cmdname, (char * const *) argv);
1886 #ifndef USE_RTL_WAIT
1887     /* XXX For the P_NOWAIT case, Borland RTL returns pinfo.dwProcessId
1888      * while VC RTL returns pinfo.hProcess. For purposes of the custom
1889      * implementation of win32_wait(), we assume the latter.
1890      */
1891     if (mode == P_NOWAIT && status >= 0)
1892         w32_child_pids[w32_num_children++] = (HANDLE)status;
1893 #endif
1894     return status;
1895 }
1896
1897 DllExport int
1898 win32_execv(const char *cmdname, const char *const *argv)
1899 {
1900     return execv(cmdname, (char *const *)argv);
1901 }
1902
1903 DllExport int
1904 win32_execvp(const char *cmdname, const char *const *argv)
1905 {
1906     return execvp(cmdname, (char *const *)argv);
1907 }
1908
1909 DllExport void
1910 win32_perror(const char *str)
1911 {
1912     perror(str);
1913 }
1914
1915 DllExport void
1916 win32_setbuf(FILE *pf, char *buf)
1917 {
1918     setbuf(pf, buf);
1919 }
1920
1921 DllExport int
1922 win32_setvbuf(FILE *pf, char *buf, int type, size_t size)
1923 {
1924     return setvbuf(pf, buf, type, size);
1925 }
1926
1927 DllExport int
1928 win32_flushall(void)
1929 {
1930     return flushall();
1931 }
1932
1933 DllExport int
1934 win32_fcloseall(void)
1935 {
1936     return fcloseall();
1937 }
1938
1939 DllExport char*
1940 win32_fgets(char *s, int n, FILE *pf)
1941 {
1942     return fgets(s, n, pf);
1943 }
1944
1945 DllExport char*
1946 win32_gets(char *s)
1947 {
1948     return gets(s);
1949 }
1950
1951 DllExport int
1952 win32_fgetc(FILE *pf)
1953 {
1954     return fgetc(pf);
1955 }
1956
1957 DllExport int
1958 win32_putc(int c, FILE *pf)
1959 {
1960     return putc(c,pf);
1961 }
1962
1963 DllExport int
1964 win32_puts(const char *s)
1965 {
1966     return puts(s);
1967 }
1968
1969 DllExport int
1970 win32_getchar(void)
1971 {
1972     return getchar();
1973 }
1974
1975 DllExport int
1976 win32_putchar(int c)
1977 {
1978     return putchar(c);
1979 }
1980
1981 #ifdef MYMALLOC
1982
1983 #ifndef USE_PERL_SBRK
1984
1985 static char *committed = NULL;
1986 static char *base      = NULL;
1987 static char *reserved  = NULL;
1988 static char *brk       = NULL;
1989 static DWORD pagesize  = 0;
1990 static DWORD allocsize = 0;
1991
1992 void *
1993 sbrk(int need)
1994 {
1995  void *result;
1996  if (!pagesize)
1997   {SYSTEM_INFO info;
1998    GetSystemInfo(&info);
1999    /* Pretend page size is larger so we don't perpetually
2000     * call the OS to commit just one page ...
2001     */
2002    pagesize = info.dwPageSize << 3;
2003    allocsize = info.dwAllocationGranularity;
2004   }
2005  /* This scheme fails eventually if request for contiguous
2006   * block is denied so reserve big blocks - this is only 
2007   * address space not memory ...
2008   */
2009  if (brk+need >= reserved)
2010   {
2011    DWORD size = 64*1024*1024;
2012    char *addr;
2013    if (committed && reserved && committed < reserved)
2014     {
2015      /* Commit last of previous chunk cannot span allocations */
2016      addr = (char *) VirtualAlloc(committed,reserved-committed,MEM_COMMIT,PAGE_READWRITE);
2017      if (addr)
2018       committed = reserved;
2019     }
2020    /* Reserve some (more) space 
2021     * Note this is a little sneaky, 1st call passes NULL as reserved
2022     * so lets system choose where we start, subsequent calls pass
2023     * the old end address so ask for a contiguous block
2024     */
2025    addr  = (char *) VirtualAlloc(reserved,size,MEM_RESERVE,PAGE_NOACCESS);
2026    if (addr)
2027     {
2028      reserved = addr+size;
2029      if (!base)
2030       base = addr;
2031      if (!committed)
2032       committed = base;
2033      if (!brk)
2034       brk = committed;
2035     }
2036    else
2037     {
2038      return (void *) -1;
2039     }
2040   }
2041  result = brk;
2042  brk += need;
2043  if (brk > committed)
2044   {
2045    DWORD size = ((brk-committed + pagesize -1)/pagesize) * pagesize;
2046    char *addr = (char *) VirtualAlloc(committed,size,MEM_COMMIT,PAGE_READWRITE);
2047    if (addr)
2048     {
2049      committed += size;
2050     }
2051    else
2052     return (void *) -1;
2053   }
2054  return result;
2055 }
2056
2057 #endif
2058 #endif
2059
2060 DllExport void*
2061 win32_malloc(size_t size)
2062 {
2063     return malloc(size);
2064 }
2065
2066 DllExport void*
2067 win32_calloc(size_t numitems, size_t size)
2068 {
2069     return calloc(numitems,size);
2070 }
2071
2072 DllExport void*
2073 win32_realloc(void *block, size_t size)
2074 {
2075     return realloc(block,size);
2076 }
2077
2078 DllExport void
2079 win32_free(void *block)
2080 {
2081     free(block);
2082 }
2083
2084
2085 int
2086 win32_open_osfhandle(long handle, int flags)
2087 {
2088     return _open_osfhandle(handle, flags);
2089 }
2090
2091 long
2092 win32_get_osfhandle(int fd)
2093 {
2094     return _get_osfhandle(fd);
2095 }
2096
2097 /*
2098  * Extras.
2099  */
2100
2101 static
2102 XS(w32_GetCwd)
2103 {
2104     dXSARGS;
2105     SV *sv = sv_newmortal();
2106     /* Make one call with zero size - return value is required size */
2107     DWORD len = GetCurrentDirectory((DWORD)0,NULL);
2108     SvUPGRADE(sv,SVt_PV);
2109     SvGROW(sv,len);
2110     SvCUR(sv) = GetCurrentDirectory((DWORD) SvLEN(sv), SvPVX(sv));
2111     /* 
2112      * If result != 0 
2113      *   then it worked, set PV valid, 
2114      *   else leave it 'undef' 
2115      */
2116     if (SvCUR(sv))
2117         SvPOK_on(sv);
2118     EXTEND(SP,1);
2119     ST(0) = sv;
2120     XSRETURN(1);
2121 }
2122
2123 static
2124 XS(w32_SetCwd)
2125 {
2126     dXSARGS;
2127     if (items != 1)
2128         croak("usage: Win32::SetCurrentDirectory($cwd)");
2129     if (SetCurrentDirectory(SvPV(ST(0),PL_na)))
2130         XSRETURN_YES;
2131
2132     XSRETURN_NO;
2133 }
2134
2135 static
2136 XS(w32_GetNextAvailDrive)
2137 {
2138     dXSARGS;
2139     char ix = 'C';
2140     char root[] = "_:\\";
2141     while (ix <= 'Z') {
2142         root[0] = ix++;
2143         if (GetDriveType(root) == 1) {
2144             root[2] = '\0';
2145             XSRETURN_PV(root);
2146         }
2147     }
2148     XSRETURN_UNDEF;
2149 }
2150
2151 static
2152 XS(w32_GetLastError)
2153 {
2154     dXSARGS;
2155     XSRETURN_IV(GetLastError());
2156 }
2157
2158 static
2159 XS(w32_LoginName)
2160 {
2161     dXSARGS;
2162     char *name = getlogin_buffer;
2163     DWORD size = sizeof(getlogin_buffer);
2164     if (GetUserName(name,&size)) {
2165         /* size includes NULL */
2166         ST(0) = sv_2mortal(newSVpv(name,size-1));
2167         XSRETURN(1);
2168     }
2169     XSRETURN_UNDEF;
2170 }
2171
2172 static
2173 XS(w32_NodeName)
2174 {
2175     dXSARGS;
2176     char name[MAX_COMPUTERNAME_LENGTH+1];
2177     DWORD size = sizeof(name);
2178     if (GetComputerName(name,&size)) {
2179         /* size does NOT include NULL :-( */
2180         ST(0) = sv_2mortal(newSVpv(name,size));
2181         XSRETURN(1);
2182     }
2183     XSRETURN_UNDEF;
2184 }
2185
2186
2187 static
2188 XS(w32_DomainName)
2189 {
2190     dXSARGS;
2191 #ifndef HAS_NETWKSTAGETINFO
2192     /* mingw32 (and Win95) don't have NetWksta*(), so do it the old way */
2193     char name[256];
2194     DWORD size = sizeof(name);
2195     if (GetUserName(name,&size)) {
2196         char sid[1024];
2197         DWORD sidlen = sizeof(sid);
2198         char dname[256];
2199         DWORD dnamelen = sizeof(dname);
2200         SID_NAME_USE snu;
2201         if (LookupAccountName(NULL, name, (PSID)&sid, &sidlen,
2202                               dname, &dnamelen, &snu)) {
2203             XSRETURN_PV(dname);         /* all that for this */
2204         }
2205     }
2206 #else
2207     /* this way is more reliable, in case user has a local account.
2208      * XXX need dynamic binding of netapi32.dll symbols or this will fail on
2209      * Win95. Probably makes more sense to move it into libwin32. */
2210     char dname[256];
2211     DWORD dnamelen = sizeof(dname);
2212     PWKSTA_INFO_100 pwi;
2213     if (NERR_Success == NetWkstaGetInfo(NULL, 100, (LPBYTE*)&pwi)) {
2214         if (pwi->wki100_langroup && *(pwi->wki100_langroup)) {
2215             WideCharToMultiByte(CP_ACP, NULL, pwi->wki100_langroup,
2216                                 -1, (LPSTR)dname, dnamelen, NULL, NULL);
2217         }
2218         else {
2219             WideCharToMultiByte(CP_ACP, NULL, pwi->wki100_computername,
2220                                 -1, (LPSTR)dname, dnamelen, NULL, NULL);
2221         }
2222         NetApiBufferFree(pwi);
2223         XSRETURN_PV(dname);
2224     }
2225 #endif
2226     XSRETURN_UNDEF;
2227 }
2228
2229 static
2230 XS(w32_FsType)
2231 {
2232     dXSARGS;
2233     char fsname[256];
2234     DWORD flags, filecomplen;
2235     if (GetVolumeInformation(NULL, NULL, 0, NULL, &filecomplen,
2236                          &flags, fsname, sizeof(fsname))) {
2237         if (GIMME == G_ARRAY) {
2238             XPUSHs(sv_2mortal(newSVpv(fsname,0)));
2239             XPUSHs(sv_2mortal(newSViv(flags)));
2240             XPUSHs(sv_2mortal(newSViv(filecomplen)));
2241             PUTBACK;
2242             return;
2243         }
2244         XSRETURN_PV(fsname);
2245     }
2246     XSRETURN_UNDEF;
2247 }
2248
2249 static
2250 XS(w32_GetOSVersion)
2251 {
2252     dXSARGS;
2253     OSVERSIONINFO osver;
2254
2255     osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
2256     if (GetVersionEx(&osver)) {
2257         XPUSHs(newSVpv(osver.szCSDVersion, 0));
2258         XPUSHs(newSViv(osver.dwMajorVersion));
2259         XPUSHs(newSViv(osver.dwMinorVersion));
2260         XPUSHs(newSViv(osver.dwBuildNumber));
2261         XPUSHs(newSViv(osver.dwPlatformId));
2262         PUTBACK;
2263         return;
2264     }
2265     XSRETURN_UNDEF;
2266 }
2267
2268 static
2269 XS(w32_IsWinNT)
2270 {
2271     dXSARGS;
2272     XSRETURN_IV(IsWinNT());
2273 }
2274
2275 static
2276 XS(w32_IsWin95)
2277 {
2278     dXSARGS;
2279     XSRETURN_IV(IsWin95());
2280 }
2281
2282 static
2283 XS(w32_FormatMessage)
2284 {
2285     dXSARGS;
2286     DWORD source = 0;
2287     char msgbuf[1024];
2288
2289     if (items != 1)
2290         croak("usage: Win32::FormatMessage($errno)");
2291
2292     if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
2293                       &source, SvIV(ST(0)), 0,
2294                       msgbuf, sizeof(msgbuf)-1, NULL))
2295         XSRETURN_PV(msgbuf);
2296
2297     XSRETURN_UNDEF;
2298 }
2299
2300 static
2301 XS(w32_Spawn)
2302 {
2303     dXSARGS;
2304     char *cmd, *args;
2305     PROCESS_INFORMATION stProcInfo;
2306     STARTUPINFO stStartInfo;
2307     BOOL bSuccess = FALSE;
2308
2309     if (items != 3)
2310         croak("usage: Win32::Spawn($cmdName, $args, $PID)");
2311
2312     cmd = SvPV(ST(0),PL_na);
2313     args = SvPV(ST(1), PL_na);
2314
2315     memset(&stStartInfo, 0, sizeof(stStartInfo));   /* Clear the block */
2316     stStartInfo.cb = sizeof(stStartInfo);           /* Set the structure size */
2317     stStartInfo.dwFlags = STARTF_USESHOWWINDOW;     /* Enable wShowWindow control */
2318     stStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;   /* Start min (normal) */
2319
2320     if (CreateProcess(
2321                 cmd,                    /* Image path */
2322                 args,                   /* Arguments for command line */
2323                 NULL,                   /* Default process security */
2324                 NULL,                   /* Default thread security */
2325                 FALSE,                  /* Must be TRUE to use std handles */
2326                 NORMAL_PRIORITY_CLASS,  /* No special scheduling */
2327                 NULL,                   /* Inherit our environment block */
2328                 NULL,                   /* Inherit our currrent directory */
2329                 &stStartInfo,           /* -> Startup info */
2330                 &stProcInfo))           /* <- Process info (if OK) */
2331     {
2332         CloseHandle(stProcInfo.hThread);/* library source code does this. */
2333         sv_setiv(ST(2), stProcInfo.dwProcessId);
2334         bSuccess = TRUE;
2335     }
2336     XSRETURN_IV(bSuccess);
2337 }
2338
2339 static
2340 XS(w32_GetTickCount)
2341 {
2342     dXSARGS;
2343     XSRETURN_IV(GetTickCount());
2344 }
2345
2346 static
2347 XS(w32_GetShortPathName)
2348 {
2349     dXSARGS;
2350     SV *shortpath;
2351     DWORD len;
2352
2353     if (items != 1)
2354         croak("usage: Win32::GetShortPathName($longPathName)");
2355
2356     shortpath = sv_mortalcopy(ST(0));
2357     SvUPGRADE(shortpath, SVt_PV);
2358     /* src == target is allowed */
2359     do {
2360         len = GetShortPathName(SvPVX(shortpath),
2361                                SvPVX(shortpath),
2362                                SvLEN(shortpath));
2363     } while (len >= SvLEN(shortpath) && sv_grow(shortpath,len+1));
2364     if (len) {
2365         SvCUR_set(shortpath,len);
2366         ST(0) = shortpath;
2367     }
2368     else
2369         ST(0) = &PL_sv_undef;
2370     XSRETURN(1);
2371 }
2372
2373 static
2374 XS(w32_Sleep)
2375 {
2376     dXSARGS;
2377     if (items != 1)
2378         croak("usage: Win32::Sleep($milliseconds)");
2379     Sleep(SvIV(ST(0)));
2380     XSRETURN_YES;
2381 }
2382
2383 void
2384 Perl_init_os_extras()
2385 {
2386     char *file = __FILE__;
2387     dXSUB_SYS;
2388
2389     w32_perlshell_tokens = Nullch;
2390     w32_perlshell_items = -1;
2391     w32_fdpid = newAV();                /* XXX needs to be in Perl_win32_init()? */
2392 #ifndef USE_RTL_WAIT
2393     w32_num_children = 0;
2394 #endif
2395
2396     /* these names are Activeware compatible */
2397     newXS("Win32::GetCwd", w32_GetCwd, file);
2398     newXS("Win32::SetCwd", w32_SetCwd, file);
2399     newXS("Win32::GetNextAvailDrive", w32_GetNextAvailDrive, file);
2400     newXS("Win32::GetLastError", w32_GetLastError, file);
2401     newXS("Win32::LoginName", w32_LoginName, file);
2402     newXS("Win32::NodeName", w32_NodeName, file);
2403     newXS("Win32::DomainName", w32_DomainName, file);
2404     newXS("Win32::FsType", w32_FsType, file);
2405     newXS("Win32::GetOSVersion", w32_GetOSVersion, file);
2406     newXS("Win32::IsWinNT", w32_IsWinNT, file);
2407     newXS("Win32::IsWin95", w32_IsWin95, file);
2408     newXS("Win32::FormatMessage", w32_FormatMessage, file);
2409     newXS("Win32::Spawn", w32_Spawn, file);
2410     newXS("Win32::GetTickCount", w32_GetTickCount, file);
2411     newXS("Win32::GetShortPathName", w32_GetShortPathName, file);
2412     newXS("Win32::Sleep", w32_Sleep, file);
2413
2414     /* XXX Bloat Alert! The following Activeware preloads really
2415      * ought to be part of Win32::Sys::*, so they're not included
2416      * here.
2417      */
2418     /* LookupAccountName
2419      * LookupAccountSID
2420      * InitiateSystemShutdown
2421      * AbortSystemShutdown
2422      * ExpandEnvrironmentStrings
2423      */
2424 }
2425
2426 void
2427 Perl_win32_init(int *argcp, char ***argvp)
2428 {
2429     /* Disable floating point errors, Perl will trap the ones we
2430      * care about.  VC++ RTL defaults to switching these off
2431      * already, but the Borland RTL doesn't.  Since we don't
2432      * want to be at the vendor's whim on the default, we set
2433      * it explicitly here.
2434      */
2435 #if !defined(_ALPHA_) && !defined(__GNUC__)
2436     _control87(MCW_EM, MCW_EM);
2437 #endif
2438     MALLOC_INIT;
2439 }
2440
2441 #ifdef USE_BINMODE_SCRIPTS
2442
2443 void
2444 win32_strip_return(SV *sv)
2445 {
2446  char *s = SvPVX(sv);
2447  char *e = s+SvCUR(sv);
2448  char *d = s;
2449  while (s < e)
2450   {
2451    if (*s == '\r' && s[1] == '\n')
2452     {
2453      *d++ = '\n';
2454      s += 2;
2455     }
2456    else 
2457     {
2458      *d++ = *s++;
2459     }   
2460   }
2461  SvCUR_set(sv,d-SvPVX(sv)); 
2462 }
2463
2464 #endif