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