regen/mk_invlists.pl: Fix bug when 2 ident tables
[perl.git] / win32 / wince.c
1 /*  WINCE.C - stuff for Windows CE
2  *
3  *  Time-stamp: <26/10/01 15:25:20 keuchel@keuchelnt>
4  *
5  *  You may distribute under the terms of either the GNU General Public
6  *  License or the Artistic License, as specified in the README file.
7  */
8
9 #define WIN32_LEAN_AND_MEAN
10 #define WIN32IO_IS_STDIO
11 #include <windows.h>
12 #include <signal.h>
13
14 #define PERLIO_NOT_STDIO 0
15
16 #define PerlIO FILE
17
18 #define wince_private
19 #include "errno.h"
20
21 #include "EXTERN.h"
22 #include "perl.h"
23
24 #define NO_XSLOCKS
25 #define PERL_NO_GET_CONTEXT
26 #include "XSUB.h"
27
28 #include "win32iop.h"
29 #include <string.h>
30 #include <stdarg.h>
31 #include <float.h>
32 #include <shellapi.h>
33 #include <process.h>
34
35 #define perl
36 #include "celib_defs.h"
37 #include "cewin32.h"
38 #include "cecrt.h"
39 #include "cewin32_defs.h"
40 #include "cecrt_defs.h"
41
42 #define GetCurrentDirectoryW XCEGetCurrentDirectoryW
43
44 #ifdef PALM_SIZE
45 #include "stdio-palmsize.h"
46 #endif
47
48 #define EXECF_EXEC 1
49 #define EXECF_SPAWN 2
50 #define EXECF_SPAWN_NOWAIT 3
51
52 #if defined(PERL_IMPLICIT_SYS)
53 #  undef do_spawn
54 #  define do_spawn g_do_spawn
55 #  undef getlogin
56 #  define getlogin g_getlogin
57 #endif
58
59 static void             get_shell(void);
60 static long             tokenize(const char *str, char **dest, char ***destv);
61 static int              do_spawn2(pTHX_ char *cmd, int exectype);
62 static BOOL             has_shell_metachars(char *ptr);
63 static long             filetime_to_clock(PFILETIME ft);
64 static BOOL             filetime_from_time(PFILETIME ft, time_t t);
65 static char *           get_emd_part(SV **leading, STRLEN *const len,
66                                      char *trailing, ...);
67 static void             remove_dead_process(long deceased);
68 static long             find_pid(pTHX_ int pid);
69 static char *           qualified_path(const char *cmd);
70 static char *           win32_get_xlib(const char *pl, const char *xlib,
71                                        const char *libname, STRLEN *const len);
72
73 #ifdef USE_ITHREADS
74 static void             remove_dead_pseudo_process(long child);
75 static long             find_pseudo_pid(pTHX_ int pid);
76 #endif
77
78 int _fmode = O_TEXT; /* celib do not provide _fmode, so we define it here */
79
80 START_EXTERN_C
81 HANDLE  w32_perldll_handle = INVALID_HANDLE_VALUE;
82 char    w32_module_name[MAX_PATH+1];
83 END_EXTERN_C
84
85 static DWORD    w32_platform = (DWORD)-1;
86
87 int
88 IsWin95(void)
89 {
90   return (win32_os_id() == VER_PLATFORM_WIN32_WINDOWS);
91 }
92
93 int
94 IsWinNT(void)
95 {
96   return (win32_os_id() == VER_PLATFORM_WIN32_NT);
97 }
98
99 int
100 IsWinCE(void)
101 {
102   return (win32_os_id() == VER_PLATFORM_WIN32_CE);
103 }
104
105 EXTERN_C void
106 set_w32_module_name(void)
107 {
108   char* ptr;
109   XCEGetModuleFileNameA((HMODULE)((w32_perldll_handle == INVALID_HANDLE_VALUE)
110                                   ? XCEGetModuleHandleA(NULL)
111                                   : w32_perldll_handle),
112                         w32_module_name, sizeof(w32_module_name));
113
114   /* normalize to forward slashes */
115   ptr = w32_module_name;
116   while (*ptr) {
117     if (*ptr == '\\')
118       *ptr = '/';
119     ++ptr;
120   }
121 }
122
123 /* *svp (if non-NULL) is expected to be POK (valid allocated SvPVX(*svp)) */
124 static char*
125 get_regstr_from(HKEY hkey, const char *valuename, SV **svp)
126 {
127     /* Retrieve a REG_SZ or REG_EXPAND_SZ from the registry */
128     HKEY handle;
129     DWORD type;
130     const char *subkey = "Software\\Perl";
131     char *str = NULL;
132     long retval;
133
134     retval = XCERegOpenKeyExA(hkey, subkey, 0, KEY_READ, &handle);
135     if (retval == ERROR_SUCCESS) {
136         DWORD datalen;
137         retval = XCERegQueryValueExA(handle, valuename, 0, &type, NULL, &datalen);
138         if (retval == ERROR_SUCCESS && type == REG_SZ) {
139             dTHX;
140             if (!*svp)
141                 *svp = sv_2mortal(newSVpvs(""));
142             SvGROW(*svp, datalen);
143             retval = XCERegQueryValueExA(handle, valuename, 0, NULL,
144                                      (PBYTE)SvPVX(*svp), &datalen);
145             if (retval == ERROR_SUCCESS) {
146                 str = SvPVX(*svp);
147                 SvCUR_set(*svp,datalen-1);
148             }
149         }
150         RegCloseKey(handle);
151     }
152     return str;
153 }
154
155 /* *svp (if non-NULL) is expected to be POK (valid allocated SvPVX(*svp)) */
156 static char*
157 get_regstr(const char *valuename, SV **svp)
158 {
159     char *str = get_regstr_from(HKEY_CURRENT_USER, valuename, svp);
160     if (!str)
161         str = get_regstr_from(HKEY_LOCAL_MACHINE, valuename, svp);
162     return str;
163 }
164
165 /* *prev_pathp (if non-NULL) is expected to be POK (valid allocated SvPVX(sv)) */
166 static char *
167 get_emd_part(SV **prev_pathp, STRLEN *const len, char *trailing_path, ...)
168 {
169     char base[10];
170     va_list ap;
171     char mod_name[MAX_PATH+1];
172     char *ptr;
173     char *optr;
174     char *strip;
175     int oldsize, newsize;
176     STRLEN baselen;
177
178     va_start(ap, trailing_path);
179     strip = va_arg(ap, char *);
180
181     sprintf(base, "%d.%d", (int)PERL_REVISION, (int)PERL_VERSION);
182     baselen = strlen(base);
183
184     if (!*w32_module_name) {
185         set_w32_module_name();
186     }
187     strcpy(mod_name, w32_module_name);
188     ptr = strrchr(mod_name, '/');
189     while (ptr && strip) {
190         /* look for directories to skip back */
191         optr = ptr;
192         *ptr = '\0';
193         ptr = strrchr(mod_name, '/');
194         /* avoid stripping component if there is no slash,
195          * or it doesn't match ... */
196         if (!ptr || stricmp(ptr+1, strip) != 0) {
197             /* ... but not if component matches m|5\.$patchlevel.*| */
198             if (!ptr || !(*strip == '5' && *(ptr+1) == '5'
199                           && strnEQ(strip, base, baselen)
200                           && strnEQ(ptr+1, base, baselen)))
201             {
202                 *optr = '/';
203                 ptr = optr;
204             }
205         }
206         strip = va_arg(ap, char *);
207     }
208     if (!ptr) {
209         ptr = mod_name;
210         *ptr++ = '.';
211         *ptr = '/';
212     }
213     va_end(ap);
214     strcpy(++ptr, trailing_path);
215
216     /* only add directory if it exists */
217     if (XCEGetFileAttributesA(mod_name) != (DWORD) -1) {
218         /* directory exists */
219         dTHX;
220         if (!*prev_pathp)
221             *prev_pathp = sv_2mortal(newSVpvs(""));
222         sv_catpvs(*prev_pathp, ";");
223         sv_catpv(*prev_pathp, mod_name);
224         if(len)
225             *len = SvCUR(*prev_pathp);
226         return SvPVX(*prev_pathp);
227     }
228
229     return NULL;
230 }
231
232 char *
233 win32_get_privlib(WIN32_NO_REGISTRY_M_(const char *pl) STRLEN *const len)
234 {
235     dTHX;
236     char *stdlib = "lib";
237     char buffer[MAX_PATH+1];
238     SV *sv = NULL;
239
240     /* $stdlib = $HKCU{"lib-$]"} || $HKLM{"lib-$]"} || $HKCU{"lib"} || $HKLM{"lib"} || "";  */
241     sprintf(buffer, "%s-%s", stdlib, pl);
242     if (!get_regstr(buffer, &sv))
243         (void)get_regstr(stdlib, &sv);
244
245     /* $stdlib .= ";$EMD/../../lib" */
246     return get_emd_part(&sv, len, stdlib, ARCHNAME, "bin", NULL);
247 }
248
249 static char *
250 win32_get_xlib(const char *pl, const char *xlib, const char *libname,
251                STRLEN *const len)
252 {
253     dTHX;
254     char regstr[40];
255     char pathstr[MAX_PATH+1];
256     SV *sv1 = NULL;
257     SV *sv2 = NULL;
258
259     /* $HKCU{"$xlib-$]"} || $HKLM{"$xlib-$]"} . ---; */
260     sprintf(regstr, "%s-%s", xlib, pl);
261     (void)get_regstr(regstr, &sv1);
262
263     /* $xlib .=
264      * ";$EMD/" . ((-d $EMD/../../../$]) ? "../../.." : "../.."). "/$libname/$]/lib";  */
265     sprintf(pathstr, "%s/%s/lib", libname, pl);
266     (void)get_emd_part(&sv1, NULL, pathstr, ARCHNAME, "bin", pl, NULL);
267
268     /* $HKCU{$xlib} || $HKLM{$xlib} . ---; */
269     (void)get_regstr(xlib, &sv2);
270
271     /* $xlib .=
272      * ";$EMD/" . ((-d $EMD/../../../$]) ? "../../.." : "../.."). "/$libname/lib";  */
273     sprintf(pathstr, "%s/lib", libname);
274     (void)get_emd_part(&sv2, NULL, pathstr, ARCHNAME, "bin", pl, NULL);
275
276     if (!sv1 && !sv2)
277         return NULL;
278     if (!sv1) {
279         sv1 = sv2;
280     } else if (sv2) {
281         sv_catpvs(sv1, ";");
282         sv_catsv(sv1, sv2);
283     }
284
285     if (len)
286         *len = SvCUR(sv1);
287     return SvPVX(sv1);
288 }
289
290 char *
291 win32_get_sitelib(const char *pl, STRLEN *const len)
292 {
293     return win32_get_xlib(pl, "sitelib", "site", len);
294 }
295
296 #ifndef PERL_VENDORLIB_NAME
297 #  define PERL_VENDORLIB_NAME   "vendor"
298 #endif
299
300 char *
301 win32_get_vendorlib(const char *pl, STRLEN *const len)
302 {
303     return win32_get_xlib(pl, "vendorlib", PERL_VENDORLIB_NAME, len);
304 }
305
306 static BOOL
307 has_shell_metachars(char *ptr)
308 {
309     int inquote = 0;
310     char quote = '\0';
311
312     /*
313      * Scan string looking for redirection (< or >) or pipe
314      * characters (|) that are not in a quoted string.
315      * Shell variable interpolation (%VAR%) can also happen inside strings.
316      */
317     while (*ptr) {
318         switch(*ptr) {
319         case '%':
320             return TRUE;
321         case '\'':
322         case '\"':
323             if (inquote) {
324                 if (quote == *ptr) {
325                     inquote = 0;
326                     quote = '\0';
327                 }
328             }
329             else {
330                 quote = *ptr;
331                 inquote++;
332             }
333             break;
334         case '>':
335         case '<':
336         case '|':
337             if (!inquote)
338                 return TRUE;
339         default:
340             break;
341         }
342         ++ptr;
343     }
344     return FALSE;
345 }
346
347 #if !defined(PERL_IMPLICIT_SYS)
348 /* since the current process environment is being updated in util.c
349  * the library functions will get the correct environment
350  */
351 PerlIO *
352 Perl_my_popen(pTHX_ const char *cmd, const char *mode)
353 {
354   printf("popen(%s)\n", cmd);
355
356   Perl_croak(aTHX_ PL_no_func, "popen");
357   return NULL;
358 }
359
360 long
361 Perl_my_pclose(pTHX_ PerlIO *fp)
362 {
363   Perl_croak(aTHX_ PL_no_func, "pclose");
364   return -1;
365 }
366 #endif
367
368 DllExport unsigned long
369 win32_os_id(void)
370 {
371     static OSVERSIONINFOA osver;
372
373     if (osver.dwPlatformId != w32_platform) {
374         memset(&osver, 0, sizeof(OSVERSIONINFOA));
375         osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
376         XCEGetVersionExA(&osver);
377         w32_platform = osver.dwPlatformId;
378     }
379     return (unsigned long)w32_platform;
380 }
381
382 DllExport int
383 win32_getpid(void)
384 {
385     int pid;
386 #ifdef USE_ITHREADS
387     dTHX;
388     if (w32_pseudo_id)
389         return -((int)w32_pseudo_id);
390 #endif
391     pid = xcegetpid();
392     return pid;
393 }
394
395 /* Tokenize a string.  Words are null-separated, and the list
396  * ends with a doubled null.  Any character (except null and
397  * including backslash) may be escaped by preceding it with a
398  * backslash (the backslash will be stripped).
399  * Returns number of words in result buffer.
400  */
401 static long
402 tokenize(const char *str, char **dest, char ***destv)
403 {
404     char *retstart = NULL;
405     char **retvstart = 0;
406     int items = -1;
407     if (str) {
408         dTHX;
409         int slen = strlen(str);
410         char *ret;
411         char **retv;
412         Newx(ret, slen+2, char);
413         Newx(retv, (slen+3)/2, char*);
414
415         retstart = ret;
416         retvstart = retv;
417         *retv = ret;
418         items = 0;
419         while (*str) {
420             *ret = *str++;
421             if (*ret == '\\' && *str)
422                 *ret = *str++;
423             else if (*ret == ' ') {
424                 while (*str == ' ')
425                     str++;
426                 if (ret == retstart)
427                     ret--;
428                 else {
429                     *ret = '\0';
430                     ++items;
431                     if (*str)
432                         *++retv = ret+1;
433                 }
434             }
435             else if (!*str)
436                 ++items;
437             ret++;
438         }
439         retvstart[items] = NULL;
440         *ret++ = '\0';
441         *ret = '\0';
442     }
443     *dest = retstart;
444     *destv = retvstart;
445     return items;
446 }
447
448 DllExport int
449 win32_pipe(int *pfd, unsigned int size, int mode)
450 {
451   dTHX;
452   Perl_croak(aTHX_ PL_no_func, "pipe");
453   return -1;
454 }
455
456 DllExport int
457 win32_times(struct tms *timebuf)
458 {
459   dTHX;
460   Perl_croak(aTHX_ PL_no_func, "times");
461   return -1;
462 }
463
464 Sighandler_t
465 win32_signal(int sig, Sighandler_t subcode)
466 {
467   return xcesignal(sig, subcode);
468 }
469
470 static void
471 get_shell(void)
472 {
473     dTHX;
474     if (!w32_perlshell_tokens) {
475         /* we don't use COMSPEC here for two reasons:
476          *  1. the same reason perl on UNIX doesn't use SHELL--rampant and
477          *     uncontrolled unportability of the ensuing scripts.
478          *  2. PERL5SHELL could be set to a shell that may not be fit for
479          *     interactive use (which is what most programs look in COMSPEC
480          *     for).
481          */
482         const char* defaultshell = (IsWinNT()
483                                     ? "cmd.exe /x/d/c" : "command.com /c");
484         const char *usershell = PerlEnv_getenv("PERL5SHELL");
485         w32_perlshell_items = tokenize(usershell ? usershell : defaultshell,
486                                        &w32_perlshell_tokens,
487                                        &w32_perlshell_vec);
488     }
489 }
490
491 int
492 Perl_do_aspawn(pTHX_ SV *really, SV **mark, SV **sp)
493 {
494   PERL_ARGS_ASSERT_DO_ASPAWN;
495
496   Perl_croak(aTHX_ PL_no_func, "aspawn");
497   return -1;
498 }
499
500 /* returns pointer to the next unquoted space or the end of the string */
501 static char*
502 find_next_space(const char *s)
503 {
504     bool in_quotes = FALSE;
505     while (*s) {
506         /* ignore doubled backslashes, or backslash+quote */
507         if (*s == '\\' && (s[1] == '\\' || s[1] == '"')) {
508             s += 2;
509         }
510         /* keep track of when we're within quotes */
511         else if (*s == '"') {
512             s++;
513             in_quotes = !in_quotes;
514         }
515         /* break it up only at spaces that aren't in quotes */
516         else if (!in_quotes && isSPACE(*s))
517             return (char*)s;
518         else
519             s++;
520     }
521     return (char*)s;
522 }
523
524 #if 1
525 static int
526 do_spawn2(pTHX_ char *cmd, int exectype)
527 {
528     char **a;
529     char *s;
530     char **argv;
531     int status = -1;
532     BOOL needToTry = TRUE;
533     char *cmd2;
534
535     /* Save an extra exec if possible. See if there are shell
536      * metacharacters in it */
537     if (!has_shell_metachars(cmd)) {
538         Newx(argv, strlen(cmd) / 2 + 2, char*);
539         Newx(cmd2, strlen(cmd) + 1, char);
540         strcpy(cmd2, cmd);
541         a = argv;
542         for (s = cmd2; *s;) {
543             while (*s && isSPACE(*s))
544                 s++;
545             if (*s)
546                 *(a++) = s;
547             s = find_next_space(s);
548             if (*s)
549                 *s++ = '\0';
550         }
551         *a = NULL;
552         if (argv[0]) {
553             switch (exectype) {
554             case EXECF_SPAWN:
555                 status = win32_spawnvp(P_WAIT, argv[0],
556                                        (const char* const*)argv);
557                 break;
558             case EXECF_SPAWN_NOWAIT:
559                 status = win32_spawnvp(P_NOWAIT, argv[0],
560                                        (const char* const*)argv);
561                 break;
562             case EXECF_EXEC:
563                 status = win32_execvp(argv[0], (const char* const*)argv);
564                 break;
565             }
566             if (status != -1 || errno == 0)
567                 needToTry = FALSE;
568         }
569         Safefree(argv);
570         Safefree(cmd2);
571     }
572     if (needToTry) {
573         char **argv;
574         int i = -1;
575         get_shell();
576         Newx(argv, w32_perlshell_items + 2, char*);
577         while (++i < w32_perlshell_items)
578             argv[i] = w32_perlshell_vec[i];
579         argv[i++] = cmd;
580         argv[i] = NULL;
581         switch (exectype) {
582         case EXECF_SPAWN:
583             status = win32_spawnvp(P_WAIT, argv[0],
584                                    (const char* const*)argv);
585             break;
586         case EXECF_SPAWN_NOWAIT:
587             status = win32_spawnvp(P_NOWAIT, argv[0],
588                                    (const char* const*)argv);
589             break;
590         case EXECF_EXEC:
591             status = win32_execvp(argv[0], (const char* const*)argv);
592             break;
593         }
594         cmd = argv[0];
595         Safefree(argv);
596     }
597     if (exectype == EXECF_SPAWN_NOWAIT) {
598         if (IsWin95())
599             PL_statusvalue = -1;        /* >16bits hint for pp_system() */
600     }
601     else {
602         if (status < 0) {
603             if (ckWARN(WARN_EXEC))
604                 Perl_warner(aTHX_ packWARN(WARN_EXEC), "Can't %s \"%s\": %s",
605                      (exectype == EXECF_EXEC ? "exec" : "spawn"),
606                      cmd, strerror(errno));
607             status = 255 * 256;
608         }
609         else
610             status *= 256;
611         PL_statusvalue = status;
612     }
613     return (status);
614 }
615
616 int
617 Perl_do_spawn(pTHX_ char *cmd)
618 {
619     PERL_ARGS_ASSERT_DO_SPAWN;
620
621     return do_spawn2(aTHX_ cmd, EXECF_SPAWN);
622 }
623
624 int
625 Perl_do_spawn_nowait(pTHX_ char *cmd)
626 {
627     PERL_ARGS_ASSERT_DO_SPAWN_NOWAIT;
628
629     return do_spawn2(aTHX_ cmd, EXECF_SPAWN_NOWAIT);
630 }
631
632 bool
633 Perl_do_exec(pTHX_ const char *cmd)
634 {
635     PERL_ARGS_ASSERT_DO_EXEC;
636
637     do_spawn2(aTHX_ cmd, EXECF_EXEC);
638     return FALSE;
639 }
640
641 /* The idea here is to read all the directory names into a string table
642  * (separated by nulls) and when one of the other dir functions is called
643  * return the pointer to the current file name.
644  */
645 DllExport DIR *
646 win32_opendir(const char *filename)
647 {
648     dTHX;
649     DIR                 *dirp;
650     long                len;
651     long                idx;
652     char                scanname[MAX_PATH+3];
653     Stat_t              sbuf;
654     WIN32_FIND_DATAA    aFindData;
655     WIN32_FIND_DATAW    wFindData;
656     HANDLE              fh;
657     char                buffer[MAX_PATH*2];
658     WCHAR               wbuffer[MAX_PATH+1];
659     char*               ptr;
660
661     len = strlen(filename);
662     if (len > MAX_PATH)
663         return NULL;
664
665     /* check to see if filename is a directory */
666     if (win32_stat(filename, &sbuf) < 0 || !S_ISDIR(sbuf.st_mode))
667         return NULL;
668
669     /* Get us a DIR structure */
670     Newxz(dirp, 1, DIR);
671
672     /* Create the search pattern */
673     strcpy(scanname, filename);
674
675     /* bare drive name means look in cwd for drive */
676     if (len == 2 && isALPHA(scanname[0]) && scanname[1] == ':') {
677         scanname[len++] = '.';
678         scanname[len++] = '/';
679     }
680     else if (scanname[len-1] != '/' && scanname[len-1] != '\\') {
681         scanname[len++] = '/';
682     }
683     scanname[len++] = '*';
684     scanname[len] = '\0';
685
686     /* do the FindFirstFile call */
687     fh = FindFirstFile(PerlDir_mapA(scanname), &aFindData);
688     dirp->handle = fh;
689     if (fh == INVALID_HANDLE_VALUE) {
690         DWORD err = GetLastError();
691         /* FindFirstFile() fails on empty drives! */
692         switch (err) {
693         case ERROR_FILE_NOT_FOUND:
694             return dirp;
695         case ERROR_NO_MORE_FILES:
696         case ERROR_PATH_NOT_FOUND:
697             errno = ENOENT;
698             break;
699         case ERROR_NOT_ENOUGH_MEMORY:
700             errno = ENOMEM;
701             break;
702         default:
703             errno = EINVAL;
704             break;
705         }
706         Safefree(dirp);
707         return NULL;
708     }
709
710     /* now allocate the first part of the string table for
711      * the filenames that we find.
712      */
713     ptr = aFindData.cFileName;
714     idx = strlen(ptr)+1;
715     if (idx < 256)
716         dirp->size = 128;
717     else
718         dirp->size = idx;
719     Newx(dirp->start, dirp->size, char);
720     strcpy(dirp->start, ptr);
721     dirp->nfiles++;
722     dirp->end = dirp->curr = dirp->start;
723     dirp->end += idx;
724     return dirp;
725 }
726
727
728 /* Readdir just returns the current string pointer and bumps the
729  * string pointer to the nDllExport entry.
730  */
731 DllExport struct direct *
732 win32_readdir(DIR *dirp)
733 {
734     long         len;
735
736     if (dirp->curr) {
737         /* first set up the structure to return */
738         len = strlen(dirp->curr);
739         strcpy(dirp->dirstr.d_name, dirp->curr);
740         dirp->dirstr.d_namlen = len;
741
742         /* Fake an inode */
743         dirp->dirstr.d_ino = dirp->curr - dirp->start;
744
745         /* Now set up for the next call to readdir */
746         dirp->curr += len + 1;
747         if (dirp->curr >= dirp->end) {
748             dTHX;
749             char*               ptr;
750             BOOL                res;
751             WIN32_FIND_DATAW    wFindData;
752             WIN32_FIND_DATAA    aFindData;
753             char                buffer[MAX_PATH*2];
754
755             /* finding the next file that matches the wildcard
756              * (which should be all of them in this directory!).
757              */
758             res = FindNextFile(dirp->handle, &aFindData);
759             if (res)
760                 ptr = aFindData.cFileName;
761             if (res) {
762                 long endpos = dirp->end - dirp->start;
763                 long newsize = endpos + strlen(ptr) + 1;
764                 /* bump the string table size by enough for the
765                  * new name and its null terminator */
766                 while (newsize > dirp->size) {
767                     long curpos = dirp->curr - dirp->start;
768                     Renew(dirp->start, dirp->size * 2, char);
769                     dirp->size *= 2;
770                     dirp->curr = dirp->start + curpos;
771                 }
772                 strcpy(dirp->start + endpos, ptr);
773                 dirp->end = dirp->start + newsize;
774                 dirp->nfiles++;
775             }
776             else
777                 dirp->curr = NULL;
778         }
779         return &(dirp->dirstr);
780     }
781     else
782         return NULL;
783 }
784
785 /* Telldir returns the current string pointer position */
786 DllExport long
787 win32_telldir(DIR *dirp)
788 {
789     return (dirp->curr - dirp->start);
790 }
791
792
793 /* Seekdir moves the string pointer to a previously saved position
794  * (returned by telldir).
795  */
796 DllExport void
797 win32_seekdir(DIR *dirp, long loc)
798 {
799     dirp->curr = dirp->start + loc;
800 }
801
802 /* Rewinddir resets the string pointer to the start */
803 DllExport void
804 win32_rewinddir(DIR *dirp)
805 {
806     dirp->curr = dirp->start;
807 }
808
809 /* free the memory allocated by opendir */
810 DllExport int
811 win32_closedir(DIR *dirp)
812 {
813     dTHX;
814     if (dirp->handle != INVALID_HANDLE_VALUE)
815         FindClose(dirp->handle);
816     Safefree(dirp->start);
817     Safefree(dirp);
818     return 1;
819 }
820
821 #else
822 /////!!!!!!!!!!! return here and do right stuff!!!!
823
824 DllExport DIR *
825 win32_opendir(const char *filename)
826 {
827   return opendir(filename);
828 }
829
830 DllExport struct direct *
831 win32_readdir(DIR *dirp)
832 {
833   return readdir(dirp);
834 }
835
836 DllExport long
837 win32_telldir(DIR *dirp)
838 {
839   dTHX;
840   Perl_croak(aTHX_ PL_no_func, "telldir");
841   return -1;
842 }
843
844 DllExport void
845 win32_seekdir(DIR *dirp, long loc)
846 {
847   dTHX;
848   Perl_croak(aTHX_ PL_no_func, "seekdir");
849 }
850
851 DllExport void
852 win32_rewinddir(DIR *dirp)
853 {
854   dTHX;
855   Perl_croak(aTHX_ PL_no_func, "rewinddir");
856 }
857
858 DllExport int
859 win32_closedir(DIR *dirp)
860 {
861   closedir(dirp);
862   return 0;
863 }
864 #endif   // 1
865
866 DllExport int
867 win32_kill(int pid, int sig)
868 {
869   dTHX;
870   Perl_croak(aTHX_ PL_no_func, "kill");
871   return -1;
872 }
873
874 DllExport int
875 win32_stat(const char *path, struct stat *sbuf)
876 {
877   return xcestat(path, sbuf);
878 }
879
880 DllExport char *
881 win32_longpath(char *path)
882 {
883   return path;
884 }
885
886 static void
887 out_of_memory(void)
888 {
889     if (PL_curinterp) {
890         dTHX;
891         /* Can't use PerlIO to write as it allocates memory */
892         PerlLIO_write(PerlIO_fileno(Perl_error_log),
893                       PL_no_mem, strlen(PL_no_mem));
894         my_exit(1);
895     }
896     exit(1);
897 }
898
899 /* The win32_ansipath() function takes a Unicode filename and converts it
900  * into the current Windows codepage. If some characters cannot be mapped,
901  * then it will convert the short name instead.
902  *
903  * The buffer to the ansi pathname must be freed with win32_free() when it
904  * it no longer needed.
905  *
906  * The argument to win32_ansipath() must exist before this function is
907  * called; otherwise there is no way to determine the short path name.
908  *
909  * Ideas for future refinement:
910  * - Only convert those segments of the path that are not in the current
911  *   codepage, but leave the other segments in their long form.
912  * - If the resulting name is longer than MAX_PATH, start converting
913  *   additional path segments into short names until the full name
914  *   is shorter than MAX_PATH.  Shorten the filename part last!
915  */
916 DllExport char *
917 win32_ansipath(const WCHAR *widename)
918 {
919     char *name;
920     size_t widelen = wcslen(widename)+1;
921     int len = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, widename, widelen,
922                                   NULL, 0, NULL, NULL);
923     name = win32_malloc(len);
924     if (!name)
925         out_of_memory();
926
927     WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, widename, widelen,
928                         name, len, NULL, NULL);
929     return name;
930 }
931
932 DllExport char *
933 win32_getenv(const char *name)
934 {
935   return xcegetenv(name);
936 }
937
938 DllExport int
939 win32_putenv(const char *name)
940 {
941   return xceputenv(name);
942 }
943
944 static long
945 filetime_to_clock(PFILETIME ft)
946 {
947     __int64 qw = ft->dwHighDateTime;
948     qw <<= 32;
949     qw |= ft->dwLowDateTime;
950     qw /= 10000;  /* File time ticks at 0.1uS, clock at 1mS */
951     return (long) qw;
952 }
953
954 /* fix utime() so it works on directories in NT */
955 static BOOL
956 filetime_from_time(PFILETIME pFileTime, time_t Time)
957 {
958     struct tm *pTM = localtime(&Time);
959     SYSTEMTIME SystemTime;
960     FILETIME LocalTime;
961
962     if (pTM == NULL)
963         return FALSE;
964
965     SystemTime.wYear   = pTM->tm_year + 1900;
966     SystemTime.wMonth  = pTM->tm_mon + 1;
967     SystemTime.wDay    = pTM->tm_mday;
968     SystemTime.wHour   = pTM->tm_hour;
969     SystemTime.wMinute = pTM->tm_min;
970     SystemTime.wSecond = pTM->tm_sec;
971     SystemTime.wMilliseconds = 0;
972
973     return SystemTimeToFileTime(&SystemTime, &LocalTime) &&
974            LocalFileTimeToFileTime(&LocalTime, pFileTime);
975 }
976
977 DllExport int
978 win32_unlink(const char *filename)
979 {
980   return xceunlink(filename);
981 }
982
983 DllExport int
984 win32_utime(const char *filename, struct utimbuf *times)
985 {
986   return xceutime(filename, (struct _utimbuf *) times);
987 }
988
989 DllExport int
990 win32_gettimeofday(struct timeval *tp, void *not_used)
991 {
992     return xcegettimeofday(tp,not_used);
993 }
994
995 DllExport int
996 win32_uname(struct utsname *name)
997 {
998     struct hostent *hep;
999     STRLEN nodemax = sizeof(name->nodename)-1;
1000     OSVERSIONINFOA osver;
1001
1002     memset(&osver, 0, sizeof(OSVERSIONINFOA));
1003     osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
1004     if (XCEGetVersionExA(&osver)) {
1005         /* sysname */
1006         switch (osver.dwPlatformId) {
1007         case VER_PLATFORM_WIN32_CE:
1008             strcpy(name->sysname, "Windows CE");
1009             break;
1010         case VER_PLATFORM_WIN32_WINDOWS:
1011             strcpy(name->sysname, "Windows");
1012             break;
1013         case VER_PLATFORM_WIN32_NT:
1014             strcpy(name->sysname, "Windows NT");
1015             break;
1016         case VER_PLATFORM_WIN32s:
1017             strcpy(name->sysname, "Win32s");
1018             break;
1019         default:
1020             strcpy(name->sysname, "Win32 Unknown");
1021             break;
1022         }
1023
1024         /* release */
1025         sprintf(name->release, "%d.%d",
1026                 osver.dwMajorVersion, osver.dwMinorVersion);
1027
1028         /* version */
1029         sprintf(name->version, "Build %d",
1030                 osver.dwPlatformId == VER_PLATFORM_WIN32_NT
1031                 ? osver.dwBuildNumber : (osver.dwBuildNumber & 0xffff));
1032         if (osver.szCSDVersion[0]) {
1033             char *buf = name->version + strlen(name->version);
1034             sprintf(buf, " (%s)", osver.szCSDVersion);
1035         }
1036     }
1037     else {
1038         *name->sysname = '\0';
1039         *name->version = '\0';
1040         *name->release = '\0';
1041     }
1042
1043     /* nodename */
1044     hep = win32_gethostbyname("localhost");
1045     if (hep) {
1046         STRLEN len = strlen(hep->h_name);
1047         if (len <= nodemax) {
1048             strcpy(name->nodename, hep->h_name);
1049         }
1050         else {
1051             strncpy(name->nodename, hep->h_name, nodemax);
1052             name->nodename[nodemax] = '\0';
1053         }
1054     }
1055     else {
1056         DWORD sz = nodemax;
1057         if (!XCEGetComputerNameA(name->nodename, &sz))
1058             *name->nodename = '\0';
1059     }
1060
1061     /* machine (architecture) */
1062     {
1063         SYSTEM_INFO info;
1064         char *arch;
1065         GetSystemInfo(&info);
1066
1067         switch (info.wProcessorArchitecture) {
1068         case PROCESSOR_ARCHITECTURE_INTEL:
1069             arch = "x86"; break;
1070         case PROCESSOR_ARCHITECTURE_MIPS:
1071             arch = "mips"; break;
1072         case PROCESSOR_ARCHITECTURE_ALPHA:
1073             arch = "alpha"; break;
1074         case PROCESSOR_ARCHITECTURE_PPC:
1075             arch = "ppc"; break;
1076         case PROCESSOR_ARCHITECTURE_ARM:
1077             arch = "arm"; break;
1078         case PROCESSOR_HITACHI_SH3:
1079             arch = "sh3"; break;
1080         case PROCESSOR_SHx_SH3:
1081             arch = "sh3"; break;
1082
1083         default:
1084             arch = "unknown"; break;
1085         }
1086         strcpy(name->machine, arch);
1087     }
1088     return 0;
1089 }
1090
1091 /* Timing related stuff */
1092
1093 int
1094 do_raise(pTHX_ int sig)
1095 {
1096     if (sig < SIG_SIZE) {
1097         Sighandler_t handler = w32_sighandler[sig];
1098         if (handler == SIG_IGN) {
1099             return 0;
1100         }
1101         else if (handler != SIG_DFL) {
1102             (*handler)(sig);
1103             return 0;
1104         }
1105         else {
1106             /* Choose correct default behaviour */
1107             switch (sig) {
1108 #ifdef SIGCLD
1109                 case SIGCLD:
1110 #endif
1111 #ifdef SIGCHLD
1112                 case SIGCHLD:
1113 #endif
1114                 case 0:
1115                     return 0;
1116                 case SIGTERM:
1117                 default:
1118                     break;
1119             }
1120         }
1121     }
1122     /* Tell caller to exit thread/process as appropriate */
1123     return 1;
1124 }
1125
1126 void
1127 sig_terminate(pTHX_ int sig)
1128 {
1129     Perl_warn(aTHX_ "Terminating on signal SIG%s(%d)\n",PL_sig_name[sig], sig);
1130     /* exit() seems to be safe, my_exit() or die() is a problem in ^C
1131        thread
1132      */
1133     exit(sig);
1134 }
1135
1136 DllExport int
1137 win32_async_check(pTHX)
1138 {
1139     MSG msg;
1140     int ours = 1;
1141     /* Passing PeekMessage -1 as HWND (2nd arg) only get PostThreadMessage() messages
1142      * and ignores window messages - should co-exist better with windows apps e.g. Tk
1143      */
1144     while (PeekMessage(&msg, (HWND)-1, 0, 0, PM_REMOVE|PM_NOYIELD)) {
1145         int sig;
1146         switch(msg.message) {
1147
1148 #if 0
1149     /* Perhaps some other messages could map to signals ? ... */
1150         case WM_CLOSE:
1151         case WM_QUIT:
1152             /* Treat WM_QUIT like SIGHUP?  */
1153             sig = SIGHUP;
1154             goto Raise;
1155             break;
1156 #endif
1157
1158         /* We use WM_USER to fake kill() with other signals */
1159         case WM_USER: {
1160             sig = msg.wParam;
1161         Raise:
1162             if (do_raise(aTHX_ sig)) {
1163                    sig_terminate(aTHX_ sig);
1164             }
1165             break;
1166         }
1167
1168         case WM_TIMER: {
1169             /* alarm() is a one-shot but SetTimer() repeats so kill it */
1170             if (w32_timerid) {
1171                 KillTimer(NULL,w32_timerid);
1172                 w32_timerid=0;
1173             }
1174             /* Now fake a call to signal handler */
1175             if (do_raise(aTHX_ 14)) {
1176                 sig_terminate(aTHX_ 14);
1177             }
1178             break;
1179         }
1180
1181         /* Otherwise do normal Win32 thing - in case it is useful */
1182         default:
1183             TranslateMessage(&msg);
1184             DispatchMessage(&msg);
1185             ours = 0;
1186             break;
1187         }
1188     }
1189     w32_poll_count = 0;
1190
1191     /* Above or other stuff may have set a signal flag */
1192     if (PL_sig_pending) {
1193         despatch_signals();
1194     }
1195     return ours;
1196 }
1197
1198 /* This function will not return until the timeout has elapsed, or until
1199  * one of the handles is ready. */
1200 DllExport DWORD
1201 win32_msgwait(pTHX_ DWORD count, LPHANDLE handles, DWORD timeout, LPDWORD resultp)
1202 {
1203     /* We may need several goes at this - so compute when we stop */
1204     DWORD ticks = 0;
1205     if (timeout != INFINITE) {
1206         ticks = GetTickCount();
1207         timeout += ticks;
1208     }
1209     while (1) {
1210         DWORD result = MsgWaitForMultipleObjects(count,handles,FALSE,timeout-ticks, QS_ALLEVENTS);
1211         if (resultp)
1212            *resultp = result;
1213         if (result == WAIT_TIMEOUT) {
1214             /* Ran out of time - explicit return of zero to avoid -ve if we
1215                have scheduling issues
1216              */
1217             return 0;
1218         }
1219         if (timeout != INFINITE) {
1220             ticks = GetTickCount();
1221         }
1222         if (result == WAIT_OBJECT_0 + count) {
1223             /* Message has arrived - check it */
1224             (void)win32_async_check(aTHX);
1225         }
1226         else {
1227            /* Not timeout or message - one of handles is ready */
1228            break;
1229         }
1230     }
1231     /* compute time left to wait */
1232     ticks = timeout - ticks;
1233     /* If we are past the end say zero */
1234     return (ticks > 0) ? ticks : 0;
1235 }
1236
1237 static UINT timerid = 0;
1238
1239 static VOID CALLBACK TimerProc(HWND win, UINT msg, UINT id, DWORD time)
1240 {
1241     dTHX;
1242     KillTimer(NULL,timerid);
1243     timerid=0;
1244     sighandler(14);
1245 }
1246
1247 DllExport unsigned int
1248 win32_sleep(unsigned int t)
1249 {
1250   return xcesleep(t);
1251 }
1252
1253 DllExport unsigned int
1254 win32_alarm(unsigned int sec)
1255 {
1256     /*
1257      * the 'obvious' implentation is SetTimer() with a callback
1258      * which does whatever receiving SIGALRM would do
1259      * we cannot use SIGALRM even via raise() as it is not
1260      * one of the supported codes in <signal.h>
1261      *
1262      * Snag is unless something is looking at the message queue
1263      * nothing happens :-(
1264      */
1265     dTHX;
1266     if (sec)
1267      {
1268       timerid = SetTimer(NULL,timerid,sec*1000,(TIMERPROC)TimerProc);
1269       if (!timerid)
1270        Perl_croak_nocontext("Cannot set timer");
1271      }
1272     else
1273      {
1274       if (timerid)
1275        {
1276         KillTimer(NULL,timerid);
1277         timerid=0;
1278        }
1279      }
1280     return 0;
1281 }
1282
1283 #ifdef HAVE_DES_FCRYPT
1284 extern char *   des_fcrypt(const char *txt, const char *salt, char *cbuf);
1285 #endif
1286
1287 DllExport char *
1288 win32_crypt(const char *txt, const char *salt)
1289 {
1290     dTHX;
1291 #ifdef HAVE_DES_FCRYPT
1292     dTHR;
1293     return des_fcrypt(txt, salt, w32_crypt_buffer);
1294 #else
1295     Perl_croak(aTHX_ "The crypt() function is unimplemented due to excessive paranoia.");
1296     return NULL;
1297 #endif
1298 }
1299
1300
1301 /*
1302  *  redirected io subsystem for all XS modules
1303  *
1304  */
1305
1306 DllExport int *
1307 win32_errno(void)
1308 {
1309     return (&errno);
1310 }
1311
1312 DllExport char ***
1313 win32_environ(void)
1314 {
1315   return (&(environ));
1316 }
1317
1318 /* the rest are the remapped stdio routines */
1319 DllExport FILE *
1320 win32_stderr(void)
1321 {
1322     return (stderr);
1323 }
1324
1325 char *g_getlogin() {
1326     return "no-getlogin";
1327 }
1328
1329 DllExport FILE *
1330 win32_stdin(void)
1331 {
1332     return (stdin);
1333 }
1334
1335 DllExport FILE *
1336 win32_stdout()
1337 {
1338     return (stdout);
1339 }
1340
1341 DllExport int
1342 win32_ferror(FILE *fp)
1343 {
1344     return (ferror(fp));
1345 }
1346
1347
1348 DllExport int
1349 win32_feof(FILE *fp)
1350 {
1351     return (feof(fp));
1352 }
1353
1354 /*
1355  * Since the errors returned by the socket error function
1356  * WSAGetLastError() are not known by the library routine strerror
1357  * we have to roll our own.
1358  */
1359
1360 DllExport char *
1361 win32_strerror(int e)
1362 {
1363   return xcestrerror(e);
1364 }
1365
1366 DllExport void
1367 win32_str_os_error(void *sv, DWORD dwErr)
1368 {
1369   dTHX;
1370
1371   sv_setpvs((SV*)sv, "Error");
1372 }
1373
1374
1375 DllExport int
1376 win32_fprintf(FILE *fp, const char *format, ...)
1377 {
1378     va_list marker;
1379     va_start(marker, format);     /* Initialize variable arguments. */
1380
1381     return (vfprintf(fp, format, marker));
1382 }
1383
1384 DllExport int
1385 win32_printf(const char *format, ...)
1386 {
1387     va_list marker;
1388     va_start(marker, format);     /* Initialize variable arguments. */
1389
1390     return (vprintf(format, marker));
1391 }
1392
1393 DllExport int
1394 win32_vfprintf(FILE *fp, const char *format, va_list args)
1395 {
1396     return (vfprintf(fp, format, args));
1397 }
1398
1399 DllExport int
1400 win32_vprintf(const char *format, va_list args)
1401 {
1402     return (vprintf(format, args));
1403 }
1404
1405 DllExport size_t
1406 win32_fread(void *buf, size_t size, size_t count, FILE *fp)
1407 {
1408   return fread(buf, size, count, fp);
1409 }
1410
1411 DllExport size_t
1412 win32_fwrite(const void *buf, size_t size, size_t count, FILE *fp)
1413 {
1414   return fwrite(buf, size, count, fp);
1415 }
1416
1417 DllExport FILE *
1418 win32_fopen(const char *filename, const char *mode)
1419 {
1420   return xcefopen(filename, mode);
1421 }
1422
1423 DllExport FILE *
1424 win32_fdopen(int handle, const char *mode)
1425 {
1426   return palm_fdopen(handle, mode);
1427 }
1428
1429 DllExport FILE *
1430 win32_freopen(const char *path, const char *mode, FILE *stream)
1431 {
1432   return xcefreopen(path, mode, stream);
1433 }
1434
1435 DllExport int
1436 win32_fclose(FILE *pf)
1437 {
1438   return xcefclose(pf);
1439 }
1440
1441 DllExport int
1442 win32_fputs(const char *s,FILE *pf)
1443 {
1444   return fputs(s, pf);
1445 }
1446
1447 DllExport int
1448 win32_fputc(int c,FILE *pf)
1449 {
1450   return fputc(c,pf);
1451 }
1452
1453 DllExport int
1454 win32_ungetc(int c,FILE *pf)
1455 {
1456   return ungetc(c,pf);
1457 }
1458
1459 DllExport int
1460 win32_getc(FILE *pf)
1461 {
1462   return getc(pf);
1463 }
1464
1465 DllExport int
1466 win32_fileno(FILE *pf)
1467 {
1468   return palm_fileno(pf);
1469 }
1470
1471 DllExport void
1472 win32_clearerr(FILE *pf)
1473 {
1474   clearerr(pf);
1475   return;
1476 }
1477
1478 DllExport int
1479 win32_fflush(FILE *pf)
1480 {
1481   return fflush(pf);
1482 }
1483
1484 DllExport long
1485 win32_ftell(FILE *pf)
1486 {
1487   return ftell(pf);
1488 }
1489
1490 DllExport int
1491 win32_fseek(FILE *pf, Off_t offset,int origin)
1492 {
1493   return fseek(pf, offset, origin);
1494 }
1495
1496 /* fpos_t seems to be int64 on hpc pro! Really stupid. */
1497 /* But maybe someday there will be such large disks in a hpc... */
1498 DllExport int
1499 win32_fgetpos(FILE *pf, fpos_t *p)
1500 {
1501   return fgetpos(pf, p);
1502 }
1503
1504 DllExport int
1505 win32_fsetpos(FILE *pf, const fpos_t *p)
1506 {
1507   return fsetpos(pf, p);
1508 }
1509
1510 DllExport void
1511 win32_rewind(FILE *pf)
1512 {
1513   fseek(pf, 0, SEEK_SET);
1514   return;
1515 }
1516
1517 DllExport int
1518 win32_tmpfd(void)
1519 {
1520     dTHX;
1521     char prefix[MAX_PATH+1];
1522     char filename[MAX_PATH+1];
1523     DWORD len = GetTempPath(MAX_PATH, prefix);
1524     if (len && len < MAX_PATH) {
1525         if (GetTempFileName(prefix, "plx", 0, filename)) {
1526             HANDLE fh = CreateFile(filename,
1527                                    DELETE | GENERIC_READ | GENERIC_WRITE,
1528                                    0,
1529                                    NULL,
1530                                    CREATE_ALWAYS,
1531                                    FILE_ATTRIBUTE_NORMAL
1532                                    | FILE_FLAG_DELETE_ON_CLOSE,
1533                                    NULL);
1534             if (fh != INVALID_HANDLE_VALUE) {
1535                 int fd = win32_open_osfhandle((intptr_t)fh, 0);
1536                 if (fd >= 0) {
1537                     DEBUG_p(PerlIO_printf(Perl_debug_log,
1538                                           "Created tmpfile=%s\n",filename));
1539                     return fd;
1540                 }
1541             }
1542         }
1543     }
1544     return -1;
1545 }
1546
1547 DllExport FILE*
1548 win32_tmpfile(void)
1549 {
1550     int fd = win32_tmpfd();
1551     if (fd >= 0)
1552         return win32_fdopen(fd, "w+b");
1553     return NULL;
1554 }
1555
1556 DllExport void
1557 win32_abort(void)
1558 {
1559   xceabort();
1560
1561   return;
1562 }
1563
1564 DllExport int
1565 win32_fstat(int fd, struct stat *sbufptr)
1566 {
1567   return xcefstat(fd, sbufptr);
1568 }
1569
1570 DllExport int
1571 win32_link(const char *oldname, const char *newname)
1572 {
1573   dTHX;
1574   Perl_croak(aTHX_ PL_no_func, "link");
1575
1576   return -1;
1577 }
1578
1579 DllExport int
1580 win32_rename(const char *oname, const char *newname)
1581 {
1582   return xcerename(oname, newname);
1583 }
1584
1585 DllExport int
1586 win32_setmode(int fd, int mode)
1587 {
1588     /* currently 'celib' seem to have this function in src, but not
1589      * exported. When it will be, we'll uncomment following line.
1590      */
1591     /* return xcesetmode(fd, mode); */
1592     return 0;
1593 }
1594
1595 DllExport int
1596 win32_chsize(int fd, Off_t size)
1597 {
1598     return chsize(fd, size);
1599 }
1600
1601 DllExport long
1602 win32_lseek(int fd, Off_t offset, int origin)
1603 {
1604   return xcelseek(fd, offset, origin);
1605 }
1606
1607 DllExport long
1608 win32_tell(int fd)
1609 {
1610   return xcelseek(fd, 0, SEEK_CUR);
1611 }
1612
1613 DllExport int
1614 win32_open(const char *path, int flag, ...)
1615 {
1616   int pmode;
1617   va_list ap;
1618
1619   va_start(ap, flag);
1620   pmode = va_arg(ap, int);
1621   va_end(ap);
1622
1623   return xceopen(path, flag, pmode);
1624 }
1625
1626 DllExport int
1627 win32_close(int fd)
1628 {
1629   return xceclose(fd);
1630 }
1631
1632 DllExport int
1633 win32_eof(int fd)
1634 {
1635   dTHX;
1636   Perl_croak(aTHX_ PL_no_func, "eof");
1637   return -1;
1638 }
1639
1640 DllExport int
1641 win32_dup(int fd)
1642 {
1643   return xcedup(fd); /* from celib/ceio.c; requires some more work on it */
1644 }
1645
1646 DllExport int
1647 win32_dup2(int fd1,int fd2)
1648 {
1649   return xcedup2(fd1,fd2);
1650 }
1651
1652 DllExport int
1653 win32_read(int fd, void *buf, unsigned int cnt)
1654 {
1655   return xceread(fd, buf, cnt);
1656 }
1657
1658 DllExport int
1659 win32_write(int fd, const void *buf, unsigned int cnt)
1660 {
1661   return xcewrite(fd, (void *) buf, cnt);
1662 }
1663
1664 DllExport int
1665 win32_mkdir(const char *dir, int mode)
1666 {
1667   return xcemkdir(dir);
1668 }
1669
1670 DllExport int
1671 win32_rmdir(const char *dir)
1672 {
1673   return xcermdir(dir);
1674 }
1675
1676 DllExport int
1677 win32_chdir(const char *dir)
1678 {
1679   return xcechdir(dir);
1680 }
1681
1682 DllExport  int
1683 win32_access(const char *path, int mode)
1684 {
1685   return xceaccess(path, mode);
1686 }
1687
1688 DllExport  int
1689 win32_chmod(const char *path, int mode)
1690 {
1691   return xcechmod(path, mode);
1692 }
1693
1694 static char *
1695 create_command_line(char *cname, STRLEN clen, const char * const *args)
1696 {
1697     dTHX;
1698     int index, argc;
1699     char *cmd, *ptr;
1700     const char *arg;
1701     STRLEN len = 0;
1702     bool bat_file = FALSE;
1703     bool cmd_shell = FALSE;
1704     bool dumb_shell = FALSE;
1705     bool extra_quotes = FALSE;
1706     bool quote_next = FALSE;
1707
1708     if (!cname)
1709         cname = (char*)args[0];
1710
1711     /* The NT cmd.exe shell has the following peculiarity that needs to be
1712      * worked around.  It strips a leading and trailing dquote when any
1713      * of the following is true:
1714      *    1. the /S switch was used
1715      *    2. there are more than two dquotes
1716      *    3. there is a special character from this set: &<>()@^|
1717      *    4. no whitespace characters within the two dquotes
1718      *    5. string between two dquotes isn't an executable file
1719      * To work around this, we always add a leading and trailing dquote
1720      * to the string, if the first argument is either "cmd.exe" or "cmd",
1721      * and there were at least two or more arguments passed to cmd.exe
1722      * (not including switches).
1723      * XXX the above rules (from "cmd /?") don't seem to be applied
1724      * always, making for the convolutions below :-(
1725      */
1726     if (cname) {
1727         if (!clen)
1728             clen = strlen(cname);
1729
1730         if (clen > 4
1731             && (stricmp(&cname[clen-4], ".bat") == 0
1732                 || (IsWinNT() && stricmp(&cname[clen-4], ".cmd") == 0)))
1733         {
1734             bat_file = TRUE;
1735             len += 3;
1736         }
1737         else {
1738             char *exe = strrchr(cname, '/');
1739             char *exe2 = strrchr(cname, '\\');
1740             if (exe2 > exe)
1741                 exe = exe2;
1742             if (exe)
1743                 ++exe;
1744             else
1745                 exe = cname;
1746             if (stricmp(exe, "cmd.exe") == 0 || stricmp(exe, "cmd") == 0) {
1747                 cmd_shell = TRUE;
1748                 len += 3;
1749             }
1750             else if (stricmp(exe, "command.com") == 0
1751                      || stricmp(exe, "command") == 0)
1752             {
1753                 dumb_shell = TRUE;
1754             }
1755         }
1756     }
1757
1758     DEBUG_p(PerlIO_printf(Perl_debug_log, "Args "));
1759     for (index = 0; (arg = (char*)args[index]) != NULL; ++index) {
1760         STRLEN curlen = strlen(arg);
1761         if (!(arg[0] == '"' && arg[curlen-1] == '"'))
1762             len += 2;   /* assume quoting needed (worst case) */
1763         len += curlen + 1;
1764         DEBUG_p(PerlIO_printf(Perl_debug_log, "[%s]",arg));
1765     }
1766     DEBUG_p(PerlIO_printf(Perl_debug_log, "\n"));
1767
1768     argc = index;
1769     Newx(cmd, len, char);
1770     ptr = cmd;
1771
1772     if (bat_file) {
1773         *ptr++ = '"';
1774         extra_quotes = TRUE;
1775     }
1776
1777     for (index = 0; (arg = (char*)args[index]) != NULL; ++index) {
1778         bool do_quote = 0;
1779         STRLEN curlen = strlen(arg);
1780
1781         /* we want to protect empty arguments and ones with spaces with
1782          * dquotes, but only if they aren't already there */
1783         if (!dumb_shell) {
1784             if (!curlen) {
1785                 do_quote = 1;
1786             }
1787             else if (quote_next) {
1788                 /* see if it really is multiple arguments pretending to
1789                  * be one and force a set of quotes around it */
1790                 if (*find_next_space(arg))
1791                     do_quote = 1;
1792             }
1793             else if (!(arg[0] == '"' && curlen > 1 && arg[curlen-1] == '"')) {
1794                 STRLEN i = 0;
1795                 while (i < curlen) {
1796                     if (isSPACE(arg[i])) {
1797                         do_quote = 1;
1798                     }
1799                     else if (arg[i] == '"') {
1800                         do_quote = 0;
1801                         break;
1802                     }
1803                     i++;
1804                 }
1805             }
1806         }
1807
1808         if (do_quote)
1809             *ptr++ = '"';
1810
1811         strcpy(ptr, arg);
1812         ptr += curlen;
1813
1814         if (do_quote)
1815             *ptr++ = '"';
1816
1817         if (args[index+1])
1818             *ptr++ = ' ';
1819
1820         if (!extra_quotes
1821             && cmd_shell
1822             && curlen >= 2
1823             && *arg  == '/'     /* see if arg is "/c", "/x/c", "/x/d/c" etc. */
1824             && stricmp(arg+curlen-2, "/c") == 0)
1825         {
1826             /* is there a next argument? */
1827             if (args[index+1]) {
1828                 /* are there two or more next arguments? */
1829                 if (args[index+2]) {
1830                     *ptr++ = '"';
1831                     extra_quotes = TRUE;
1832                 }
1833                 else {
1834                     /* single argument, force quoting if it has spaces */
1835                     quote_next = TRUE;
1836                 }
1837             }
1838         }
1839     }
1840
1841     if (extra_quotes)
1842         *ptr++ = '"';
1843
1844     *ptr = '\0';
1845
1846     return cmd;
1847 }
1848
1849 static char *
1850 qualified_path(const char *cmd)
1851 {
1852     dTHX;
1853     char *pathstr;
1854     char *fullcmd, *curfullcmd;
1855     STRLEN cmdlen = 0;
1856     int has_slash = 0;
1857
1858     if (!cmd)
1859         return NULL;
1860     fullcmd = (char*)cmd;
1861     while (*fullcmd) {
1862         if (*fullcmd == '/' || *fullcmd == '\\')
1863             has_slash++;
1864         fullcmd++;
1865         cmdlen++;
1866     }
1867
1868     /* look in PATH */
1869     pathstr = PerlEnv_getenv("PATH");
1870     Newx(fullcmd, MAX_PATH+1, char);
1871     curfullcmd = fullcmd;
1872
1873     while (1) {
1874         DWORD res;
1875
1876         /* start by appending the name to the current prefix */
1877         strcpy(curfullcmd, cmd);
1878         curfullcmd += cmdlen;
1879
1880         /* if it doesn't end with '.', or has no extension, try adding
1881          * a trailing .exe first */
1882         if (cmd[cmdlen-1] != '.'
1883             && (cmdlen < 4 || cmd[cmdlen-4] != '.'))
1884         {
1885             strcpy(curfullcmd, ".exe");
1886             res = GetFileAttributes(fullcmd);
1887             if (res != 0xFFFFFFFF && !(res & FILE_ATTRIBUTE_DIRECTORY))
1888                 return fullcmd;
1889             *curfullcmd = '\0';
1890         }
1891
1892         /* that failed, try the bare name */
1893         res = GetFileAttributes(fullcmd);
1894         if (res != 0xFFFFFFFF && !(res & FILE_ATTRIBUTE_DIRECTORY))
1895             return fullcmd;
1896
1897         /* quit if no other path exists, or if cmd already has path */
1898         if (!pathstr || !*pathstr || has_slash)
1899             break;
1900
1901         /* skip leading semis */
1902         while (*pathstr == ';')
1903             pathstr++;
1904
1905         /* build a new prefix from scratch */
1906         curfullcmd = fullcmd;
1907         while (*pathstr && *pathstr != ';') {
1908             if (*pathstr == '"') {      /* foo;"baz;etc";bar */
1909                 pathstr++;              /* skip initial '"' */
1910                 while (*pathstr && *pathstr != '"') {
1911                     if ((STRLEN)(curfullcmd-fullcmd) < MAX_PATH-cmdlen-5)
1912                         *curfullcmd++ = *pathstr;
1913                     pathstr++;
1914                 }
1915                 if (*pathstr)
1916                     pathstr++;          /* skip trailing '"' */
1917             }
1918             else {
1919                 if ((STRLEN)(curfullcmd-fullcmd) < MAX_PATH-cmdlen-5)
1920                     *curfullcmd++ = *pathstr;
1921                 pathstr++;
1922             }
1923         }
1924         if (*pathstr)
1925             pathstr++;                  /* skip trailing semi */
1926         if (curfullcmd > fullcmd        /* append a dir separator */
1927             && curfullcmd[-1] != '/' && curfullcmd[-1] != '\\')
1928         {
1929             *curfullcmd++ = '\\';
1930         }
1931     }
1932
1933     Safefree(fullcmd);
1934     return NULL;
1935 }
1936
1937 /* The following are just place holders.
1938  * Some hosts may provide and environment that the OS is
1939  * not tracking, therefore, these host must provide that
1940  * environment and the current directory to CreateProcess
1941  */
1942
1943 DllExport void*
1944 win32_get_childenv(void)
1945 {
1946     return NULL;
1947 }
1948
1949 DllExport void
1950 win32_free_childenv(void* d)
1951 {
1952 }
1953
1954 DllExport void
1955 win32_clearenv(void)
1956 {
1957     char *envv = GetEnvironmentStrings();
1958     char *cur = envv;
1959     STRLEN len;
1960     while (*cur) {
1961         char *end = strchr(cur,'=');
1962         if (end && end != cur) {
1963             *end = '\0';
1964             xcesetenv(cur, "", 0);
1965             *end = '=';
1966             cur = end + strlen(end+1)+2;
1967         }
1968         else if ((len = strlen(cur)))
1969             cur += len+1;
1970     }
1971     FreeEnvironmentStrings(envv);
1972 }
1973
1974 DllExport char*
1975 win32_get_childdir(void)
1976 {
1977     dTHX;
1978     char* ptr;
1979     char szfilename[MAX_PATH+1];
1980     GetCurrentDirectoryA(MAX_PATH+1, szfilename);
1981
1982     Newx(ptr, strlen(szfilename)+1, char);
1983     strcpy(ptr, szfilename);
1984     return ptr;
1985 }
1986
1987 DllExport void
1988 win32_free_childdir(char* d)
1989 {
1990     dTHX;
1991     Safefree(d);
1992 }
1993
1994 /* XXX this needs to be made more compatible with the spawnvp()
1995  * provided by the various RTLs.  In particular, searching for
1996  * *.{com,bat,cmd} files (as done by the RTLs) is unimplemented.
1997  * This doesn't significantly affect perl itself, because we
1998  * always invoke things using PERL5SHELL if a direct attempt to
1999  * spawn the executable fails.
2000  *
2001  * XXX splitting and rejoining the commandline between do_aspawn()
2002  * and win32_spawnvp() could also be avoided.
2003  */
2004
2005 DllExport int
2006 win32_spawnvp(int mode, const char *cmdname, const char *const *argv)
2007 {
2008 #ifdef USE_RTL_SPAWNVP
2009     return spawnvp(mode, cmdname, (char * const *)argv);
2010 #else
2011     dTHX;
2012     int ret;
2013     void* env;
2014     char* dir;
2015     child_IO_table tbl;
2016     STARTUPINFO StartupInfo;
2017     PROCESS_INFORMATION ProcessInformation;
2018     DWORD create = 0;
2019     char *cmd;
2020     char *fullcmd = NULL;
2021     char *cname = (char *)cmdname;
2022     STRLEN clen = 0;
2023
2024     if (cname) {
2025         clen = strlen(cname);
2026         /* if command name contains dquotes, must remove them */
2027         if (strchr(cname, '"')) {
2028             cmd = cname;
2029             Newx(cname,clen+1,char);
2030             clen = 0;
2031             while (*cmd) {
2032                 if (*cmd != '"') {
2033                     cname[clen] = *cmd;
2034                     ++clen;
2035                 }
2036                 ++cmd;
2037             }
2038             cname[clen] = '\0';
2039         }
2040     }
2041
2042     cmd = create_command_line(cname, clen, argv);
2043
2044     env = PerlEnv_get_childenv();
2045     dir = PerlEnv_get_childdir();
2046
2047     switch(mode) {
2048     case P_NOWAIT:      /* asynch + remember result */
2049         if (w32_num_children >= MAXIMUM_WAIT_OBJECTS) {
2050             errno = EAGAIN;
2051             ret = -1;
2052             goto RETVAL;
2053         }
2054         /* Create a new process group so we can use GenerateConsoleCtrlEvent()
2055          * in win32_kill()
2056          */
2057         /* not supported on CE create |= CREATE_NEW_PROCESS_GROUP; */
2058         /* FALL THROUGH */
2059
2060     case P_WAIT:        /* synchronous execution */
2061         break;
2062     default:            /* invalid mode */
2063         errno = EINVAL;
2064         ret = -1;
2065         goto RETVAL;
2066     }
2067     memset(&StartupInfo,0,sizeof(StartupInfo));
2068     StartupInfo.cb = sizeof(StartupInfo);
2069     memset(&tbl,0,sizeof(tbl));
2070     PerlEnv_get_child_IO(&tbl);
2071     StartupInfo.dwFlags         = tbl.dwFlags;
2072     StartupInfo.dwX             = tbl.dwX;
2073     StartupInfo.dwY             = tbl.dwY;
2074     StartupInfo.dwXSize         = tbl.dwXSize;
2075     StartupInfo.dwYSize         = tbl.dwYSize;
2076     StartupInfo.dwXCountChars   = tbl.dwXCountChars;
2077     StartupInfo.dwYCountChars   = tbl.dwYCountChars;
2078     StartupInfo.dwFillAttribute = tbl.dwFillAttribute;
2079     StartupInfo.wShowWindow     = tbl.wShowWindow;
2080     StartupInfo.hStdInput       = tbl.childStdIn;
2081     StartupInfo.hStdOutput      = tbl.childStdOut;
2082     StartupInfo.hStdError       = tbl.childStdErr;
2083     if (StartupInfo.hStdInput == INVALID_HANDLE_VALUE &&
2084         StartupInfo.hStdOutput == INVALID_HANDLE_VALUE &&
2085         StartupInfo.hStdError == INVALID_HANDLE_VALUE)
2086     {
2087         create |= CREATE_NEW_CONSOLE;
2088     }
2089     else {
2090         StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
2091     }
2092     if (w32_use_showwindow) {
2093         StartupInfo.dwFlags |= STARTF_USESHOWWINDOW;
2094         StartupInfo.wShowWindow = w32_showwindow;
2095     }
2096
2097     DEBUG_p(PerlIO_printf(Perl_debug_log, "Spawning [%s] with [%s]\n",
2098                           cname,cmd));
2099 RETRY:
2100     if (!CreateProcess(cname,           /* search PATH to find executable */
2101                        cmd,             /* executable, and its arguments */
2102                        NULL,            /* process attributes */
2103                        NULL,            /* thread attributes */
2104                        TRUE,            /* inherit handles */
2105                        create,          /* creation flags */
2106                        (LPVOID)env,     /* inherit environment */
2107                        dir,             /* inherit cwd */
2108                        &StartupInfo,
2109                        &ProcessInformation))
2110     {
2111         /* initial NULL argument to CreateProcess() does a PATH
2112          * search, but it always first looks in the directory
2113          * where the current process was started, which behavior
2114          * is undesirable for backward compatibility.  So we
2115          * jump through our own hoops by picking out the path
2116          * we really want it to use. */
2117         if (!fullcmd) {
2118             fullcmd = qualified_path(cname);
2119             if (fullcmd) {
2120                 if (cname != cmdname)
2121                     Safefree(cname);
2122                 cname = fullcmd;
2123                 DEBUG_p(PerlIO_printf(Perl_debug_log,
2124                                       "Retrying [%s] with same args\n",
2125                                       cname));
2126                 goto RETRY;
2127             }
2128         }
2129         errno = ENOENT;
2130         ret = -1;
2131         goto RETVAL;
2132     }
2133
2134     if (mode == P_NOWAIT) {
2135         /* asynchronous spawn -- store handle, return PID */
2136         ret = (int)ProcessInformation.dwProcessId;
2137         if (IsWin95() && ret < 0)
2138             ret = -ret;
2139
2140         w32_child_handles[w32_num_children] = ProcessInformation.hProcess;
2141         w32_child_pids[w32_num_children] = (DWORD)ret;
2142         ++w32_num_children;
2143     }
2144     else  {
2145         DWORD status;
2146         win32_msgwait(aTHX_ 1, &ProcessInformation.hProcess, INFINITE, NULL);
2147         /* FIXME: if msgwait returned due to message perhaps forward the
2148            "signal" to the process
2149          */
2150         GetExitCodeProcess(ProcessInformation.hProcess, &status);
2151         ret = (int)status;
2152         CloseHandle(ProcessInformation.hProcess);
2153     }
2154
2155     CloseHandle(ProcessInformation.hThread);
2156
2157 RETVAL:
2158     PerlEnv_free_childenv(env);
2159     PerlEnv_free_childdir(dir);
2160     Safefree(cmd);
2161     if (cname != cmdname)
2162         Safefree(cname);
2163     return ret;
2164 #endif
2165 }
2166
2167 DllExport int
2168 win32_execv(const char *cmdname, const char *const *argv)
2169 {
2170   dTHX;
2171   Perl_croak(aTHX_ PL_no_func, "execv");
2172   return -1;
2173 }
2174
2175 DllExport int
2176 win32_execvp(const char *cmdname, const char *const *argv)
2177 {
2178   dTHX;
2179   Perl_croak(aTHX_ PL_no_func, "execvp");
2180   return -1;
2181 }
2182
2183 DllExport void
2184 win32_perror(const char *str)
2185 {
2186   xceperror(str);
2187 }
2188
2189 DllExport void
2190 win32_setbuf(FILE *pf, char *buf)
2191 {
2192   dTHX;
2193   Perl_croak(aTHX_ PL_no_func, "setbuf");
2194 }
2195
2196 DllExport int
2197 win32_setvbuf(FILE *pf, char *buf, int type, size_t size)
2198 {
2199   return setvbuf(pf, buf, type, size);
2200 }
2201
2202 DllExport int
2203 win32_flushall(void)
2204 {
2205   return flushall();
2206 }
2207
2208 DllExport int
2209 win32_fcloseall(void)
2210 {
2211   return fcloseall();
2212 }
2213
2214 DllExport char*
2215 win32_fgets(char *s, int n, FILE *pf)
2216 {
2217   return fgets(s, n, pf);
2218 }
2219
2220 DllExport char*
2221 win32_gets(char *s)
2222 {
2223   return gets(s);
2224 }
2225
2226 DllExport int
2227 win32_fgetc(FILE *pf)
2228 {
2229   return fgetc(pf);
2230 }
2231
2232 DllExport int
2233 win32_putc(int c, FILE *pf)
2234 {
2235   return putc(c,pf);
2236 }
2237
2238 DllExport int
2239 win32_puts(const char *s)
2240 {
2241   return puts(s);
2242 }
2243
2244 DllExport int
2245 win32_getchar(void)
2246 {
2247   return getchar();
2248 }
2249
2250 DllExport int
2251 win32_putchar(int c)
2252 {
2253   return putchar(c);
2254 }
2255
2256 #ifdef MYMALLOC
2257
2258 #ifndef USE_PERL_SBRK
2259
2260 static char *committed = NULL;
2261 static char *base      = NULL;
2262 static char *reserved  = NULL;
2263 static char *brk       = NULL;
2264 static DWORD pagesize  = 0;
2265 static DWORD allocsize = 0;
2266
2267 void *
2268 sbrk(int need)
2269 {
2270  void *result;
2271  if (!pagesize)
2272   {SYSTEM_INFO info;
2273    GetSystemInfo(&info);
2274    /* Pretend page size is larger so we don't perpetually
2275     * call the OS to commit just one page ...
2276     */
2277    pagesize = info.dwPageSize << 3;
2278    allocsize = info.dwAllocationGranularity;
2279   }
2280  /* This scheme fails eventually if request for contiguous
2281   * block is denied so reserve big blocks - this is only
2282   * address space not memory ...
2283   */
2284  if (brk+need >= reserved)
2285   {
2286    DWORD size = 64*1024*1024;
2287    char *addr;
2288    if (committed && reserved && committed < reserved)
2289     {
2290      /* Commit last of previous chunk cannot span allocations */
2291      addr = (char *) VirtualAlloc(committed,reserved-committed,MEM_COMMIT,PAGE_READWRITE);
2292      if (addr)
2293       committed = reserved;
2294     }
2295    /* Reserve some (more) space
2296     * Note this is a little sneaky, 1st call passes NULL as reserved
2297     * so lets system choose where we start, subsequent calls pass
2298     * the old end address so ask for a contiguous block
2299     */
2300    addr  = (char *) VirtualAlloc(reserved,size,MEM_RESERVE,PAGE_NOACCESS);
2301    if (addr)
2302     {
2303      reserved = addr+size;
2304      if (!base)
2305       base = addr;
2306      if (!committed)
2307       committed = base;
2308      if (!brk)
2309       brk = committed;
2310     }
2311    else
2312     {
2313      return (void *) -1;
2314     }
2315   }
2316  result = brk;
2317  brk += need;
2318  if (brk > committed)
2319   {
2320    DWORD size = ((brk-committed + pagesize -1)/pagesize) * pagesize;
2321    char *addr = (char *) VirtualAlloc(committed,size,MEM_COMMIT,PAGE_READWRITE);
2322    if (addr)
2323     {
2324      committed += size;
2325     }
2326    else
2327     return (void *) -1;
2328   }
2329  return result;
2330 }
2331
2332 #endif
2333 #endif
2334
2335 DllExport void*
2336 win32_malloc(size_t size)
2337 {
2338     return malloc(size);
2339 }
2340
2341 DllExport void*
2342 win32_calloc(size_t numitems, size_t size)
2343 {
2344     return calloc(numitems,size);
2345 }
2346
2347 DllExport void*
2348 win32_realloc(void *block, size_t size)
2349 {
2350     return realloc(block,size);
2351 }
2352
2353 DllExport void
2354 win32_free(void *block)
2355 {
2356     free(block);
2357 }
2358
2359 int
2360 win32_open_osfhandle(intptr_t osfhandle, int flags)
2361 {
2362     int fh;
2363     char fileflags=0;           /* _osfile flags */
2364
2365     Perl_croak_nocontext("win32_open_osfhandle() TBD on this platform");
2366     return 0;
2367 }
2368
2369 int
2370 win32_get_osfhandle(int fd)
2371 {
2372     int fh;
2373     char fileflags=0;           /* _osfile flags */
2374
2375     Perl_croak_nocontext("win32_get_osfhandle() TBD on this platform");
2376     return 0;
2377 }
2378
2379 FILE *
2380 win32_fdupopen(FILE *pf)
2381 {
2382     FILE* pfdup;
2383     fpos_t pos;
2384     char mode[3];
2385     int fileno = win32_dup(win32_fileno(pf));
2386     int fmode = palm_fgetmode(pfdup);
2387
2388     fprintf(stderr,"DEBUG for win32_fdupopen()\n");
2389
2390     /* open the file in the same mode */
2391     if(fmode & O_RDONLY) {
2392         mode[0] = 'r';
2393         mode[1] = 0;
2394     }
2395     else if(fmode & O_APPEND) {
2396         mode[0] = 'a';
2397         mode[1] = 0;
2398     }
2399     else if(fmode & O_RDWR) {
2400         mode[0] = 'r';
2401         mode[1] = '+';
2402         mode[2] = 0;
2403     }
2404
2405     /* it appears that the binmode is attached to the
2406      * file descriptor so binmode files will be handled
2407      * correctly
2408      */
2409     pfdup = win32_fdopen(fileno, mode);
2410
2411     /* move the file pointer to the same position */
2412     if (!fgetpos(pf, &pos)) {
2413         fsetpos(pfdup, &pos);
2414     }
2415     return pfdup;
2416 }
2417
2418 DllExport void*
2419 win32_dynaload(const char* filename)
2420 {
2421     dTHX;
2422     HMODULE hModule;
2423
2424     hModule = XCELoadLibraryA(filename);
2425
2426     return hModule;
2427 }
2428
2429 /* this is needed by Cwd.pm... */
2430
2431 static
2432 XS(w32_GetCwd)
2433 {
2434   dXSARGS;
2435   char buf[MAX_PATH];
2436   SV *sv = sv_newmortal();
2437
2438   xcegetcwd(buf, sizeof(buf));
2439
2440   sv_setpv(sv, xcestrdup(buf));
2441   EXTEND(SP,1);
2442   SvPOK_on(sv);
2443   ST(0) = sv;
2444   SvTAINTED_on(ST(0));
2445   XSRETURN(1);
2446 }
2447
2448 static
2449 XS(w32_SetCwd)
2450 {
2451   dXSARGS;
2452
2453   if (items != 1)
2454     Perl_croak(aTHX_ "usage: Win32::SetCwd($cwd)");
2455
2456   if (!xcechdir(SvPV_nolen(ST(0))))
2457     XSRETURN_YES;
2458
2459   XSRETURN_NO;
2460 }
2461
2462 static
2463 XS(w32_GetTickCount)
2464 {
2465     dXSARGS;
2466     DWORD msec = GetTickCount();
2467     EXTEND(SP,1);
2468     if ((IV)msec > 0)
2469         XSRETURN_IV(msec);
2470     XSRETURN_NV(msec);
2471 }
2472
2473 static
2474 XS(w32_GetOSVersion)
2475 {
2476     dXSARGS;
2477     OSVERSIONINFOA osver;
2478
2479     osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
2480     if (!XCEGetVersionExA(&osver)) {
2481       XSRETURN_EMPTY;
2482     }
2483     mXPUSHp(osver.szCSDVersion, strlen(osver.szCSDVersion));
2484     mXPUSHi(osver.dwMajorVersion);
2485     mXPUSHi(osver.dwMinorVersion);
2486     mXPUSHi(osver.dwBuildNumber);
2487     /* WINCE = 3 */
2488     mXPUSHi(osver.dwPlatformId);
2489     PUTBACK;
2490 }
2491
2492 static
2493 XS(w32_IsWinNT)
2494 {
2495     dXSARGS;
2496     EXTEND(SP,1);
2497     XSRETURN_IV(IsWinNT());
2498 }
2499
2500 static
2501 XS(w32_IsWin95)
2502 {
2503     dXSARGS;
2504     EXTEND(SP,1);
2505     XSRETURN_IV(IsWin95());
2506 }
2507
2508 static
2509 XS(w32_IsWinCE)
2510 {
2511     dXSARGS;
2512     EXTEND(SP,1);
2513     XSRETURN_IV(IsWinCE());
2514 }
2515
2516 static
2517 XS(w32_GetOemInfo)
2518 {
2519   dXSARGS;
2520   wchar_t wbuf[126];
2521   char buf[126];
2522
2523   if(SystemParametersInfoW(SPI_GETOEMINFO, sizeof(wbuf), wbuf, FALSE))
2524     WideCharToMultiByte(CP_ACP, 0, wbuf, -1, buf, sizeof(buf), 0, 0);
2525   else
2526     sprintf(buf, "SystemParametersInfo failed: %d", GetLastError());
2527
2528   EXTEND(SP,1);
2529   XSRETURN_PV(buf);
2530 }
2531
2532 static
2533 XS(w32_Sleep)
2534 {
2535     dXSARGS;
2536     if (items != 1)
2537         Perl_croak(aTHX_ "usage: Win32::Sleep($milliseconds)");
2538     Sleep(SvIV(ST(0)));
2539     XSRETURN_YES;
2540 }
2541
2542 static
2543 XS(w32_CopyFile)
2544 {
2545     dXSARGS;
2546     BOOL bResult;
2547     if (items != 3)
2548         Perl_croak(aTHX_ "usage: Win32::CopyFile($from, $to, $overwrite)");
2549
2550     {
2551       char szSourceFile[MAX_PATH+1];
2552       strcpy(szSourceFile, PerlDir_mapA(SvPV_nolen(ST(0))));
2553       bResult = XCECopyFileA(szSourceFile, SvPV_nolen(ST(1)),
2554                              !SvTRUE(ST(2)));
2555     }
2556
2557     if (bResult)
2558         XSRETURN_YES;
2559
2560     XSRETURN_NO;
2561 }
2562
2563 static
2564 XS(w32_MessageBox)
2565 {
2566     dXSARGS;
2567
2568     char *txt;
2569     unsigned int res;
2570     unsigned int flags = MB_OK;
2571
2572     txt = SvPV_nolen(ST(0));
2573
2574     if (items < 1 || items > 2)
2575         Perl_croak(aTHX_ "usage: Win32::MessageBox($txt, [$flags])");
2576
2577     if(items == 2)
2578       flags = SvIV(ST(1));
2579
2580     res = XCEMessageBoxA(NULL, txt, "Perl", flags);
2581
2582     XSRETURN_IV(res);
2583 }
2584
2585 static
2586 XS(w32_GetPowerStatus)
2587 {
2588   dXSARGS;
2589
2590   SYSTEM_POWER_STATUS_EX sps;
2591
2592   if(GetSystemPowerStatusEx(&sps, TRUE) == FALSE)
2593     {
2594       XSRETURN_EMPTY;
2595     }
2596
2597   mXPUSHi(sps.ACLineStatus);
2598   mXPUSHi(sps.BatteryFlag);
2599   mXPUSHi(sps.BatteryLifePercent);
2600   mXPUSHi(sps.BatteryLifeTime);
2601   mXPUSHi(sps.BatteryFullLifeTime);
2602   mXPUSHi(sps.BackupBatteryFlag);
2603   mXPUSHi(sps.BackupBatteryLifePercent);
2604   mXPUSHi(sps.BackupBatteryLifeTime);
2605   mXPUSHi(sps.BackupBatteryFullLifeTime);
2606
2607   PUTBACK;
2608 }
2609
2610 #if UNDER_CE > 200
2611 static
2612 XS(w32_ShellEx)
2613 {
2614   dXSARGS;
2615
2616   char buf[126];
2617   SHELLEXECUTEINFO si;
2618   char *file, *verb;
2619   wchar_t wfile[MAX_PATH];
2620   wchar_t wverb[20];
2621
2622   if (items != 2)
2623     Perl_croak(aTHX_ "usage: Win32::ShellEx($file, $verb)");
2624
2625   file = SvPV_nolen(ST(0));
2626   verb = SvPV_nolen(ST(1));
2627
2628   memset(&si, 0, sizeof(si));
2629   si.cbSize = sizeof(si);
2630   si.fMask = SEE_MASK_FLAG_NO_UI;
2631
2632   MultiByteToWideChar(CP_ACP, 0, verb, -1,
2633                       wverb, sizeof(wverb)/2);
2634   si.lpVerb = (TCHAR *)wverb;
2635
2636   MultiByteToWideChar(CP_ACP, 0, file, -1,
2637                       wfile, sizeof(wfile)/2);
2638   si.lpFile = (TCHAR *)wfile;
2639
2640   if(ShellExecuteEx(&si) == FALSE)
2641     {
2642       XSRETURN_NO;
2643     }
2644   XSRETURN_YES;
2645 }
2646 #endif
2647
2648 void
2649 Perl_init_os_extras(void)
2650 {
2651     dTHX;
2652     char *file = __FILE__;
2653     dXSUB_SYS;
2654
2655     w32_perlshell_tokens = NULL;
2656     w32_perlshell_items = -1;
2657     w32_fdpid = newAV(); /* XX needs to be in Perl_win32_init()? */
2658     Newx(w32_children, 1, child_tab);
2659     w32_num_children = 0;
2660
2661     newXS("Win32::GetCwd", w32_GetCwd, file);
2662     newXS("Win32::SetCwd", w32_SetCwd, file);
2663     newXS("Win32::GetTickCount", w32_GetTickCount, file);
2664     newXS("Win32::GetOSVersion", w32_GetOSVersion, file);
2665 #if UNDER_CE > 200
2666     newXS("Win32::ShellEx", w32_ShellEx, file);
2667 #endif
2668     newXS("Win32::IsWinNT", w32_IsWinNT, file);
2669     newXS("Win32::IsWin95", w32_IsWin95, file);
2670     newXS("Win32::IsWinCE", w32_IsWinCE, file);
2671     newXS("Win32::CopyFile", w32_CopyFile, file);
2672     newXS("Win32::Sleep", w32_Sleep, file);
2673     newXS("Win32::MessageBox", w32_MessageBox, file);
2674     newXS("Win32::GetPowerStatus", w32_GetPowerStatus, file);
2675     newXS("Win32::GetOemInfo", w32_GetOemInfo, file);
2676 }
2677
2678 void
2679 myexit(void)
2680 {
2681   char buf[126];
2682
2683   puts("Hit return");
2684   fgets(buf, sizeof(buf), stdin);
2685 }
2686
2687 void
2688 Perl_win32_init(int *argcp, char ***argvp)
2689 {
2690 #ifdef UNDER_CE
2691   char *p;
2692
2693   if((p = xcegetenv("PERLDEBUG")) && (p[0] == 'y' || p[0] == 'Y'))
2694     atexit(myexit);
2695 #endif
2696
2697   MALLOC_INIT;
2698 }
2699
2700 DllExport void
2701 Perl_win32_term(void)
2702 {
2703     dTHX;
2704     HINTS_REFCNT_TERM;
2705     OP_REFCNT_TERM;
2706     PERLIO_TERM;
2707     MALLOC_TERM;
2708     LOCALE_TERM;
2709 }
2710
2711 void
2712 win32_get_child_IO(child_IO_table* ptbl)
2713 {
2714     ptbl->childStdIn    = GetStdHandle(STD_INPUT_HANDLE);
2715     ptbl->childStdOut   = GetStdHandle(STD_OUTPUT_HANDLE);
2716     ptbl->childStdErr   = GetStdHandle(STD_ERROR_HANDLE);
2717 }
2718
2719 win32_flock(int fd, int oper)
2720 {
2721   dTHX;
2722   Perl_croak(aTHX_ PL_no_func, "flock");
2723   return -1;
2724 }
2725
2726 DllExport int
2727 win32_waitpid(int pid, int *status, int flags)
2728 {
2729   dTHX;
2730   Perl_croak(aTHX_ PL_no_func, "waitpid");
2731   return -1;
2732 }
2733
2734 DllExport int
2735 win32_wait(int *status)
2736 {
2737   dTHX;
2738   Perl_croak(aTHX_ PL_no_func, "wait");
2739   return -1;
2740 }
2741
2742 int
2743 wce_reopen_stdout(char *fname)
2744 {
2745   if(xcefreopen(fname, "w", stdout) == NULL)
2746     return -1;
2747
2748   return 0;
2749 }
2750
2751 void
2752 wce_hitreturn()
2753 {
2754   char buf[126];
2755
2756   printf("Hit RETURN");
2757   fflush(stdout);
2758   fgets(buf, sizeof(buf), stdin);
2759   return;
2760 }
2761
2762 /* //////////////////////////////////////////////////////////////////// */
2763
2764 #undef getcwd
2765
2766 char *
2767 getcwd(char *buf, size_t size)
2768 {
2769   return xcegetcwd(buf, size);
2770 }
2771
2772
2773 DllExport PerlIO*
2774 win32_popenlist(const char *mode, IV narg, SV **args)
2775 {
2776  dTHX;
2777  Perl_croak(aTHX_ "List form of pipe open not implemented");
2778  return NULL;
2779 }
2780
2781 /*
2782  * a popen() clone that respects PERL5SHELL
2783  *
2784  * changed to return PerlIO* rather than FILE * by BKS, 11-11-2000
2785  */
2786
2787 DllExport PerlIO*
2788 win32_popen(const char *command, const char *mode)
2789 {
2790     return _popen(command, mode);
2791 }
2792
2793 /*
2794  * pclose() clone
2795  */
2796
2797 DllExport int
2798 win32_pclose(PerlIO *pf)
2799 {
2800     return _pclose(pf);
2801 }
2802
2803 #ifdef HAVE_INTERP_INTERN
2804
2805
2806 static void
2807 win32_csighandler(int sig)
2808 {
2809 #if 0
2810     dTHXa(PERL_GET_SIG_CONTEXT);
2811     Perl_warn(aTHX_ "Got signal %d",sig);
2812 #endif
2813     /* Does nothing */
2814 }
2815
2816 void
2817 Perl_sys_intern_init(pTHX)
2818 {
2819     int i;
2820     w32_perlshell_tokens        = NULL;
2821     w32_perlshell_vec           = (char**)NULL;
2822     w32_perlshell_items         = 0;
2823     w32_fdpid                   = newAV();
2824     Newx(w32_children, 1, child_tab);
2825     w32_num_children            = 0;
2826 #  ifdef USE_ITHREADS
2827     w32_pseudo_id               = 0;
2828     Newx(w32_pseudo_children, 1, child_tab);
2829     w32_num_pseudo_children     = 0;
2830 #  endif
2831     w32_init_socktype           = 0;
2832     w32_timerid                 = 0;
2833     w32_poll_count              = 0;
2834 }
2835
2836 void
2837 Perl_sys_intern_clear(pTHX)
2838 {
2839     Safefree(w32_perlshell_tokens);
2840     Safefree(w32_perlshell_vec);
2841     /* NOTE: w32_fdpid is freed by sv_clean_all() */
2842     Safefree(w32_children);
2843     if (w32_timerid) {
2844         KillTimer(NULL,w32_timerid);
2845         w32_timerid=0;
2846     }
2847 #  ifdef USE_ITHREADS
2848     Safefree(w32_pseudo_children);
2849 #  endif
2850 }
2851
2852 #  ifdef USE_ITHREADS
2853
2854 void
2855 Perl_sys_intern_dup(pTHX_ struct interp_intern *src, struct interp_intern *dst)
2856 {
2857     dst->perlshell_tokens       = NULL;
2858     dst->perlshell_vec          = (char**)NULL;
2859     dst->perlshell_items        = 0;
2860     dst->fdpid                  = newAV();
2861     Newxz(dst->children, 1, child_tab);
2862     dst->pseudo_id              = 0;
2863     Newxz(dst->pseudo_children, 1, child_tab);
2864     dst->thr_intern.Winit_socktype = 0;
2865     dst->timerid                 = 0;
2866     dst->poll_count              = 0;
2867     Copy(src->sigtable,dst->sigtable,SIG_SIZE,Sighandler_t);
2868 }
2869 #  endif /* USE_ITHREADS */
2870 #endif /* HAVE_INTERP_INTERN */
2871
2872 // added to remove undefied symbol error in CodeWarrior compilation
2873 int
2874 Perl_Ireentrant_buffer_ptr(aTHX)
2875 {
2876         return 0;
2877 }