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