emit more appropriate diagnostic for failed glob (variant
[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 <sys/stat.h>
39 #include "EXTERN.h"
40 #include "perl.h"
41
42 #define NO_XSLOCKS
43 #ifdef PERL_OBJECT
44 extern CPerlObj* pPerl;
45 #endif
46 #include "XSUB.h"
47
48 #include "Win32iop.h"
49 #include <fcntl.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(long deceased);
99 static long             find_pid(int pid);
100 static char *           qualified_path(const char *cmd);
101
102 HANDLE  w32_perldll_handle = INVALID_HANDLE_VALUE;
103 char    w32_module_name[MAX_PATH+1];
104 static DWORD    w32_platform = (DWORD)-1;
105
106 #ifdef USE_THREADS
107 #  ifdef USE_DECLSPEC_THREAD
108 __declspec(thread) char strerror_buffer[512];
109 __declspec(thread) char getlogin_buffer[128];
110 __declspec(thread) char w32_perllib_root[MAX_PATH+1];
111 #    ifdef HAVE_DES_FCRYPT
112 __declspec(thread) char crypt_buffer[30];
113 #    endif
114 #  else
115 #    define strerror_buffer     (thr->i.Wstrerror_buffer)
116 #    define getlogin_buffer     (thr->i.Wgetlogin_buffer)
117 #    define w32_perllib_root    (thr->i.Ww32_perllib_root)
118 #    define crypt_buffer        (thr->i.Wcrypt_buffer)
119 #  endif
120 #else
121 static char     strerror_buffer[512];
122 static char     getlogin_buffer[128];
123 static char     w32_perllib_root[MAX_PATH+1];
124 #  ifdef HAVE_DES_FCRYPT
125 static char     crypt_buffer[30];
126 #  endif
127 #endif
128
129 int 
130 IsWin95(void) {
131     return (os_id() == VER_PLATFORM_WIN32_WINDOWS);
132 }
133
134 int
135 IsWinNT(void) {
136     return (os_id() == VER_PLATFORM_WIN32_NT);
137 }
138
139 /* *ptr is expected to point to valid allocated space (can't be NULL) */
140 char*
141 GetRegStrFromKey(HKEY hkey, const char *lpszValueName, char** ptr, DWORD* lpDataLen)
142 {
143     /* Retrieve a REG_SZ or REG_EXPAND_SZ from the registry */
144     HKEY handle;
145     DWORD type;
146     const char *subkey = "Software\\Perl";
147     char *str = Nullch;
148     long retval;
149
150     retval = RegOpenKeyEx(hkey, subkey, 0, KEY_READ, &handle);
151     if (retval == ERROR_SUCCESS) {
152         retval = RegQueryValueEx(handle, lpszValueName, 0, &type, NULL, lpDataLen);
153         if (retval == ERROR_SUCCESS && type == REG_SZ) {
154             Renew(*ptr, *lpDataLen, char);
155             retval = RegQueryValueEx(handle, lpszValueName, 0, NULL,
156                                      (PBYTE)*ptr, lpDataLen);
157             if (retval == ERROR_SUCCESS)
158                 str = *ptr;
159         }
160         RegCloseKey(handle);
161     }
162     return str;
163 }
164
165 /* *ptr is expected to point to valid allocated space (can't be NULL) */
166 char*
167 GetRegStr(const char *lpszValueName, char** ptr, DWORD* lpDataLen)
168 {
169     char *str = GetRegStrFromKey(HKEY_CURRENT_USER, lpszValueName, ptr, lpDataLen);
170     if (!str)
171         str = GetRegStrFromKey(HKEY_LOCAL_MACHINE, lpszValueName, ptr, lpDataLen);
172     return str;
173 }
174
175 /* *prev_path is expected to point to valid allocated space (can't be NULL) */
176 static char *
177 get_emd_part(char **prev_path, char *trailing_path, ...)
178 {
179     char base[10];
180     va_list ap;
181     char mod_name[MAX_PATH+1];
182     char *ptr;
183     char *optr;
184     char *strip;
185     int oldsize, newsize;
186
187     va_start(ap, trailing_path);
188     strip = va_arg(ap, char *);
189
190     sprintf(base, "%5.3f",
191             (double)PERL_REVISION + ((double)PERL_VERSION / (double)1000));
192
193     if (!*w32_module_name) {
194         GetModuleFileName((HMODULE)((w32_perldll_handle == INVALID_HANDLE_VALUE)
195                                     ? GetModuleHandle(NULL)
196                                     : w32_perldll_handle),
197                           w32_module_name, sizeof(w32_module_name));
198
199         /* try to get full path to binary (which may be mangled when perl is
200          * run from a 16-bit app) */
201         /*PerlIO_printf(PerlIO_stderr(), "Before %s\n", w32_module_name);*/
202         (void)win32_longpath(w32_module_name);
203         /*PerlIO_printf(PerlIO_stderr(), "After  %s\n", w32_module_name);*/
204
205         /* normalize to forward slashes */
206         ptr = w32_module_name;
207         while (*ptr) {
208             if (*ptr == '\\')
209                 *ptr = '/';
210             ++ptr;
211         }
212     }
213     strcpy(mod_name, w32_module_name);
214     ptr = strrchr(mod_name, '/');
215     while (ptr && strip) {
216         /* look for directories to skip back */
217         optr = ptr;
218         *ptr = '\0';
219         ptr = strrchr(mod_name, '/');
220         if (!ptr || stricmp(ptr+1, strip) != 0) {
221             if(!(*strip == '5' && *(ptr+1) == '5'
222                  && strncmp(strip, base, 5) == 0
223                  && strncmp(ptr+1, base, 5) == 0))
224             {
225                 *optr = '/';
226                 ptr = optr;
227             }
228         }
229         strip = va_arg(ap, char *);
230     }
231     if (!ptr) {
232         ptr = mod_name;
233         *ptr++ = '.';
234         *ptr = '/';
235     }
236     va_end(ap);
237     strcpy(++ptr, trailing_path);
238
239     /* only add directory if it exists */
240     if (GetFileAttributes(mod_name) != (DWORD) -1) {
241         /* directory exists */
242         newsize = strlen(mod_name) + 1;
243         oldsize = strlen(*prev_path) + 1;
244         newsize += oldsize;                     /* includes plus 1 for ';' */
245         Renew(*prev_path, newsize, char);
246         (*prev_path)[oldsize-1] = ';';
247         strcpy(&(*prev_path)[oldsize], mod_name);
248         return *prev_path;
249     }
250
251     return Nullch;
252 }
253
254 char *
255 win32_get_privlib(char *pl)
256 {
257     char *stdlib = "lib";
258     char buffer[MAX_PATH+1];
259     char **path;
260     DWORD datalen;
261     SV *sv = sv_2mortal(newSVpvn("",127));
262
263     /* $stdlib = $HKCU{"lib-$]"} || $HKLM{"lib-$]"} || $HKCU{"lib"} || $HKLM{"lib"} || "";  */
264     sprintf(buffer, "%s-%s", stdlib, pl);
265     path = &SvPVX(sv);
266     if (!GetRegStr(buffer, path, &datalen))
267         (void)GetRegStr(stdlib, path, &datalen);
268
269     /* $stdlib .= ";$EMD/../../lib" */
270     (void)get_emd_part(path, stdlib, ARCHNAME, "bin", Nullch);
271     SvCUR_set(sv, strlen(*path));
272     SvLEN_set(sv, SvCUR(sv)+1);
273     return SvPVX(sv);
274 }
275
276 char *
277 win32_get_sitelib(char *pl)
278 {
279     char *sitelib = "sitelib";
280     char regstr[40];
281     char pathstr[MAX_PATH+1];
282     DWORD datalen;
283     char **path1, *str1 = Nullch;
284     char **path2, *str2 = Nullch;
285     int len, newsize;
286     SV *sv1 = sv_2mortal(newSVpvn("",127));
287     SV *sv2 = sv_2mortal(newSVpvn("",127));
288
289     /* $HKCU{"sitelib-$]"} || $HKLM{"sitelib-$]"} . ---; */
290     sprintf(regstr, "%s-%s", sitelib, pl);
291     path1 = &SvPVX(sv1);
292     (void)GetRegStr(regstr, path1, &datalen);
293
294     /* $sitelib .=
295      * ";$EMD/" . ((-d $EMD/../../../$]) ? "../../.." : "../.."). "/site/$]/lib";  */
296     sprintf(pathstr, "site/%s/lib", pl);
297     str1 = get_emd_part(path1, pathstr, ARCHNAME, "bin", pl, Nullch);
298     if (!str1 && strlen(pl) == 7) {
299         /* pl may have been SUBVERSION-specific; try again without
300          * SUBVERSION */
301         sprintf(pathstr, "site/%.5s/lib", pl);
302         str1 = get_emd_part(path1, pathstr, ARCHNAME, "bin", pl, Nullch);
303     }
304
305     /* $HKCU{'sitelib'} || $HKLM{'sitelib'} . ---; */
306     path2 = &SvPVX(sv2);
307     (void)GetRegStr(sitelib, path2, &datalen);
308
309     /* $sitelib .=
310      * ";$EMD/" . ((-d $EMD/../../../$]) ? "../../.." : "../.."). "/site/lib";  */
311     str2 = get_emd_part(path2, "site/lib", ARCHNAME, "bin", pl, Nullch);
312
313     SvCUR_set(sv1, strlen(*path1));
314     SvLEN_set(sv1, SvCUR(sv1)+1);
315     SvCUR_set(sv2, strlen(*path2));
316     SvLEN_set(sv2, SvCUR(sv2)+1);
317
318     if (!str1)
319         return *path2;
320     if (!str2)
321         return *path1;
322
323     sv_catpvn(sv1, ";", 1);
324     sv_catsv(sv1, sv2);
325
326     return SvPVX(sv1);
327 }
328
329
330 static BOOL
331 has_shell_metachars(char *ptr)
332 {
333     int inquote = 0;
334     char quote = '\0';
335
336     /*
337      * Scan string looking for redirection (< or >) or pipe
338      * characters (|) that are not in a quoted string.
339      * Shell variable interpolation (%VAR%) can also happen inside strings.
340      */
341     while (*ptr) {
342         switch(*ptr) {
343         case '%':
344             return TRUE;
345         case '\'':
346         case '\"':
347             if (inquote) {
348                 if (quote == *ptr) {
349                     inquote = 0;
350                     quote = '\0';
351                 }
352             }
353             else {
354                 quote = *ptr;
355                 inquote++;
356             }
357             break;
358         case '>':
359         case '<':
360         case '|':
361             if (!inquote)
362                 return TRUE;
363         default:
364             break;
365         }
366         ++ptr;
367     }
368     return FALSE;
369 }
370
371 #if !defined(PERL_OBJECT)
372 /* since the current process environment is being updated in util.c
373  * the library functions will get the correct environment
374  */
375 PerlIO *
376 my_popen(char *cmd, char *mode)
377 {
378 #ifdef FIXCMD
379 #define fixcmd(x)       {                                       \
380                             char *pspace = strchr((x),' ');     \
381                             if (pspace) {                       \
382                                 char *p = (x);                  \
383                                 while (p < pspace) {            \
384                                     if (*p == '/')              \
385                                         *p = '\\';              \
386                                     p++;                        \
387                                 }                               \
388                             }                                   \
389                         }
390 #else
391 #define fixcmd(x)
392 #endif
393     fixcmd(cmd);
394     PERL_FLUSHALL_FOR_CHILD;
395     return win32_popen(cmd, mode);
396 }
397
398 long
399 my_pclose(PerlIO *fp)
400 {
401     return win32_pclose(fp);
402 }
403 #endif
404
405 static DWORD
406 os_id(void)
407 {
408     static OSVERSIONINFO osver;
409
410     if (osver.dwPlatformId != w32_platform) {
411         memset(&osver, 0, sizeof(OSVERSIONINFO));
412         osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
413         GetVersionEx(&osver);
414         w32_platform = osver.dwPlatformId;
415     }
416     return (w32_platform);
417 }
418
419 /* Tokenize a string.  Words are null-separated, and the list
420  * ends with a doubled null.  Any character (except null and
421  * including backslash) may be escaped by preceding it with a
422  * backslash (the backslash will be stripped).
423  * Returns number of words in result buffer.
424  */
425 static long
426 tokenize(char *str, char **dest, char ***destv)
427 {
428     char *retstart = Nullch;
429     char **retvstart = 0;
430     int items = -1;
431     if (str) {
432         int slen = strlen(str);
433         register char *ret;
434         register char **retv;
435         New(1307, ret, slen+2, char);
436         New(1308, retv, (slen+3)/2, char*);
437
438         retstart = ret;
439         retvstart = retv;
440         *retv = ret;
441         items = 0;
442         while (*str) {
443             *ret = *str++;
444             if (*ret == '\\' && *str)
445                 *ret = *str++;
446             else if (*ret == ' ') {
447                 while (*str == ' ')
448                     str++;
449                 if (ret == retstart)
450                     ret--;
451                 else {
452                     *ret = '\0';
453                     ++items;
454                     if (*str)
455                         *++retv = ret+1;
456                 }
457             }
458             else if (!*str)
459                 ++items;
460             ret++;
461         }
462         retvstart[items] = Nullch;
463         *ret++ = '\0';
464         *ret = '\0';
465     }
466     *dest = retstart;
467     *destv = retvstart;
468     return items;
469 }
470
471 static void
472 get_shell(void)
473 {
474     if (!w32_perlshell_tokens) {
475         /* we don't use COMSPEC here for two reasons:
476          *  1. the same reason perl on UNIX doesn't use SHELL--rampant and
477          *     uncontrolled unportability of the ensuing scripts.
478          *  2. PERL5SHELL could be set to a shell that may not be fit for
479          *     interactive use (which is what most programs look in COMSPEC
480          *     for).
481          */
482         char* defaultshell = (IsWinNT() ? "cmd.exe /x/c" : "command.com /c");
483         char *usershell = getenv("PERL5SHELL");
484         w32_perlshell_items = tokenize(usershell ? usershell : defaultshell,
485                                        &w32_perlshell_tokens,
486                                        &w32_perlshell_vec);
487     }
488 }
489
490 int
491 do_aspawn(void *vreally, void **vmark, void **vsp)
492 {
493     SV *really = (SV*)vreally;
494     SV **mark = (SV**)vmark;
495     SV **sp = (SV**)vsp;
496     char **argv;
497     char *str;
498     int status;
499     int flag = P_WAIT;
500     int index = 0;
501
502     if (sp <= mark)
503         return -1;
504
505     get_shell();
506     New(1306, argv, (sp - mark) + w32_perlshell_items + 2, char*);
507
508     if (SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
509         ++mark;
510         flag = SvIVx(*mark);
511     }
512
513     while (++mark <= sp) {
514         if (*mark && (str = SvPV_nolen(*mark)))
515             argv[index++] = str;
516         else
517             argv[index++] = "";
518     }
519     argv[index++] = 0;
520    
521     status = win32_spawnvp(flag,
522                            (const char*)(really ? SvPV_nolen(really) : argv[0]),
523                            (const char* const*)argv);
524
525     if (status < 0 && (errno == ENOEXEC || errno == ENOENT)) {
526         /* possible shell-builtin, invoke with shell */
527         int sh_items;
528         sh_items = w32_perlshell_items;
529         while (--index >= 0)
530             argv[index+sh_items] = argv[index];
531         while (--sh_items >= 0)
532             argv[sh_items] = w32_perlshell_vec[sh_items];
533    
534         status = win32_spawnvp(flag,
535                                (const char*)(really ? SvPV_nolen(really) : argv[0]),
536                                (const char* const*)argv);
537     }
538
539     if (flag != P_NOWAIT) {
540         if (status < 0) {
541             if (PL_dowarn)
542                 warn("Can't spawn \"%s\": %s", argv[0], strerror(errno));
543             status = 255 * 256;
544         }
545         else
546             status *= 256;
547         PL_statusvalue = status;
548     }
549     Safefree(argv);
550     return (status);
551 }
552
553 int
554 do_spawn2(char *cmd, int exectype)
555 {
556     char **a;
557     char *s;
558     char **argv;
559     int status = -1;
560     BOOL needToTry = TRUE;
561     char *cmd2;
562
563     /* Save an extra exec if possible. See if there are shell
564      * metacharacters in it */
565     if (!has_shell_metachars(cmd)) {
566         New(1301,argv, strlen(cmd) / 2 + 2, char*);
567         New(1302,cmd2, strlen(cmd) + 1, char);
568         strcpy(cmd2, cmd);
569         a = argv;
570         for (s = cmd2; *s;) {
571             while (*s && isspace(*s))
572                 s++;
573             if (*s)
574                 *(a++) = s;
575             while (*s && !isspace(*s))
576                 s++;
577             if (*s)
578                 *s++ = '\0';
579         }
580         *a = Nullch;
581         if (argv[0]) {
582             switch (exectype) {
583             case EXECF_SPAWN:
584                 status = win32_spawnvp(P_WAIT, argv[0],
585                                        (const char* const*)argv);
586                 break;
587             case EXECF_SPAWN_NOWAIT:
588                 status = win32_spawnvp(P_NOWAIT, argv[0],
589                                        (const char* const*)argv);
590                 break;
591             case EXECF_EXEC:
592                 status = win32_execvp(argv[0], (const char* const*)argv);
593                 break;
594             }
595             if (status != -1 || errno == 0)
596                 needToTry = FALSE;
597         }
598         Safefree(argv);
599         Safefree(cmd2);
600     }
601     if (needToTry) {
602         char **argv;
603         int i = -1;
604         get_shell();
605         New(1306, argv, w32_perlshell_items + 2, char*);
606         while (++i < w32_perlshell_items)
607             argv[i] = w32_perlshell_vec[i];
608         argv[i++] = cmd;
609         argv[i] = Nullch;
610         switch (exectype) {
611         case EXECF_SPAWN:
612             status = win32_spawnvp(P_WAIT, argv[0],
613                                    (const char* const*)argv);
614             break;
615         case EXECF_SPAWN_NOWAIT:
616             status = win32_spawnvp(P_NOWAIT, argv[0],
617                                    (const char* const*)argv);
618             break;
619         case EXECF_EXEC:
620             status = win32_execvp(argv[0], (const char* const*)argv);
621             break;
622         }
623         cmd = argv[0];
624         Safefree(argv);
625     }
626     if (exectype != EXECF_SPAWN_NOWAIT) {
627         if (status < 0) {
628             if (PL_dowarn)
629                 warn("Can't %s \"%s\": %s",
630                      (exectype == EXECF_EXEC ? "exec" : "spawn"),
631                      cmd, strerror(errno));
632             status = 255 * 256;
633         }
634         else
635             status *= 256;
636         PL_statusvalue = status;
637     }
638     return (status);
639 }
640
641 int
642 do_spawn(char *cmd)
643 {
644     return do_spawn2(cmd, EXECF_SPAWN);
645 }
646
647 int
648 do_spawn_nowait(char *cmd)
649 {
650     return do_spawn2(cmd, EXECF_SPAWN_NOWAIT);
651 }
652
653 bool
654 do_exec(char *cmd)
655 {
656     do_spawn2(cmd, EXECF_EXEC);
657     return FALSE;
658 }
659
660 /* The idea here is to read all the directory names into a string table
661  * (separated by nulls) and when one of the other dir functions is called
662  * return the pointer to the current file name.
663  */
664 DIR *
665 win32_opendir(char *filename)
666 {
667     DIR                 *p;
668     long                len;
669     long                idx;
670     char                scanname[MAX_PATH+3];
671     struct stat         sbuf;
672     WIN32_FIND_DATA     FindData;
673     HANDLE              fh;
674
675     len = strlen(filename);
676     if (len > MAX_PATH)
677         return NULL;
678
679     /* check to see if filename is a directory */
680     if (win32_stat(filename, &sbuf) < 0 || !S_ISDIR(sbuf.st_mode))
681         return NULL;
682
683     /* Get us a DIR structure */
684     Newz(1303, p, 1, DIR);
685     if (p == NULL)
686         return NULL;
687
688     /* Create the search pattern */
689     strcpy(scanname, filename);
690
691     /* bare drive name means look in cwd for drive */
692     if (len == 2 && isALPHA(scanname[0]) && scanname[1] == ':') {
693         scanname[len++] = '.';
694         scanname[len++] = '/';
695     }
696     else if (scanname[len-1] != '/' && scanname[len-1] != '\\') {
697         scanname[len++] = '/';
698     }
699     scanname[len++] = '*';
700     scanname[len] = '\0';
701
702     /* do the FindFirstFile call */
703     fh = FindFirstFile(scanname, &FindData);
704     if (fh == INVALID_HANDLE_VALUE) {
705         /* FindFirstFile() fails on empty drives! */
706         if (GetLastError() == ERROR_FILE_NOT_FOUND)
707             return p;
708         Safefree( p);
709         return NULL;
710     }
711
712     /* now allocate the first part of the string table for
713      * the filenames that we find.
714      */
715     idx = strlen(FindData.cFileName)+1;
716     New(1304, p->start, idx, char);
717     if (p->start == NULL)
718         croak("opendir: malloc failed!\n");
719     strcpy(p->start, FindData.cFileName);
720     p->nfiles++;
721
722     /* loop finding all the files that match the wildcard
723      * (which should be all of them in this directory!).
724      * the variable idx should point one past the null terminator
725      * of the previous string found.
726      */
727     while (FindNextFile(fh, &FindData)) {
728         len = strlen(FindData.cFileName);
729         /* bump the string table size by enough for the
730          * new name and it's null terminator
731          */
732         Renew(p->start, idx+len+1, char);
733         if (p->start == NULL)
734             croak("opendir: malloc failed!\n");
735         strcpy(&p->start[idx], FindData.cFileName);
736         p->nfiles++;
737         idx += len+1;
738     }
739     FindClose(fh);
740     p->size = idx;
741     p->curr = p->start;
742     return p;
743 }
744
745
746 /* Readdir just returns the current string pointer and bumps the
747  * string pointer to the nDllExport entry.
748  */
749 struct direct *
750 win32_readdir(DIR *dirp)
751 {
752     int         len;
753     static int  dummy = 0;
754
755     if (dirp->curr) {
756         /* first set up the structure to return */
757         len = strlen(dirp->curr);
758         strcpy(dirp->dirstr.d_name, dirp->curr);
759         dirp->dirstr.d_namlen = len;
760
761         /* Fake an inode */
762         dirp->dirstr.d_ino = dummy++;
763
764         /* Now set up for the nDllExport call to readdir */
765         dirp->curr += len + 1;
766         if (dirp->curr >= (dirp->start + dirp->size)) {
767             dirp->curr = NULL;
768         }
769
770         return &(dirp->dirstr);
771     } 
772     else
773         return NULL;
774 }
775
776 /* Telldir returns the current string pointer position */
777 long
778 win32_telldir(DIR *dirp)
779 {
780     return (long) dirp->curr;
781 }
782
783
784 /* Seekdir moves the string pointer to a previously saved position
785  *(Saved by telldir).
786  */
787 void
788 win32_seekdir(DIR *dirp, long loc)
789 {
790     dirp->curr = (char *)loc;
791 }
792
793 /* Rewinddir resets the string pointer to the start */
794 void
795 win32_rewinddir(DIR *dirp)
796 {
797     dirp->curr = dirp->start;
798 }
799
800 /* free the memory allocated by opendir */
801 int
802 win32_closedir(DIR *dirp)
803 {
804     Safefree(dirp->start);
805     Safefree(dirp);
806     return 1;
807 }
808
809
810 /*
811  * various stubs
812  */
813
814
815 /* Ownership
816  *
817  * Just pretend that everyone is a superuser. NT will let us know if
818  * we don\'t really have permission to do something.
819  */
820
821 #define ROOT_UID    ((uid_t)0)
822 #define ROOT_GID    ((gid_t)0)
823
824 uid_t
825 getuid(void)
826 {
827     return ROOT_UID;
828 }
829
830 uid_t
831 geteuid(void)
832 {
833     return ROOT_UID;
834 }
835
836 gid_t
837 getgid(void)
838 {
839     return ROOT_GID;
840 }
841
842 gid_t
843 getegid(void)
844 {
845     return ROOT_GID;
846 }
847
848 int
849 setuid(uid_t auid)
850
851     return (auid == ROOT_UID ? 0 : -1);
852 }
853
854 int
855 setgid(gid_t agid)
856 {
857     return (agid == ROOT_GID ? 0 : -1);
858 }
859
860 char *
861 getlogin(void)
862 {
863     dTHR;
864     char *buf = getlogin_buffer;
865     DWORD size = sizeof(getlogin_buffer);
866     if (GetUserName(buf,&size))
867         return buf;
868     return (char*)NULL;
869 }
870
871 int
872 chown(const char *path, uid_t owner, gid_t group)
873 {
874     /* XXX noop */
875     return 0;
876 }
877
878 static long
879 find_pid(int pid)
880 {
881     long child;
882     for (child = 0 ; child < w32_num_children ; ++child) {
883         if (w32_child_pids[child] == pid)
884             return child;
885     }
886     return -1;
887 }
888
889 static void
890 remove_dead_process(long child)
891 {
892     if (child >= 0) {
893         CloseHandle(w32_child_handles[child]);
894         Copy(&w32_child_handles[child+1], &w32_child_handles[child],
895              (w32_num_children-child-1), HANDLE);
896         Copy(&w32_child_pids[child+1], &w32_child_pids[child],
897              (w32_num_children-child-1), DWORD);
898         w32_num_children--;
899     }
900 }
901
902 DllExport int
903 win32_kill(int pid, int sig)
904 {
905     HANDLE hProcess;
906     hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, pid);
907     if (hProcess && TerminateProcess(hProcess, sig))
908         CloseHandle(hProcess);
909     else {
910         errno = EINVAL;
911         return -1;
912     }
913     return 0;
914 }
915
916 /*
917  * File system stuff
918  */
919
920 DllExport unsigned int
921 win32_sleep(unsigned int t)
922 {
923     Sleep(t*1000);
924     return 0;
925 }
926
927 DllExport int
928 win32_stat(const char *path, struct stat *buffer)
929 {
930     char        t[MAX_PATH+1]; 
931     int         l = strlen(path);
932     int         res;
933
934     if (l > 1) {
935         switch(path[l - 1]) {
936         /* FindFirstFile() and stat() are buggy with a trailing
937          * backslash, so change it to a forward slash :-( */
938         case '\\':
939             strncpy(t, path, l-1);
940             t[l - 1] = '/';
941             t[l] = '\0';
942             path = t;
943             break;
944         /* FindFirstFile() is buggy with "x:", so add a dot :-( */
945         case ':':
946             if (l == 2 && isALPHA(path[0])) {
947                 t[0] = path[0]; t[1] = ':'; t[2] = '.'; t[3] = '\0';
948                 l = 3;
949                 path = t;
950             }
951             break;
952         }
953     }
954     res = stat(path,buffer);
955     if (res < 0) {
956         /* CRT is buggy on sharenames, so make sure it really isn't.
957          * XXX using GetFileAttributesEx() will enable us to set
958          * buffer->st_*time (but note that's not available on the
959          * Windows of 1995) */
960         DWORD r = GetFileAttributes(path);
961         if (r != 0xffffffff && (r & FILE_ATTRIBUTE_DIRECTORY)) {
962             /* buffer may still contain old garbage since stat() failed */
963             Zero(buffer, 1, struct stat);
964             buffer->st_mode = S_IFDIR | S_IREAD;
965             errno = 0;
966             if (!(r & FILE_ATTRIBUTE_READONLY))
967                 buffer->st_mode |= S_IWRITE | S_IEXEC;
968             return 0;
969         }
970     }
971     else {
972         if (l == 3 && isALPHA(path[0]) && path[1] == ':'
973             && (path[2] == '\\' || path[2] == '/'))
974         {
975             /* The drive can be inaccessible, some _stat()s are buggy */
976             if (!GetVolumeInformation(path,NULL,0,NULL,NULL,NULL,NULL,0)) {
977                 errno = ENOENT;
978                 return -1;
979             }
980         }
981 #ifdef __BORLANDC__
982         if (S_ISDIR(buffer->st_mode))
983             buffer->st_mode |= S_IWRITE | S_IEXEC;
984         else if (S_ISREG(buffer->st_mode)) {
985             if (l >= 4 && path[l-4] == '.') {
986                 const char *e = path + l - 3;
987                 if (strnicmp(e,"exe",3)
988                     && strnicmp(e,"bat",3)
989                     && strnicmp(e,"com",3)
990                     && (IsWin95() || strnicmp(e,"cmd",3)))
991                     buffer->st_mode &= ~S_IEXEC;
992                 else
993                     buffer->st_mode |= S_IEXEC;
994             }
995             else
996                 buffer->st_mode &= ~S_IEXEC;
997         }
998 #endif
999     }
1000     return res;
1001 }
1002
1003 /* Find the longname of a given path.  path is destructively modified.
1004  * It should have space for at least MAX_PATH characters. */
1005 DllExport char *
1006 win32_longpath(char *path)
1007 {
1008     WIN32_FIND_DATA fdata;
1009     HANDLE fhand;
1010     char tmpbuf[MAX_PATH+1];
1011     char *tmpstart = tmpbuf;
1012     char *start = path;
1013     char sep;
1014     if (!path)
1015         return Nullch;
1016
1017     /* drive prefix */
1018     if (isALPHA(path[0]) && path[1] == ':' &&
1019         (path[2] == '/' || path[2] == '\\'))
1020     {
1021         start = path + 2;
1022         *tmpstart++ = path[0];
1023         *tmpstart++ = ':';
1024     }
1025     /* UNC prefix */
1026     else if ((path[0] == '/' || path[0] == '\\') &&
1027              (path[1] == '/' || path[1] == '\\'))
1028     {
1029         start = path + 2;
1030         *tmpstart++ = path[0];
1031         *tmpstart++ = path[1];
1032         /* copy machine name */
1033         while (*start && *start != '/' && *start != '\\')
1034             *tmpstart++ = *start++;
1035         if (*start) {
1036             *tmpstart++ = *start;
1037             start++;
1038             /* copy share name */
1039             while (*start && *start != '/' && *start != '\\')
1040                 *tmpstart++ = *start++;
1041         }
1042     }
1043     sep = *start++;
1044     if (sep == '/' || sep == '\\')
1045         *tmpstart++ = sep;
1046     *tmpstart = '\0';
1047     while (sep) {
1048         /* walk up to slash */
1049         while (*start && *start != '/' && *start != '\\')
1050             ++start;
1051
1052         /* discard doubled slashes */
1053         while (*start && (start[1] == '/' || start[1] == '\\'))
1054             ++start;
1055         sep = *start;
1056
1057         /* stop and find full name of component */
1058         *start = '\0';
1059         fhand = FindFirstFile(path,&fdata);
1060         if (fhand != INVALID_HANDLE_VALUE) {
1061             strcpy(tmpstart, fdata.cFileName);
1062             tmpstart += strlen(fdata.cFileName);
1063             if (sep)
1064                 *tmpstart++ = sep;
1065             *tmpstart = '\0';
1066             *start++ = sep;
1067             FindClose(fhand);
1068         }
1069         else {
1070             /* failed a step, just return without side effects */
1071             /*PerlIO_printf(PerlIO_stderr(), "Failed to find %s\n", path);*/
1072             *start = sep;
1073             return Nullch;
1074         }
1075     }
1076     strcpy(path,tmpbuf);
1077     return path;
1078 }
1079
1080 #ifndef USE_WIN32_RTL_ENV
1081
1082 DllExport char *
1083 win32_getenv(const char *name)
1084 {
1085     static char *curitem = Nullch;      /* XXX threadead */
1086     static DWORD curlen = 0;            /* XXX threadead */
1087     DWORD needlen;
1088     if (!curitem) {
1089         curlen = 512;
1090         New(1305,curitem,curlen,char);
1091     }
1092
1093     needlen = GetEnvironmentVariable(name,curitem,curlen);
1094     if (needlen != 0) {
1095         while (needlen > curlen) {
1096             Renew(curitem,needlen,char);
1097             curlen = needlen;
1098             needlen = GetEnvironmentVariable(name,curitem,curlen);
1099         }
1100     }
1101     else {
1102         /* allow any environment variables that begin with 'PERL'
1103            to be stored in the registry */
1104         if (curitem)
1105             *curitem = '\0';
1106
1107         if (strncmp(name, "PERL", 4) == 0) {
1108             if (curitem) {
1109                 Safefree(curitem);
1110                 curitem = Nullch;
1111                 curlen = 0;
1112             }
1113             curitem = GetRegStr(name, &curitem, &curlen);
1114         }
1115     }
1116     if (curitem && *curitem == '\0')
1117         return Nullch;
1118
1119     return curitem;
1120 }
1121
1122 DllExport int
1123 win32_putenv(const char *name)
1124 {
1125     char* curitem;
1126     char* val;
1127     int relval = -1;
1128     if(name) {
1129         New(1309,curitem,strlen(name)+1,char);
1130         strcpy(curitem, name);
1131         val = strchr(curitem, '=');
1132         if(val) {
1133             /* The sane way to deal with the environment.
1134              * Has these advantages over putenv() & co.:
1135              *  * enables us to store a truly empty value in the
1136              *    environment (like in UNIX).
1137              *  * we don't have to deal with RTL globals, bugs and leaks.
1138              *  * Much faster.
1139              * Why you may want to enable USE_WIN32_RTL_ENV:
1140              *  * environ[] and RTL functions will not reflect changes,
1141              *    which might be an issue if extensions want to access
1142              *    the env. via RTL.  This cuts both ways, since RTL will
1143              *    not see changes made by extensions that call the Win32
1144              *    functions directly, either.
1145              * GSAR 97-06-07
1146              */
1147             *val++ = '\0';
1148             if(SetEnvironmentVariable(curitem, *val ? val : NULL))
1149                 relval = 0;
1150         }
1151         Safefree(curitem);
1152     }
1153     return relval;
1154 }
1155
1156 #endif
1157
1158 static long
1159 filetime_to_clock(PFILETIME ft)
1160 {
1161  __int64 qw = ft->dwHighDateTime;
1162  qw <<= 32;
1163  qw |= ft->dwLowDateTime;
1164  qw /= 10000;  /* File time ticks at 0.1uS, clock at 1mS */
1165  return (long) qw;
1166 }
1167
1168 DllExport int
1169 win32_times(struct tms *timebuf)
1170 {
1171     FILETIME user;
1172     FILETIME kernel;
1173     FILETIME dummy;
1174     if (GetProcessTimes(GetCurrentProcess(), &dummy, &dummy, 
1175                         &kernel,&user)) {
1176         timebuf->tms_utime = filetime_to_clock(&user);
1177         timebuf->tms_stime = filetime_to_clock(&kernel);
1178         timebuf->tms_cutime = 0;
1179         timebuf->tms_cstime = 0;
1180         
1181     } else { 
1182         /* That failed - e.g. Win95 fallback to clock() */
1183         clock_t t = clock();
1184         timebuf->tms_utime = t;
1185         timebuf->tms_stime = 0;
1186         timebuf->tms_cutime = 0;
1187         timebuf->tms_cstime = 0;
1188     }
1189     return 0;
1190 }
1191
1192 /* fix utime() so it works on directories in NT
1193  * thanks to Jan Dubois <jan.dubois@ibm.net>
1194  */
1195 static BOOL
1196 filetime_from_time(PFILETIME pFileTime, time_t Time)
1197 {
1198     struct tm *pTM = gmtime(&Time);
1199     SYSTEMTIME SystemTime;
1200
1201     if (pTM == NULL)
1202         return FALSE;
1203
1204     SystemTime.wYear   = pTM->tm_year + 1900;
1205     SystemTime.wMonth  = pTM->tm_mon + 1;
1206     SystemTime.wDay    = pTM->tm_mday;
1207     SystemTime.wHour   = pTM->tm_hour;
1208     SystemTime.wMinute = pTM->tm_min;
1209     SystemTime.wSecond = pTM->tm_sec;
1210     SystemTime.wMilliseconds = 0;
1211
1212     return SystemTimeToFileTime(&SystemTime, pFileTime);
1213 }
1214
1215 DllExport int
1216 win32_utime(const char *filename, struct utimbuf *times)
1217 {
1218     HANDLE handle;
1219     FILETIME ftCreate;
1220     FILETIME ftAccess;
1221     FILETIME ftWrite;
1222     struct utimbuf TimeBuffer;
1223
1224     int rc = utime(filename,times);
1225     /* EACCES: path specifies directory or readonly file */
1226     if (rc == 0 || errno != EACCES /* || !IsWinNT() */)
1227         return rc;
1228
1229     if (times == NULL) {
1230         times = &TimeBuffer;
1231         time(&times->actime);
1232         times->modtime = times->actime;
1233     }
1234
1235     /* This will (and should) still fail on readonly files */
1236     handle = CreateFile(filename, GENERIC_READ | GENERIC_WRITE,
1237                         FILE_SHARE_READ | FILE_SHARE_DELETE, NULL,
1238                         OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
1239     if (handle == INVALID_HANDLE_VALUE)
1240         return rc;
1241
1242     if (GetFileTime(handle, &ftCreate, &ftAccess, &ftWrite) &&
1243         filetime_from_time(&ftAccess, times->actime) &&
1244         filetime_from_time(&ftWrite, times->modtime) &&
1245         SetFileTime(handle, &ftCreate, &ftAccess, &ftWrite))
1246     {
1247         rc = 0;
1248     }
1249
1250     CloseHandle(handle);
1251     return rc;
1252 }
1253
1254 DllExport int
1255 win32_uname(struct utsname *name)
1256 {
1257     struct hostent *hep;
1258     STRLEN nodemax = sizeof(name->nodename)-1;
1259     OSVERSIONINFO osver;
1260
1261     memset(&osver, 0, sizeof(OSVERSIONINFO));
1262     osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
1263     if (GetVersionEx(&osver)) {
1264         /* sysname */
1265         switch (osver.dwPlatformId) {
1266         case VER_PLATFORM_WIN32_WINDOWS:
1267             strcpy(name->sysname, "Windows");
1268             break;
1269         case VER_PLATFORM_WIN32_NT:
1270             strcpy(name->sysname, "Windows NT");
1271             break;
1272         case VER_PLATFORM_WIN32s:
1273             strcpy(name->sysname, "Win32s");
1274             break;
1275         default:
1276             strcpy(name->sysname, "Win32 Unknown");
1277             break;
1278         }
1279
1280         /* release */
1281         sprintf(name->release, "%d.%d",
1282                 osver.dwMajorVersion, osver.dwMinorVersion);
1283
1284         /* version */
1285         sprintf(name->version, "Build %d",
1286                 osver.dwPlatformId == VER_PLATFORM_WIN32_NT
1287                 ? osver.dwBuildNumber : (osver.dwBuildNumber & 0xffff));
1288         if (osver.szCSDVersion[0]) {
1289             char *buf = name->version + strlen(name->version);
1290             sprintf(buf, " (%s)", osver.szCSDVersion);
1291         }
1292     }
1293     else {
1294         *name->sysname = '\0';
1295         *name->version = '\0';
1296         *name->release = '\0';
1297     }
1298
1299     /* nodename */
1300     hep = win32_gethostbyname("localhost");
1301     if (hep) {
1302         STRLEN len = strlen(hep->h_name);
1303         if (len <= nodemax) {
1304             strcpy(name->nodename, hep->h_name);
1305         }
1306         else {
1307             strncpy(name->nodename, hep->h_name, nodemax);
1308             name->nodename[nodemax] = '\0';
1309         }
1310     }
1311     else {
1312         DWORD sz = nodemax;
1313         if (!GetComputerName(name->nodename, &sz))
1314             *name->nodename = '\0';
1315     }
1316
1317     /* machine (architecture) */
1318     {
1319         SYSTEM_INFO info;
1320         char *arch;
1321         GetSystemInfo(&info);
1322
1323 #ifdef __MINGW32__
1324         switch (info.DUMMYUNIONNAME.DUMMYSTRUCTNAME.wProcessorArchitecture) {
1325 #else
1326 #ifdef __BORLANDC__
1327         switch (info.u.s.wProcessorArchitecture) {
1328 #else
1329         switch (info.wProcessorArchitecture) {
1330 #endif
1331 #endif
1332         case PROCESSOR_ARCHITECTURE_INTEL:
1333             arch = "x86"; break;
1334         case PROCESSOR_ARCHITECTURE_MIPS:
1335             arch = "mips"; break;
1336         case PROCESSOR_ARCHITECTURE_ALPHA:
1337             arch = "alpha"; break;
1338         case PROCESSOR_ARCHITECTURE_PPC:
1339             arch = "ppc"; break;
1340         default:
1341             arch = "unknown"; break;
1342         }
1343         strcpy(name->machine, arch);
1344     }
1345     return 0;
1346 }
1347
1348 DllExport int
1349 win32_waitpid(int pid, int *status, int flags)
1350 {
1351     int retval = -1;
1352     if (pid == -1) 
1353         return win32_wait(status);
1354     else {
1355         long child = find_pid(pid);
1356         if (child >= 0) {
1357             HANDLE hProcess = w32_child_handles[child];
1358             DWORD waitcode = WaitForSingleObject(hProcess, INFINITE);
1359             if (waitcode != WAIT_FAILED) {
1360                 if (GetExitCodeProcess(hProcess, &waitcode)) {
1361                     *status = (int)((waitcode & 0xff) << 8);
1362                     retval = (int)w32_child_pids[child];
1363                     remove_dead_process(child);
1364                     return retval;
1365                 }
1366             }
1367             else
1368                 errno = ECHILD;
1369         }
1370         else {
1371             retval = cwait(status, pid, WAIT_CHILD);
1372             /* cwait() returns "correctly" on Borland */
1373 #ifndef __BORLANDC__
1374             if (status)
1375                 *status *= 256;
1376 #endif
1377         }
1378     }
1379     return retval >= 0 ? pid : retval;                
1380 }
1381
1382 DllExport int
1383 win32_wait(int *status)
1384 {
1385     /* XXX this wait emulation only knows about processes
1386      * spawned via win32_spawnvp(P_NOWAIT, ...).
1387      */
1388     int i, retval;
1389     DWORD exitcode, waitcode;
1390
1391     if (!w32_num_children) {
1392         errno = ECHILD;
1393         return -1;
1394     }
1395
1396     /* if a child exists, wait for it to die */
1397     waitcode = WaitForMultipleObjects(w32_num_children,
1398                                       w32_child_handles,
1399                                       FALSE,
1400                                       INFINITE);
1401     if (waitcode != WAIT_FAILED) {
1402         if (waitcode >= WAIT_ABANDONED_0
1403             && waitcode < WAIT_ABANDONED_0 + w32_num_children)
1404             i = waitcode - WAIT_ABANDONED_0;
1405         else
1406             i = waitcode - WAIT_OBJECT_0;
1407         if (GetExitCodeProcess(w32_child_handles[i], &exitcode) ) {
1408             *status = (int)((exitcode & 0xff) << 8);
1409             retval = (int)w32_child_pids[i];
1410             remove_dead_process(i);
1411             return retval;
1412         }
1413     }
1414
1415 FAILED:
1416     errno = GetLastError();
1417     return -1;
1418 }
1419
1420 static UINT timerid = 0;
1421
1422 static VOID CALLBACK TimerProc(HWND win, UINT msg, UINT id, DWORD time)
1423 {
1424  KillTimer(NULL,timerid);
1425  timerid=0;  
1426  sighandler(14);
1427 }
1428
1429 DllExport unsigned int
1430 win32_alarm(unsigned int sec)
1431 {
1432     /* 
1433      * the 'obvious' implentation is SetTimer() with a callback
1434      * which does whatever receiving SIGALRM would do 
1435      * we cannot use SIGALRM even via raise() as it is not 
1436      * one of the supported codes in <signal.h>
1437      *
1438      * Snag is unless something is looking at the message queue
1439      * nothing happens :-(
1440      */ 
1441     if (sec)
1442      {
1443       timerid = SetTimer(NULL,timerid,sec*1000,(TIMERPROC)TimerProc);
1444       if (!timerid)
1445        croak("Cannot set timer");
1446      } 
1447     else
1448      {
1449       if (timerid)
1450        {
1451         KillTimer(NULL,timerid);
1452         timerid=0;  
1453        }
1454      }
1455     return 0;
1456 }
1457
1458 #if defined(HAVE_DES_FCRYPT) || defined(PERL_OBJECT)
1459 #ifdef HAVE_DES_FCRYPT
1460 extern char *   des_fcrypt(const char *txt, const char *salt, char *cbuf);
1461 #endif
1462
1463 DllExport char *
1464 win32_crypt(const char *txt, const char *salt)
1465 {
1466 #ifdef HAVE_DES_FCRYPT
1467     dTHR;
1468     return des_fcrypt(txt, salt, crypt_buffer);
1469 #else
1470     die("The crypt() function is unimplemented due to excessive paranoia.");
1471     return Nullch;
1472 #endif
1473 }
1474 #endif
1475
1476 #ifdef USE_FIXED_OSFHANDLE
1477
1478 EXTERN_C int __cdecl _alloc_osfhnd(void);
1479 EXTERN_C int __cdecl _set_osfhnd(int fh, long value);
1480 EXTERN_C void __cdecl _lock_fhandle(int);
1481 EXTERN_C void __cdecl _unlock_fhandle(int);
1482 EXTERN_C void __cdecl _unlock(int);
1483
1484 #if     (_MSC_VER >= 1000)
1485 typedef struct  {
1486     long osfhnd;    /* underlying OS file HANDLE */
1487     char osfile;    /* attributes of file (e.g., open in text mode?) */
1488     char pipech;    /* one char buffer for handles opened on pipes */
1489 #if defined (_MT) && !defined (DLL_FOR_WIN32S)
1490     int lockinitflag;
1491     CRITICAL_SECTION lock;
1492 #endif  /* defined (_MT) && !defined (DLL_FOR_WIN32S) */
1493 }       ioinfo;
1494
1495 EXTERN_C ioinfo * __pioinfo[];
1496
1497 #define IOINFO_L2E                      5
1498 #define IOINFO_ARRAY_ELTS       (1 << IOINFO_L2E)
1499 #define _pioinfo(i)     (__pioinfo[i >> IOINFO_L2E] + (i & (IOINFO_ARRAY_ELTS - 1)))
1500 #define _osfile(i)      (_pioinfo(i)->osfile)
1501
1502 #else   /* (_MSC_VER >= 1000) */
1503 extern char _osfile[];
1504 #endif  /* (_MSC_VER >= 1000) */
1505
1506 #define FOPEN                   0x01    /* file handle open */
1507 #define FAPPEND                 0x20    /* file handle opened O_APPEND */
1508 #define FDEV                    0x40    /* file handle refers to device */
1509 #define FTEXT                   0x80    /* file handle is in text mode */
1510
1511 #define _STREAM_LOCKS   26              /* Table of stream locks */
1512 #define _LAST_STREAM_LOCK  (_STREAM_LOCKS+_NSTREAM_-1)  /* Last stream lock */
1513 #define _FH_LOCKS          (_LAST_STREAM_LOCK+1)        /* Table of fh locks */
1514
1515 /***
1516 *int my_open_osfhandle(long osfhandle, int flags) - open C Runtime file handle
1517 *
1518 *Purpose:
1519 *       This function allocates a free C Runtime file handle and associates
1520 *       it with the Win32 HANDLE specified by the first parameter. This is a
1521 *               temperary fix for WIN95's brain damage GetFileType() error on socket
1522 *               we just bypass that call for socket
1523 *
1524 *Entry:
1525 *       long osfhandle - Win32 HANDLE to associate with C Runtime file handle.
1526 *       int flags      - flags to associate with C Runtime file handle.
1527 *
1528 *Exit:
1529 *       returns index of entry in fh, if successful
1530 *       return -1, if no free entry is found
1531 *
1532 *Exceptions:
1533 *
1534 *******************************************************************************/
1535
1536 static int
1537 my_open_osfhandle(long osfhandle, int flags)
1538 {
1539     int fh;
1540     char fileflags;             /* _osfile flags */
1541
1542     /* copy relevant flags from second parameter */
1543     fileflags = FDEV;
1544
1545     if (flags & O_APPEND)
1546         fileflags |= FAPPEND;
1547
1548     if (flags & O_TEXT)
1549         fileflags |= FTEXT;
1550
1551     /* attempt to allocate a C Runtime file handle */
1552     if ((fh = _alloc_osfhnd()) == -1) {
1553         errno = EMFILE;         /* too many open files */
1554         _doserrno = 0L;         /* not an OS error */
1555         return -1;              /* return error to caller */
1556     }
1557
1558     /* the file is open. now, set the info in _osfhnd array */
1559     _set_osfhnd(fh, osfhandle);
1560
1561     fileflags |= FOPEN;         /* mark as open */
1562
1563 #if (_MSC_VER >= 1000)
1564     _osfile(fh) = fileflags;    /* set osfile entry */
1565     _unlock_fhandle(fh);
1566 #else
1567     _osfile[fh] = fileflags;    /* set osfile entry */
1568     _unlock(fh+_FH_LOCKS);              /* unlock handle */
1569 #endif
1570
1571     return fh;                  /* return handle */
1572 }
1573
1574 #define _open_osfhandle my_open_osfhandle
1575 #endif  /* USE_FIXED_OSFHANDLE */
1576
1577 /* simulate flock by locking a range on the file */
1578
1579 #define LK_ERR(f,i)     ((f) ? (i = 0) : (errno = GetLastError()))
1580 #define LK_LEN          0xffff0000
1581
1582 DllExport int
1583 win32_flock(int fd, int oper)
1584 {
1585     OVERLAPPED o;
1586     int i = -1;
1587     HANDLE fh;
1588
1589     if (!IsWinNT()) {
1590         croak("flock() unimplemented on this platform");
1591         return -1;
1592     }
1593     fh = (HANDLE)_get_osfhandle(fd);
1594     memset(&o, 0, sizeof(o));
1595
1596     switch(oper) {
1597     case LOCK_SH:               /* shared lock */
1598         LK_ERR(LockFileEx(fh, 0, 0, LK_LEN, 0, &o),i);
1599         break;
1600     case LOCK_EX:               /* exclusive lock */
1601         LK_ERR(LockFileEx(fh, LOCKFILE_EXCLUSIVE_LOCK, 0, LK_LEN, 0, &o),i);
1602         break;
1603     case LOCK_SH|LOCK_NB:       /* non-blocking shared lock */
1604         LK_ERR(LockFileEx(fh, LOCKFILE_FAIL_IMMEDIATELY, 0, LK_LEN, 0, &o),i);
1605         break;
1606     case LOCK_EX|LOCK_NB:       /* non-blocking exclusive lock */
1607         LK_ERR(LockFileEx(fh,
1608                        LOCKFILE_EXCLUSIVE_LOCK|LOCKFILE_FAIL_IMMEDIATELY,
1609                        0, LK_LEN, 0, &o),i);
1610         break;
1611     case LOCK_UN:               /* unlock lock */
1612         LK_ERR(UnlockFileEx(fh, 0, LK_LEN, 0, &o),i);
1613         break;
1614     default:                    /* unknown */
1615         errno = EINVAL;
1616         break;
1617     }
1618     return i;
1619 }
1620
1621 #undef LK_ERR
1622 #undef LK_LEN
1623
1624 /*
1625  *  redirected io subsystem for all XS modules
1626  *
1627  */
1628
1629 DllExport int *
1630 win32_errno(void)
1631 {
1632     return (&errno);
1633 }
1634
1635 DllExport char ***
1636 win32_environ(void)
1637 {
1638     return (&(_environ));
1639 }
1640
1641 /* the rest are the remapped stdio routines */
1642 DllExport FILE *
1643 win32_stderr(void)
1644 {
1645     return (stderr);
1646 }
1647
1648 DllExport FILE *
1649 win32_stdin(void)
1650 {
1651     return (stdin);
1652 }
1653
1654 DllExport FILE *
1655 win32_stdout()
1656 {
1657     return (stdout);
1658 }
1659
1660 DllExport int
1661 win32_ferror(FILE *fp)
1662 {
1663     return (ferror(fp));
1664 }
1665
1666
1667 DllExport int
1668 win32_feof(FILE *fp)
1669 {
1670     return (feof(fp));
1671 }
1672
1673 /*
1674  * Since the errors returned by the socket error function 
1675  * WSAGetLastError() are not known by the library routine strerror
1676  * we have to roll our own.
1677  */
1678
1679 DllExport char *
1680 win32_strerror(int e) 
1681 {
1682 #ifndef __BORLANDC__            /* Borland intolerance */
1683     extern int sys_nerr;
1684 #endif
1685     DWORD source = 0;
1686
1687     if (e < 0 || e > sys_nerr) {
1688         dTHR;
1689         if (e < 0)
1690             e = GetLastError();
1691
1692         if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, &source, e, 0,
1693                          strerror_buffer, sizeof(strerror_buffer), NULL) == 0) 
1694             strcpy(strerror_buffer, "Unknown Error");
1695
1696         return strerror_buffer;
1697     }
1698     return strerror(e);
1699 }
1700
1701 DllExport void
1702 win32_str_os_error(void *sv, DWORD dwErr)
1703 {
1704     DWORD dwLen;
1705     char *sMsg;
1706     dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER
1707                           |FORMAT_MESSAGE_IGNORE_INSERTS
1708                           |FORMAT_MESSAGE_FROM_SYSTEM, NULL,
1709                            dwErr, 0, (char *)&sMsg, 1, NULL);
1710     if (0 < dwLen) {
1711         while (0 < dwLen  &&  isspace(sMsg[--dwLen]))
1712             ;
1713         if ('.' != sMsg[dwLen])
1714             dwLen++;
1715         sMsg[dwLen]= '\0';
1716     }
1717     if (0 == dwLen) {
1718         sMsg = (char*)LocalAlloc(0, 64/**sizeof(TCHAR)*/);
1719         dwLen = sprintf(sMsg,
1720                         "Unknown error #0x%lX (lookup 0x%lX)",
1721                         dwErr, GetLastError());
1722     }
1723     sv_setpvn((SV*)sv, sMsg, dwLen);
1724     LocalFree(sMsg);
1725 }
1726
1727
1728 DllExport int
1729 win32_fprintf(FILE *fp, const char *format, ...)
1730 {
1731     va_list marker;
1732     va_start(marker, format);     /* Initialize variable arguments. */
1733
1734     return (vfprintf(fp, format, marker));
1735 }
1736
1737 DllExport int
1738 win32_printf(const char *format, ...)
1739 {
1740     va_list marker;
1741     va_start(marker, format);     /* Initialize variable arguments. */
1742
1743     return (vprintf(format, marker));
1744 }
1745
1746 DllExport int
1747 win32_vfprintf(FILE *fp, const char *format, va_list args)
1748 {
1749     return (vfprintf(fp, format, args));
1750 }
1751
1752 DllExport int
1753 win32_vprintf(const char *format, va_list args)
1754 {
1755     return (vprintf(format, args));
1756 }
1757
1758 DllExport size_t
1759 win32_fread(void *buf, size_t size, size_t count, FILE *fp)
1760 {
1761     return fread(buf, size, count, fp);
1762 }
1763
1764 DllExport size_t
1765 win32_fwrite(const void *buf, size_t size, size_t count, FILE *fp)
1766 {
1767     return fwrite(buf, size, count, fp);
1768 }
1769
1770 DllExport FILE *
1771 win32_fopen(const char *filename, const char *mode)
1772 {
1773     if (stricmp(filename, "/dev/null")==0)
1774         return fopen("NUL", mode);
1775     return fopen(filename, mode);
1776 }
1777
1778 #ifndef USE_SOCKETS_AS_HANDLES
1779 #undef fdopen
1780 #define fdopen my_fdopen
1781 #endif
1782
1783 DllExport FILE *
1784 win32_fdopen( int handle, const char *mode)
1785 {
1786     return fdopen(handle, (char *) mode);
1787 }
1788
1789 DllExport FILE *
1790 win32_freopen( const char *path, const char *mode, FILE *stream)
1791 {
1792     if (stricmp(path, "/dev/null")==0)
1793         return freopen("NUL", mode, stream);
1794     return freopen(path, mode, stream);
1795 }
1796
1797 DllExport int
1798 win32_fclose(FILE *pf)
1799 {
1800     return my_fclose(pf);       /* defined in win32sck.c */
1801 }
1802
1803 DllExport int
1804 win32_fputs(const char *s,FILE *pf)
1805 {
1806     return fputs(s, pf);
1807 }
1808
1809 DllExport int
1810 win32_fputc(int c,FILE *pf)
1811 {
1812     return fputc(c,pf);
1813 }
1814
1815 DllExport int
1816 win32_ungetc(int c,FILE *pf)
1817 {
1818     return ungetc(c,pf);
1819 }
1820
1821 DllExport int
1822 win32_getc(FILE *pf)
1823 {
1824     return getc(pf);
1825 }
1826
1827 DllExport int
1828 win32_fileno(FILE *pf)
1829 {
1830     return fileno(pf);
1831 }
1832
1833 DllExport void
1834 win32_clearerr(FILE *pf)
1835 {
1836     clearerr(pf);
1837     return;
1838 }
1839
1840 DllExport int
1841 win32_fflush(FILE *pf)
1842 {
1843     return fflush(pf);
1844 }
1845
1846 DllExport long
1847 win32_ftell(FILE *pf)
1848 {
1849     return ftell(pf);
1850 }
1851
1852 DllExport int
1853 win32_fseek(FILE *pf,long offset,int origin)
1854 {
1855     return fseek(pf, offset, origin);
1856 }
1857
1858 DllExport int
1859 win32_fgetpos(FILE *pf,fpos_t *p)
1860 {
1861     return fgetpos(pf, p);
1862 }
1863
1864 DllExport int
1865 win32_fsetpos(FILE *pf,const fpos_t *p)
1866 {
1867     return fsetpos(pf, p);
1868 }
1869
1870 DllExport void
1871 win32_rewind(FILE *pf)
1872 {
1873     rewind(pf);
1874     return;
1875 }
1876
1877 DllExport FILE*
1878 win32_tmpfile(void)
1879 {
1880     return tmpfile();
1881 }
1882
1883 DllExport void
1884 win32_abort(void)
1885 {
1886     abort();
1887     return;
1888 }
1889
1890 DllExport int
1891 win32_fstat(int fd,struct stat *sbufptr)
1892 {
1893     return fstat(fd,sbufptr);
1894 }
1895
1896 DllExport int
1897 win32_pipe(int *pfd, unsigned int size, int mode)
1898 {
1899     return _pipe(pfd, size, mode);
1900 }
1901
1902 /*
1903  * a popen() clone that respects PERL5SHELL
1904  */
1905
1906 DllExport FILE*
1907 win32_popen(const char *command, const char *mode)
1908 {
1909 #ifdef USE_RTL_POPEN
1910     return _popen(command, mode);
1911 #else
1912     int p[2];
1913     int parent, child;
1914     int stdfd, oldfd;
1915     int ourmode;
1916     int childpid;
1917
1918     /* establish which ends read and write */
1919     if (strchr(mode,'w')) {
1920         stdfd = 0;              /* stdin */
1921         parent = 1;
1922         child = 0;
1923     }
1924     else if (strchr(mode,'r')) {
1925         stdfd = 1;              /* stdout */
1926         parent = 0;
1927         child = 1;
1928     }
1929     else
1930         return NULL;
1931
1932     /* set the correct mode */
1933     if (strchr(mode,'b'))
1934         ourmode = O_BINARY;
1935     else if (strchr(mode,'t'))
1936         ourmode = O_TEXT;
1937     else
1938         ourmode = _fmode & (O_TEXT | O_BINARY);
1939
1940     /* the child doesn't inherit handles */
1941     ourmode |= O_NOINHERIT;
1942
1943     if (win32_pipe( p, 512, ourmode) == -1)
1944         return NULL;
1945
1946     /* save current stdfd */
1947     if ((oldfd = win32_dup(stdfd)) == -1)
1948         goto cleanup;
1949
1950     /* make stdfd go to child end of pipe (implicitly closes stdfd) */
1951     /* stdfd will be inherited by the child */
1952     if (win32_dup2(p[child], stdfd) == -1)
1953         goto cleanup;
1954
1955     /* close the child end in parent */
1956     win32_close(p[child]);
1957
1958     /* start the child */
1959     if ((childpid = do_spawn_nowait((char*)command)) == -1)
1960         goto cleanup;
1961
1962     /* revert stdfd to whatever it was before */
1963     if (win32_dup2(oldfd, stdfd) == -1)
1964         goto cleanup;
1965
1966     /* close saved handle */
1967     win32_close(oldfd);
1968
1969     sv_setiv(*av_fetch(w32_fdpid, p[parent], TRUE), childpid);
1970
1971     /* we have an fd, return a file stream */
1972     return (win32_fdopen(p[parent], (char *)mode));
1973
1974 cleanup:
1975     /* we don't need to check for errors here */
1976     win32_close(p[0]);
1977     win32_close(p[1]);
1978     if (oldfd != -1) {
1979         win32_dup2(oldfd, stdfd);
1980         win32_close(oldfd);
1981     }
1982     return (NULL);
1983
1984 #endif /* USE_RTL_POPEN */
1985 }
1986
1987 /*
1988  * pclose() clone
1989  */
1990
1991 DllExport int
1992 win32_pclose(FILE *pf)
1993 {
1994 #ifdef USE_RTL_POPEN
1995     return _pclose(pf);
1996 #else
1997
1998     int childpid, status;
1999     SV *sv;
2000
2001     sv = *av_fetch(w32_fdpid, win32_fileno(pf), TRUE);
2002     if (SvIOK(sv))
2003         childpid = SvIVX(sv);
2004     else
2005         childpid = 0;
2006
2007     if (!childpid) {
2008         errno = EBADF;
2009         return -1;
2010     }
2011
2012     win32_fclose(pf);
2013     SvIVX(sv) = 0;
2014
2015     if (win32_waitpid(childpid, &status, 0) == -1)
2016         return -1;
2017
2018     return status;
2019
2020 #endif /* USE_RTL_POPEN */
2021 }
2022
2023 DllExport int
2024 win32_rename(const char *oname, const char *newname)
2025 {
2026     /* XXX despite what the documentation says about MoveFileEx(),
2027      * it doesn't work under Windows95!
2028      */
2029     if (IsWinNT()) {
2030         if (!MoveFileEx(oname,newname,
2031                         MOVEFILE_COPY_ALLOWED|MOVEFILE_REPLACE_EXISTING)) {
2032             DWORD err = GetLastError();
2033             switch (err) {
2034             case ERROR_BAD_NET_NAME:
2035             case ERROR_BAD_NETPATH:
2036             case ERROR_BAD_PATHNAME:
2037             case ERROR_FILE_NOT_FOUND:
2038             case ERROR_FILENAME_EXCED_RANGE:
2039             case ERROR_INVALID_DRIVE:
2040             case ERROR_NO_MORE_FILES:
2041             case ERROR_PATH_NOT_FOUND:
2042                 errno = ENOENT;
2043                 break;
2044             default:
2045                 errno = EACCES;
2046                 break;
2047             }
2048             return -1;
2049         }
2050         return 0;
2051     }
2052     else {
2053         int retval = 0;
2054         char tmpname[MAX_PATH+1];
2055         char dname[MAX_PATH+1];
2056         char *endname = Nullch;
2057         STRLEN tmplen = 0;
2058         DWORD from_attr, to_attr;
2059
2060         /* if oname doesn't exist, do nothing */
2061         from_attr = GetFileAttributes(oname);
2062         if (from_attr == 0xFFFFFFFF) {
2063             errno = ENOENT;
2064             return -1;
2065         }
2066
2067         /* if newname exists, rename it to a temporary name so that we
2068          * don't delete it in case oname happens to be the same file
2069          * (but perhaps accessed via a different path)
2070          */
2071         to_attr = GetFileAttributes(newname);
2072         if (to_attr != 0xFFFFFFFF) {
2073             /* if newname is a directory, we fail
2074              * XXX could overcome this with yet more convoluted logic */
2075             if (to_attr & FILE_ATTRIBUTE_DIRECTORY) {
2076                 errno = EACCES;
2077                 return -1;
2078             }
2079             tmplen = strlen(newname);
2080             strcpy(tmpname,newname);
2081             endname = tmpname+tmplen;
2082             for (; endname > tmpname ; --endname) {
2083                 if (*endname == '/' || *endname == '\\') {
2084                     *endname = '\0';
2085                     break;
2086                 }
2087             }
2088             if (endname > tmpname)
2089                 endname = strcpy(dname,tmpname);
2090             else
2091                 endname = ".";
2092
2093             /* get a temporary filename in same directory
2094              * XXX is this really the best we can do? */
2095             if (!GetTempFileName((LPCTSTR)endname, "plr", 0, tmpname)) {
2096                 errno = ENOENT;
2097                 return -1;
2098             }
2099             DeleteFile(tmpname);
2100
2101             retval = rename(newname, tmpname);
2102             if (retval != 0) {
2103                 errno = EACCES;
2104                 return retval;
2105             }
2106         }
2107
2108         /* rename oname to newname */
2109         retval = rename(oname, newname);
2110
2111         /* if we created a temporary file before ... */
2112         if (endname != Nullch) {
2113             /* ...and rename succeeded, delete temporary file/directory */
2114             if (retval == 0)
2115                 DeleteFile(tmpname);
2116             /* else restore it to what it was */
2117             else
2118                 (void)rename(tmpname, newname);
2119         }
2120         return retval;
2121     }
2122 }
2123
2124 DllExport int
2125 win32_setmode(int fd, int mode)
2126 {
2127     return setmode(fd, mode);
2128 }
2129
2130 DllExport long
2131 win32_lseek(int fd, long offset, int origin)
2132 {
2133     return lseek(fd, offset, origin);
2134 }
2135
2136 DllExport long
2137 win32_tell(int fd)
2138 {
2139     return tell(fd);
2140 }
2141
2142 DllExport int
2143 win32_open(const char *path, int flag, ...)
2144 {
2145     va_list ap;
2146     int pmode;
2147
2148     va_start(ap, flag);
2149     pmode = va_arg(ap, int);
2150     va_end(ap);
2151
2152     if (stricmp(path, "/dev/null")==0)
2153         return open("NUL", flag, pmode);
2154     return open(path,flag,pmode);
2155 }
2156
2157 DllExport int
2158 win32_close(int fd)
2159 {
2160     return close(fd);
2161 }
2162
2163 DllExport int
2164 win32_eof(int fd)
2165 {
2166     return eof(fd);
2167 }
2168
2169 DllExport int
2170 win32_dup(int fd)
2171 {
2172     return dup(fd);
2173 }
2174
2175 DllExport int
2176 win32_dup2(int fd1,int fd2)
2177 {
2178     return dup2(fd1,fd2);
2179 }
2180
2181 DllExport int
2182 win32_read(int fd, void *buf, unsigned int cnt)
2183 {
2184     return read(fd, buf, cnt);
2185 }
2186
2187 DllExport int
2188 win32_write(int fd, const void *buf, unsigned int cnt)
2189 {
2190     return write(fd, buf, cnt);
2191 }
2192
2193 DllExport int
2194 win32_mkdir(const char *dir, int mode)
2195 {
2196     return mkdir(dir); /* just ignore mode */
2197 }
2198
2199 DllExport int
2200 win32_rmdir(const char *dir)
2201 {
2202     return rmdir(dir);
2203 }
2204
2205 DllExport int
2206 win32_chdir(const char *dir)
2207 {
2208     return chdir(dir);
2209 }
2210
2211 static char *
2212 create_command_line(const char* command, const char * const *args)
2213 {
2214     int index;
2215     char *cmd, *ptr, *arg;
2216     STRLEN len = strlen(command) + 1;
2217
2218     for (index = 0; (ptr = (char*)args[index]) != NULL; ++index)
2219         len += strlen(ptr) + 1;
2220
2221     New(1310, cmd, len, char);
2222     ptr = cmd;
2223     strcpy(ptr, command);
2224
2225     for (index = 0; (arg = (char*)args[index]) != NULL; ++index) {
2226         ptr += strlen(ptr);
2227         *ptr++ = ' ';
2228         strcpy(ptr, arg);
2229     }
2230
2231     return cmd;
2232 }
2233
2234 static char *
2235 qualified_path(const char *cmd)
2236 {
2237     char *pathstr;
2238     char *fullcmd, *curfullcmd;
2239     STRLEN cmdlen = 0;
2240     int has_slash = 0;
2241
2242     if (!cmd)
2243         return Nullch;
2244     fullcmd = (char*)cmd;
2245     while (*fullcmd) {
2246         if (*fullcmd == '/' || *fullcmd == '\\')
2247             has_slash++;
2248         fullcmd++;
2249         cmdlen++;
2250     }
2251
2252     /* look in PATH */
2253     pathstr = win32_getenv("PATH");
2254     New(0, fullcmd, MAX_PATH+1, char);
2255     curfullcmd = fullcmd;
2256
2257     while (1) {
2258         DWORD res;
2259
2260         /* start by appending the name to the current prefix */
2261         strcpy(curfullcmd, cmd);
2262         curfullcmd += cmdlen;
2263
2264         /* if it doesn't end with '.', or has no extension, try adding
2265          * a trailing .exe first */
2266         if (cmd[cmdlen-1] != '.'
2267             && (cmdlen < 4 || cmd[cmdlen-4] != '.'))
2268         {
2269             strcpy(curfullcmd, ".exe");
2270             res = GetFileAttributes(fullcmd);
2271             if (res != 0xFFFFFFFF && !(res & FILE_ATTRIBUTE_DIRECTORY))
2272                 return fullcmd;
2273             *curfullcmd = '\0';
2274         }
2275
2276         /* that failed, try the bare name */
2277         res = GetFileAttributes(fullcmd);
2278         if (res != 0xFFFFFFFF && !(res & FILE_ATTRIBUTE_DIRECTORY))
2279             return fullcmd;
2280
2281         /* quit if no other path exists, or if cmd already has path */
2282         if (!pathstr || !*pathstr || has_slash)
2283             break;
2284
2285         /* skip leading semis */
2286         while (*pathstr == ';')
2287             pathstr++;
2288
2289         /* build a new prefix from scratch */
2290         curfullcmd = fullcmd;
2291         while (*pathstr && *pathstr != ';') {
2292             if (*pathstr == '"') {      /* foo;"baz;etc";bar */
2293                 pathstr++;              /* skip initial '"' */
2294                 while (*pathstr && *pathstr != '"') {
2295                     if (curfullcmd-fullcmd < MAX_PATH-cmdlen-5)
2296                         *curfullcmd++ = *pathstr;
2297                     pathstr++;
2298                 }
2299                 if (*pathstr)
2300                     pathstr++;          /* skip trailing '"' */
2301             }
2302             else {
2303                 if (curfullcmd-fullcmd < MAX_PATH-cmdlen-5)
2304                     *curfullcmd++ = *pathstr;
2305                 pathstr++;
2306             }
2307         }
2308         if (*pathstr)
2309             pathstr++;                  /* skip trailing semi */
2310         if (curfullcmd > fullcmd        /* append a dir separator */
2311             && curfullcmd[-1] != '/' && curfullcmd[-1] != '\\')
2312         {
2313             *curfullcmd++ = '\\';
2314         }
2315     }
2316 GIVE_UP:
2317     Safefree(fullcmd);
2318     return Nullch;
2319 }
2320
2321 /* XXX this needs to be made more compatible with the spawnvp()
2322  * provided by the various RTLs.  In particular, searching for
2323  * *.{com,bat,cmd} files (as done by the RTLs) is unimplemented.
2324  * This doesn't significantly affect perl itself, because we
2325  * always invoke things using PERL5SHELL if a direct attempt to
2326  * spawn the executable fails.
2327  * 
2328  * XXX splitting and rejoining the commandline between do_aspawn()
2329  * and win32_spawnvp() could also be avoided.
2330  */
2331
2332 DllExport int
2333 win32_spawnvp(int mode, const char *cmdname, const char *const *argv)
2334 {
2335 #ifdef USE_RTL_SPAWNVP
2336     return spawnvp(mode, cmdname, (char * const *)argv);
2337 #else
2338     DWORD ret;
2339     STARTUPINFO StartupInfo;
2340     PROCESS_INFORMATION ProcessInformation;
2341     DWORD create = 0;
2342
2343     char *cmd = create_command_line(cmdname, strcmp(cmdname, argv[0]) == 0
2344                                              ? &argv[1] : argv);
2345     char *fullcmd = Nullch;
2346
2347     switch(mode) {
2348     case P_NOWAIT:      /* asynch + remember result */
2349         if (w32_num_children >= MAXIMUM_WAIT_OBJECTS) {
2350             errno = EAGAIN;
2351             ret = -1;
2352             goto RETVAL;
2353         }
2354         /* FALL THROUGH */
2355     case P_WAIT:        /* synchronous execution */
2356         break;
2357     default:            /* invalid mode */
2358         errno = EINVAL;
2359         ret = -1;
2360         goto RETVAL;
2361     }
2362     memset(&StartupInfo,0,sizeof(StartupInfo));
2363     StartupInfo.cb = sizeof(StartupInfo);
2364     StartupInfo.wShowWindow = SW_SHOWDEFAULT;
2365
2366 RETRY:
2367     if (!CreateProcess(cmdname,         /* search PATH to find executable */
2368                        cmd,             /* executable, and its arguments */
2369                        NULL,            /* process attributes */
2370                        NULL,            /* thread attributes */
2371                        TRUE,            /* inherit handles */
2372                        create,          /* creation flags */
2373                        NULL,            /* inherit environment */
2374                        NULL,            /* inherit cwd */
2375                        &StartupInfo,
2376                        &ProcessInformation))
2377     {
2378         /* initial NULL argument to CreateProcess() does a PATH
2379          * search, but it always first looks in the directory
2380          * where the current process was started, which behavior
2381          * is undesirable for backward compatibility.  So we
2382          * jump through our own hoops by picking out the path
2383          * we really want it to use. */
2384         if (!fullcmd) {
2385             fullcmd = qualified_path(cmdname);
2386             if (fullcmd) {
2387                 cmdname = fullcmd;
2388                 goto RETRY;
2389             }
2390         }
2391         errno = ENOENT;
2392         ret = -1;
2393         goto RETVAL;
2394     }
2395
2396     if (mode == P_NOWAIT) {
2397         /* asynchronous spawn -- store handle, return PID */
2398         w32_child_handles[w32_num_children] = ProcessInformation.hProcess;
2399         ret = w32_child_pids[w32_num_children] = ProcessInformation.dwProcessId;
2400         ++w32_num_children;
2401     }
2402     else  {
2403         WaitForSingleObject(ProcessInformation.hProcess, INFINITE);
2404         GetExitCodeProcess(ProcessInformation.hProcess, &ret);
2405         CloseHandle(ProcessInformation.hProcess);
2406     }
2407
2408     CloseHandle(ProcessInformation.hThread);
2409 RETVAL:
2410     Safefree(cmd);
2411     Safefree(fullcmd);
2412     return (int)ret;
2413 #endif
2414 }
2415
2416 DllExport int
2417 win32_execv(const char *cmdname, const char *const *argv)
2418 {
2419     return execv(cmdname, (char *const *)argv);
2420 }
2421
2422 DllExport int
2423 win32_execvp(const char *cmdname, const char *const *argv)
2424 {
2425     return execvp(cmdname, (char *const *)argv);
2426 }
2427
2428 DllExport void
2429 win32_perror(const char *str)
2430 {
2431     perror(str);
2432 }
2433
2434 DllExport void
2435 win32_setbuf(FILE *pf, char *buf)
2436 {
2437     setbuf(pf, buf);
2438 }
2439
2440 DllExport int
2441 win32_setvbuf(FILE *pf, char *buf, int type, size_t size)
2442 {
2443     return setvbuf(pf, buf, type, size);
2444 }
2445
2446 DllExport int
2447 win32_flushall(void)
2448 {
2449     return flushall();
2450 }
2451
2452 DllExport int
2453 win32_fcloseall(void)
2454 {
2455     return fcloseall();
2456 }
2457
2458 DllExport char*
2459 win32_fgets(char *s, int n, FILE *pf)
2460 {
2461     return fgets(s, n, pf);
2462 }
2463
2464 DllExport char*
2465 win32_gets(char *s)
2466 {
2467     return gets(s);
2468 }
2469
2470 DllExport int
2471 win32_fgetc(FILE *pf)
2472 {
2473     return fgetc(pf);
2474 }
2475
2476 DllExport int
2477 win32_putc(int c, FILE *pf)
2478 {
2479     return putc(c,pf);
2480 }
2481
2482 DllExport int
2483 win32_puts(const char *s)
2484 {
2485     return puts(s);
2486 }
2487
2488 DllExport int
2489 win32_getchar(void)
2490 {
2491     return getchar();
2492 }
2493
2494 DllExport int
2495 win32_putchar(int c)
2496 {
2497     return putchar(c);
2498 }
2499
2500 #ifdef MYMALLOC
2501
2502 #ifndef USE_PERL_SBRK
2503
2504 static char *committed = NULL;
2505 static char *base      = NULL;
2506 static char *reserved  = NULL;
2507 static char *brk       = NULL;
2508 static DWORD pagesize  = 0;
2509 static DWORD allocsize = 0;
2510
2511 void *
2512 sbrk(int need)
2513 {
2514  void *result;
2515  if (!pagesize)
2516   {SYSTEM_INFO info;
2517    GetSystemInfo(&info);
2518    /* Pretend page size is larger so we don't perpetually
2519     * call the OS to commit just one page ...
2520     */
2521    pagesize = info.dwPageSize << 3;
2522    allocsize = info.dwAllocationGranularity;
2523   }
2524  /* This scheme fails eventually if request for contiguous
2525   * block is denied so reserve big blocks - this is only 
2526   * address space not memory ...
2527   */
2528  if (brk+need >= reserved)
2529   {
2530    DWORD size = 64*1024*1024;
2531    char *addr;
2532    if (committed && reserved && committed < reserved)
2533     {
2534      /* Commit last of previous chunk cannot span allocations */
2535      addr = (char *) VirtualAlloc(committed,reserved-committed,MEM_COMMIT,PAGE_READWRITE);
2536      if (addr)
2537       committed = reserved;
2538     }
2539    /* Reserve some (more) space 
2540     * Note this is a little sneaky, 1st call passes NULL as reserved
2541     * so lets system choose where we start, subsequent calls pass
2542     * the old end address so ask for a contiguous block
2543     */
2544    addr  = (char *) VirtualAlloc(reserved,size,MEM_RESERVE,PAGE_NOACCESS);
2545    if (addr)
2546     {
2547      reserved = addr+size;
2548      if (!base)
2549       base = addr;
2550      if (!committed)
2551       committed = base;
2552      if (!brk)
2553       brk = committed;
2554     }
2555    else
2556     {
2557      return (void *) -1;
2558     }
2559   }
2560  result = brk;
2561  brk += need;
2562  if (brk > committed)
2563   {
2564    DWORD size = ((brk-committed + pagesize -1)/pagesize) * pagesize;
2565    char *addr = (char *) VirtualAlloc(committed,size,MEM_COMMIT,PAGE_READWRITE);
2566    if (addr)
2567     {
2568      committed += size;
2569     }
2570    else
2571     return (void *) -1;
2572   }
2573  return result;
2574 }
2575
2576 #endif
2577 #endif
2578
2579 DllExport void*
2580 win32_malloc(size_t size)
2581 {
2582     return malloc(size);
2583 }
2584
2585 DllExport void*
2586 win32_calloc(size_t numitems, size_t size)
2587 {
2588     return calloc(numitems,size);
2589 }
2590
2591 DllExport void*
2592 win32_realloc(void *block, size_t size)
2593 {
2594     return realloc(block,size);
2595 }
2596
2597 DllExport void
2598 win32_free(void *block)
2599 {
2600     free(block);
2601 }
2602
2603
2604 int
2605 win32_open_osfhandle(long handle, int flags)
2606 {
2607     return _open_osfhandle(handle, flags);
2608 }
2609
2610 long
2611 win32_get_osfhandle(int fd)
2612 {
2613     return _get_osfhandle(fd);
2614 }
2615
2616 /*
2617  * Extras.
2618  */
2619
2620 static
2621 XS(w32_GetCwd)
2622 {
2623     dXSARGS;
2624     SV *sv = sv_newmortal();
2625     /* Make one call with zero size - return value is required size */
2626     DWORD len = GetCurrentDirectory((DWORD)0,NULL);
2627     SvUPGRADE(sv,SVt_PV);
2628     SvGROW(sv,len);
2629     SvCUR(sv) = GetCurrentDirectory((DWORD) SvLEN(sv), SvPVX(sv));
2630     /* 
2631      * If result != 0 
2632      *   then it worked, set PV valid, 
2633      *   else leave it 'undef' 
2634      */
2635     EXTEND(SP,1);
2636     if (SvCUR(sv)) {
2637         SvPOK_on(sv);
2638         ST(0) = sv;
2639         XSRETURN(1);
2640     }
2641     XSRETURN_UNDEF;
2642 }
2643
2644 static
2645 XS(w32_SetCwd)
2646 {
2647     dXSARGS;
2648     if (items != 1)
2649         croak("usage: Win32::SetCurrentDirectory($cwd)");
2650     if (SetCurrentDirectory(SvPV_nolen(ST(0))))
2651         XSRETURN_YES;
2652
2653     XSRETURN_NO;
2654 }
2655
2656 static
2657 XS(w32_GetNextAvailDrive)
2658 {
2659     dXSARGS;
2660     char ix = 'C';
2661     char root[] = "_:\\";
2662
2663     EXTEND(SP,1);
2664     while (ix <= 'Z') {
2665         root[0] = ix++;
2666         if (GetDriveType(root) == 1) {
2667             root[2] = '\0';
2668             XSRETURN_PV(root);
2669         }
2670     }
2671     XSRETURN_UNDEF;
2672 }
2673
2674 static
2675 XS(w32_GetLastError)
2676 {
2677     dXSARGS;
2678     EXTEND(SP,1);
2679     XSRETURN_IV(GetLastError());
2680 }
2681
2682 static
2683 XS(w32_SetLastError)
2684 {
2685     dXSARGS;
2686     if (items != 1)
2687         croak("usage: Win32::SetLastError($error)");
2688     SetLastError(SvIV(ST(0)));
2689     XSRETURN_EMPTY;
2690 }
2691
2692 static
2693 XS(w32_LoginName)
2694 {
2695     dXSARGS;
2696     char *name = getlogin_buffer;
2697     DWORD size = sizeof(getlogin_buffer);
2698     EXTEND(SP,1);
2699     if (GetUserName(name,&size)) {
2700         /* size includes NULL */
2701         ST(0) = sv_2mortal(newSVpvn(name,size-1));
2702         XSRETURN(1);
2703     }
2704     XSRETURN_UNDEF;
2705 }
2706
2707 static
2708 XS(w32_NodeName)
2709 {
2710     dXSARGS;
2711     char name[MAX_COMPUTERNAME_LENGTH+1];
2712     DWORD size = sizeof(name);
2713     EXTEND(SP,1);
2714     if (GetComputerName(name,&size)) {
2715         /* size does NOT include NULL :-( */
2716         ST(0) = sv_2mortal(newSVpvn(name,size));
2717         XSRETURN(1);
2718     }
2719     XSRETURN_UNDEF;
2720 }
2721
2722
2723 static
2724 XS(w32_DomainName)
2725 {
2726     dXSARGS;
2727 #ifndef HAS_NETWKSTAGETINFO
2728     /* mingw32 (and Win95) don't have NetWksta*(), so do it the old way */
2729     char name[256];
2730     DWORD size = sizeof(name);
2731     EXTEND(SP,1);
2732     if (GetUserName(name,&size)) {
2733         char sid[1024];
2734         DWORD sidlen = sizeof(sid);
2735         char dname[256];
2736         DWORD dnamelen = sizeof(dname);
2737         SID_NAME_USE snu;
2738         if (LookupAccountName(NULL, name, (PSID)&sid, &sidlen,
2739                               dname, &dnamelen, &snu)) {
2740             XSRETURN_PV(dname);         /* all that for this */
2741         }
2742     }
2743 #else
2744     /* this way is more reliable, in case user has a local account.
2745      * XXX need dynamic binding of netapi32.dll symbols or this will fail on
2746      * Win95. Probably makes more sense to move it into libwin32. */
2747     char dname[256];
2748     DWORD dnamelen = sizeof(dname);
2749     PWKSTA_INFO_100 pwi;
2750     EXTEND(SP,1);
2751     if (NERR_Success == NetWkstaGetInfo(NULL, 100, (LPBYTE*)&pwi)) {
2752         if (pwi->wki100_langroup && *(pwi->wki100_langroup)) {
2753             WideCharToMultiByte(CP_ACP, NULL, pwi->wki100_langroup,
2754                                 -1, (LPSTR)dname, dnamelen, NULL, NULL);
2755         }
2756         else {
2757             WideCharToMultiByte(CP_ACP, NULL, pwi->wki100_computername,
2758                                 -1, (LPSTR)dname, dnamelen, NULL, NULL);
2759         }
2760         NetApiBufferFree(pwi);
2761         XSRETURN_PV(dname);
2762     }
2763 #endif
2764     XSRETURN_UNDEF;
2765 }
2766
2767 static
2768 XS(w32_FsType)
2769 {
2770     dXSARGS;
2771     char fsname[256];
2772     DWORD flags, filecomplen;
2773     if (GetVolumeInformation(NULL, NULL, 0, NULL, &filecomplen,
2774                          &flags, fsname, sizeof(fsname))) {
2775         if (GIMME_V == G_ARRAY) {
2776             XPUSHs(sv_2mortal(newSVpvn(fsname,strlen(fsname))));
2777             XPUSHs(sv_2mortal(newSViv(flags)));
2778             XPUSHs(sv_2mortal(newSViv(filecomplen)));
2779             PUTBACK;
2780             return;
2781         }
2782         EXTEND(SP,1);
2783         XSRETURN_PV(fsname);
2784     }
2785     XSRETURN_EMPTY;
2786 }
2787
2788 static
2789 XS(w32_GetOSVersion)
2790 {
2791     dXSARGS;
2792     OSVERSIONINFO osver;
2793
2794     osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
2795     if (GetVersionEx(&osver)) {
2796         XPUSHs(newSVpvn(osver.szCSDVersion, strlen(osver.szCSDVersion)));
2797         XPUSHs(newSViv(osver.dwMajorVersion));
2798         XPUSHs(newSViv(osver.dwMinorVersion));
2799         XPUSHs(newSViv(osver.dwBuildNumber));
2800         XPUSHs(newSViv(osver.dwPlatformId));
2801         PUTBACK;
2802         return;
2803     }
2804     XSRETURN_EMPTY;
2805 }
2806
2807 static
2808 XS(w32_IsWinNT)
2809 {
2810     dXSARGS;
2811     EXTEND(SP,1);
2812     XSRETURN_IV(IsWinNT());
2813 }
2814
2815 static
2816 XS(w32_IsWin95)
2817 {
2818     dXSARGS;
2819     EXTEND(SP,1);
2820     XSRETURN_IV(IsWin95());
2821 }
2822
2823 static
2824 XS(w32_FormatMessage)
2825 {
2826     dXSARGS;
2827     DWORD source = 0;
2828     char msgbuf[1024];
2829
2830     if (items != 1)
2831         croak("usage: Win32::FormatMessage($errno)");
2832
2833     if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
2834                       &source, SvIV(ST(0)), 0,
2835                       msgbuf, sizeof(msgbuf)-1, NULL))
2836         XSRETURN_PV(msgbuf);
2837
2838     XSRETURN_UNDEF;
2839 }
2840
2841 static
2842 XS(w32_Spawn)
2843 {
2844     dXSARGS;
2845     char *cmd, *args;
2846     PROCESS_INFORMATION stProcInfo;
2847     STARTUPINFO stStartInfo;
2848     BOOL bSuccess = FALSE;
2849
2850     if (items != 3)
2851         croak("usage: Win32::Spawn($cmdName, $args, $PID)");
2852
2853     cmd = SvPV_nolen(ST(0));
2854     args = SvPV_nolen(ST(1));
2855
2856     memset(&stStartInfo, 0, sizeof(stStartInfo));   /* Clear the block */
2857     stStartInfo.cb = sizeof(stStartInfo);           /* Set the structure size */
2858     stStartInfo.dwFlags = STARTF_USESHOWWINDOW;     /* Enable wShowWindow control */
2859     stStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;   /* Start min (normal) */
2860
2861     if (CreateProcess(
2862                 cmd,                    /* Image path */
2863                 args,                   /* Arguments for command line */
2864                 NULL,                   /* Default process security */
2865                 NULL,                   /* Default thread security */
2866                 FALSE,                  /* Must be TRUE to use std handles */
2867                 NORMAL_PRIORITY_CLASS,  /* No special scheduling */
2868                 NULL,                   /* Inherit our environment block */
2869                 NULL,                   /* Inherit our currrent directory */
2870                 &stStartInfo,           /* -> Startup info */
2871                 &stProcInfo))           /* <- Process info (if OK) */
2872     {
2873         CloseHandle(stProcInfo.hThread);/* library source code does this. */
2874         sv_setiv(ST(2), stProcInfo.dwProcessId);
2875         bSuccess = TRUE;
2876     }
2877     XSRETURN_IV(bSuccess);
2878 }
2879
2880 static
2881 XS(w32_GetTickCount)
2882 {
2883     dXSARGS;
2884     DWORD msec = GetTickCount();
2885     EXTEND(SP,1);
2886     if ((IV)msec > 0)
2887         XSRETURN_IV(msec);
2888     XSRETURN_NV(msec);
2889 }
2890
2891 static
2892 XS(w32_GetShortPathName)
2893 {
2894     dXSARGS;
2895     SV *shortpath;
2896     DWORD len;
2897
2898     if (items != 1)
2899         croak("usage: Win32::GetShortPathName($longPathName)");
2900
2901     shortpath = sv_mortalcopy(ST(0));
2902     SvUPGRADE(shortpath, SVt_PV);
2903     /* src == target is allowed */
2904     do {
2905         len = GetShortPathName(SvPVX(shortpath),
2906                                SvPVX(shortpath),
2907                                SvLEN(shortpath));
2908     } while (len >= SvLEN(shortpath) && sv_grow(shortpath,len+1));
2909     if (len) {
2910         SvCUR_set(shortpath,len);
2911         ST(0) = shortpath;
2912         XSRETURN(1);
2913     }
2914     XSRETURN_UNDEF;
2915 }
2916
2917 static
2918 XS(w32_GetFullPathName)
2919 {
2920     dXSARGS;
2921     SV *filename;
2922     SV *fullpath;
2923     char *filepart;
2924     DWORD len;
2925
2926     if (items != 1)
2927         croak("usage: Win32::GetFullPathName($filename)");
2928
2929     filename = ST(0);
2930     fullpath = sv_mortalcopy(filename);
2931     SvUPGRADE(fullpath, SVt_PV);
2932     do {
2933         len = GetFullPathName(SvPVX(filename),
2934                               SvLEN(fullpath),
2935                               SvPVX(fullpath),
2936                               &filepart);
2937     } while (len >= SvLEN(fullpath) && sv_grow(fullpath,len+1));
2938     if (len) {
2939         if (GIMME_V == G_ARRAY) {
2940             EXTEND(SP,1);
2941             XST_mPV(1,filepart);
2942             len = filepart - SvPVX(fullpath);
2943             items = 2;
2944         }
2945         SvCUR_set(fullpath,len);
2946         ST(0) = fullpath;
2947         XSRETURN(items);
2948     }
2949     XSRETURN_EMPTY;
2950 }
2951
2952 static
2953 XS(w32_GetLongPathName)
2954 {
2955     dXSARGS;
2956     SV *path;
2957     char tmpbuf[MAX_PATH+1];
2958     char *pathstr;
2959     STRLEN len;
2960
2961     if (items != 1)
2962         croak("usage: Win32::GetLongPathName($pathname)");
2963
2964     path = ST(0);
2965     pathstr = SvPV(path,len);
2966     strcpy(tmpbuf, pathstr);
2967     pathstr = win32_longpath(tmpbuf);
2968     if (pathstr) {
2969         ST(0) = sv_2mortal(newSVpvn(pathstr, strlen(pathstr)));
2970         XSRETURN(1);
2971     }
2972     XSRETURN_EMPTY;
2973 }
2974
2975 static
2976 XS(w32_Sleep)
2977 {
2978     dXSARGS;
2979     if (items != 1)
2980         croak("usage: Win32::Sleep($milliseconds)");
2981     Sleep(SvIV(ST(0)));
2982     XSRETURN_YES;
2983 }
2984
2985 static
2986 XS(w32_CopyFile)
2987 {
2988     dXSARGS;
2989     if (items != 3)
2990         croak("usage: Win32::CopyFile($from, $to, $overwrite)");
2991     if (CopyFile(SvPV_nolen(ST(0)), SvPV_nolen(ST(1)), !SvTRUE(ST(2))))
2992         XSRETURN_YES;
2993     XSRETURN_NO;
2994 }
2995
2996 void
2997 Perl_init_os_extras()
2998 {
2999     char *file = __FILE__;
3000     dXSUB_SYS;
3001
3002     w32_perlshell_tokens = Nullch;
3003     w32_perlshell_items = -1;
3004     w32_fdpid = newAV();                /* XXX needs to be in Perl_win32_init()? */
3005     New(1313, w32_children, 1, child_tab);
3006     w32_num_children = 0;
3007
3008     /* these names are Activeware compatible */
3009     newXS("Win32::GetCwd", w32_GetCwd, file);
3010     newXS("Win32::SetCwd", w32_SetCwd, file);
3011     newXS("Win32::GetNextAvailDrive", w32_GetNextAvailDrive, file);
3012     newXS("Win32::GetLastError", w32_GetLastError, file);
3013     newXS("Win32::SetLastError", w32_SetLastError, file);
3014     newXS("Win32::LoginName", w32_LoginName, file);
3015     newXS("Win32::NodeName", w32_NodeName, file);
3016     newXS("Win32::DomainName", w32_DomainName, file);
3017     newXS("Win32::FsType", w32_FsType, file);
3018     newXS("Win32::GetOSVersion", w32_GetOSVersion, file);
3019     newXS("Win32::IsWinNT", w32_IsWinNT, file);
3020     newXS("Win32::IsWin95", w32_IsWin95, file);
3021     newXS("Win32::FormatMessage", w32_FormatMessage, file);
3022     newXS("Win32::Spawn", w32_Spawn, file);
3023     newXS("Win32::GetTickCount", w32_GetTickCount, file);
3024     newXS("Win32::GetShortPathName", w32_GetShortPathName, file);
3025     newXS("Win32::GetFullPathName", w32_GetFullPathName, file);
3026     newXS("Win32::GetLongPathName", w32_GetLongPathName, file);
3027     newXS("Win32::CopyFile", w32_CopyFile, file);
3028     newXS("Win32::Sleep", w32_Sleep, file);
3029
3030     /* XXX Bloat Alert! The following Activeware preloads really
3031      * ought to be part of Win32::Sys::*, so they're not included
3032      * here.
3033      */
3034     /* LookupAccountName
3035      * LookupAccountSID
3036      * InitiateSystemShutdown
3037      * AbortSystemShutdown
3038      * ExpandEnvrironmentStrings
3039      */
3040 }
3041
3042 void
3043 Perl_win32_init(int *argcp, char ***argvp)
3044 {
3045     /* Disable floating point errors, Perl will trap the ones we
3046      * care about.  VC++ RTL defaults to switching these off
3047      * already, but the Borland RTL doesn't.  Since we don't
3048      * want to be at the vendor's whim on the default, we set
3049      * it explicitly here.
3050      */
3051 #if !defined(_ALPHA_) && !defined(__GNUC__)
3052     _control87(MCW_EM, MCW_EM);
3053 #endif
3054     MALLOC_INIT;
3055 }
3056
3057 #ifdef USE_BINMODE_SCRIPTS
3058
3059 void
3060 win32_strip_return(SV *sv)
3061 {
3062  char *s = SvPVX(sv);
3063  char *e = s+SvCUR(sv);
3064  char *d = s;
3065  while (s < e)
3066   {
3067    if (*s == '\r' && s[1] == '\n')
3068     {
3069      *d++ = '\n';
3070      s += 2;
3071     }
3072    else 
3073     {
3074      *d++ = *s++;
3075     }   
3076   }
3077  SvCUR_set(sv,d-SvPVX(sv)); 
3078 }
3079
3080 #endif