This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
9a0f910b42654d8826fe3893c906b9818f31f961
[perl5.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 #include <windows.h>
15
16 /* #include "config.h" */
17
18 #define PERLIO_NOT_STDIO 0 
19 #if !defined(PERLIO_IS_STDIO) && !defined(USE_SFIO)
20 #define PerlIO FILE
21 #endif
22
23 #include "EXTERN.h"
24 #include "perl.h"
25 #include <fcntl.h>
26 #include <sys/stat.h>
27 #include <assert.h>
28 #include <string.h>
29 #include <stdarg.h>
30
31 #define CROAK croak
32 #define WARN warn
33
34 static DWORD IdOS(void);
35
36 extern WIN32_IOSUBSYSTEM        win32stdio;
37 __declspec(thread) PWIN32_IOSUBSYSTEM   pIOSubSystem = &win32stdio;
38 /*__declspec(thread) PWIN32_IOSUBSYSTEM pIOSubSystem = NULL;*/
39
40 BOOL  ProbeEnv = FALSE;
41 DWORD Win32System = (DWORD)-1;
42 char  szShellPath[MAX_PATH+1];
43 char  szPerlLibRoot[MAX_PATH+1];
44 HANDLE PerlDllHandle = INVALID_HANDLE_VALUE;
45
46 int 
47 IsWin95(void) {
48     return (IdOS() == VER_PLATFORM_WIN32_WINDOWS);
49 }
50
51 int
52 IsWinNT(void) {
53     return (IdOS() == VER_PLATFORM_WIN32_NT);
54 }
55
56 void *
57 SetIOSubSystem(void *p)
58 {
59     PWIN32_IOSUBSYSTEM old = pIOSubSystem;
60     if (p) {
61         PWIN32_IOSUBSYSTEM pio = (PWIN32_IOSUBSYSTEM)p;
62         if (pio->signature_begin == 12345678L
63             && pio->signature_end == 87654321L) {
64             pIOSubSystem = pio;
65         }
66     }
67     else {
68         pIOSubSystem = &win32stdio;
69     }
70     return old;
71 }
72
73 char *
74 win32PerlLibPath(void)
75 {
76     char *end;
77     GetModuleFileName((PerlDllHandle == INVALID_HANDLE_VALUE) 
78                       ? GetModuleHandle(NULL)
79                       : PerlDllHandle,
80                       szPerlLibRoot, 
81                       sizeof(szPerlLibRoot));
82
83     *(end = strrchr(szPerlLibRoot, '\\')) = '\0';
84     if (stricmp(end-4,"\\bin") == 0)
85      end -= 4;
86     strcpy(end,"\\lib");
87     return (szPerlLibRoot);
88 }
89
90 BOOL
91 HasRedirection(char *ptr)
92 {
93     int inquote = 0;
94     char quote = '\0';
95
96     /*
97      * Scan string looking for redirection (< or >) or pipe
98      * characters (|) that are not in a quoted string
99      */
100     while(*ptr) {
101         switch(*ptr) {
102         case '\'':
103         case '\"':
104             if(inquote) {
105                 if(quote == *ptr) {
106                     inquote = 0;
107                     quote = '\0';
108                 }
109             }
110             else {
111                 quote = *ptr;
112                 inquote++;
113             }
114             break;
115         case '>':
116         case '<':
117         case '|':
118             if(!inquote)
119                 return TRUE;
120         default:
121             break;
122         }
123         ++ptr;
124     }
125     return FALSE;
126 }
127
128 /* since the current process environment is being updated in util.c
129  * the library functions will get the correct environment
130  */
131 PerlIO *
132 my_popen(char *cmd, char *mode)
133 {
134 #ifdef FIXCMD
135 #define fixcmd(x)       {                                       \
136                             char *pspace = strchr((x),' ');     \
137                             if (pspace) {                       \
138                                 char *p = (x);                  \
139                                 while (p < pspace) {            \
140                                     if (*p == '/')              \
141                                         *p = '\\';              \
142                                     p++;                        \
143                                 }                               \
144                             }                                   \
145                         }
146 #else
147 #define fixcmd(x)
148 #endif
149
150 #if 1
151 /* was #ifndef PERLDLL, but the #else stuff doesn't work on NT
152  * GSAR 97/03/13
153  */
154     fixcmd(cmd);
155     return win32_popen(cmd, mode);
156 #else
157 /*
158  * There seems to be some problems for the _popen call in a DLL
159  * this trick at the moment seems to work but it is never test
160  * on NT yet
161  *
162  */ 
163 #       ifdef __cplusplus
164 #define EXT_C_FUNC      extern "C"
165 #       else
166 #define EXT_C_FUNC      extern
167 #       endif
168
169     EXT_C_FUNC int __cdecl _set_osfhnd(int fh, long value);
170     EXT_C_FUNC void __cdecl _lock_fhandle(int);
171     EXT_C_FUNC void __cdecl _unlock_fhandle(int);
172
173     BOOL        fSuccess;
174     PerlIO      *pf;            /* to store the _popen return value */
175     int         tm = 0;         /* flag indicating tDllExport or binary mode */
176     int         fhNeeded, fhInherited, fhDup;
177     int         ineeded, iinherited;
178     DWORD       dwDup;
179     int         phdls[2];       /* I/O handles for pipe */
180     HANDLE      hPIn, hPOut, hPErr,
181                 hSaveStdin, hSaveStdout, hSaveStderr,
182                 hPNeeded, hPInherited, hPDuped;
183      
184     /* first check for errors in the arguments */
185     if ( (cmd == NULL) || (mode == NULL)
186          || ((*mode != 'w') && (*mode != _T('r'))) )
187         goto error1;
188
189     if ( *(mode + 1) == _T('t') )
190         tm = _O_TEXT;
191     else if ( *(mode + 1) == _T('b') )
192         tm = _O_BINARY;
193     else
194         tm = (*mode == 'w' ? _O_BINARY : _O_TEXT);
195
196
197     fixcmd(cmd);
198     if (&win32stdio != pIOSubSystem)
199         return win32_popen(cmd, mode);
200
201 #ifdef EFG
202     if ( _pipe( phdls, 1024, tm ) == -1 )
203 #else
204     if ( win32_pipe( phdls, 1024, tm ) == -1 )
205 #endif
206         goto error1;
207
208     /* save the current situation */
209     hSaveStdin = GetStdHandle(STD_INPUT_HANDLE); 
210     hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE); 
211     hSaveStderr = GetStdHandle(STD_ERROR_HANDLE); 
212
213     if (*mode == _T('w')) {
214         ineeded = 1;
215         dwDup   = STD_INPUT_HANDLE;
216         iinherited = 0;
217     }
218     else {
219         ineeded = 0;
220         dwDup   = STD_OUTPUT_HANDLE;
221         iinherited = 1;
222     }
223
224     fhNeeded = phdls[ineeded];
225     fhInherited = phdls[iinherited];
226
227     fSuccess = DuplicateHandle(GetCurrentProcess(), 
228                                (HANDLE) stolen_get_osfhandle(fhNeeded), 
229                                GetCurrentProcess(), 
230                                &hPNeeded, 
231                                0, 
232                                FALSE,       /* not inherited */ 
233                                DUPLICATE_SAME_ACCESS); 
234
235     if (!fSuccess)
236         goto error2;
237
238     fhDup = stolen_open_osfhandle((long) hPNeeded, tm);
239     win32_dup2(fhDup, fhNeeded);
240     win32_close(fhDup);
241
242 #ifdef AAA
243     /* Close the Out pipe, child won't need it */
244     hPDuped = (HANDLE) stolen_get_osfhandle(fhNeeded);
245
246     _lock_fhandle(fhNeeded);
247     _set_osfhnd(fhNeeded, (long)hPNeeded); /* put in ours duplicated one */
248     _unlock_fhandle(fhNeeded);
249
250     CloseHandle(hPDuped);       /* close the handle first */
251 #endif
252
253     if (!SetStdHandle(dwDup, (HANDLE) stolen_get_osfhandle(fhInherited)))
254         goto error2;
255
256     /*
257      * make sure the child see the same stderr as the calling program
258      */
259     if (!SetStdHandle(STD_ERROR_HANDLE,
260                       (HANDLE)stolen_get_osfhandle(win32_fileno(win32_stderr()))))
261         goto error2;
262
263     pf = win32_popen(cmd, mode);        /* ask _popen to do the job */
264
265     /* restore to where we were */
266     SetStdHandle(STD_INPUT_HANDLE, hSaveStdin);
267     SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout);
268     SetStdHandle(STD_ERROR_HANDLE, hSaveStderr);
269
270     /* we don't need it any more, that's for the child */
271     win32_close(fhInherited);
272
273     if (NULL == pf) {
274         /* something wrong */
275         win32_close(fhNeeded);
276         goto error1;
277     }
278     else {
279         /*
280          * here we steal the file handle in pf and stuff ours in
281          */
282         win32_dup2(fhNeeded, win32_fileno(pf));
283         win32_close(fhNeeded);
284     }
285     return (pf);
286
287 error2:
288     win32_close(fhNeeded);
289     win32_close(fhInherited);
290
291 error1:
292     return (NULL);
293
294 #endif
295 }
296
297 long
298 my_pclose(PerlIO *fp)
299 {
300     return win32_pclose(fp);
301 }
302
303 static DWORD
304 IdOS(void)
305 {
306     static OSVERSIONINFO osver;
307
308     if (osver.dwPlatformId != Win32System) {
309         memset(&osver, 0, sizeof(OSVERSIONINFO));
310         osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
311         GetVersionEx(&osver);
312         Win32System = osver.dwPlatformId;
313     }
314     return (Win32System);
315 }
316
317 static char *
318 GetShell(void)
319 {
320     static char* szWin95ShellEntry = "Win95Shell";
321     static char* szWin95DefaultShell = "Cmd32.exe";
322     static char* szWinNTDefaultShell = "cmd.exe";
323    
324     if (!ProbeEnv) {
325         ProbeEnv = TRUE;
326         if (IsWin95()) {
327             strcpy(szShellPath, szWin95DefaultShell);
328         }
329         else {
330             strcpy(szShellPath, szWinNTDefaultShell);
331         }
332     }
333     return szShellPath;
334 }
335
336 int
337 do_aspawn(void* really, void** mark, void** arglast)
338 {
339     char **argv;
340     char *strPtr;
341     char *cmd;
342     int status;
343     unsigned int length;
344     int index = 0;
345     SV *sv = (SV*)really;
346     SV** pSv = (SV**)mark;
347
348     New(1310, argv, (arglast - mark) + 4, char*);
349
350     if(sv != Nullsv) {
351         cmd = SvPV(sv, length);
352     }
353     else {
354         argv[index++] = cmd = GetShell();
355         argv[index++] = "/x";   /* always enable command extensions */
356         argv[index++] = "/c";
357     }
358
359     while(++pSv <= (SV**)arglast) {
360         sv = *pSv;
361         strPtr = SvPV(sv, length);
362         if(strPtr != NULL && *strPtr != '\0')
363             argv[index++] = strPtr;
364     }
365     argv[index++] = 0;
366    
367     status = win32_spawnvpe(P_WAIT, cmd, (const char* const*)argv,
368                             (const char* const*)environ);
369
370     Safefree(argv);
371
372     if (status < 0) {
373         if (dowarn)
374             warn("Can't spawn \"%s\": %s", cmd, strerror(errno));
375         status = 255 << 8;
376     }
377     return (status);
378 }
379
380 int
381 do_spawn(char *cmd)
382 {
383     char **a;
384     char *s;
385     char **argv;
386     int status = -1;
387     BOOL needToTry = TRUE;
388     char *shell, *cmd2;
389
390     /* save an extra exec if possible */
391     shell = GetShell();
392
393     /* see if there are shell metacharacters in it */
394     if(!HasRedirection(cmd)) {
395         New(1301,argv, strlen(cmd) / 2 + 2, char*);
396         New(1302,cmd2, strlen(cmd) + 1, char);
397         strcpy(cmd2, cmd);
398         a = argv;
399         for (s = cmd2; *s;) {
400             while (*s && isspace(*s))
401                 s++;
402             if (*s)
403                 *(a++) = s;
404             while(*s && !isspace(*s))
405                 s++;
406             if(*s)
407                 *s++ = '\0';
408         }
409         *a = Nullch;
410         if(argv[0]) {
411             status = win32_spawnvpe(P_WAIT,
412                                     argv[0],
413                                     (const char* const*)argv,
414                                     (const char* const*)environ);
415             if(status != -1 || errno == 0)
416                 needToTry = FALSE;
417         }
418         Safefree(argv);
419         Safefree(cmd2);
420     }
421     if(needToTry) {
422         status = win32_spawnle(P_WAIT,
423                                shell,
424                                shell,
425                                "/x",
426                                "/c", cmd, (char*)0, environ);
427     }
428     if (status < 0) {
429         if (dowarn)
430             warn("Can't spawn \"%s\": %s", needToTry ? shell : argv[0],
431                  strerror(errno));
432         status = 255 << 8;
433     }
434     return (status);
435 }
436
437
438 #define PATHLEN 1024
439
440 /* The idea here is to read all the directory names into a string table
441  * (separated by nulls) and when one of the other dir functions is called
442  * return the pointer to the current file name.
443  */
444 DIR *
445 opendir(char *filename)
446 {
447     DIR            *p;
448     long            len;
449     long            idx;
450     char            scannamespc[PATHLEN];
451     char       *scanname = scannamespc;
452     struct stat     sbuf;
453     WIN32_FIND_DATA FindData;
454     HANDLE          fh;
455 /*  char            root[_MAX_PATH];*/
456 /*  char            volname[_MAX_PATH];*/
457 /*  DWORD           serial, maxname, flags;*/
458 /*  BOOL            downcase;*/
459 /*  char           *dummy;*/
460
461     /* check to see if filename is a directory */
462     if(stat(filename, &sbuf) < 0 || sbuf.st_mode & _S_IFDIR == 0) {
463         return NULL;
464     }
465
466     /* get the file system characteristics */
467 /*  if(GetFullPathName(filename, MAX_PATH, root, &dummy)) {
468  *      if(dummy = strchr(root, '\\'))
469  *          *++dummy = '\0';
470  *      if(GetVolumeInformation(root, volname, MAX_PATH, &serial,
471  *                              &maxname, &flags, 0, 0)) {
472  *          downcase = !(flags & FS_CASE_IS_PRESERVED);
473  *      }
474  *  }
475  *  else {
476  *      downcase = TRUE;
477  *  }
478  */
479     /* Get us a DIR structure */
480     Newz(1303, p, 1, DIR);
481     if(p == NULL)
482         return NULL;
483
484     /* Create the search pattern */
485     strcpy(scanname, filename);
486
487     if(index("/\\", *(scanname + strlen(scanname) - 1)) == NULL)
488         strcat(scanname, "/*");
489     else
490         strcat(scanname, "*");
491
492     /* do the FindFirstFile call */
493     fh = FindFirstFile(scanname, &FindData);
494     if(fh == INVALID_HANDLE_VALUE) {
495         return NULL;
496     }
497
498     /* now allocate the first part of the string table for
499      * the filenames that we find.
500      */
501     idx = strlen(FindData.cFileName)+1;
502     New(1304, p->start, idx, char);
503     if(p->start == NULL) {
504         CROAK("opendir: malloc failed!\n");
505     }
506     strcpy(p->start, FindData.cFileName);
507 /*  if(downcase)
508  *      strlwr(p->start);
509  */
510     p->nfiles++;
511
512     /* loop finding all the files that match the wildcard
513      * (which should be all of them in this directory!).
514      * the variable idx should point one past the null terminator
515      * of the previous string found.
516      */
517     while (FindNextFile(fh, &FindData)) {
518         len = strlen(FindData.cFileName);
519         /* bump the string table size by enough for the
520          * new name and it's null terminator
521          */
522         Renew(p->start, idx+len+1, char);
523         if(p->start == NULL) {
524             CROAK("opendir: malloc failed!\n");
525         }
526         strcpy(&p->start[idx], FindData.cFileName);
527 /*      if (downcase) 
528  *          strlwr(&p->start[idx]);
529  */
530                 p->nfiles++;
531                 idx += len+1;
532         }
533         FindClose(fh);
534         p->size = idx;
535         p->curr = p->start;
536         return p;
537 }
538
539
540 /* Readdir just returns the current string pointer and bumps the
541  * string pointer to the nDllExport entry.
542  */
543 struct direct *
544 readdir(DIR *dirp)
545 {
546     int         len;
547     static int  dummy = 0;
548
549     if (dirp->curr) {
550         /* first set up the structure to return */
551         len = strlen(dirp->curr);
552         strcpy(dirp->dirstr.d_name, dirp->curr);
553         dirp->dirstr.d_namlen = len;
554
555         /* Fake an inode */
556         dirp->dirstr.d_ino = dummy++;
557
558         /* Now set up for the nDllExport call to readdir */
559         dirp->curr += len + 1;
560         if (dirp->curr >= (dirp->start + dirp->size)) {
561             dirp->curr = NULL;
562         }
563
564         return &(dirp->dirstr);
565     } 
566     else
567         return NULL;
568 }
569
570 /* Telldir returns the current string pointer position */
571 long
572 telldir(DIR *dirp)
573 {
574     return (long) dirp->curr;
575 }
576
577
578 /* Seekdir moves the string pointer to a previously saved position
579  *(Saved by telldir).
580  */
581 void
582 seekdir(DIR *dirp, long loc)
583 {
584     dirp->curr = (char *)loc;
585 }
586
587 /* Rewinddir resets the string pointer to the start */
588 void
589 rewinddir(DIR *dirp)
590 {
591     dirp->curr = dirp->start;
592 }
593
594 /* free the memory allocated by opendir */
595 int
596 closedir(DIR *dirp)
597 {
598     Safefree(dirp->start);
599     Safefree(dirp);
600     return 1;
601 }
602
603
604 /*
605  * various stubs
606  */
607
608
609 /* Ownership
610  *
611  * Just pretend that everyone is a superuser. NT will let us know if
612  * we don\'t really have permission to do something.
613  */
614
615 #define ROOT_UID    ((uid_t)0)
616 #define ROOT_GID    ((gid_t)0)
617
618 uid_t
619 getuid(void)
620 {
621     return ROOT_UID;
622 }
623
624 uid_t
625 geteuid(void)
626 {
627     return ROOT_UID;
628 }
629
630 gid_t
631 getgid(void)
632 {
633     return ROOT_GID;
634 }
635
636 gid_t
637 getegid(void)
638 {
639     return ROOT_GID;
640 }
641
642 int
643 setuid(uid_t uid)
644
645     return (uid == ROOT_UID ? 0 : -1);
646 }
647
648 int
649 setgid(gid_t gid)
650 {
651     return (gid == ROOT_GID ? 0 : -1);
652 }
653
654 /*
655  * pretended kill
656  */
657 int
658 kill(int pid, int sig)
659 {
660     HANDLE hProcess= OpenProcess(PROCESS_ALL_ACCESS, TRUE, pid);
661
662     if (hProcess == NULL) {
663         CROAK("kill process failed!\n");
664     }
665     else {
666         if (!TerminateProcess(hProcess, sig))
667             CROAK("kill process failed!\n");
668         CloseHandle(hProcess);
669     }
670     return 0;
671 }
672       
673 /*
674  * File system stuff
675  */
676
677 int
678 ioctl(int i, unsigned int u, char *data)
679 {
680     CROAK("ioctl not implemented!\n");
681     return -1;
682 }
683
684 unsigned int
685 sleep(unsigned int t)
686 {
687     Sleep(t*1000);
688     return 0;
689 }
690
691
692 #undef rename
693
694 int
695 myrename(char *OldFileName, char *newname)
696 {
697     if(_access(newname, 0) != -1) {     /* file exists */
698         _unlink(newname);
699     }
700     return rename(OldFileName, newname);
701 }
702
703
704 DllExport int
705 win32_stat(const char *path, struct stat *buffer)
706 {
707     char                t[MAX_PATH]; 
708     const char  *p = path;
709     int         l = strlen(path);
710
711     if (l > 1) {
712         switch(path[l - 1]) {
713         case '\\':
714         case '/':
715             if (path[l - 2] != ':') {
716                 strncpy(t, path, l - 1);
717                 t[l - 1] = 0;
718                 p = t;
719             };
720         }
721     }
722     return stat(p, buffer);
723 }
724
725 #undef times
726 int
727 mytimes(struct tms *timebuf)
728 {
729     clock_t     t = clock();
730     timebuf->tms_utime = t;
731     timebuf->tms_stime = 0;
732     timebuf->tms_cutime = 0;
733     timebuf->tms_cstime = 0;
734
735     return 0;
736 }
737
738 #undef alarm
739 unsigned int
740 myalarm(unsigned int sec)
741 {
742     /* we warn the usuage of alarm function */
743     if (sec != 0)
744         WARN("dummy function alarm called, program might not function as expected\n");
745     return 0;
746 }
747
748 /*
749  *  redirected io subsystem for all XS modules
750  *
751  */
752
753 DllExport int *
754 win32_errno(void)
755 {
756     return (pIOSubSystem->pfnerrno());
757 }
758
759 DllExport char ***
760 win32_environ(void)
761 {
762     return (pIOSubSystem->pfnenviron());
763 }
764
765 /* the rest are the remapped stdio routines */
766 DllExport FILE *
767 win32_stderr(void)
768 {
769     return (pIOSubSystem->pfnstderr());
770 }
771
772 DllExport FILE *
773 win32_stdin(void)
774 {
775     return (pIOSubSystem->pfnstdin());
776 }
777
778 DllExport FILE *
779 win32_stdout()
780 {
781     return (pIOSubSystem->pfnstdout());
782 }
783
784 DllExport int
785 win32_ferror(FILE *fp)
786 {
787     return (pIOSubSystem->pfnferror(fp));
788 }
789
790
791 DllExport int
792 win32_feof(FILE *fp)
793 {
794     return (pIOSubSystem->pfnfeof(fp));
795 }
796
797 /*
798  * Since the errors returned by the socket error function 
799  * WSAGetLastError() are not known by the library routine strerror
800  * we have to roll our own.
801  */
802
803 __declspec(thread) char strerror_buffer[512];
804
805 DllExport char *
806 win32_strerror(int e) 
807 {
808     extern int sys_nerr;
809     DWORD source = 0;
810
811     if(e < 0 || e > sys_nerr) {
812         if(e < 0)
813             e = GetLastError();
814
815         if(FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, &source, e, 0,
816                          strerror_buffer, sizeof(strerror_buffer), NULL) == 0) 
817             strcpy(strerror_buffer, "Unknown Error");
818
819         return strerror_buffer;
820     }
821     return pIOSubSystem->pfnstrerror(e);
822 }
823
824 DllExport int
825 win32_fprintf(FILE *fp, const char *format, ...)
826 {
827     va_list marker;
828     va_start(marker, format);     /* Initialize variable arguments. */
829
830     return (pIOSubSystem->pfnvfprintf(fp, format, marker));
831 }
832
833 DllExport int
834 win32_printf(const char *format, ...)
835 {
836     va_list marker;
837     va_start(marker, format);     /* Initialize variable arguments. */
838
839     return (pIOSubSystem->pfnvprintf(format, marker));
840 }
841
842 DllExport int
843 win32_vfprintf(FILE *fp, const char *format, va_list args)
844 {
845     return (pIOSubSystem->pfnvfprintf(fp, format, args));
846 }
847
848 DllExport int
849 win32_vprintf(const char *format, va_list args)
850 {
851     return (pIOSubSystem->pfnvprintf(format, args));
852 }
853
854 DllExport size_t
855 win32_fread(void *buf, size_t size, size_t count, FILE *fp)
856 {
857     return pIOSubSystem->pfnfread(buf, size, count, fp);
858 }
859
860 DllExport size_t
861 win32_fwrite(const void *buf, size_t size, size_t count, FILE *fp)
862 {
863     return pIOSubSystem->pfnfwrite(buf, size, count, fp);
864 }
865
866 DllExport FILE *
867 win32_fopen(const char *filename, const char *mode)
868 {
869     if (stricmp(filename, "/dev/null")==0)
870         return pIOSubSystem->pfnfopen("NUL", mode);
871     return pIOSubSystem->pfnfopen(filename, mode);
872 }
873
874 DllExport FILE *
875 win32_fdopen( int handle, const char *mode)
876 {
877     return pIOSubSystem->pfnfdopen(handle, mode);
878 }
879
880 DllExport FILE *
881 win32_freopen( const char *path, const char *mode, FILE *stream)
882 {
883     if (stricmp(path, "/dev/null")==0)
884         return pIOSubSystem->pfnfreopen("NUL", mode, stream);
885     return pIOSubSystem->pfnfreopen(path, mode, stream);
886 }
887
888 DllExport int
889 win32_fclose(FILE *pf)
890 {
891     return pIOSubSystem->pfnfclose(pf);
892 }
893
894 DllExport int
895 win32_fputs(const char *s,FILE *pf)
896 {
897     return pIOSubSystem->pfnfputs(s, pf);
898 }
899
900 DllExport int
901 win32_fputc(int c,FILE *pf)
902 {
903     return pIOSubSystem->pfnfputc(c,pf);
904 }
905
906 DllExport int
907 win32_ungetc(int c,FILE *pf)
908 {
909     return pIOSubSystem->pfnungetc(c,pf);
910 }
911
912 DllExport int
913 win32_getc(FILE *pf)
914 {
915     return pIOSubSystem->pfngetc(pf);
916 }
917
918 DllExport int
919 win32_fileno(FILE *pf)
920 {
921     return pIOSubSystem->pfnfileno(pf);
922 }
923
924 DllExport void
925 win32_clearerr(FILE *pf)
926 {
927     pIOSubSystem->pfnclearerr(pf);
928     return;
929 }
930
931 DllExport int
932 win32_fflush(FILE *pf)
933 {
934     return pIOSubSystem->pfnfflush(pf);
935 }
936
937 DllExport long
938 win32_ftell(FILE *pf)
939 {
940     return pIOSubSystem->pfnftell(pf);
941 }
942
943 DllExport int
944 win32_fseek(FILE *pf,long offset,int origin)
945 {
946     return pIOSubSystem->pfnfseek(pf, offset, origin);
947 }
948
949 DllExport int
950 win32_fgetpos(FILE *pf,fpos_t *p)
951 {
952     return pIOSubSystem->pfnfgetpos(pf, p);
953 }
954
955 DllExport int
956 win32_fsetpos(FILE *pf,const fpos_t *p)
957 {
958     return pIOSubSystem->pfnfsetpos(pf, p);
959 }
960
961 DllExport void
962 win32_rewind(FILE *pf)
963 {
964     pIOSubSystem->pfnrewind(pf);
965     return;
966 }
967
968 DllExport FILE*
969 win32_tmpfile(void)
970 {
971     return pIOSubSystem->pfntmpfile();
972 }
973
974 DllExport void
975 win32_abort(void)
976 {
977     pIOSubSystem->pfnabort();
978     return;
979 }
980
981 DllExport int
982 win32_fstat(int fd,struct stat *bufptr)
983 {
984     return pIOSubSystem->pfnfstat(fd,bufptr);
985 }
986
987 DllExport int
988 win32_pipe(int *pfd, unsigned int size, int mode)
989 {
990     return pIOSubSystem->pfnpipe(pfd, size, mode);
991 }
992
993 DllExport FILE*
994 win32_popen(const char *command, const char *mode)
995 {
996     return pIOSubSystem->pfnpopen(command, mode);
997 }
998
999 DllExport int
1000 win32_pclose(FILE *pf)
1001 {
1002     return pIOSubSystem->pfnpclose(pf);
1003 }
1004
1005 DllExport int
1006 win32_setmode(int fd, int mode)
1007 {
1008     return pIOSubSystem->pfnsetmode(fd, mode);
1009 }
1010
1011 DllExport long
1012 win32_lseek(int fd, long offset, int origin)
1013 {
1014     return pIOSubSystem->pfnlseek(fd, offset, origin);
1015 }
1016
1017 DllExport long
1018 win32_tell(int fd)
1019 {
1020     return pIOSubSystem->pfntell(fd);
1021 }
1022
1023 DllExport int
1024 win32_open(const char *path, int flag, ...)
1025 {
1026     va_list ap;
1027     int pmode;
1028
1029     va_start(ap, flag);
1030     pmode = va_arg(ap, int);
1031     va_end(ap);
1032
1033     if (stricmp(path, "/dev/null")==0)
1034         return pIOSubSystem->pfnopen("NUL", flag, pmode);
1035     return pIOSubSystem->pfnopen(path,flag,pmode);
1036 }
1037
1038 DllExport int
1039 win32_close(int fd)
1040 {
1041     return pIOSubSystem->pfnclose(fd);
1042 }
1043
1044 DllExport int
1045 win32_eof(int fd)
1046 {
1047     return pIOSubSystem->pfneof(fd);
1048 }
1049
1050 DllExport int
1051 win32_dup(int fd)
1052 {
1053     return pIOSubSystem->pfndup(fd);
1054 }
1055
1056 DllExport int
1057 win32_dup2(int fd1,int fd2)
1058 {
1059     return pIOSubSystem->pfndup2(fd1,fd2);
1060 }
1061
1062 DllExport int
1063 win32_read(int fd, char *buf, unsigned int cnt)
1064 {
1065     return pIOSubSystem->pfnread(fd, buf, cnt);
1066 }
1067
1068 DllExport int
1069 win32_write(int fd, const char *buf, unsigned int cnt)
1070 {
1071     return pIOSubSystem->pfnwrite(fd, buf, cnt);
1072 }
1073
1074 DllExport int
1075 win32_mkdir(const char *dir, int mode)
1076 {
1077     return pIOSubSystem->pfnmkdir(dir); /* just ignore mode */
1078 }
1079
1080 DllExport int
1081 win32_rmdir(const char *dir)
1082 {
1083     return pIOSubSystem->pfnrmdir(dir);
1084 }
1085
1086 DllExport int
1087 win32_chdir(const char *dir)
1088 {
1089     return pIOSubSystem->pfnchdir(dir);
1090 }
1091
1092 DllExport int
1093 win32_spawnvpe(int mode, const char *cmdname,
1094                const char *const *argv, const char *const *envp)
1095 {
1096     return pIOSubSystem->pfnspawnvpe(mode, cmdname, argv, envp);
1097 }
1098
1099 DllExport int
1100 win32_spawnle(int mode, const char *cmdname, const char *arglist,...)
1101 {
1102     const char* const*  envp;
1103     const char* const*  argp;
1104
1105     argp = &arglist;
1106     while (*argp++) ;
1107     envp = (const char* const*)*argp;
1108
1109     return pIOSubSystem->pfnspawnvpe(mode, cmdname, &arglist, envp);
1110 }
1111
1112 int
1113 stolen_open_osfhandle(long handle, int flags)
1114 {
1115     return pIOSubSystem->pfn_open_osfhandle(handle, flags);
1116 }
1117
1118 long
1119 stolen_get_osfhandle(int fd)
1120 {
1121     return pIOSubSystem->pfn_get_osfhandle(fd);
1122 }
1123
1124
1125 /*
1126  * Extras.
1127  */
1128
1129 /* simulate flock by locking a range on the file */
1130
1131 #define LK_ERR(f,i)     ((f) ? (i = 0) : (errno = GetLastError()))
1132 #define LK_LEN          0xffff0000
1133
1134 DllExport int
1135 win32_flock(int fd, int oper)
1136 {
1137     OVERLAPPED o;
1138     int i = -1;
1139     HANDLE fh;
1140
1141     if (!IsWinNT()) {
1142         croak("flock() unimplemented on this platform");
1143         return -1;
1144     }
1145
1146     fh = (HANDLE)stolen_get_osfhandle(fd);
1147     memset(&o, 0, sizeof(o));
1148
1149     switch(oper) {
1150     case LOCK_SH:               /* shared lock */
1151         LK_ERR(LockFileEx(fh, 0, 0, LK_LEN, 0, &o),i);
1152         break;
1153     case LOCK_EX:               /* exclusive lock */
1154         LK_ERR(LockFileEx(fh, LOCKFILE_EXCLUSIVE_LOCK, 0, LK_LEN, 0, &o),i);
1155         break;
1156     case LOCK_SH|LOCK_NB:       /* non-blocking shared lock */
1157         LK_ERR(LockFileEx(fh, LOCKFILE_FAIL_IMMEDIATELY, 0, LK_LEN, 0, &o),i);
1158         break;
1159     case LOCK_EX|LOCK_NB:       /* non-blocking exclusive lock */
1160         LK_ERR(LockFileEx(fh,
1161                        LOCKFILE_EXCLUSIVE_LOCK|LOCKFILE_FAIL_IMMEDIATELY,
1162                        0, LK_LEN, 0, &o),i);
1163         break;
1164     case LOCK_UN:               /* unlock lock */
1165         LK_ERR(UnlockFileEx(fh, 0, LK_LEN, 0, &o),i);
1166         break;
1167     default:                    /* unknown */
1168         errno = EINVAL;
1169         break;
1170     }
1171     return i;
1172 }
1173
1174 #undef LK_ERR
1175 #undef LK_LEN
1176