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