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