858f86e6b04be7a272c1c75d39fc2eca18d3fbee
[p5sagit/p5-mst-13.2.git] / win32 / win32.c
1 /* WIN32.C
2  *
3  * (c) 1995 Microsoft Corporation. All rights reserved. 
4  *              Developed by hip communications inc., http://info.hip.com/info/
5  * Portions (c) 1993 Intergraph Corporation. All rights reserved.
6  *
7  *    You may distribute under the terms of either the GNU General Public
8  *    License or the Artistic License, as specified in the README file.
9  */
10
11 #define WIN32_LEAN_AND_MEAN
12 #define WIN32IO_IS_STDIO
13 #include <tchar.h>
14 #ifdef __GNUC__
15 #define Win32_Winsock
16 #endif
17 #include <windows.h>
18
19 #ifndef __MINGW32__
20 #include <lmcons.h>
21 #include <lmerr.h>
22 /* ugliness to work around a buggy struct definition in lmwksta.h */
23 #undef LPTSTR
24 #define LPTSTR LPWSTR
25 #include <lmwksta.h>
26 #undef LPTSTR
27 #define LPTSTR LPSTR
28 #include <lmapibuf.h>
29 #endif /* __MINGW32__ */
30
31 /* #include "config.h" */
32
33 #define PERLIO_NOT_STDIO 0 
34 #if !defined(PERLIO_IS_STDIO) && !defined(USE_SFIO)
35 #define PerlIO FILE
36 #endif
37
38 #include "EXTERN.h"
39 #include "perl.h"
40
41 #define NO_XSLOCKS
42 #ifdef PERL_OBJECT
43 extern CPerlObj* pPerl;
44 #endif
45 #include "XSUB.h"
46
47 #include "Win32iop.h"
48 #include <fcntl.h>
49 #include <sys/stat.h>
50 #ifndef __GNUC__
51 /* assert.h conflicts with #define of assert in perl.h */
52 #include <assert.h>
53 #endif
54 #include <string.h>
55 #include <stdarg.h>
56 #include <float.h>
57 #include <time.h>
58 #if defined(_MSC_VER) || defined(__MINGW32__)
59 #include <sys/utime.h>
60 #else
61 #include <utime.h>
62 #endif
63
64 #ifdef __GNUC__
65 /* Mingw32 defaults to globing command line 
66  * So we turn it off like this:
67  */
68 int _CRT_glob = 0;
69 #endif
70
71 #define EXECF_EXEC 1
72 #define EXECF_SPAWN 2
73 #define EXECF_SPAWN_NOWAIT 3
74
75 #if defined(PERL_OBJECT)
76 #undef win32_get_privlib
77 #define win32_get_privlib g_win32_get_privlib
78 #undef win32_get_sitelib
79 #define win32_get_sitelib g_win32_get_sitelib
80 #undef do_aspawn
81 #define do_aspawn g_do_aspawn
82 #undef do_spawn
83 #define do_spawn g_do_spawn
84 #undef do_exec
85 #define do_exec g_do_exec
86 #undef getlogin
87 #define getlogin g_getlogin
88 #endif
89
90 static DWORD            os_id(void);
91 static void             get_shell(void);
92 static long             tokenize(char *str, char **dest, char ***destv);
93         int             do_spawn2(char *cmd, int exectype);
94 static BOOL             has_shell_metachars(char *ptr);
95 static long             filetime_to_clock(PFILETIME ft);
96 static BOOL             filetime_from_time(PFILETIME ft, time_t t);
97 static char *           get_emd_part(char **leading, char *trailing, ...);
98 static void             remove_dead_process(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 __BORLANDC__
1324         switch (info.u.s.wProcessorArchitecture) {
1325 #else
1326         switch (info.wProcessorArchitecture) {
1327 #endif
1328         case PROCESSOR_ARCHITECTURE_INTEL:
1329             arch = "x86"; break;
1330         case PROCESSOR_ARCHITECTURE_MIPS:
1331             arch = "mips"; break;
1332         case PROCESSOR_ARCHITECTURE_ALPHA:
1333             arch = "alpha"; break;
1334         case PROCESSOR_ARCHITECTURE_PPC:
1335             arch = "ppc"; break;
1336         default:
1337             arch = "unknown"; break;
1338         }
1339         strcpy(name->machine, arch);
1340     }
1341     return 0;
1342 }
1343
1344 DllExport int
1345 win32_waitpid(int pid, int *status, int flags)
1346 {
1347     int retval = -1;
1348     if (pid == -1) 
1349         return win32_wait(status);
1350     else {
1351         long child = find_pid(pid);
1352         if (child >= 0) {
1353             HANDLE hProcess = w32_child_handles[child];
1354             DWORD waitcode = WaitForSingleObject(hProcess, INFINITE);
1355             if (waitcode != WAIT_FAILED) {
1356                 if (GetExitCodeProcess(hProcess, &waitcode)) {
1357                     *status = (int)((waitcode & 0xff) << 8);
1358                     retval = (int)w32_child_pids[child];
1359                     remove_dead_process(child);
1360                     return retval;
1361                 }
1362             }
1363             else
1364                 errno = ECHILD;
1365         }
1366         else {
1367             retval = cwait(status, pid, WAIT_CHILD);
1368             /* cwait() returns "correctly" on Borland */
1369 #ifndef __BORLANDC__
1370             if (status)
1371                 *status *= 256;
1372 #endif
1373         }
1374     }
1375     return retval >= 0 ? pid : retval;                
1376 }
1377
1378 DllExport int
1379 win32_wait(int *status)
1380 {
1381     /* XXX this wait emulation only knows about processes
1382      * spawned via win32_spawnvp(P_NOWAIT, ...).
1383      */
1384     int i, retval;
1385     DWORD exitcode, waitcode;
1386
1387     if (!w32_num_children) {
1388         errno = ECHILD;
1389         return -1;
1390     }
1391
1392     /* if a child exists, wait for it to die */
1393     waitcode = WaitForMultipleObjects(w32_num_children,
1394                                       w32_child_handles,
1395                                       FALSE,
1396                                       INFINITE);
1397     if (waitcode != WAIT_FAILED) {
1398         if (waitcode >= WAIT_ABANDONED_0
1399             && waitcode < WAIT_ABANDONED_0 + w32_num_children)
1400             i = waitcode - WAIT_ABANDONED_0;
1401         else
1402             i = waitcode - WAIT_OBJECT_0;
1403         if (GetExitCodeProcess(w32_child_handles[i], &exitcode) ) {
1404             *status = (int)((exitcode & 0xff) << 8);
1405             retval = (int)w32_child_pids[i];
1406             remove_dead_process(i);
1407             return retval;
1408         }
1409     }
1410
1411 FAILED:
1412     errno = GetLastError();
1413     return -1;
1414 }
1415
1416 static UINT timerid = 0;
1417
1418 static VOID CALLBACK TimerProc(HWND win, UINT msg, UINT id, DWORD time)
1419 {
1420  KillTimer(NULL,timerid);
1421  timerid=0;  
1422  sighandler(14);
1423 }
1424
1425 DllExport unsigned int
1426 win32_alarm(unsigned int sec)
1427 {
1428     /* 
1429      * the 'obvious' implentation is SetTimer() with a callback
1430      * which does whatever receiving SIGALRM would do 
1431      * we cannot use SIGALRM even via raise() as it is not 
1432      * one of the supported codes in <signal.h>
1433      *
1434      * Snag is unless something is looking at the message queue
1435      * nothing happens :-(
1436      */ 
1437     if (sec)
1438      {
1439       timerid = SetTimer(NULL,timerid,sec*1000,(TIMERPROC)TimerProc);
1440       if (!timerid)
1441        croak("Cannot set timer");
1442      } 
1443     else
1444      {
1445       if (timerid)
1446        {
1447         KillTimer(NULL,timerid);
1448         timerid=0;  
1449        }
1450      }
1451     return 0;
1452 }
1453
1454 #if defined(HAVE_DES_FCRYPT) || defined(PERL_OBJECT)
1455 #ifdef HAVE_DES_FCRYPT
1456 extern char *   des_fcrypt(const char *txt, const char *salt, char *cbuf);
1457 #endif
1458
1459 DllExport char *
1460 win32_crypt(const char *txt, const char *salt)
1461 {
1462 #ifdef HAVE_DES_FCRYPT
1463     dTHR;
1464     return des_fcrypt(txt, salt, crypt_buffer);
1465 #else
1466     die("The crypt() function is unimplemented due to excessive paranoia.");
1467     return Nullch;
1468 #endif
1469 }
1470 #endif
1471
1472 #ifdef USE_FIXED_OSFHANDLE
1473
1474 EXTERN_C int __cdecl _alloc_osfhnd(void);
1475 EXTERN_C int __cdecl _set_osfhnd(int fh, long value);
1476 EXTERN_C void __cdecl _lock_fhandle(int);
1477 EXTERN_C void __cdecl _unlock_fhandle(int);
1478 EXTERN_C void __cdecl _unlock(int);
1479
1480 #if     (_MSC_VER >= 1000)
1481 typedef struct  {
1482     long osfhnd;    /* underlying OS file HANDLE */
1483     char osfile;    /* attributes of file (e.g., open in text mode?) */
1484     char pipech;    /* one char buffer for handles opened on pipes */
1485 #if defined (_MT) && !defined (DLL_FOR_WIN32S)
1486     int lockinitflag;
1487     CRITICAL_SECTION lock;
1488 #endif  /* defined (_MT) && !defined (DLL_FOR_WIN32S) */
1489 }       ioinfo;
1490
1491 EXTERN_C ioinfo * __pioinfo[];
1492
1493 #define IOINFO_L2E                      5
1494 #define IOINFO_ARRAY_ELTS       (1 << IOINFO_L2E)
1495 #define _pioinfo(i)     (__pioinfo[i >> IOINFO_L2E] + (i & (IOINFO_ARRAY_ELTS - 1)))
1496 #define _osfile(i)      (_pioinfo(i)->osfile)
1497
1498 #else   /* (_MSC_VER >= 1000) */
1499 extern char _osfile[];
1500 #endif  /* (_MSC_VER >= 1000) */
1501
1502 #define FOPEN                   0x01    /* file handle open */
1503 #define FAPPEND                 0x20    /* file handle opened O_APPEND */
1504 #define FDEV                    0x40    /* file handle refers to device */
1505 #define FTEXT                   0x80    /* file handle is in text mode */
1506
1507 #define _STREAM_LOCKS   26              /* Table of stream locks */
1508 #define _LAST_STREAM_LOCK  (_STREAM_LOCKS+_NSTREAM_-1)  /* Last stream lock */
1509 #define _FH_LOCKS          (_LAST_STREAM_LOCK+1)        /* Table of fh locks */
1510
1511 /***
1512 *int my_open_osfhandle(long osfhandle, int flags) - open C Runtime file handle
1513 *
1514 *Purpose:
1515 *       This function allocates a free C Runtime file handle and associates
1516 *       it with the Win32 HANDLE specified by the first parameter. This is a
1517 *               temperary fix for WIN95's brain damage GetFileType() error on socket
1518 *               we just bypass that call for socket
1519 *
1520 *Entry:
1521 *       long osfhandle - Win32 HANDLE to associate with C Runtime file handle.
1522 *       int flags      - flags to associate with C Runtime file handle.
1523 *
1524 *Exit:
1525 *       returns index of entry in fh, if successful
1526 *       return -1, if no free entry is found
1527 *
1528 *Exceptions:
1529 *
1530 *******************************************************************************/
1531
1532 static int
1533 my_open_osfhandle(long osfhandle, int flags)
1534 {
1535     int fh;
1536     char fileflags;             /* _osfile flags */
1537
1538     /* copy relevant flags from second parameter */
1539     fileflags = FDEV;
1540
1541     if (flags & O_APPEND)
1542         fileflags |= FAPPEND;
1543
1544     if (flags & O_TEXT)
1545         fileflags |= FTEXT;
1546
1547     /* attempt to allocate a C Runtime file handle */
1548     if ((fh = _alloc_osfhnd()) == -1) {
1549         errno = EMFILE;         /* too many open files */
1550         _doserrno = 0L;         /* not an OS error */
1551         return -1;              /* return error to caller */
1552     }
1553
1554     /* the file is open. now, set the info in _osfhnd array */
1555     _set_osfhnd(fh, osfhandle);
1556
1557     fileflags |= FOPEN;         /* mark as open */
1558
1559 #if (_MSC_VER >= 1000)
1560     _osfile(fh) = fileflags;    /* set osfile entry */
1561     _unlock_fhandle(fh);
1562 #else
1563     _osfile[fh] = fileflags;    /* set osfile entry */
1564     _unlock(fh+_FH_LOCKS);              /* unlock handle */
1565 #endif
1566
1567     return fh;                  /* return handle */
1568 }
1569
1570 #define _open_osfhandle my_open_osfhandle
1571 #endif  /* USE_FIXED_OSFHANDLE */
1572
1573 /* simulate flock by locking a range on the file */
1574
1575 #define LK_ERR(f,i)     ((f) ? (i = 0) : (errno = GetLastError()))
1576 #define LK_LEN          0xffff0000
1577
1578 DllExport int
1579 win32_flock(int fd, int oper)
1580 {
1581     OVERLAPPED o;
1582     int i = -1;
1583     HANDLE fh;
1584
1585     if (!IsWinNT()) {
1586         croak("flock() unimplemented on this platform");
1587         return -1;
1588     }
1589     fh = (HANDLE)_get_osfhandle(fd);
1590     memset(&o, 0, sizeof(o));
1591
1592     switch(oper) {
1593     case LOCK_SH:               /* shared lock */
1594         LK_ERR(LockFileEx(fh, 0, 0, LK_LEN, 0, &o),i);
1595         break;
1596     case LOCK_EX:               /* exclusive lock */
1597         LK_ERR(LockFileEx(fh, LOCKFILE_EXCLUSIVE_LOCK, 0, LK_LEN, 0, &o),i);
1598         break;
1599     case LOCK_SH|LOCK_NB:       /* non-blocking shared lock */
1600         LK_ERR(LockFileEx(fh, LOCKFILE_FAIL_IMMEDIATELY, 0, LK_LEN, 0, &o),i);
1601         break;
1602     case LOCK_EX|LOCK_NB:       /* non-blocking exclusive lock */
1603         LK_ERR(LockFileEx(fh,
1604                        LOCKFILE_EXCLUSIVE_LOCK|LOCKFILE_FAIL_IMMEDIATELY,
1605                        0, LK_LEN, 0, &o),i);
1606         break;
1607     case LOCK_UN:               /* unlock lock */
1608         LK_ERR(UnlockFileEx(fh, 0, LK_LEN, 0, &o),i);
1609         break;
1610     default:                    /* unknown */
1611         errno = EINVAL;
1612         break;
1613     }
1614     return i;
1615 }
1616
1617 #undef LK_ERR
1618 #undef LK_LEN
1619
1620 /*
1621  *  redirected io subsystem for all XS modules
1622  *
1623  */
1624
1625 DllExport int *
1626 win32_errno(void)
1627 {
1628     return (&errno);
1629 }
1630
1631 DllExport char ***
1632 win32_environ(void)
1633 {
1634     return (&(_environ));
1635 }
1636
1637 /* the rest are the remapped stdio routines */
1638 DllExport FILE *
1639 win32_stderr(void)
1640 {
1641     return (stderr);
1642 }
1643
1644 DllExport FILE *
1645 win32_stdin(void)
1646 {
1647     return (stdin);
1648 }
1649
1650 DllExport FILE *
1651 win32_stdout()
1652 {
1653     return (stdout);
1654 }
1655
1656 DllExport int
1657 win32_ferror(FILE *fp)
1658 {
1659     return (ferror(fp));
1660 }
1661
1662
1663 DllExport int
1664 win32_feof(FILE *fp)
1665 {
1666     return (feof(fp));
1667 }
1668
1669 /*
1670  * Since the errors returned by the socket error function 
1671  * WSAGetLastError() are not known by the library routine strerror
1672  * we have to roll our own.
1673  */
1674
1675 DllExport char *
1676 win32_strerror(int e) 
1677 {
1678 #ifndef __BORLANDC__            /* Borland intolerance */
1679     extern int sys_nerr;
1680 #endif
1681     DWORD source = 0;
1682
1683     if (e < 0 || e > sys_nerr) {
1684         dTHR;
1685         if (e < 0)
1686             e = GetLastError();
1687
1688         if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, &source, e, 0,
1689                          strerror_buffer, sizeof(strerror_buffer), NULL) == 0) 
1690             strcpy(strerror_buffer, "Unknown Error");
1691
1692         return strerror_buffer;
1693     }
1694     return strerror(e);
1695 }
1696
1697 DllExport void
1698 win32_str_os_error(void *sv, DWORD dwErr)
1699 {
1700     DWORD dwLen;
1701     char *sMsg;
1702     dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER
1703                           |FORMAT_MESSAGE_IGNORE_INSERTS
1704                           |FORMAT_MESSAGE_FROM_SYSTEM, NULL,
1705                            dwErr, 0, (char *)&sMsg, 1, NULL);
1706     if (0 < dwLen) {
1707         while (0 < dwLen  &&  isspace(sMsg[--dwLen]))
1708             ;
1709         if ('.' != sMsg[dwLen])
1710             dwLen++;
1711         sMsg[dwLen]= '\0';
1712     }
1713     if (0 == dwLen) {
1714         sMsg = (char*)LocalAlloc(0, 64/**sizeof(TCHAR)*/);
1715         dwLen = sprintf(sMsg,
1716                         "Unknown error #0x%lX (lookup 0x%lX)",
1717                         dwErr, GetLastError());
1718     }
1719     sv_setpvn((SV*)sv, sMsg, dwLen);
1720     LocalFree(sMsg);
1721 }
1722
1723
1724 DllExport int
1725 win32_fprintf(FILE *fp, const char *format, ...)
1726 {
1727     va_list marker;
1728     va_start(marker, format);     /* Initialize variable arguments. */
1729
1730     return (vfprintf(fp, format, marker));
1731 }
1732
1733 DllExport int
1734 win32_printf(const char *format, ...)
1735 {
1736     va_list marker;
1737     va_start(marker, format);     /* Initialize variable arguments. */
1738
1739     return (vprintf(format, marker));
1740 }
1741
1742 DllExport int
1743 win32_vfprintf(FILE *fp, const char *format, va_list args)
1744 {
1745     return (vfprintf(fp, format, args));
1746 }
1747
1748 DllExport int
1749 win32_vprintf(const char *format, va_list args)
1750 {
1751     return (vprintf(format, args));
1752 }
1753
1754 DllExport size_t
1755 win32_fread(void *buf, size_t size, size_t count, FILE *fp)
1756 {
1757     return fread(buf, size, count, fp);
1758 }
1759
1760 DllExport size_t
1761 win32_fwrite(const void *buf, size_t size, size_t count, FILE *fp)
1762 {
1763     return fwrite(buf, size, count, fp);
1764 }
1765
1766 DllExport FILE *
1767 win32_fopen(const char *filename, const char *mode)
1768 {
1769     if (stricmp(filename, "/dev/null")==0)
1770         return fopen("NUL", mode);
1771     return fopen(filename, mode);
1772 }
1773
1774 #ifndef USE_SOCKETS_AS_HANDLES
1775 #undef fdopen
1776 #define fdopen my_fdopen
1777 #endif
1778
1779 DllExport FILE *
1780 win32_fdopen( int handle, const char *mode)
1781 {
1782     return fdopen(handle, (char *) mode);
1783 }
1784
1785 DllExport FILE *
1786 win32_freopen( const char *path, const char *mode, FILE *stream)
1787 {
1788     if (stricmp(path, "/dev/null")==0)
1789         return freopen("NUL", mode, stream);
1790     return freopen(path, mode, stream);
1791 }
1792
1793 DllExport int
1794 win32_fclose(FILE *pf)
1795 {
1796     return my_fclose(pf);       /* defined in win32sck.c */
1797 }
1798
1799 DllExport int
1800 win32_fputs(const char *s,FILE *pf)
1801 {
1802     return fputs(s, pf);
1803 }
1804
1805 DllExport int
1806 win32_fputc(int c,FILE *pf)
1807 {
1808     return fputc(c,pf);
1809 }
1810
1811 DllExport int
1812 win32_ungetc(int c,FILE *pf)
1813 {
1814     return ungetc(c,pf);
1815 }
1816
1817 DllExport int
1818 win32_getc(FILE *pf)
1819 {
1820     return getc(pf);
1821 }
1822
1823 DllExport int
1824 win32_fileno(FILE *pf)
1825 {
1826     return fileno(pf);
1827 }
1828
1829 DllExport void
1830 win32_clearerr(FILE *pf)
1831 {
1832     clearerr(pf);
1833     return;
1834 }
1835
1836 DllExport int
1837 win32_fflush(FILE *pf)
1838 {
1839     return fflush(pf);
1840 }
1841
1842 DllExport long
1843 win32_ftell(FILE *pf)
1844 {
1845     return ftell(pf);
1846 }
1847
1848 DllExport int
1849 win32_fseek(FILE *pf,long offset,int origin)
1850 {
1851     return fseek(pf, offset, origin);
1852 }
1853
1854 DllExport int
1855 win32_fgetpos(FILE *pf,fpos_t *p)
1856 {
1857     return fgetpos(pf, p);
1858 }
1859
1860 DllExport int
1861 win32_fsetpos(FILE *pf,const fpos_t *p)
1862 {
1863     return fsetpos(pf, p);
1864 }
1865
1866 DllExport void
1867 win32_rewind(FILE *pf)
1868 {
1869     rewind(pf);
1870     return;
1871 }
1872
1873 DllExport FILE*
1874 win32_tmpfile(void)
1875 {
1876     return tmpfile();
1877 }
1878
1879 DllExport void
1880 win32_abort(void)
1881 {
1882     abort();
1883     return;
1884 }
1885
1886 DllExport int
1887 win32_fstat(int fd,struct stat *sbufptr)
1888 {
1889     return fstat(fd,sbufptr);
1890 }
1891
1892 DllExport int
1893 win32_pipe(int *pfd, unsigned int size, int mode)
1894 {
1895     return _pipe(pfd, size, mode);
1896 }
1897
1898 /*
1899  * a popen() clone that respects PERL5SHELL
1900  */
1901
1902 DllExport FILE*
1903 win32_popen(const char *command, const char *mode)
1904 {
1905 #ifdef USE_RTL_POPEN
1906     return _popen(command, mode);
1907 #else
1908     int p[2];
1909     int parent, child;
1910     int stdfd, oldfd;
1911     int ourmode;
1912     int childpid;
1913
1914     /* establish which ends read and write */
1915     if (strchr(mode,'w')) {
1916         stdfd = 0;              /* stdin */
1917         parent = 1;
1918         child = 0;
1919     }
1920     else if (strchr(mode,'r')) {
1921         stdfd = 1;              /* stdout */
1922         parent = 0;
1923         child = 1;
1924     }
1925     else
1926         return NULL;
1927
1928     /* set the correct mode */
1929     if (strchr(mode,'b'))
1930         ourmode = O_BINARY;
1931     else if (strchr(mode,'t'))
1932         ourmode = O_TEXT;
1933     else
1934         ourmode = _fmode & (O_TEXT | O_BINARY);
1935
1936     /* the child doesn't inherit handles */
1937     ourmode |= O_NOINHERIT;
1938
1939     if (win32_pipe( p, 512, ourmode) == -1)
1940         return NULL;
1941
1942     /* save current stdfd */
1943     if ((oldfd = win32_dup(stdfd)) == -1)
1944         goto cleanup;
1945
1946     /* make stdfd go to child end of pipe (implicitly closes stdfd) */
1947     /* stdfd will be inherited by the child */
1948     if (win32_dup2(p[child], stdfd) == -1)
1949         goto cleanup;
1950
1951     /* close the child end in parent */
1952     win32_close(p[child]);
1953
1954     /* start the child */
1955     if ((childpid = do_spawn_nowait((char*)command)) == -1)
1956         goto cleanup;
1957
1958     /* revert stdfd to whatever it was before */
1959     if (win32_dup2(oldfd, stdfd) == -1)
1960         goto cleanup;
1961
1962     /* close saved handle */
1963     win32_close(oldfd);
1964
1965     sv_setiv(*av_fetch(w32_fdpid, p[parent], TRUE), childpid);
1966
1967     /* we have an fd, return a file stream */
1968     return (win32_fdopen(p[parent], (char *)mode));
1969
1970 cleanup:
1971     /* we don't need to check for errors here */
1972     win32_close(p[0]);
1973     win32_close(p[1]);
1974     if (oldfd != -1) {
1975         win32_dup2(oldfd, stdfd);
1976         win32_close(oldfd);
1977     }
1978     return (NULL);
1979
1980 #endif /* USE_RTL_POPEN */
1981 }
1982
1983 /*
1984  * pclose() clone
1985  */
1986
1987 DllExport int
1988 win32_pclose(FILE *pf)
1989 {
1990 #ifdef USE_RTL_POPEN
1991     return _pclose(pf);
1992 #else
1993
1994     int childpid, status;
1995     SV *sv;
1996
1997     sv = *av_fetch(w32_fdpid, win32_fileno(pf), TRUE);
1998     if (SvIOK(sv))
1999         childpid = SvIVX(sv);
2000     else
2001         childpid = 0;
2002
2003     if (!childpid) {
2004         errno = EBADF;
2005         return -1;
2006     }
2007
2008     win32_fclose(pf);
2009     SvIVX(sv) = 0;
2010
2011     if (win32_waitpid(childpid, &status, 0) == -1)
2012         return -1;
2013
2014     return status;
2015
2016 #endif /* USE_RTL_POPEN */
2017 }
2018
2019 DllExport int
2020 win32_rename(const char *oname, const char *newname)
2021 {
2022     /* XXX despite what the documentation says about MoveFileEx(),
2023      * it doesn't work under Windows95!
2024      */
2025     if (IsWinNT()) {
2026         if (!MoveFileEx(oname,newname,
2027                         MOVEFILE_COPY_ALLOWED|MOVEFILE_REPLACE_EXISTING)) {
2028             DWORD err = GetLastError();
2029             switch (err) {
2030             case ERROR_BAD_NET_NAME:
2031             case ERROR_BAD_NETPATH:
2032             case ERROR_BAD_PATHNAME:
2033             case ERROR_FILE_NOT_FOUND:
2034             case ERROR_FILENAME_EXCED_RANGE:
2035             case ERROR_INVALID_DRIVE:
2036             case ERROR_NO_MORE_FILES:
2037             case ERROR_PATH_NOT_FOUND:
2038                 errno = ENOENT;
2039                 break;
2040             default:
2041                 errno = EACCES;
2042                 break;
2043             }
2044             return -1;
2045         }
2046         return 0;
2047     }
2048     else {
2049         int retval = 0;
2050         char tmpname[MAX_PATH+1];
2051         char dname[MAX_PATH+1];
2052         char *endname = Nullch;
2053         STRLEN tmplen = 0;
2054         DWORD from_attr, to_attr;
2055
2056         /* if oname doesn't exist, do nothing */
2057         from_attr = GetFileAttributes(oname);
2058         if (from_attr == 0xFFFFFFFF) {
2059             errno = ENOENT;
2060             return -1;
2061         }
2062
2063         /* if newname exists, rename it to a temporary name so that we
2064          * don't delete it in case oname happens to be the same file
2065          * (but perhaps accessed via a different path)
2066          */
2067         to_attr = GetFileAttributes(newname);
2068         if (to_attr != 0xFFFFFFFF) {
2069             /* if newname is a directory, we fail
2070              * XXX could overcome this with yet more convoluted logic */
2071             if (to_attr & FILE_ATTRIBUTE_DIRECTORY) {
2072                 errno = EACCES;
2073                 return -1;
2074             }
2075             tmplen = strlen(newname);
2076             strcpy(tmpname,newname);
2077             endname = tmpname+tmplen;
2078             for (; endname > tmpname ; --endname) {
2079                 if (*endname == '/' || *endname == '\\') {
2080                     *endname = '\0';
2081                     break;
2082                 }
2083             }
2084             if (endname > tmpname)
2085                 endname = strcpy(dname,tmpname);
2086             else
2087                 endname = ".";
2088
2089             /* get a temporary filename in same directory
2090              * XXX is this really the best we can do? */
2091             if (!GetTempFileName((LPCTSTR)endname, "plr", 0, tmpname)) {
2092                 errno = ENOENT;
2093                 return -1;
2094             }
2095             DeleteFile(tmpname);
2096
2097             retval = rename(newname, tmpname);
2098             if (retval != 0) {
2099                 errno = EACCES;
2100                 return retval;
2101             }
2102         }
2103
2104         /* rename oname to newname */
2105         retval = rename(oname, newname);
2106
2107         /* if we created a temporary file before ... */
2108         if (endname != Nullch) {
2109             /* ...and rename succeeded, delete temporary file/directory */
2110             if (retval == 0)
2111                 DeleteFile(tmpname);
2112             /* else restore it to what it was */
2113             else
2114                 (void)rename(tmpname, newname);
2115         }
2116         return retval;
2117     }
2118 }
2119
2120 DllExport int
2121 win32_setmode(int fd, int mode)
2122 {
2123     return setmode(fd, mode);
2124 }
2125
2126 DllExport long
2127 win32_lseek(int fd, long offset, int origin)
2128 {
2129     return lseek(fd, offset, origin);
2130 }
2131
2132 DllExport long
2133 win32_tell(int fd)
2134 {
2135     return tell(fd);
2136 }
2137
2138 DllExport int
2139 win32_open(const char *path, int flag, ...)
2140 {
2141     va_list ap;
2142     int pmode;
2143
2144     va_start(ap, flag);
2145     pmode = va_arg(ap, int);
2146     va_end(ap);
2147
2148     if (stricmp(path, "/dev/null")==0)
2149         return open("NUL", flag, pmode);
2150     return open(path,flag,pmode);
2151 }
2152
2153 DllExport int
2154 win32_close(int fd)
2155 {
2156     return close(fd);
2157 }
2158
2159 DllExport int
2160 win32_eof(int fd)
2161 {
2162     return eof(fd);
2163 }
2164
2165 DllExport int
2166 win32_dup(int fd)
2167 {
2168     return dup(fd);
2169 }
2170
2171 DllExport int
2172 win32_dup2(int fd1,int fd2)
2173 {
2174     return dup2(fd1,fd2);
2175 }
2176
2177 DllExport int
2178 win32_read(int fd, void *buf, unsigned int cnt)
2179 {
2180     return read(fd, buf, cnt);
2181 }
2182
2183 DllExport int
2184 win32_write(int fd, const void *buf, unsigned int cnt)
2185 {
2186     return write(fd, buf, cnt);
2187 }
2188
2189 DllExport int
2190 win32_mkdir(const char *dir, int mode)
2191 {
2192     return mkdir(dir); /* just ignore mode */
2193 }
2194
2195 DllExport int
2196 win32_rmdir(const char *dir)
2197 {
2198     return rmdir(dir);
2199 }
2200
2201 DllExport int
2202 win32_chdir(const char *dir)
2203 {
2204     return chdir(dir);
2205 }
2206
2207 static char *
2208 create_command_line(const char* command, const char * const *args)
2209 {
2210     int index;
2211     char *cmd, *ptr, *arg;
2212     STRLEN len = strlen(command) + 1;
2213
2214     for (index = 0; (ptr = (char*)args[index]) != NULL; ++index)
2215         len += strlen(ptr) + 1;
2216
2217     New(1310, cmd, len, char);
2218     ptr = cmd;
2219     strcpy(ptr, command);
2220
2221     for (index = 0; (arg = (char*)args[index]) != NULL; ++index) {
2222         ptr += strlen(ptr);
2223         *ptr++ = ' ';
2224         strcpy(ptr, arg);
2225     }
2226
2227     return cmd;
2228 }
2229
2230 static char *
2231 qualified_path(const char *cmd)
2232 {
2233     char *pathstr;
2234     char *fullcmd, *curfullcmd;
2235     STRLEN cmdlen = 0;
2236     int has_slash = 0;
2237
2238     if (!cmd)
2239         return Nullch;
2240     fullcmd = (char*)cmd;
2241     while (*fullcmd) {
2242         if (*fullcmd == '/' || *fullcmd == '\\')
2243             has_slash++;
2244         fullcmd++;
2245         cmdlen++;
2246     }
2247
2248     /* look in PATH */
2249     pathstr = win32_getenv("PATH");
2250     New(0, fullcmd, MAX_PATH+1, char);
2251     curfullcmd = fullcmd;
2252
2253     while (1) {
2254         DWORD res;
2255
2256         /* start by appending the name to the current prefix */
2257         strcpy(curfullcmd, cmd);
2258         curfullcmd += cmdlen;
2259
2260         /* if it doesn't end with '.', or has no extension, try adding
2261          * a trailing .exe first */
2262         if (cmd[cmdlen-1] != '.'
2263             && (cmdlen < 4 || cmd[cmdlen-4] != '.'))
2264         {
2265             strcpy(curfullcmd, ".exe");
2266             res = GetFileAttributes(fullcmd);
2267             if (res != 0xFFFFFFFF && !(res & FILE_ATTRIBUTE_DIRECTORY))
2268                 return fullcmd;
2269             *curfullcmd = '\0';
2270         }
2271
2272         /* that failed, try the bare name */
2273         res = GetFileAttributes(fullcmd);
2274         if (res != 0xFFFFFFFF && !(res & FILE_ATTRIBUTE_DIRECTORY))
2275             return fullcmd;
2276
2277         /* quit if no other path exists, or if cmd already has path */
2278         if (!pathstr || !*pathstr || has_slash)
2279             break;
2280
2281         /* skip leading semis */
2282         while (*pathstr == ';')
2283             pathstr++;
2284
2285         /* build a new prefix from scratch */
2286         curfullcmd = fullcmd;
2287         while (*pathstr && *pathstr != ';') {
2288             if (*pathstr == '"') {      /* foo;"baz;etc";bar */
2289                 pathstr++;              /* skip initial '"' */
2290                 while (*pathstr && *pathstr != '"') {
2291                     if (curfullcmd-fullcmd < MAX_PATH-cmdlen-5)
2292                         *curfullcmd++ = *pathstr;
2293                     pathstr++;
2294                 }
2295                 if (*pathstr)
2296                     pathstr++;          /* skip trailing '"' */
2297             }
2298             else {
2299                 if (curfullcmd-fullcmd < MAX_PATH-cmdlen-5)
2300                     *curfullcmd++ = *pathstr;
2301                 pathstr++;
2302             }
2303         }
2304         if (*pathstr)
2305             pathstr++;                  /* skip trailing semi */
2306         if (curfullcmd > fullcmd        /* append a dir separator */
2307             && curfullcmd[-1] != '/' && curfullcmd[-1] != '\\')
2308         {
2309             *curfullcmd++ = '\\';
2310         }
2311     }
2312 GIVE_UP:
2313     Safefree(fullcmd);
2314     return Nullch;
2315 }
2316
2317 /* XXX this needs to be made more compatible with the spawnvp()
2318  * provided by the various RTLs.  In particular, searching for
2319  * *.{com,bat,cmd} files (as done by the RTLs) is unimplemented.
2320  * This doesn't significantly affect perl itself, because we
2321  * always invoke things using PERL5SHELL if a direct attempt to
2322  * spawn the executable fails.
2323  * 
2324  * XXX splitting and rejoining the commandline between do_aspawn()
2325  * and win32_spawnvp() could also be avoided.
2326  */
2327
2328 DllExport int
2329 win32_spawnvp(int mode, const char *cmdname, const char *const *argv)
2330 {
2331 #ifdef USE_RTL_SPAWNVP
2332     return spawnvp(mode, cmdname, (char * const *)argv);
2333 #else
2334     DWORD ret;
2335     STARTUPINFO StartupInfo;
2336     PROCESS_INFORMATION ProcessInformation;
2337     DWORD create = 0;
2338
2339     char *cmd = create_command_line(cmdname, strcmp(cmdname, argv[0]) == 0
2340                                              ? &argv[1] : argv);
2341     char *fullcmd = Nullch;
2342
2343     switch(mode) {
2344     case P_NOWAIT:      /* asynch + remember result */
2345         if (w32_num_children >= MAXIMUM_WAIT_OBJECTS) {
2346             errno = EAGAIN;
2347             ret = -1;
2348             goto RETVAL;
2349         }
2350         /* FALL THROUGH */
2351     case P_WAIT:        /* synchronous execution */
2352         break;
2353     default:            /* invalid mode */
2354         errno = EINVAL;
2355         ret = -1;
2356         goto RETVAL;
2357     }
2358     memset(&StartupInfo,0,sizeof(StartupInfo));
2359     StartupInfo.cb = sizeof(StartupInfo);
2360     StartupInfo.wShowWindow = SW_SHOWDEFAULT;
2361
2362 RETRY:
2363     if (!CreateProcess(cmdname,         /* search PATH to find executable */
2364                        cmd,             /* executable, and its arguments */
2365                        NULL,            /* process attributes */
2366                        NULL,            /* thread attributes */
2367                        TRUE,            /* inherit handles */
2368                        create,          /* creation flags */
2369                        NULL,            /* inherit environment */
2370                        NULL,            /* inherit cwd */
2371                        &StartupInfo,
2372                        &ProcessInformation))
2373     {
2374         /* initial NULL argument to CreateProcess() does a PATH
2375          * search, but it always first looks in the directory
2376          * where the current process was started, which behavior
2377          * is undesirable for backward compatibility.  So we
2378          * jump through our own hoops by picking out the path
2379          * we really want it to use. */
2380         if (!fullcmd) {
2381             fullcmd = qualified_path(cmdname);
2382             if (fullcmd) {
2383                 cmdname = fullcmd;
2384                 goto RETRY;
2385             }
2386         }
2387         errno = ENOENT;
2388         ret = -1;
2389         goto RETVAL;
2390     }
2391
2392     if (mode == P_NOWAIT) {
2393         /* asynchronous spawn -- store handle, return PID */
2394         w32_child_handles[w32_num_children] = ProcessInformation.hProcess;
2395         ret = w32_child_pids[w32_num_children] = ProcessInformation.dwProcessId;
2396         ++w32_num_children;
2397     }
2398     else  {
2399         WaitForSingleObject(ProcessInformation.hProcess, INFINITE);
2400         GetExitCodeProcess(ProcessInformation.hProcess, &ret);
2401         CloseHandle(ProcessInformation.hProcess);
2402     }
2403
2404     CloseHandle(ProcessInformation.hThread);
2405 RETVAL:
2406     Safefree(cmd);
2407     Safefree(fullcmd);
2408     return (int)ret;
2409 #endif
2410 }
2411
2412 DllExport int
2413 win32_execv(const char *cmdname, const char *const *argv)
2414 {
2415     return execv(cmdname, (char *const *)argv);
2416 }
2417
2418 DllExport int
2419 win32_execvp(const char *cmdname, const char *const *argv)
2420 {
2421     return execvp(cmdname, (char *const *)argv);
2422 }
2423
2424 DllExport void
2425 win32_perror(const char *str)
2426 {
2427     perror(str);
2428 }
2429
2430 DllExport void
2431 win32_setbuf(FILE *pf, char *buf)
2432 {
2433     setbuf(pf, buf);
2434 }
2435
2436 DllExport int
2437 win32_setvbuf(FILE *pf, char *buf, int type, size_t size)
2438 {
2439     return setvbuf(pf, buf, type, size);
2440 }
2441
2442 DllExport int
2443 win32_flushall(void)
2444 {
2445     return flushall();
2446 }
2447
2448 DllExport int
2449 win32_fcloseall(void)
2450 {
2451     return fcloseall();
2452 }
2453
2454 DllExport char*
2455 win32_fgets(char *s, int n, FILE *pf)
2456 {
2457     return fgets(s, n, pf);
2458 }
2459
2460 DllExport char*
2461 win32_gets(char *s)
2462 {
2463     return gets(s);
2464 }
2465
2466 DllExport int
2467 win32_fgetc(FILE *pf)
2468 {
2469     return fgetc(pf);
2470 }
2471
2472 DllExport int
2473 win32_putc(int c, FILE *pf)
2474 {
2475     return putc(c,pf);
2476 }
2477
2478 DllExport int
2479 win32_puts(const char *s)
2480 {
2481     return puts(s);
2482 }
2483
2484 DllExport int
2485 win32_getchar(void)
2486 {
2487     return getchar();
2488 }
2489
2490 DllExport int
2491 win32_putchar(int c)
2492 {
2493     return putchar(c);
2494 }
2495
2496 #ifdef MYMALLOC
2497
2498 #ifndef USE_PERL_SBRK
2499
2500 static char *committed = NULL;
2501 static char *base      = NULL;
2502 static char *reserved  = NULL;
2503 static char *brk       = NULL;
2504 static DWORD pagesize  = 0;
2505 static DWORD allocsize = 0;
2506
2507 void *
2508 sbrk(int need)
2509 {
2510  void *result;
2511  if (!pagesize)
2512   {SYSTEM_INFO info;
2513    GetSystemInfo(&info);
2514    /* Pretend page size is larger so we don't perpetually
2515     * call the OS to commit just one page ...
2516     */
2517    pagesize = info.dwPageSize << 3;
2518    allocsize = info.dwAllocationGranularity;
2519   }
2520  /* This scheme fails eventually if request for contiguous
2521   * block is denied so reserve big blocks - this is only 
2522   * address space not memory ...
2523   */
2524  if (brk+need >= reserved)
2525   {
2526    DWORD size = 64*1024*1024;
2527    char *addr;
2528    if (committed && reserved && committed < reserved)
2529     {
2530      /* Commit last of previous chunk cannot span allocations */
2531      addr = (char *) VirtualAlloc(committed,reserved-committed,MEM_COMMIT,PAGE_READWRITE);
2532      if (addr)
2533       committed = reserved;
2534     }
2535    /* Reserve some (more) space 
2536     * Note this is a little sneaky, 1st call passes NULL as reserved
2537     * so lets system choose where we start, subsequent calls pass
2538     * the old end address so ask for a contiguous block
2539     */
2540    addr  = (char *) VirtualAlloc(reserved,size,MEM_RESERVE,PAGE_NOACCESS);
2541    if (addr)
2542     {
2543      reserved = addr+size;
2544      if (!base)
2545       base = addr;
2546      if (!committed)
2547       committed = base;
2548      if (!brk)
2549       brk = committed;
2550     }
2551    else
2552     {
2553      return (void *) -1;
2554     }
2555   }
2556  result = brk;
2557  brk += need;
2558  if (brk > committed)
2559   {
2560    DWORD size = ((brk-committed + pagesize -1)/pagesize) * pagesize;
2561    char *addr = (char *) VirtualAlloc(committed,size,MEM_COMMIT,PAGE_READWRITE);
2562    if (addr)
2563     {
2564      committed += size;
2565     }
2566    else
2567     return (void *) -1;
2568   }
2569  return result;
2570 }
2571
2572 #endif
2573 #endif
2574
2575 DllExport void*
2576 win32_malloc(size_t size)
2577 {
2578     return malloc(size);
2579 }
2580
2581 DllExport void*
2582 win32_calloc(size_t numitems, size_t size)
2583 {
2584     return calloc(numitems,size);
2585 }
2586
2587 DllExport void*
2588 win32_realloc(void *block, size_t size)
2589 {
2590     return realloc(block,size);
2591 }
2592
2593 DllExport void
2594 win32_free(void *block)
2595 {
2596     free(block);
2597 }
2598
2599
2600 int
2601 win32_open_osfhandle(long handle, int flags)
2602 {
2603     return _open_osfhandle(handle, flags);
2604 }
2605
2606 long
2607 win32_get_osfhandle(int fd)
2608 {
2609     return _get_osfhandle(fd);
2610 }
2611
2612 /*
2613  * Extras.
2614  */
2615
2616 static
2617 XS(w32_GetCwd)
2618 {
2619     dXSARGS;
2620     SV *sv = sv_newmortal();
2621     /* Make one call with zero size - return value is required size */
2622     DWORD len = GetCurrentDirectory((DWORD)0,NULL);
2623     SvUPGRADE(sv,SVt_PV);
2624     SvGROW(sv,len);
2625     SvCUR(sv) = GetCurrentDirectory((DWORD) SvLEN(sv), SvPVX(sv));
2626     /* 
2627      * If result != 0 
2628      *   then it worked, set PV valid, 
2629      *   else leave it 'undef' 
2630      */
2631     EXTEND(SP,1);
2632     if (SvCUR(sv)) {
2633         SvPOK_on(sv);
2634         ST(0) = sv;
2635         XSRETURN(1);
2636     }
2637     XSRETURN_UNDEF;
2638 }
2639
2640 static
2641 XS(w32_SetCwd)
2642 {
2643     dXSARGS;
2644     if (items != 1)
2645         croak("usage: Win32::SetCurrentDirectory($cwd)");
2646     if (SetCurrentDirectory(SvPV_nolen(ST(0))))
2647         XSRETURN_YES;
2648
2649     XSRETURN_NO;
2650 }
2651
2652 static
2653 XS(w32_GetNextAvailDrive)
2654 {
2655     dXSARGS;
2656     char ix = 'C';
2657     char root[] = "_:\\";
2658
2659     EXTEND(SP,1);
2660     while (ix <= 'Z') {
2661         root[0] = ix++;
2662         if (GetDriveType(root) == 1) {
2663             root[2] = '\0';
2664             XSRETURN_PV(root);
2665         }
2666     }
2667     XSRETURN_UNDEF;
2668 }
2669
2670 static
2671 XS(w32_GetLastError)
2672 {
2673     dXSARGS;
2674     EXTEND(SP,1);
2675     XSRETURN_IV(GetLastError());
2676 }
2677
2678 static
2679 XS(w32_SetLastError)
2680 {
2681     dXSARGS;
2682     if (items != 1)
2683         croak("usage: Win32::SetLastError($error)");
2684     SetLastError(SvIV(ST(0)));
2685     XSRETURN_EMPTY;
2686 }
2687
2688 static
2689 XS(w32_LoginName)
2690 {
2691     dXSARGS;
2692     char *name = getlogin_buffer;
2693     DWORD size = sizeof(getlogin_buffer);
2694     EXTEND(SP,1);
2695     if (GetUserName(name,&size)) {
2696         /* size includes NULL */
2697         ST(0) = sv_2mortal(newSVpvn(name,size-1));
2698         XSRETURN(1);
2699     }
2700     XSRETURN_UNDEF;
2701 }
2702
2703 static
2704 XS(w32_NodeName)
2705 {
2706     dXSARGS;
2707     char name[MAX_COMPUTERNAME_LENGTH+1];
2708     DWORD size = sizeof(name);
2709     EXTEND(SP,1);
2710     if (GetComputerName(name,&size)) {
2711         /* size does NOT include NULL :-( */
2712         ST(0) = sv_2mortal(newSVpvn(name,size));
2713         XSRETURN(1);
2714     }
2715     XSRETURN_UNDEF;
2716 }
2717
2718
2719 static
2720 XS(w32_DomainName)
2721 {
2722     dXSARGS;
2723 #ifndef HAS_NETWKSTAGETINFO
2724     /* mingw32 (and Win95) don't have NetWksta*(), so do it the old way */
2725     char name[256];
2726     DWORD size = sizeof(name);
2727     EXTEND(SP,1);
2728     if (GetUserName(name,&size)) {
2729         char sid[1024];
2730         DWORD sidlen = sizeof(sid);
2731         char dname[256];
2732         DWORD dnamelen = sizeof(dname);
2733         SID_NAME_USE snu;
2734         if (LookupAccountName(NULL, name, (PSID)&sid, &sidlen,
2735                               dname, &dnamelen, &snu)) {
2736             XSRETURN_PV(dname);         /* all that for this */
2737         }
2738     }
2739 #else
2740     /* this way is more reliable, in case user has a local account.
2741      * XXX need dynamic binding of netapi32.dll symbols or this will fail on
2742      * Win95. Probably makes more sense to move it into libwin32. */
2743     char dname[256];
2744     DWORD dnamelen = sizeof(dname);
2745     PWKSTA_INFO_100 pwi;
2746     EXTEND(SP,1);
2747     if (NERR_Success == NetWkstaGetInfo(NULL, 100, (LPBYTE*)&pwi)) {
2748         if (pwi->wki100_langroup && *(pwi->wki100_langroup)) {
2749             WideCharToMultiByte(CP_ACP, NULL, pwi->wki100_langroup,
2750                                 -1, (LPSTR)dname, dnamelen, NULL, NULL);
2751         }
2752         else {
2753             WideCharToMultiByte(CP_ACP, NULL, pwi->wki100_computername,
2754                                 -1, (LPSTR)dname, dnamelen, NULL, NULL);
2755         }
2756         NetApiBufferFree(pwi);
2757         XSRETURN_PV(dname);
2758     }
2759 #endif
2760     XSRETURN_UNDEF;
2761 }
2762
2763 static
2764 XS(w32_FsType)
2765 {
2766     dXSARGS;
2767     char fsname[256];
2768     DWORD flags, filecomplen;
2769     if (GetVolumeInformation(NULL, NULL, 0, NULL, &filecomplen,
2770                          &flags, fsname, sizeof(fsname))) {
2771         if (GIMME_V == G_ARRAY) {
2772             XPUSHs(sv_2mortal(newSVpvn(fsname,strlen(fsname))));
2773             XPUSHs(sv_2mortal(newSViv(flags)));
2774             XPUSHs(sv_2mortal(newSViv(filecomplen)));
2775             PUTBACK;
2776             return;
2777         }
2778         EXTEND(SP,1);
2779         XSRETURN_PV(fsname);
2780     }
2781     XSRETURN_EMPTY;
2782 }
2783
2784 static
2785 XS(w32_GetOSVersion)
2786 {
2787     dXSARGS;
2788     OSVERSIONINFO osver;
2789
2790     osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
2791     if (GetVersionEx(&osver)) {
2792         XPUSHs(newSVpvn(osver.szCSDVersion, strlen(osver.szCSDVersion)));
2793         XPUSHs(newSViv(osver.dwMajorVersion));
2794         XPUSHs(newSViv(osver.dwMinorVersion));
2795         XPUSHs(newSViv(osver.dwBuildNumber));
2796         XPUSHs(newSViv(osver.dwPlatformId));
2797         PUTBACK;
2798         return;
2799     }
2800     XSRETURN_EMPTY;
2801 }
2802
2803 static
2804 XS(w32_IsWinNT)
2805 {
2806     dXSARGS;
2807     EXTEND(SP,1);
2808     XSRETURN_IV(IsWinNT());
2809 }
2810
2811 static
2812 XS(w32_IsWin95)
2813 {
2814     dXSARGS;
2815     EXTEND(SP,1);
2816     XSRETURN_IV(IsWin95());
2817 }
2818
2819 static
2820 XS(w32_FormatMessage)
2821 {
2822     dXSARGS;
2823     DWORD source = 0;
2824     char msgbuf[1024];
2825
2826     if (items != 1)
2827         croak("usage: Win32::FormatMessage($errno)");
2828
2829     if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
2830                       &source, SvIV(ST(0)), 0,
2831                       msgbuf, sizeof(msgbuf)-1, NULL))
2832         XSRETURN_PV(msgbuf);
2833
2834     XSRETURN_UNDEF;
2835 }
2836
2837 static
2838 XS(w32_Spawn)
2839 {
2840     dXSARGS;
2841     char *cmd, *args;
2842     PROCESS_INFORMATION stProcInfo;
2843     STARTUPINFO stStartInfo;
2844     BOOL bSuccess = FALSE;
2845
2846     if (items != 3)
2847         croak("usage: Win32::Spawn($cmdName, $args, $PID)");
2848
2849     cmd = SvPV_nolen(ST(0));
2850     args = SvPV_nolen(ST(1));
2851
2852     memset(&stStartInfo, 0, sizeof(stStartInfo));   /* Clear the block */
2853     stStartInfo.cb = sizeof(stStartInfo);           /* Set the structure size */
2854     stStartInfo.dwFlags = STARTF_USESHOWWINDOW;     /* Enable wShowWindow control */
2855     stStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;   /* Start min (normal) */
2856
2857     if (CreateProcess(
2858                 cmd,                    /* Image path */
2859                 args,                   /* Arguments for command line */
2860                 NULL,                   /* Default process security */
2861                 NULL,                   /* Default thread security */
2862                 FALSE,                  /* Must be TRUE to use std handles */
2863                 NORMAL_PRIORITY_CLASS,  /* No special scheduling */
2864                 NULL,                   /* Inherit our environment block */
2865                 NULL,                   /* Inherit our currrent directory */
2866                 &stStartInfo,           /* -> Startup info */
2867                 &stProcInfo))           /* <- Process info (if OK) */
2868     {
2869         CloseHandle(stProcInfo.hThread);/* library source code does this. */
2870         sv_setiv(ST(2), stProcInfo.dwProcessId);
2871         bSuccess = TRUE;
2872     }
2873     XSRETURN_IV(bSuccess);
2874 }
2875
2876 static
2877 XS(w32_GetTickCount)
2878 {
2879     dXSARGS;
2880     DWORD msec = GetTickCount();
2881     EXTEND(SP,1);
2882     if ((IV)msec > 0)
2883         XSRETURN_IV(msec);
2884     XSRETURN_NV(msec);
2885 }
2886
2887 static
2888 XS(w32_GetShortPathName)
2889 {
2890     dXSARGS;
2891     SV *shortpath;
2892     DWORD len;
2893
2894     if (items != 1)
2895         croak("usage: Win32::GetShortPathName($longPathName)");
2896
2897     shortpath = sv_mortalcopy(ST(0));
2898     SvUPGRADE(shortpath, SVt_PV);
2899     /* src == target is allowed */
2900     do {
2901         len = GetShortPathName(SvPVX(shortpath),
2902                                SvPVX(shortpath),
2903                                SvLEN(shortpath));
2904     } while (len >= SvLEN(shortpath) && sv_grow(shortpath,len+1));
2905     if (len) {
2906         SvCUR_set(shortpath,len);
2907         ST(0) = shortpath;
2908         XSRETURN(1);
2909     }
2910     XSRETURN_UNDEF;
2911 }
2912
2913 static
2914 XS(w32_GetFullPathName)
2915 {
2916     dXSARGS;
2917     SV *filename;
2918     SV *fullpath;
2919     char *filepart;
2920     DWORD len;
2921
2922     if (items != 1)
2923         croak("usage: Win32::GetFullPathName($filename)");
2924
2925     filename = ST(0);
2926     fullpath = sv_mortalcopy(filename);
2927     SvUPGRADE(fullpath, SVt_PV);
2928     do {
2929         len = GetFullPathName(SvPVX(filename),
2930                               SvLEN(fullpath),
2931                               SvPVX(fullpath),
2932                               &filepart);
2933     } while (len >= SvLEN(fullpath) && sv_grow(fullpath,len+1));
2934     if (len) {
2935         if (GIMME_V == G_ARRAY) {
2936             EXTEND(SP,1);
2937             XST_mPV(1,filepart);
2938             len = filepart - SvPVX(fullpath);
2939             items = 2;
2940         }
2941         SvCUR_set(fullpath,len);
2942         ST(0) = fullpath;
2943         XSRETURN(items);
2944     }
2945     XSRETURN_EMPTY;
2946 }
2947
2948 static
2949 XS(w32_GetLongPathName)
2950 {
2951     dXSARGS;
2952     SV *path;
2953     char tmpbuf[MAX_PATH+1];
2954     char *pathstr;
2955     STRLEN len;
2956
2957     if (items != 1)
2958         croak("usage: Win32::GetLongPathName($pathname)");
2959
2960     path = ST(0);
2961     pathstr = SvPV(path,len);
2962     strcpy(tmpbuf, pathstr);
2963     pathstr = win32_longpath(tmpbuf);
2964     if (pathstr) {
2965         ST(0) = sv_2mortal(newSVpvn(pathstr, strlen(pathstr)));
2966         XSRETURN(1);
2967     }
2968     XSRETURN_EMPTY;
2969 }
2970
2971 static
2972 XS(w32_Sleep)
2973 {
2974     dXSARGS;
2975     if (items != 1)
2976         croak("usage: Win32::Sleep($milliseconds)");
2977     Sleep(SvIV(ST(0)));
2978     XSRETURN_YES;
2979 }
2980
2981 static
2982 XS(w32_CopyFile)
2983 {
2984     dXSARGS;
2985     if (items != 3)
2986         croak("usage: Win32::CopyFile($from, $to, $overwrite)");
2987     if (CopyFile(SvPV_nolen(ST(0)), SvPV_nolen(ST(1)), !SvTRUE(ST(2))))
2988         XSRETURN_YES;
2989     XSRETURN_NO;
2990 }
2991
2992 void
2993 Perl_init_os_extras()
2994 {
2995     char *file = __FILE__;
2996     dXSUB_SYS;
2997
2998     w32_perlshell_tokens = Nullch;
2999     w32_perlshell_items = -1;
3000     w32_fdpid = newAV();                /* XXX needs to be in Perl_win32_init()? */
3001     New(1313, w32_children, 1, child_tab);
3002     w32_num_children = 0;
3003
3004     /* these names are Activeware compatible */
3005     newXS("Win32::GetCwd", w32_GetCwd, file);
3006     newXS("Win32::SetCwd", w32_SetCwd, file);
3007     newXS("Win32::GetNextAvailDrive", w32_GetNextAvailDrive, file);
3008     newXS("Win32::GetLastError", w32_GetLastError, file);
3009     newXS("Win32::SetLastError", w32_SetLastError, file);
3010     newXS("Win32::LoginName", w32_LoginName, file);
3011     newXS("Win32::NodeName", w32_NodeName, file);
3012     newXS("Win32::DomainName", w32_DomainName, file);
3013     newXS("Win32::FsType", w32_FsType, file);
3014     newXS("Win32::GetOSVersion", w32_GetOSVersion, file);
3015     newXS("Win32::IsWinNT", w32_IsWinNT, file);
3016     newXS("Win32::IsWin95", w32_IsWin95, file);
3017     newXS("Win32::FormatMessage", w32_FormatMessage, file);
3018     newXS("Win32::Spawn", w32_Spawn, file);
3019     newXS("Win32::GetTickCount", w32_GetTickCount, file);
3020     newXS("Win32::GetShortPathName", w32_GetShortPathName, file);
3021     newXS("Win32::GetFullPathName", w32_GetFullPathName, file);
3022     newXS("Win32::GetLongPathName", w32_GetLongPathName, file);
3023     newXS("Win32::CopyFile", w32_CopyFile, file);
3024     newXS("Win32::Sleep", w32_Sleep, file);
3025
3026     /* XXX Bloat Alert! The following Activeware preloads really
3027      * ought to be part of Win32::Sys::*, so they're not included
3028      * here.
3029      */
3030     /* LookupAccountName
3031      * LookupAccountSID
3032      * InitiateSystemShutdown
3033      * AbortSystemShutdown
3034      * ExpandEnvrironmentStrings
3035      */
3036 }
3037
3038 void
3039 Perl_win32_init(int *argcp, char ***argvp)
3040 {
3041     /* Disable floating point errors, Perl will trap the ones we
3042      * care about.  VC++ RTL defaults to switching these off
3043      * already, but the Borland RTL doesn't.  Since we don't
3044      * want to be at the vendor's whim on the default, we set
3045      * it explicitly here.
3046      */
3047 #if !defined(_ALPHA_) && !defined(__GNUC__)
3048     _control87(MCW_EM, MCW_EM);
3049 #endif
3050     MALLOC_INIT;
3051 }
3052
3053 #ifdef USE_BINMODE_SCRIPTS
3054
3055 void
3056 win32_strip_return(SV *sv)
3057 {
3058  char *s = SvPVX(sv);
3059  char *e = s+SvCUR(sv);
3060  char *d = s;
3061  while (s < e)
3062   {
3063    if (*s == '\r' && s[1] == '\n')
3064     {
3065      *d++ = '\n';
3066      s += 2;
3067     }
3068    else 
3069     {
3070      *d++ = *s++;
3071     }   
3072   }
3073  SvCUR_set(sv,d-SvPVX(sv)); 
3074 }
3075
3076 #endif