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