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