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