s/isspace/isSPACE/g and make sure the CRT version is always
[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         if (sMsg)
1720             dwLen = sprintf(sMsg,
1721                             "Unknown error #0x%lX (lookup 0x%lX)",
1722                             dwErr, GetLastError());
1723     }
1724     if (sMsg) {
1725         sv_setpvn((SV*)sv, sMsg, dwLen);
1726         LocalFree(sMsg);
1727     }
1728 }
1729
1730
1731 DllExport int
1732 win32_fprintf(FILE *fp, const char *format, ...)
1733 {
1734     va_list marker;
1735     va_start(marker, format);     /* Initialize variable arguments. */
1736
1737     return (vfprintf(fp, format, marker));
1738 }
1739
1740 DllExport int
1741 win32_printf(const char *format, ...)
1742 {
1743     va_list marker;
1744     va_start(marker, format);     /* Initialize variable arguments. */
1745
1746     return (vprintf(format, marker));
1747 }
1748
1749 DllExport int
1750 win32_vfprintf(FILE *fp, const char *format, va_list args)
1751 {
1752     return (vfprintf(fp, format, args));
1753 }
1754
1755 DllExport int
1756 win32_vprintf(const char *format, va_list args)
1757 {
1758     return (vprintf(format, args));
1759 }
1760
1761 DllExport size_t
1762 win32_fread(void *buf, size_t size, size_t count, FILE *fp)
1763 {
1764     return fread(buf, size, count, fp);
1765 }
1766
1767 DllExport size_t
1768 win32_fwrite(const void *buf, size_t size, size_t count, FILE *fp)
1769 {
1770     return fwrite(buf, size, count, fp);
1771 }
1772
1773 DllExport FILE *
1774 win32_fopen(const char *filename, const char *mode)
1775 {
1776     if (stricmp(filename, "/dev/null")==0)
1777         return fopen("NUL", mode);
1778     return fopen(filename, mode);
1779 }
1780
1781 #ifndef USE_SOCKETS_AS_HANDLES
1782 #undef fdopen
1783 #define fdopen my_fdopen
1784 #endif
1785
1786 DllExport FILE *
1787 win32_fdopen( int handle, const char *mode)
1788 {
1789     return fdopen(handle, (char *) mode);
1790 }
1791
1792 DllExport FILE *
1793 win32_freopen( const char *path, const char *mode, FILE *stream)
1794 {
1795     if (stricmp(path, "/dev/null")==0)
1796         return freopen("NUL", mode, stream);
1797     return freopen(path, mode, stream);
1798 }
1799
1800 DllExport int
1801 win32_fclose(FILE *pf)
1802 {
1803     return my_fclose(pf);       /* defined in win32sck.c */
1804 }
1805
1806 DllExport int
1807 win32_fputs(const char *s,FILE *pf)
1808 {
1809     return fputs(s, pf);
1810 }
1811
1812 DllExport int
1813 win32_fputc(int c,FILE *pf)
1814 {
1815     return fputc(c,pf);
1816 }
1817
1818 DllExport int
1819 win32_ungetc(int c,FILE *pf)
1820 {
1821     return ungetc(c,pf);
1822 }
1823
1824 DllExport int
1825 win32_getc(FILE *pf)
1826 {
1827     return getc(pf);
1828 }
1829
1830 DllExport int
1831 win32_fileno(FILE *pf)
1832 {
1833     return fileno(pf);
1834 }
1835
1836 DllExport void
1837 win32_clearerr(FILE *pf)
1838 {
1839     clearerr(pf);
1840     return;
1841 }
1842
1843 DllExport int
1844 win32_fflush(FILE *pf)
1845 {
1846     return fflush(pf);
1847 }
1848
1849 DllExport long
1850 win32_ftell(FILE *pf)
1851 {
1852     return ftell(pf);
1853 }
1854
1855 DllExport int
1856 win32_fseek(FILE *pf,long offset,int origin)
1857 {
1858     return fseek(pf, offset, origin);
1859 }
1860
1861 DllExport int
1862 win32_fgetpos(FILE *pf,fpos_t *p)
1863 {
1864     return fgetpos(pf, p);
1865 }
1866
1867 DllExport int
1868 win32_fsetpos(FILE *pf,const fpos_t *p)
1869 {
1870     return fsetpos(pf, p);
1871 }
1872
1873 DllExport void
1874 win32_rewind(FILE *pf)
1875 {
1876     rewind(pf);
1877     return;
1878 }
1879
1880 DllExport FILE*
1881 win32_tmpfile(void)
1882 {
1883     return tmpfile();
1884 }
1885
1886 DllExport void
1887 win32_abort(void)
1888 {
1889     abort();
1890     return;
1891 }
1892
1893 DllExport int
1894 win32_fstat(int fd,struct stat *sbufptr)
1895 {
1896     return fstat(fd,sbufptr);
1897 }
1898
1899 DllExport int
1900 win32_pipe(int *pfd, unsigned int size, int mode)
1901 {
1902     return _pipe(pfd, size, mode);
1903 }
1904
1905 /*
1906  * a popen() clone that respects PERL5SHELL
1907  */
1908
1909 DllExport FILE*
1910 win32_popen(const char *command, const char *mode)
1911 {
1912 #ifdef USE_RTL_POPEN
1913     return _popen(command, mode);
1914 #else
1915     int p[2];
1916     int parent, child;
1917     int stdfd, oldfd;
1918     int ourmode;
1919     int childpid;
1920
1921     /* establish which ends read and write */
1922     if (strchr(mode,'w')) {
1923         stdfd = 0;              /* stdin */
1924         parent = 1;
1925         child = 0;
1926     }
1927     else if (strchr(mode,'r')) {
1928         stdfd = 1;              /* stdout */
1929         parent = 0;
1930         child = 1;
1931     }
1932     else
1933         return NULL;
1934
1935     /* set the correct mode */
1936     if (strchr(mode,'b'))
1937         ourmode = O_BINARY;
1938     else if (strchr(mode,'t'))
1939         ourmode = O_TEXT;
1940     else
1941         ourmode = _fmode & (O_TEXT | O_BINARY);
1942
1943     /* the child doesn't inherit handles */
1944     ourmode |= O_NOINHERIT;
1945
1946     if (win32_pipe( p, 512, ourmode) == -1)
1947         return NULL;
1948
1949     /* save current stdfd */
1950     if ((oldfd = win32_dup(stdfd)) == -1)
1951         goto cleanup;
1952
1953     /* make stdfd go to child end of pipe (implicitly closes stdfd) */
1954     /* stdfd will be inherited by the child */
1955     if (win32_dup2(p[child], stdfd) == -1)
1956         goto cleanup;
1957
1958     /* close the child end in parent */
1959     win32_close(p[child]);
1960
1961     /* start the child */
1962     if ((childpid = do_spawn_nowait((char*)command)) == -1)
1963         goto cleanup;
1964
1965     /* revert stdfd to whatever it was before */
1966     if (win32_dup2(oldfd, stdfd) == -1)
1967         goto cleanup;
1968
1969     /* close saved handle */
1970     win32_close(oldfd);
1971
1972     sv_setiv(*av_fetch(w32_fdpid, p[parent], TRUE), childpid);
1973
1974     /* we have an fd, return a file stream */
1975     return (win32_fdopen(p[parent], (char *)mode));
1976
1977 cleanup:
1978     /* we don't need to check for errors here */
1979     win32_close(p[0]);
1980     win32_close(p[1]);
1981     if (oldfd != -1) {
1982         win32_dup2(oldfd, stdfd);
1983         win32_close(oldfd);
1984     }
1985     return (NULL);
1986
1987 #endif /* USE_RTL_POPEN */
1988 }
1989
1990 /*
1991  * pclose() clone
1992  */
1993
1994 DllExport int
1995 win32_pclose(FILE *pf)
1996 {
1997 #ifdef USE_RTL_POPEN
1998     return _pclose(pf);
1999 #else
2000
2001     int childpid, status;
2002     SV *sv;
2003
2004     sv = *av_fetch(w32_fdpid, win32_fileno(pf), TRUE);
2005     if (SvIOK(sv))
2006         childpid = SvIVX(sv);
2007     else
2008         childpid = 0;
2009
2010     if (!childpid) {
2011         errno = EBADF;
2012         return -1;
2013     }
2014
2015     win32_fclose(pf);
2016     SvIVX(sv) = 0;
2017
2018     if (win32_waitpid(childpid, &status, 0) == -1)
2019         return -1;
2020
2021     return status;
2022
2023 #endif /* USE_RTL_POPEN */
2024 }
2025
2026 DllExport int
2027 win32_rename(const char *oname, const char *newname)
2028 {
2029     /* XXX despite what the documentation says about MoveFileEx(),
2030      * it doesn't work under Windows95!
2031      */
2032     if (IsWinNT()) {
2033         if (!MoveFileEx(oname,newname,
2034                         MOVEFILE_COPY_ALLOWED|MOVEFILE_REPLACE_EXISTING)) {
2035             DWORD err = GetLastError();
2036             switch (err) {
2037             case ERROR_BAD_NET_NAME:
2038             case ERROR_BAD_NETPATH:
2039             case ERROR_BAD_PATHNAME:
2040             case ERROR_FILE_NOT_FOUND:
2041             case ERROR_FILENAME_EXCED_RANGE:
2042             case ERROR_INVALID_DRIVE:
2043             case ERROR_NO_MORE_FILES:
2044             case ERROR_PATH_NOT_FOUND:
2045                 errno = ENOENT;
2046                 break;
2047             default:
2048                 errno = EACCES;
2049                 break;
2050             }
2051             return -1;
2052         }
2053         return 0;
2054     }
2055     else {
2056         int retval = 0;
2057         char tmpname[MAX_PATH+1];
2058         char dname[MAX_PATH+1];
2059         char *endname = Nullch;
2060         STRLEN tmplen = 0;
2061         DWORD from_attr, to_attr;
2062
2063         /* if oname doesn't exist, do nothing */
2064         from_attr = GetFileAttributes(oname);
2065         if (from_attr == 0xFFFFFFFF) {
2066             errno = ENOENT;
2067             return -1;
2068         }
2069
2070         /* if newname exists, rename it to a temporary name so that we
2071          * don't delete it in case oname happens to be the same file
2072          * (but perhaps accessed via a different path)
2073          */
2074         to_attr = GetFileAttributes(newname);
2075         if (to_attr != 0xFFFFFFFF) {
2076             /* if newname is a directory, we fail
2077              * XXX could overcome this with yet more convoluted logic */
2078             if (to_attr & FILE_ATTRIBUTE_DIRECTORY) {
2079                 errno = EACCES;
2080                 return -1;
2081             }
2082             tmplen = strlen(newname);
2083             strcpy(tmpname,newname);
2084             endname = tmpname+tmplen;
2085             for (; endname > tmpname ; --endname) {
2086                 if (*endname == '/' || *endname == '\\') {
2087                     *endname = '\0';
2088                     break;
2089                 }
2090             }
2091             if (endname > tmpname)
2092                 endname = strcpy(dname,tmpname);
2093             else
2094                 endname = ".";
2095
2096             /* get a temporary filename in same directory
2097              * XXX is this really the best we can do? */
2098             if (!GetTempFileName((LPCTSTR)endname, "plr", 0, tmpname)) {
2099                 errno = ENOENT;
2100                 return -1;
2101             }
2102             DeleteFile(tmpname);
2103
2104             retval = rename(newname, tmpname);
2105             if (retval != 0) {
2106                 errno = EACCES;
2107                 return retval;
2108             }
2109         }
2110
2111         /* rename oname to newname */
2112         retval = rename(oname, newname);
2113
2114         /* if we created a temporary file before ... */
2115         if (endname != Nullch) {
2116             /* ...and rename succeeded, delete temporary file/directory */
2117             if (retval == 0)
2118                 DeleteFile(tmpname);
2119             /* else restore it to what it was */
2120             else
2121                 (void)rename(tmpname, newname);
2122         }
2123         return retval;
2124     }
2125 }
2126
2127 DllExport int
2128 win32_setmode(int fd, int mode)
2129 {
2130     return setmode(fd, mode);
2131 }
2132
2133 DllExport long
2134 win32_lseek(int fd, long offset, int origin)
2135 {
2136     return lseek(fd, offset, origin);
2137 }
2138
2139 DllExport long
2140 win32_tell(int fd)
2141 {
2142     return tell(fd);
2143 }
2144
2145 DllExport int
2146 win32_open(const char *path, int flag, ...)
2147 {
2148     va_list ap;
2149     int pmode;
2150
2151     va_start(ap, flag);
2152     pmode = va_arg(ap, int);
2153     va_end(ap);
2154
2155     if (stricmp(path, "/dev/null")==0)
2156         return open("NUL", flag, pmode);
2157     return open(path,flag,pmode);
2158 }
2159
2160 DllExport int
2161 win32_close(int fd)
2162 {
2163     return close(fd);
2164 }
2165
2166 DllExport int
2167 win32_eof(int fd)
2168 {
2169     return eof(fd);
2170 }
2171
2172 DllExport int
2173 win32_dup(int fd)
2174 {
2175     return dup(fd);
2176 }
2177
2178 DllExport int
2179 win32_dup2(int fd1,int fd2)
2180 {
2181     return dup2(fd1,fd2);
2182 }
2183
2184 DllExport int
2185 win32_read(int fd, void *buf, unsigned int cnt)
2186 {
2187     return read(fd, buf, cnt);
2188 }
2189
2190 DllExport int
2191 win32_write(int fd, const void *buf, unsigned int cnt)
2192 {
2193     return write(fd, buf, cnt);
2194 }
2195
2196 DllExport int
2197 win32_mkdir(const char *dir, int mode)
2198 {
2199     return mkdir(dir); /* just ignore mode */
2200 }
2201
2202 DllExport int
2203 win32_rmdir(const char *dir)
2204 {
2205     return rmdir(dir);
2206 }
2207
2208 DllExport int
2209 win32_chdir(const char *dir)
2210 {
2211     return chdir(dir);
2212 }
2213
2214 static char *
2215 create_command_line(const char* command, const char * const *args)
2216 {
2217     int index;
2218     char *cmd, *ptr, *arg;
2219     STRLEN len = strlen(command) + 1;
2220
2221     for (index = 0; (ptr = (char*)args[index]) != NULL; ++index)
2222         len += strlen(ptr) + 1;
2223
2224     New(1310, cmd, len, char);
2225     ptr = cmd;
2226     strcpy(ptr, command);
2227
2228     for (index = 0; (arg = (char*)args[index]) != NULL; ++index) {
2229         ptr += strlen(ptr);
2230         *ptr++ = ' ';
2231         strcpy(ptr, arg);
2232     }
2233
2234     return cmd;
2235 }
2236
2237 static char *
2238 qualified_path(const char *cmd)
2239 {
2240     char *pathstr;
2241     char *fullcmd, *curfullcmd;
2242     STRLEN cmdlen = 0;
2243     int has_slash = 0;
2244
2245     if (!cmd)
2246         return Nullch;
2247     fullcmd = (char*)cmd;
2248     while (*fullcmd) {
2249         if (*fullcmd == '/' || *fullcmd == '\\')
2250             has_slash++;
2251         fullcmd++;
2252         cmdlen++;
2253     }
2254
2255     /* look in PATH */
2256     pathstr = win32_getenv("PATH");
2257     New(0, fullcmd, MAX_PATH+1, char);
2258     curfullcmd = fullcmd;
2259
2260     while (1) {
2261         DWORD res;
2262
2263         /* start by appending the name to the current prefix */
2264         strcpy(curfullcmd, cmd);
2265         curfullcmd += cmdlen;
2266
2267         /* if it doesn't end with '.', or has no extension, try adding
2268          * a trailing .exe first */
2269         if (cmd[cmdlen-1] != '.'
2270             && (cmdlen < 4 || cmd[cmdlen-4] != '.'))
2271         {
2272             strcpy(curfullcmd, ".exe");
2273             res = GetFileAttributes(fullcmd);
2274             if (res != 0xFFFFFFFF && !(res & FILE_ATTRIBUTE_DIRECTORY))
2275                 return fullcmd;
2276             *curfullcmd = '\0';
2277         }
2278
2279         /* that failed, try the bare name */
2280         res = GetFileAttributes(fullcmd);
2281         if (res != 0xFFFFFFFF && !(res & FILE_ATTRIBUTE_DIRECTORY))
2282             return fullcmd;
2283
2284         /* quit if no other path exists, or if cmd already has path */
2285         if (!pathstr || !*pathstr || has_slash)
2286             break;
2287
2288         /* skip leading semis */
2289         while (*pathstr == ';')
2290             pathstr++;
2291
2292         /* build a new prefix from scratch */
2293         curfullcmd = fullcmd;
2294         while (*pathstr && *pathstr != ';') {
2295             if (*pathstr == '"') {      /* foo;"baz;etc";bar */
2296                 pathstr++;              /* skip initial '"' */
2297                 while (*pathstr && *pathstr != '"') {
2298                     if (curfullcmd-fullcmd < MAX_PATH-cmdlen-5)
2299                         *curfullcmd++ = *pathstr;
2300                     pathstr++;
2301                 }
2302                 if (*pathstr)
2303                     pathstr++;          /* skip trailing '"' */
2304             }
2305             else {
2306                 if (curfullcmd-fullcmd < MAX_PATH-cmdlen-5)
2307                     *curfullcmd++ = *pathstr;
2308                 pathstr++;
2309             }
2310         }
2311         if (*pathstr)
2312             pathstr++;                  /* skip trailing semi */
2313         if (curfullcmd > fullcmd        /* append a dir separator */
2314             && curfullcmd[-1] != '/' && curfullcmd[-1] != '\\')
2315         {
2316             *curfullcmd++ = '\\';
2317         }
2318     }
2319 GIVE_UP:
2320     Safefree(fullcmd);
2321     return Nullch;
2322 }
2323
2324 /* XXX this needs to be made more compatible with the spawnvp()
2325  * provided by the various RTLs.  In particular, searching for
2326  * *.{com,bat,cmd} files (as done by the RTLs) is unimplemented.
2327  * This doesn't significantly affect perl itself, because we
2328  * always invoke things using PERL5SHELL if a direct attempt to
2329  * spawn the executable fails.
2330  * 
2331  * XXX splitting and rejoining the commandline between do_aspawn()
2332  * and win32_spawnvp() could also be avoided.
2333  */
2334
2335 DllExport int
2336 win32_spawnvp(int mode, const char *cmdname, const char *const *argv)
2337 {
2338 #ifdef USE_RTL_SPAWNVP
2339     return spawnvp(mode, cmdname, (char * const *)argv);
2340 #else
2341     DWORD ret;
2342     STARTUPINFO StartupInfo;
2343     PROCESS_INFORMATION ProcessInformation;
2344     DWORD create = 0;
2345
2346     char *cmd = create_command_line(cmdname, strcmp(cmdname, argv[0]) == 0
2347                                              ? &argv[1] : argv);
2348     char *fullcmd = Nullch;
2349
2350     switch(mode) {
2351     case P_NOWAIT:      /* asynch + remember result */
2352         if (w32_num_children >= MAXIMUM_WAIT_OBJECTS) {
2353             errno = EAGAIN;
2354             ret = -1;
2355             goto RETVAL;
2356         }
2357         /* FALL THROUGH */
2358     case P_WAIT:        /* synchronous execution */
2359         break;
2360     default:            /* invalid mode */
2361         errno = EINVAL;
2362         ret = -1;
2363         goto RETVAL;
2364     }
2365     memset(&StartupInfo,0,sizeof(StartupInfo));
2366     StartupInfo.cb = sizeof(StartupInfo);
2367     StartupInfo.wShowWindow = SW_SHOWDEFAULT;
2368
2369 RETRY:
2370     if (!CreateProcess(cmdname,         /* search PATH to find executable */
2371                        cmd,             /* executable, and its arguments */
2372                        NULL,            /* process attributes */
2373                        NULL,            /* thread attributes */
2374                        TRUE,            /* inherit handles */
2375                        create,          /* creation flags */
2376                        NULL,            /* inherit environment */
2377                        NULL,            /* inherit cwd */
2378                        &StartupInfo,
2379                        &ProcessInformation))
2380     {
2381         /* initial NULL argument to CreateProcess() does a PATH
2382          * search, but it always first looks in the directory
2383          * where the current process was started, which behavior
2384          * is undesirable for backward compatibility.  So we
2385          * jump through our own hoops by picking out the path
2386          * we really want it to use. */
2387         if (!fullcmd) {
2388             fullcmd = qualified_path(cmdname);
2389             if (fullcmd) {
2390                 cmdname = fullcmd;
2391                 goto RETRY;
2392             }
2393         }
2394         errno = ENOENT;
2395         ret = -1;
2396         goto RETVAL;
2397     }
2398
2399     if (mode == P_NOWAIT) {
2400         /* asynchronous spawn -- store handle, return PID */
2401         w32_child_handles[w32_num_children] = ProcessInformation.hProcess;
2402         ret = w32_child_pids[w32_num_children] = ProcessInformation.dwProcessId;
2403         ++w32_num_children;
2404     }
2405     else  {
2406         WaitForSingleObject(ProcessInformation.hProcess, INFINITE);
2407         GetExitCodeProcess(ProcessInformation.hProcess, &ret);
2408         CloseHandle(ProcessInformation.hProcess);
2409     }
2410
2411     CloseHandle(ProcessInformation.hThread);
2412 RETVAL:
2413     Safefree(cmd);
2414     Safefree(fullcmd);
2415     return (int)ret;
2416 #endif
2417 }
2418
2419 DllExport int
2420 win32_execv(const char *cmdname, const char *const *argv)
2421 {
2422     return execv(cmdname, (char *const *)argv);
2423 }
2424
2425 DllExport int
2426 win32_execvp(const char *cmdname, const char *const *argv)
2427 {
2428     return execvp(cmdname, (char *const *)argv);
2429 }
2430
2431 DllExport void
2432 win32_perror(const char *str)
2433 {
2434     perror(str);
2435 }
2436
2437 DllExport void
2438 win32_setbuf(FILE *pf, char *buf)
2439 {
2440     setbuf(pf, buf);
2441 }
2442
2443 DllExport int
2444 win32_setvbuf(FILE *pf, char *buf, int type, size_t size)
2445 {
2446     return setvbuf(pf, buf, type, size);
2447 }
2448
2449 DllExport int
2450 win32_flushall(void)
2451 {
2452     return flushall();
2453 }
2454
2455 DllExport int
2456 win32_fcloseall(void)
2457 {
2458     return fcloseall();
2459 }
2460
2461 DllExport char*
2462 win32_fgets(char *s, int n, FILE *pf)
2463 {
2464     return fgets(s, n, pf);
2465 }
2466
2467 DllExport char*
2468 win32_gets(char *s)
2469 {
2470     return gets(s);
2471 }
2472
2473 DllExport int
2474 win32_fgetc(FILE *pf)
2475 {
2476     return fgetc(pf);
2477 }
2478
2479 DllExport int
2480 win32_putc(int c, FILE *pf)
2481 {
2482     return putc(c,pf);
2483 }
2484
2485 DllExport int
2486 win32_puts(const char *s)
2487 {
2488     return puts(s);
2489 }
2490
2491 DllExport int
2492 win32_getchar(void)
2493 {
2494     return getchar();
2495 }
2496
2497 DllExport int
2498 win32_putchar(int c)
2499 {
2500     return putchar(c);
2501 }
2502
2503 #ifdef MYMALLOC
2504
2505 #ifndef USE_PERL_SBRK
2506
2507 static char *committed = NULL;
2508 static char *base      = NULL;
2509 static char *reserved  = NULL;
2510 static char *brk       = NULL;
2511 static DWORD pagesize  = 0;
2512 static DWORD allocsize = 0;
2513
2514 void *
2515 sbrk(int need)
2516 {
2517  void *result;
2518  if (!pagesize)
2519   {SYSTEM_INFO info;
2520    GetSystemInfo(&info);
2521    /* Pretend page size is larger so we don't perpetually
2522     * call the OS to commit just one page ...
2523     */
2524    pagesize = info.dwPageSize << 3;
2525    allocsize = info.dwAllocationGranularity;
2526   }
2527  /* This scheme fails eventually if request for contiguous
2528   * block is denied so reserve big blocks - this is only 
2529   * address space not memory ...
2530   */
2531  if (brk+need >= reserved)
2532   {
2533    DWORD size = 64*1024*1024;
2534    char *addr;
2535    if (committed && reserved && committed < reserved)
2536     {
2537      /* Commit last of previous chunk cannot span allocations */
2538      addr = (char *) VirtualAlloc(committed,reserved-committed,MEM_COMMIT,PAGE_READWRITE);
2539      if (addr)
2540       committed = reserved;
2541     }
2542    /* Reserve some (more) space 
2543     * Note this is a little sneaky, 1st call passes NULL as reserved
2544     * so lets system choose where we start, subsequent calls pass
2545     * the old end address so ask for a contiguous block
2546     */
2547    addr  = (char *) VirtualAlloc(reserved,size,MEM_RESERVE,PAGE_NOACCESS);
2548    if (addr)
2549     {
2550      reserved = addr+size;
2551      if (!base)
2552       base = addr;
2553      if (!committed)
2554       committed = base;
2555      if (!brk)
2556       brk = committed;
2557     }
2558    else
2559     {
2560      return (void *) -1;
2561     }
2562   }
2563  result = brk;
2564  brk += need;
2565  if (brk > committed)
2566   {
2567    DWORD size = ((brk-committed + pagesize -1)/pagesize) * pagesize;
2568    char *addr = (char *) VirtualAlloc(committed,size,MEM_COMMIT,PAGE_READWRITE);
2569    if (addr)
2570     {
2571      committed += size;
2572     }
2573    else
2574     return (void *) -1;
2575   }
2576  return result;
2577 }
2578
2579 #endif
2580 #endif
2581
2582 DllExport void*
2583 win32_malloc(size_t size)
2584 {
2585     return malloc(size);
2586 }
2587
2588 DllExport void*
2589 win32_calloc(size_t numitems, size_t size)
2590 {
2591     return calloc(numitems,size);
2592 }
2593
2594 DllExport void*
2595 win32_realloc(void *block, size_t size)
2596 {
2597     return realloc(block,size);
2598 }
2599
2600 DllExport void
2601 win32_free(void *block)
2602 {
2603     free(block);
2604 }
2605
2606
2607 int
2608 win32_open_osfhandle(long handle, int flags)
2609 {
2610     return _open_osfhandle(handle, flags);
2611 }
2612
2613 long
2614 win32_get_osfhandle(int fd)
2615 {
2616     return _get_osfhandle(fd);
2617 }
2618
2619 /*
2620  * Extras.
2621  */
2622
2623 static
2624 XS(w32_GetCwd)
2625 {
2626     dXSARGS;
2627     SV *sv = sv_newmortal();
2628     /* Make one call with zero size - return value is required size */
2629     DWORD len = GetCurrentDirectory((DWORD)0,NULL);
2630     SvUPGRADE(sv,SVt_PV);
2631     SvGROW(sv,len);
2632     SvCUR(sv) = GetCurrentDirectory((DWORD) SvLEN(sv), SvPVX(sv));
2633     /* 
2634      * If result != 0 
2635      *   then it worked, set PV valid, 
2636      *   else leave it 'undef' 
2637      */
2638     EXTEND(SP,1);
2639     if (SvCUR(sv)) {
2640         SvPOK_on(sv);
2641         ST(0) = sv;
2642         XSRETURN(1);
2643     }
2644     XSRETURN_UNDEF;
2645 }
2646
2647 static
2648 XS(w32_SetCwd)
2649 {
2650     dXSARGS;
2651     if (items != 1)
2652         croak("usage: Win32::SetCurrentDirectory($cwd)");
2653     if (SetCurrentDirectory(SvPV_nolen(ST(0))))
2654         XSRETURN_YES;
2655
2656     XSRETURN_NO;
2657 }
2658
2659 static
2660 XS(w32_GetNextAvailDrive)
2661 {
2662     dXSARGS;
2663     char ix = 'C';
2664     char root[] = "_:\\";
2665
2666     EXTEND(SP,1);
2667     while (ix <= 'Z') {
2668         root[0] = ix++;
2669         if (GetDriveType(root) == 1) {
2670             root[2] = '\0';
2671             XSRETURN_PV(root);
2672         }
2673     }
2674     XSRETURN_UNDEF;
2675 }
2676
2677 static
2678 XS(w32_GetLastError)
2679 {
2680     dXSARGS;
2681     EXTEND(SP,1);
2682     XSRETURN_IV(GetLastError());
2683 }
2684
2685 static
2686 XS(w32_SetLastError)
2687 {
2688     dXSARGS;
2689     if (items != 1)
2690         croak("usage: Win32::SetLastError($error)");
2691     SetLastError(SvIV(ST(0)));
2692     XSRETURN_EMPTY;
2693 }
2694
2695 static
2696 XS(w32_LoginName)
2697 {
2698     dXSARGS;
2699     char *name = getlogin_buffer;
2700     DWORD size = sizeof(getlogin_buffer);
2701     EXTEND(SP,1);
2702     if (GetUserName(name,&size)) {
2703         /* size includes NULL */
2704         ST(0) = sv_2mortal(newSVpvn(name,size-1));
2705         XSRETURN(1);
2706     }
2707     XSRETURN_UNDEF;
2708 }
2709
2710 static
2711 XS(w32_NodeName)
2712 {
2713     dXSARGS;
2714     char name[MAX_COMPUTERNAME_LENGTH+1];
2715     DWORD size = sizeof(name);
2716     EXTEND(SP,1);
2717     if (GetComputerName(name,&size)) {
2718         /* size does NOT include NULL :-( */
2719         ST(0) = sv_2mortal(newSVpvn(name,size));
2720         XSRETURN(1);
2721     }
2722     XSRETURN_UNDEF;
2723 }
2724
2725
2726 static
2727 XS(w32_DomainName)
2728 {
2729     dXSARGS;
2730 #ifndef HAS_NETWKSTAGETINFO
2731     /* mingw32 (and Win95) don't have NetWksta*(), so do it the old way */
2732     char name[256];
2733     DWORD size = sizeof(name);
2734     EXTEND(SP,1);
2735     if (GetUserName(name,&size)) {
2736         char sid[1024];
2737         DWORD sidlen = sizeof(sid);
2738         char dname[256];
2739         DWORD dnamelen = sizeof(dname);
2740         SID_NAME_USE snu;
2741         if (LookupAccountName(NULL, name, (PSID)&sid, &sidlen,
2742                               dname, &dnamelen, &snu)) {
2743             XSRETURN_PV(dname);         /* all that for this */
2744         }
2745     }
2746 #else
2747     /* this way is more reliable, in case user has a local account.
2748      * XXX need dynamic binding of netapi32.dll symbols or this will fail on
2749      * Win95. Probably makes more sense to move it into libwin32. */
2750     char dname[256];
2751     DWORD dnamelen = sizeof(dname);
2752     PWKSTA_INFO_100 pwi;
2753     EXTEND(SP,1);
2754     if (NERR_Success == NetWkstaGetInfo(NULL, 100, (LPBYTE*)&pwi)) {
2755         if (pwi->wki100_langroup && *(pwi->wki100_langroup)) {
2756             WideCharToMultiByte(CP_ACP, NULL, pwi->wki100_langroup,
2757                                 -1, (LPSTR)dname, dnamelen, NULL, NULL);
2758         }
2759         else {
2760             WideCharToMultiByte(CP_ACP, NULL, pwi->wki100_computername,
2761                                 -1, (LPSTR)dname, dnamelen, NULL, NULL);
2762         }
2763         NetApiBufferFree(pwi);
2764         XSRETURN_PV(dname);
2765     }
2766 #endif
2767     XSRETURN_UNDEF;
2768 }
2769
2770 static
2771 XS(w32_FsType)
2772 {
2773     dXSARGS;
2774     char fsname[256];
2775     DWORD flags, filecomplen;
2776     if (GetVolumeInformation(NULL, NULL, 0, NULL, &filecomplen,
2777                          &flags, fsname, sizeof(fsname))) {
2778         if (GIMME_V == G_ARRAY) {
2779             XPUSHs(sv_2mortal(newSVpvn(fsname,strlen(fsname))));
2780             XPUSHs(sv_2mortal(newSViv(flags)));
2781             XPUSHs(sv_2mortal(newSViv(filecomplen)));
2782             PUTBACK;
2783             return;
2784         }
2785         EXTEND(SP,1);
2786         XSRETURN_PV(fsname);
2787     }
2788     XSRETURN_EMPTY;
2789 }
2790
2791 static
2792 XS(w32_GetOSVersion)
2793 {
2794     dXSARGS;
2795     OSVERSIONINFO osver;
2796
2797     osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
2798     if (GetVersionEx(&osver)) {
2799         XPUSHs(newSVpvn(osver.szCSDVersion, strlen(osver.szCSDVersion)));
2800         XPUSHs(newSViv(osver.dwMajorVersion));
2801         XPUSHs(newSViv(osver.dwMinorVersion));
2802         XPUSHs(newSViv(osver.dwBuildNumber));
2803         XPUSHs(newSViv(osver.dwPlatformId));
2804         PUTBACK;
2805         return;
2806     }
2807     XSRETURN_EMPTY;
2808 }
2809
2810 static
2811 XS(w32_IsWinNT)
2812 {
2813     dXSARGS;
2814     EXTEND(SP,1);
2815     XSRETURN_IV(IsWinNT());
2816 }
2817
2818 static
2819 XS(w32_IsWin95)
2820 {
2821     dXSARGS;
2822     EXTEND(SP,1);
2823     XSRETURN_IV(IsWin95());
2824 }
2825
2826 static
2827 XS(w32_FormatMessage)
2828 {
2829     dXSARGS;
2830     DWORD source = 0;
2831     char msgbuf[1024];
2832
2833     if (items != 1)
2834         croak("usage: Win32::FormatMessage($errno)");
2835
2836     if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
2837                       &source, SvIV(ST(0)), 0,
2838                       msgbuf, sizeof(msgbuf)-1, NULL))
2839         XSRETURN_PV(msgbuf);
2840
2841     XSRETURN_UNDEF;
2842 }
2843
2844 static
2845 XS(w32_Spawn)
2846 {
2847     dXSARGS;
2848     char *cmd, *args;
2849     PROCESS_INFORMATION stProcInfo;
2850     STARTUPINFO stStartInfo;
2851     BOOL bSuccess = FALSE;
2852
2853     if (items != 3)
2854         croak("usage: Win32::Spawn($cmdName, $args, $PID)");
2855
2856     cmd = SvPV_nolen(ST(0));
2857     args = SvPV_nolen(ST(1));
2858
2859     memset(&stStartInfo, 0, sizeof(stStartInfo));   /* Clear the block */
2860     stStartInfo.cb = sizeof(stStartInfo);           /* Set the structure size */
2861     stStartInfo.dwFlags = STARTF_USESHOWWINDOW;     /* Enable wShowWindow control */
2862     stStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;   /* Start min (normal) */
2863
2864     if (CreateProcess(
2865                 cmd,                    /* Image path */
2866                 args,                   /* Arguments for command line */
2867                 NULL,                   /* Default process security */
2868                 NULL,                   /* Default thread security */
2869                 FALSE,                  /* Must be TRUE to use std handles */
2870                 NORMAL_PRIORITY_CLASS,  /* No special scheduling */
2871                 NULL,                   /* Inherit our environment block */
2872                 NULL,                   /* Inherit our currrent directory */
2873                 &stStartInfo,           /* -> Startup info */
2874                 &stProcInfo))           /* <- Process info (if OK) */
2875     {
2876         CloseHandle(stProcInfo.hThread);/* library source code does this. */
2877         sv_setiv(ST(2), stProcInfo.dwProcessId);
2878         bSuccess = TRUE;
2879     }
2880     XSRETURN_IV(bSuccess);
2881 }
2882
2883 static
2884 XS(w32_GetTickCount)
2885 {
2886     dXSARGS;
2887     DWORD msec = GetTickCount();
2888     EXTEND(SP,1);
2889     if ((IV)msec > 0)
2890         XSRETURN_IV(msec);
2891     XSRETURN_NV(msec);
2892 }
2893
2894 static
2895 XS(w32_GetShortPathName)
2896 {
2897     dXSARGS;
2898     SV *shortpath;
2899     DWORD len;
2900
2901     if (items != 1)
2902         croak("usage: Win32::GetShortPathName($longPathName)");
2903
2904     shortpath = sv_mortalcopy(ST(0));
2905     SvUPGRADE(shortpath, SVt_PV);
2906     /* src == target is allowed */
2907     do {
2908         len = GetShortPathName(SvPVX(shortpath),
2909                                SvPVX(shortpath),
2910                                SvLEN(shortpath));
2911     } while (len >= SvLEN(shortpath) && sv_grow(shortpath,len+1));
2912     if (len) {
2913         SvCUR_set(shortpath,len);
2914         ST(0) = shortpath;
2915         XSRETURN(1);
2916     }
2917     XSRETURN_UNDEF;
2918 }
2919
2920 static
2921 XS(w32_GetFullPathName)
2922 {
2923     dXSARGS;
2924     SV *filename;
2925     SV *fullpath;
2926     char *filepart;
2927     DWORD len;
2928
2929     if (items != 1)
2930         croak("usage: Win32::GetFullPathName($filename)");
2931
2932     filename = ST(0);
2933     fullpath = sv_mortalcopy(filename);
2934     SvUPGRADE(fullpath, SVt_PV);
2935     do {
2936         len = GetFullPathName(SvPVX(filename),
2937                               SvLEN(fullpath),
2938                               SvPVX(fullpath),
2939                               &filepart);
2940     } while (len >= SvLEN(fullpath) && sv_grow(fullpath,len+1));
2941     if (len) {
2942         if (GIMME_V == G_ARRAY) {
2943             EXTEND(SP,1);
2944             XST_mPV(1,filepart);
2945             len = filepart - SvPVX(fullpath);
2946             items = 2;
2947         }
2948         SvCUR_set(fullpath,len);
2949         ST(0) = fullpath;
2950         XSRETURN(items);
2951     }
2952     XSRETURN_EMPTY;
2953 }
2954
2955 static
2956 XS(w32_GetLongPathName)
2957 {
2958     dXSARGS;
2959     SV *path;
2960     char tmpbuf[MAX_PATH+1];
2961     char *pathstr;
2962     STRLEN len;
2963
2964     if (items != 1)
2965         croak("usage: Win32::GetLongPathName($pathname)");
2966
2967     path = ST(0);
2968     pathstr = SvPV(path,len);
2969     strcpy(tmpbuf, pathstr);
2970     pathstr = win32_longpath(tmpbuf);
2971     if (pathstr) {
2972         ST(0) = sv_2mortal(newSVpvn(pathstr, strlen(pathstr)));
2973         XSRETURN(1);
2974     }
2975     XSRETURN_EMPTY;
2976 }
2977
2978 static
2979 XS(w32_Sleep)
2980 {
2981     dXSARGS;
2982     if (items != 1)
2983         croak("usage: Win32::Sleep($milliseconds)");
2984     Sleep(SvIV(ST(0)));
2985     XSRETURN_YES;
2986 }
2987
2988 static
2989 XS(w32_CopyFile)
2990 {
2991     dXSARGS;
2992     if (items != 3)
2993         croak("usage: Win32::CopyFile($from, $to, $overwrite)");
2994     if (CopyFile(SvPV_nolen(ST(0)), SvPV_nolen(ST(1)), !SvTRUE(ST(2))))
2995         XSRETURN_YES;
2996     XSRETURN_NO;
2997 }
2998
2999 void
3000 Perl_init_os_extras()
3001 {
3002     char *file = __FILE__;
3003     dXSUB_SYS;
3004
3005     w32_perlshell_tokens = Nullch;
3006     w32_perlshell_items = -1;
3007     w32_fdpid = newAV();                /* XXX needs to be in Perl_win32_init()? */
3008     New(1313, w32_children, 1, child_tab);
3009     w32_num_children = 0;
3010
3011     /* these names are Activeware compatible */
3012     newXS("Win32::GetCwd", w32_GetCwd, file);
3013     newXS("Win32::SetCwd", w32_SetCwd, file);
3014     newXS("Win32::GetNextAvailDrive", w32_GetNextAvailDrive, file);
3015     newXS("Win32::GetLastError", w32_GetLastError, file);
3016     newXS("Win32::SetLastError", w32_SetLastError, file);
3017     newXS("Win32::LoginName", w32_LoginName, file);
3018     newXS("Win32::NodeName", w32_NodeName, file);
3019     newXS("Win32::DomainName", w32_DomainName, file);
3020     newXS("Win32::FsType", w32_FsType, file);
3021     newXS("Win32::GetOSVersion", w32_GetOSVersion, file);
3022     newXS("Win32::IsWinNT", w32_IsWinNT, file);
3023     newXS("Win32::IsWin95", w32_IsWin95, file);
3024     newXS("Win32::FormatMessage", w32_FormatMessage, file);
3025     newXS("Win32::Spawn", w32_Spawn, file);
3026     newXS("Win32::GetTickCount", w32_GetTickCount, file);
3027     newXS("Win32::GetShortPathName", w32_GetShortPathName, file);
3028     newXS("Win32::GetFullPathName", w32_GetFullPathName, file);
3029     newXS("Win32::GetLongPathName", w32_GetLongPathName, file);
3030     newXS("Win32::CopyFile", w32_CopyFile, file);
3031     newXS("Win32::Sleep", w32_Sleep, file);
3032
3033     /* XXX Bloat Alert! The following Activeware preloads really
3034      * ought to be part of Win32::Sys::*, so they're not included
3035      * here.
3036      */
3037     /* LookupAccountName
3038      * LookupAccountSID
3039      * InitiateSystemShutdown
3040      * AbortSystemShutdown
3041      * ExpandEnvrironmentStrings
3042      */
3043 }
3044
3045 void
3046 Perl_win32_init(int *argcp, char ***argvp)
3047 {
3048     /* Disable floating point errors, Perl will trap the ones we
3049      * care about.  VC++ RTL defaults to switching these off
3050      * already, but the Borland RTL doesn't.  Since we don't
3051      * want to be at the vendor's whim on the default, we set
3052      * it explicitly here.
3053      */
3054 #if !defined(_ALPHA_) && !defined(__GNUC__)
3055     _control87(MCW_EM, MCW_EM);
3056 #endif
3057     MALLOC_INIT;
3058 }
3059
3060 #ifdef USE_BINMODE_SCRIPTS
3061
3062 void
3063 win32_strip_return(SV *sv)
3064 {
3065  char *s = SvPVX(sv);
3066  char *e = s+SvCUR(sv);
3067  char *d = s;
3068  while (s < e)
3069   {
3070    if (*s == '\r' && s[1] == '\n')
3071     {
3072      *d++ = '\n';
3073      s += 2;
3074     }
3075    else 
3076     {
3077      *d++ = *s++;
3078     }   
3079   }
3080  SvCUR_set(sv,d-SvPVX(sv)); 
3081 }
3082
3083 #endif