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