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