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