This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Yet another twist.
[perl5.git] / win32 / win32.c
CommitLineData
68dc0745 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 */
3da9a137 10#define PERLIO_NOT_STDIO 0
0a753a76 11#define WIN32_LEAN_AND_MEAN
12#define WIN32IO_IS_STDIO
13#include <tchar.h>
a835ef8a
NIS
14#ifdef __GNUC__
15#define Win32_Winsock
16#endif
0a753a76 17#include <windows.h>
f8fb7c90
GS
18#ifndef __MINGW32__ /* GCC/Mingw32-2.95.2 forgot the WINAPI on CommandLineToArgvW() */
19# include <shellapi.h>
20#else
21 LPWSTR* WINAPI CommandLineToArgvW(LPCWSTR lpCommandLine, int * pNumArgs);
22#endif
5db10396
GS
23#include <winnt.h>
24#include <io.h>
c843839f 25#include <signal.h>
0a753a76 26
68dc0745 27/* #include "config.h" */
0a753a76 28
0a753a76 29#if !defined(PERLIO_IS_STDIO) && !defined(USE_SFIO)
30#define PerlIO FILE
31#endif
32
7a9ec5a3 33#include <sys/stat.h>
0a753a76 34#include "EXTERN.h"
35#include "perl.h"
c69f6586
GS
36
37#define NO_XSLOCKS
c5be433b 38#define PERL_NO_GET_CONTEXT
ad2e33dc 39#include "XSUB.h"
c69f6586
GS
40
41#include "Win32iop.h"
0a753a76 42#include <fcntl.h>
5b0d9cbe
NIS
43#ifndef __GNUC__
44/* assert.h conflicts with #define of assert in perl.h */
0a753a76 45#include <assert.h>
5b0d9cbe 46#endif
0a753a76 47#include <string.h>
48#include <stdarg.h>
ad2e33dc 49#include <float.h>
ad0751ec 50#include <time.h>
3730b96e 51#if defined(_MSC_VER) || defined(__MINGW32__)
ad0751ec
GS
52#include <sys/utime.h>
53#else
54#include <utime.h>
55#endif
5b0d9cbe
NIS
56#ifdef __GNUC__
57/* Mingw32 defaults to globing command line
58 * So we turn it off like this:
59 */
60int _CRT_glob = 0;
61#endif
62
2b260de0 63#if defined(__MINGW32__)
f8fb7c90
GS
64/* Mingw32 is missing some prototypes */
65FILE * _wfopen(LPCWSTR wszFileName, LPCWSTR wszMode);
66FILE * _wfdopen(int nFd, LPCWSTR wszMode);
67FILE * _freopen(LPCWSTR wszFileName, LPCWSTR wszMode, FILE * pOldStream);
68int _flushall();
69int _fcloseall();
2b260de0
GS
70#endif
71
72#if defined(__BORLANDC__)
0b94c7bb
GS
73# define _stat stat
74# define _utimbuf utimbuf
75#endif
76
6890e559
GS
77#define EXECF_EXEC 1
78#define EXECF_SPAWN 2
79#define EXECF_SPAWN_NOWAIT 3
80
32e30700
GS
81#if defined(PERL_IMPLICIT_SYS)
82# undef win32_get_privlib
83# define win32_get_privlib g_win32_get_privlib
84# undef win32_get_sitelib
85# define win32_get_sitelib g_win32_get_sitelib
4ea817c6
GS
86# undef win32_get_vendorlib
87# define win32_get_vendorlib g_win32_get_vendorlib
32e30700
GS
88# undef do_spawn
89# define do_spawn g_do_spawn
90# undef getlogin
91# define getlogin g_getlogin
92#endif
93
ce1da67e 94static void get_shell(void);
dff6d3cd 95static long tokenize(const char *str, char **dest, char ***destv);
c5be433b 96 int do_spawn2(char *cmd, int exectype);
e200fe59 97static BOOL has_shell_metachars(char *ptr);
2d7a9237 98static long filetime_to_clock(PFILETIME ft);
ad0751ec 99static BOOL filetime_from_time(PFILETIME ft, time_t t);
c5be433b 100static char * get_emd_part(SV **leading, char *trailing, ...);
0aaad0ff
GS
101static void remove_dead_process(long deceased);
102static long find_pid(int pid);
103static char * qualified_path(const char *cmd);
4ea817c6
GS
104static char * win32_get_xlib(const char *pl, const char *xlib,
105 const char *libname);
106
7766f137
GS
107#ifdef USE_ITHREADS
108static void remove_dead_pseudo_process(long child);
109static long find_pseudo_pid(int pid);
110#endif
c69f6586 111
7766f137 112START_EXTERN_C
2d7a9237 113HANDLE w32_perldll_handle = INVALID_HANDLE_VALUE;
8ac9c18d 114char w32_module_name[MAX_PATH+1];
7766f137
GS
115END_EXTERN_C
116
4b556e6c 117static DWORD w32_platform = (DWORD)-1;
50892819 118
7766f137
GS
119#define ONE_K_BUFSIZE 1024
120
3fe9a6f1 121int
ba106d47
GS
122IsWin95(void)
123{
0cb96387 124 return (win32_os_id() == VER_PLATFORM_WIN32_WINDOWS);
3fe9a6f1 125}
126
127int
ba106d47
GS
128IsWinNT(void)
129{
0cb96387 130 return (win32_os_id() == VER_PLATFORM_WIN32_NT);
3fe9a6f1 131}
0a753a76 132
2fa86c13
GS
133EXTERN_C void
134set_w32_module_name(void)
135{
136 char* ptr;
137 GetModuleFileName((HMODULE)((w32_perldll_handle == INVALID_HANDLE_VALUE)
138 ? GetModuleHandle(NULL)
139 : w32_perldll_handle),
140 w32_module_name, sizeof(w32_module_name));
141
142 /* try to get full path to binary (which may be mangled when perl is
143 * run from a 16-bit app) */
144 /*PerlIO_printf(Perl_debug_log, "Before %s\n", w32_module_name);*/
145 (void)win32_longpath(w32_module_name);
146 /*PerlIO_printf(Perl_debug_log, "After %s\n", w32_module_name);*/
147
148 /* normalize to forward slashes */
149 ptr = w32_module_name;
150 while (*ptr) {
151 if (*ptr == '\\')
152 *ptr = '/';
153 ++ptr;
154 }
155}
156
c5be433b 157/* *svp (if non-NULL) is expected to be POK (valid allocated SvPVX(*svp)) */
51371543 158static char*
c5be433b 159get_regstr_from(HKEY hkey, const char *valuename, SV **svp)
349ad1fe
GS
160{
161 /* Retrieve a REG_SZ or REG_EXPAND_SZ from the registry */
00dc2f4f
GS
162 HKEY handle;
163 DWORD type;
164 const char *subkey = "Software\\Perl";
349ad1fe 165 char *str = Nullch;
00dc2f4f
GS
166 long retval;
167
168 retval = RegOpenKeyEx(hkey, subkey, 0, KEY_READ, &handle);
349ad1fe 169 if (retval == ERROR_SUCCESS) {
51371543
GS
170 DWORD datalen;
171 retval = RegQueryValueEx(handle, valuename, 0, &type, NULL, &datalen);
1c94caf4
GS
172 if (retval == ERROR_SUCCESS
173 && (type == REG_SZ || type == REG_EXPAND_SZ))
174 {
acfe0abc 175 dTHX;
c5be433b
GS
176 if (!*svp)
177 *svp = sv_2mortal(newSVpvn("",0));
178 SvGROW(*svp, datalen);
51371543 179 retval = RegQueryValueEx(handle, valuename, 0, NULL,
c5be433b 180 (PBYTE)SvPVX(*svp), &datalen);
51371543 181 if (retval == ERROR_SUCCESS) {
c5be433b
GS
182 str = SvPVX(*svp);
183 SvCUR_set(*svp,datalen-1);
51371543 184 }
00dc2f4f
GS
185 }
186 RegCloseKey(handle);
187 }
349ad1fe 188 return str;
00dc2f4f
GS
189}
190
c5be433b 191/* *svp (if non-NULL) is expected to be POK (valid allocated SvPVX(*svp)) */
51371543 192static char*
c5be433b 193get_regstr(const char *valuename, SV **svp)
00dc2f4f 194{
c5be433b 195 char *str = get_regstr_from(HKEY_CURRENT_USER, valuename, svp);
349ad1fe 196 if (!str)
c5be433b 197 str = get_regstr_from(HKEY_LOCAL_MACHINE, valuename, svp);
349ad1fe 198 return str;
00dc2f4f
GS
199}
200
c5be433b 201/* *prev_pathp (if non-NULL) is expected to be POK (valid allocated SvPVX(sv)) */
e5a95ffb 202static char *
c5be433b 203get_emd_part(SV **prev_pathp, char *trailing_path, ...)
00dc2f4f 204{
dc9e4912 205 char base[10];
e5a95ffb 206 va_list ap;
e24c7c18 207 char mod_name[MAX_PATH+1];
00dc2f4f 208 char *ptr;
e5a95ffb
GS
209 char *optr;
210 char *strip;
211 int oldsize, newsize;
273cf8d1 212 STRLEN baselen;
e5a95ffb
GS
213
214 va_start(ap, trailing_path);
215 strip = va_arg(ap, char *);
216
273cf8d1
GS
217 sprintf(base, "%d.%d", (int)PERL_REVISION, (int)PERL_VERSION);
218 baselen = strlen(base);
dc9e4912 219
8ac9c18d 220 if (!*w32_module_name) {
2fa86c13 221 set_w32_module_name();
95140b98 222 }
8ac9c18d 223 strcpy(mod_name, w32_module_name);
95140b98 224 ptr = strrchr(mod_name, '/');
e5a95ffb
GS
225 while (ptr && strip) {
226 /* look for directories to skip back */
227 optr = ptr;
00dc2f4f 228 *ptr = '\0';
95140b98 229 ptr = strrchr(mod_name, '/');
1c39adb2
GS
230 /* avoid stripping component if there is no slash,
231 * or it doesn't match ... */
e5a95ffb 232 if (!ptr || stricmp(ptr+1, strip) != 0) {
273cf8d1 233 /* ... but not if component matches m|5\.$patchlevel.*| */
1c39adb2 234 if (!ptr || !(*strip == '5' && *(ptr+1) == '5'
273cf8d1
GS
235 && strncmp(strip, base, baselen) == 0
236 && strncmp(ptr+1, base, baselen) == 0))
95140b98
GS
237 {
238 *optr = '/';
80252599
GS
239 ptr = optr;
240 }
00dc2f4f 241 }
e5a95ffb 242 strip = va_arg(ap, char *);
00dc2f4f 243 }
e5a95ffb
GS
244 if (!ptr) {
245 ptr = mod_name;
246 *ptr++ = '.';
95140b98 247 *ptr = '/';
00dc2f4f 248 }
e5a95ffb
GS
249 va_end(ap);
250 strcpy(++ptr, trailing_path);
251
dc9e4912 252 /* only add directory if it exists */
349ad1fe 253 if (GetFileAttributes(mod_name) != (DWORD) -1) {
dc9e4912 254 /* directory exists */
acfe0abc 255 dTHX;
c5be433b
GS
256 if (!*prev_pathp)
257 *prev_pathp = sv_2mortal(newSVpvn("",0));
258 sv_catpvn(*prev_pathp, ";", 1);
259 sv_catpv(*prev_pathp, mod_name);
260 return SvPVX(*prev_pathp);
00dc2f4f 261 }
00dc2f4f 262
cf11f4bf 263 return Nullch;
00dc2f4f
GS
264}
265
266char *
4ea817c6 267win32_get_privlib(const char *pl)
00dc2f4f 268{
acfe0abc 269 dTHX;
e5a95ffb
GS
270 char *stdlib = "lib";
271 char buffer[MAX_PATH+1];
51371543 272 SV *sv = Nullsv;
00dc2f4f 273
e5a95ffb
GS
274 /* $stdlib = $HKCU{"lib-$]"} || $HKLM{"lib-$]"} || $HKCU{"lib"} || $HKLM{"lib"} || ""; */
275 sprintf(buffer, "%s-%s", stdlib, pl);
c5be433b
GS
276 if (!get_regstr(buffer, &sv))
277 (void)get_regstr(stdlib, &sv);
00dc2f4f 278
e5a95ffb 279 /* $stdlib .= ";$EMD/../../lib" */
c5be433b 280 return get_emd_part(&sv, stdlib, ARCHNAME, "bin", Nullch);
00dc2f4f
GS
281}
282
4ea817c6
GS
283static char *
284win32_get_xlib(const char *pl, const char *xlib, const char *libname)
00dc2f4f 285{
acfe0abc 286 dTHX;
e5a95ffb 287 char regstr[40];
e24c7c18 288 char pathstr[MAX_PATH+1];
e5a95ffb 289 DWORD datalen;
e5a95ffb 290 int len, newsize;
51371543
GS
291 SV *sv1 = Nullsv;
292 SV *sv2 = Nullsv;
00dc2f4f 293
4ea817c6
GS
294 /* $HKCU{"$xlib-$]"} || $HKLM{"$xlib-$]"} . ---; */
295 sprintf(regstr, "%s-%s", xlib, pl);
c5be433b 296 (void)get_regstr(regstr, &sv1);
e5a95ffb 297
4ea817c6
GS
298 /* $xlib .=
299 * ";$EMD/" . ((-d $EMD/../../../$]) ? "../../.." : "../.."). "/$libname/$]/lib"; */
300 sprintf(pathstr, "%s/%s/lib", libname, pl);
c5be433b 301 (void)get_emd_part(&sv1, pathstr, ARCHNAME, "bin", pl, Nullch);
00dc2f4f 302
4ea817c6
GS
303 /* $HKCU{$xlib} || $HKLM{$xlib} . ---; */
304 (void)get_regstr(xlib, &sv2);
00dc2f4f 305
4ea817c6
GS
306 /* $xlib .=
307 * ";$EMD/" . ((-d $EMD/../../../$]) ? "../../.." : "../.."). "/$libname/lib"; */
308 sprintf(pathstr, "%s/lib", libname);
309 (void)get_emd_part(&sv2, pathstr, ARCHNAME, "bin", pl, Nullch);
e5a95ffb 310
51371543
GS
311 if (!sv1 && !sv2)
312 return Nullch;
313 if (!sv1)
314 return SvPVX(sv2);
315 if (!sv2)
316 return SvPVX(sv1);
e5a95ffb 317
349ad1fe
GS
318 sv_catpvn(sv1, ";", 1);
319 sv_catsv(sv1, sv2);
e5a95ffb 320
349ad1fe 321 return SvPVX(sv1);
68dc0745 322}
0a753a76 323
4ea817c6
GS
324char *
325win32_get_sitelib(const char *pl)
326{
327 return win32_get_xlib(pl, "sitelib", "site");
328}
329
330#ifndef PERL_VENDORLIB_NAME
331# define PERL_VENDORLIB_NAME "vendor"
332#endif
333
334char *
335win32_get_vendorlib(const char *pl)
336{
337 return win32_get_xlib(pl, "vendorlib", PERL_VENDORLIB_NAME);
338}
b4793f7f 339
2d7a9237 340static BOOL
e200fe59 341has_shell_metachars(char *ptr)
68dc0745 342{
343 int inquote = 0;
344 char quote = '\0';
345
346 /*
347 * Scan string looking for redirection (< or >) or pipe
e200fe59
JD
348 * characters (|) that are not in a quoted string.
349 * Shell variable interpolation (%VAR%) can also happen inside strings.
68dc0745 350 */
9404a519 351 while (*ptr) {
68dc0745 352 switch(*ptr) {
e200fe59
JD
353 case '%':
354 return TRUE;
68dc0745 355 case '\'':
356 case '\"':
9404a519
GS
357 if (inquote) {
358 if (quote == *ptr) {
68dc0745 359 inquote = 0;
360 quote = '\0';
0a753a76 361 }
68dc0745 362 }
363 else {
364 quote = *ptr;
365 inquote++;
366 }
367 break;
368 case '>':
369 case '<':
370 case '|':
9404a519 371 if (!inquote)
68dc0745 372 return TRUE;
373 default:
374 break;
0a753a76 375 }
68dc0745 376 ++ptr;
377 }
378 return FALSE;
0a753a76 379}
380
32e30700 381#if !defined(PERL_IMPLICIT_SYS)
68dc0745 382/* since the current process environment is being updated in util.c
383 * the library functions will get the correct environment
384 */
385PerlIO *
4f63d024 386Perl_my_popen(pTHX_ char *cmd, char *mode)
0a753a76 387{
388#ifdef FIXCMD
7766f137
GS
389#define fixcmd(x) { \
390 char *pspace = strchr((x),' '); \
391 if (pspace) { \
392 char *p = (x); \
393 while (p < pspace) { \
394 if (*p == '/') \
395 *p = '\\'; \
396 p++; \
397 } \
398 } \
399 }
0a753a76 400#else
401#define fixcmd(x)
402#endif
68dc0745 403 fixcmd(cmd);
45bc9206 404 PERL_FLUSHALL_FOR_CHILD;
0a753a76 405 return win32_popen(cmd, mode);
0a753a76 406}
407
68dc0745 408long
4f63d024 409Perl_my_pclose(pTHX_ PerlIO *fp)
0a753a76 410{
411 return win32_pclose(fp);
412}
c69f6586 413#endif
0a753a76 414
0cb96387
GS
415DllExport unsigned long
416win32_os_id(void)
0a753a76 417{
8b10511d 418 static OSVERSIONINFO osver;
0a753a76 419
2d7a9237 420 if (osver.dwPlatformId != w32_platform) {
8b10511d
GS
421 memset(&osver, 0, sizeof(OSVERSIONINFO));
422 osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
423 GetVersionEx(&osver);
2d7a9237 424 w32_platform = osver.dwPlatformId;
8b10511d 425 }
0cb96387 426 return (unsigned long)w32_platform;
0a753a76 427}
428
7766f137
GS
429DllExport int
430win32_getpid(void)
431{
922b1888 432 int pid;
7766f137 433#ifdef USE_ITHREADS
acfe0abc 434 dTHX;
7766f137
GS
435 if (w32_pseudo_id)
436 return -((int)w32_pseudo_id);
437#endif
922b1888
GS
438 pid = _getpid();
439 /* Windows 9x appears to always reports a pid for threads and processes
440 * that has the high bit set. So we treat the lower 31 bits as the
441 * "real" PID for Perl's purposes. */
442 if (IsWin95() && pid < 0)
443 pid = -pid;
444 return pid;
7766f137
GS
445}
446
ce1da67e
GS
447/* Tokenize a string. Words are null-separated, and the list
448 * ends with a doubled null. Any character (except null and
449 * including backslash) may be escaped by preceding it with a
450 * backslash (the backslash will be stripped).
451 * Returns number of words in result buffer.
452 */
453static long
dff6d3cd 454tokenize(const char *str, char **dest, char ***destv)
ce1da67e
GS
455{
456 char *retstart = Nullch;
457 char **retvstart = 0;
458 int items = -1;
459 if (str) {
acfe0abc 460 dTHX;
ce1da67e
GS
461 int slen = strlen(str);
462 register char *ret;
463 register char **retv;
464 New(1307, ret, slen+2, char);
465 New(1308, retv, (slen+3)/2, char*);
466
467 retstart = ret;
468 retvstart = retv;
469 *retv = ret;
470 items = 0;
471 while (*str) {
472 *ret = *str++;
473 if (*ret == '\\' && *str)
474 *ret = *str++;
475 else if (*ret == ' ') {
476 while (*str == ' ')
477 str++;
478 if (ret == retstart)
479 ret--;
480 else {
481 *ret = '\0';
482 ++items;
483 if (*str)
484 *++retv = ret+1;
485 }
486 }
487 else if (!*str)
488 ++items;
489 ret++;
490 }
491 retvstart[items] = Nullch;
492 *ret++ = '\0';
493 *ret = '\0';
494 }
495 *dest = retstart;
496 *destv = retvstart;
497 return items;
498}
499
500static void
2d7a9237 501get_shell(void)
0a753a76 502{
acfe0abc 503 dTHX;
ce1da67e 504 if (!w32_perlshell_tokens) {
174c211a
GS
505 /* we don't use COMSPEC here for two reasons:
506 * 1. the same reason perl on UNIX doesn't use SHELL--rampant and
507 * uncontrolled unportability of the ensuing scripts.
508 * 2. PERL5SHELL could be set to a shell that may not be fit for
509 * interactive use (which is what most programs look in COMSPEC
510 * for).
511 */
dff6d3cd
GS
512 const char* defaultshell = (IsWinNT()
513 ? "cmd.exe /x/c" : "command.com /c");
2fb9ab56 514 const char *usershell = PerlEnv_getenv("PERL5SHELL");
ce1da67e
GS
515 w32_perlshell_items = tokenize(usershell ? usershell : defaultshell,
516 &w32_perlshell_tokens,
517 &w32_perlshell_vec);
68dc0745 518 }
0a753a76 519}
520
68dc0745 521int
c5be433b 522do_aspawn(void *vreally, void **vmark, void **vsp)
0a753a76 523{
acfe0abc 524 dTHX;
2d7a9237
GS
525 SV *really = (SV*)vreally;
526 SV **mark = (SV**)vmark;
527 SV **sp = (SV**)vsp;
68dc0745 528 char **argv;
2d7a9237 529 char *str;
68dc0745 530 int status;
2d7a9237 531 int flag = P_WAIT;
68dc0745 532 int index = 0;
68dc0745 533
2d7a9237
GS
534 if (sp <= mark)
535 return -1;
68dc0745 536
ce1da67e
GS
537 get_shell();
538 New(1306, argv, (sp - mark) + w32_perlshell_items + 2, char*);
2d7a9237
GS
539
540 if (SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
541 ++mark;
542 flag = SvIVx(*mark);
68dc0745 543 }
544
9404a519 545 while (++mark <= sp) {
bb897dfc 546 if (*mark && (str = SvPV_nolen(*mark)))
2d7a9237
GS
547 argv[index++] = str;
548 else
549 argv[index++] = "";
68dc0745 550 }
551 argv[index++] = 0;
552
2d7a9237 553 status = win32_spawnvp(flag,
bb897dfc 554 (const char*)(really ? SvPV_nolen(really) : argv[0]),
2d7a9237
GS
555 (const char* const*)argv);
556
80252599 557 if (status < 0 && (errno == ENOEXEC || errno == ENOENT)) {
2d7a9237 558 /* possible shell-builtin, invoke with shell */
ce1da67e
GS
559 int sh_items;
560 sh_items = w32_perlshell_items;
2d7a9237
GS
561 while (--index >= 0)
562 argv[index+sh_items] = argv[index];
ce1da67e
GS
563 while (--sh_items >= 0)
564 argv[sh_items] = w32_perlshell_vec[sh_items];
2d7a9237
GS
565
566 status = win32_spawnvp(flag,
bb897dfc 567 (const char*)(really ? SvPV_nolen(really) : argv[0]),
2d7a9237
GS
568 (const char* const*)argv);
569 }
68dc0745 570
922b1888
GS
571 if (flag == P_NOWAIT) {
572 if (IsWin95())
573 PL_statusvalue = -1; /* >16bits hint for pp_system() */
574 }
575 else {
50892819 576 if (status < 0) {
0453d815
PM
577 if (ckWARN(WARN_EXEC))
578 Perl_warner(aTHX_ WARN_EXEC, "Can't spawn \"%s\": %s", argv[0], strerror(errno));
50892819
GS
579 status = 255 * 256;
580 }
581 else
582 status *= 256;
b28d0864 583 PL_statusvalue = status;
5aabfad6 584 }
ce1da67e 585 Safefree(argv);
50892819 586 return (status);
68dc0745 587}
588
dd7038b3
JH
589/* returns pointer to the next unquoted space or the end of the string */
590static char*
591find_next_space(const char *s)
592{
593 bool in_quotes = FALSE;
594 while (*s) {
595 /* ignore doubled backslashes, or backslash+quote */
596 if (*s == '\\' && (s[1] == '\\' || s[1] == '"')) {
597 s += 2;
598 }
599 /* keep track of when we're within quotes */
600 else if (*s == '"') {
601 s++;
602 in_quotes = !in_quotes;
603 }
604 /* break it up only at spaces that aren't in quotes */
605 else if (!in_quotes && isSPACE(*s))
606 return (char*)s;
607 else
608 s++;
609 }
610 return (char*)s;
611}
612
c69f6586 613int
c5be433b 614do_spawn2(char *cmd, int exectype)
68dc0745 615{
acfe0abc 616 dTHX;
68dc0745 617 char **a;
618 char *s;
619 char **argv;
620 int status = -1;
621 BOOL needToTry = TRUE;
2d7a9237 622 char *cmd2;
68dc0745 623
2d7a9237
GS
624 /* Save an extra exec if possible. See if there are shell
625 * metacharacters in it */
e200fe59 626 if (!has_shell_metachars(cmd)) {
fc36a67e 627 New(1301,argv, strlen(cmd) / 2 + 2, char*);
628 New(1302,cmd2, strlen(cmd) + 1, char);
68dc0745 629 strcpy(cmd2, cmd);
630 a = argv;
631 for (s = cmd2; *s;) {
de030af3 632 while (*s && isSPACE(*s))
68dc0745 633 s++;
634 if (*s)
635 *(a++) = s;
dd7038b3 636 s = find_next_space(s);
9404a519 637 if (*s)
68dc0745 638 *s++ = '\0';
0a753a76 639 }
68dc0745 640 *a = Nullch;
ce1da67e 641 if (argv[0]) {
6890e559
GS
642 switch (exectype) {
643 case EXECF_SPAWN:
644 status = win32_spawnvp(P_WAIT, argv[0],
645 (const char* const*)argv);
646 break;
647 case EXECF_SPAWN_NOWAIT:
648 status = win32_spawnvp(P_NOWAIT, argv[0],
649 (const char* const*)argv);
650 break;
651 case EXECF_EXEC:
652 status = win32_execvp(argv[0], (const char* const*)argv);
653 break;
654 }
2d7a9237 655 if (status != -1 || errno == 0)
68dc0745 656 needToTry = FALSE;
0a753a76 657 }
0a753a76 658 Safefree(argv);
68dc0745 659 Safefree(cmd2);
660 }
2d7a9237 661 if (needToTry) {
ce1da67e
GS
662 char **argv;
663 int i = -1;
664 get_shell();
665 New(1306, argv, w32_perlshell_items + 2, char*);
666 while (++i < w32_perlshell_items)
667 argv[i] = w32_perlshell_vec[i];
2d7a9237
GS
668 argv[i++] = cmd;
669 argv[i] = Nullch;
6890e559
GS
670 switch (exectype) {
671 case EXECF_SPAWN:
672 status = win32_spawnvp(P_WAIT, argv[0],
673 (const char* const*)argv);
674 break;
675 case EXECF_SPAWN_NOWAIT:
676 status = win32_spawnvp(P_NOWAIT, argv[0],
677 (const char* const*)argv);
678 break;
679 case EXECF_EXEC:
680 status = win32_execvp(argv[0], (const char* const*)argv);
681 break;
682 }
ce1da67e
GS
683 cmd = argv[0];
684 Safefree(argv);
68dc0745 685 }
922b1888
GS
686 if (exectype == EXECF_SPAWN_NOWAIT) {
687 if (IsWin95())
688 PL_statusvalue = -1; /* >16bits hint for pp_system() */
689 }
690 else {
50892819 691 if (status < 0) {
0453d815
PM
692 if (ckWARN(WARN_EXEC))
693 Perl_warner(aTHX_ WARN_EXEC, "Can't %s \"%s\": %s",
50892819
GS
694 (exectype == EXECF_EXEC ? "exec" : "spawn"),
695 cmd, strerror(errno));
696 status = 255 * 256;
697 }
698 else
699 status *= 256;
b28d0864 700 PL_statusvalue = status;
5aabfad6 701 }
50892819 702 return (status);
0a753a76 703}
704
6890e559 705int
c5be433b 706do_spawn(char *cmd)
6890e559 707{
c5be433b 708 return do_spawn2(cmd, EXECF_SPAWN);
6890e559
GS
709}
710
2d7a9237 711int
c5be433b 712do_spawn_nowait(char *cmd)
2d7a9237 713{
c5be433b 714 return do_spawn2(cmd, EXECF_SPAWN_NOWAIT);
2d7a9237
GS
715}
716
6890e559 717bool
4f63d024 718Perl_do_exec(pTHX_ char *cmd)
6890e559 719{
c5be433b 720 do_spawn2(cmd, EXECF_EXEC);
6890e559
GS
721 return FALSE;
722}
723
68dc0745 724/* The idea here is to read all the directory names into a string table
725 * (separated by nulls) and when one of the other dir functions is called
726 * return the pointer to the current file name.
727 */
c5be433b 728DllExport DIR *
ce2e26e5 729win32_opendir(char *filename)
0a753a76 730{
acfe0abc 731 dTHX;
95136add 732 DIR *dirp;
9404a519
GS
733 long len;
734 long idx;
735 char scanname[MAX_PATH+3];
736 struct stat sbuf;
7fac1903
GS
737 WIN32_FIND_DATAA aFindData;
738 WIN32_FIND_DATAW wFindData;
9404a519 739 HANDLE fh;
7fac1903 740 char buffer[MAX_PATH*2];
82867ecf 741 WCHAR wbuffer[MAX_PATH+1];
95136add 742 char* ptr;
9404a519
GS
743
744 len = strlen(filename);
745 if (len > MAX_PATH)
746 return NULL;
68dc0745 747
748 /* check to see if filename is a directory */
69d3ab13 749 if (win32_stat(filename, &sbuf) < 0 || !S_ISDIR(sbuf.st_mode))
24caa93f 750 return NULL;
68dc0745 751
68dc0745 752 /* Get us a DIR structure */
95136add 753 Newz(1303, dirp, 1, DIR);
68dc0745 754
755 /* Create the search pattern */
756 strcpy(scanname, filename);
23db2e2d
GS
757
758 /* bare drive name means look in cwd for drive */
759 if (len == 2 && isALPHA(scanname[0]) && scanname[1] == ':') {
760 scanname[len++] = '.';
761 scanname[len++] = '/';
762 }
763 else if (scanname[len-1] != '/' && scanname[len-1] != '\\') {
9404a519 764 scanname[len++] = '/';
23db2e2d 765 }
9404a519
GS
766 scanname[len++] = '*';
767 scanname[len] = '\0';
68dc0745 768
769 /* do the FindFirstFile call */
7fac1903 770 if (USING_WIDE()) {
0cb96387 771 A2WHELPER(scanname, wbuffer, sizeof(wbuffer));
7766f137 772 fh = FindFirstFileW(PerlDir_mapW(wbuffer), &wFindData);
7fac1903
GS
773 }
774 else {
7766f137 775 fh = FindFirstFileA(PerlDir_mapA(scanname), &aFindData);
7fac1903 776 }
95136add 777 dirp->handle = fh;
9404a519 778 if (fh == INVALID_HANDLE_VALUE) {
95136add 779 DWORD err = GetLastError();
21e72512 780 /* FindFirstFile() fails on empty drives! */
95136add
GS
781 switch (err) {
782 case ERROR_FILE_NOT_FOUND:
783 return dirp;
784 case ERROR_NO_MORE_FILES:
785 case ERROR_PATH_NOT_FOUND:
786 errno = ENOENT;
787 break;
788 case ERROR_NOT_ENOUGH_MEMORY:
789 errno = ENOMEM;
790 break;
791 default:
792 errno = EINVAL;
793 break;
794 }
795 Safefree(dirp);
68dc0745 796 return NULL;
797 }
798
799 /* now allocate the first part of the string table for
800 * the filenames that we find.
801 */
7fac1903 802 if (USING_WIDE()) {
0cb96387 803 W2AHELPER(wFindData.cFileName, buffer, sizeof(buffer));
7fac1903
GS
804 ptr = buffer;
805 }
806 else {
807 ptr = aFindData.cFileName;
808 }
809 idx = strlen(ptr)+1;
95136add
GS
810 if (idx < 256)
811 dirp->size = 128;
812 else
813 dirp->size = idx;
814 New(1304, dirp->start, dirp->size, char);
815 strcpy(dirp->start, ptr);
816 dirp->nfiles++;
817 dirp->end = dirp->curr = dirp->start;
818 dirp->end += idx;
819 return dirp;
0a753a76 820}
821
822
68dc0745 823/* Readdir just returns the current string pointer and bumps the
824 * string pointer to the nDllExport entry.
825 */
c5be433b 826DllExport struct direct *
ce2e26e5 827win32_readdir(DIR *dirp)
0a753a76 828{
95136add 829 long len;
0a753a76 830
68dc0745 831 if (dirp->curr) {
832 /* first set up the structure to return */
833 len = strlen(dirp->curr);
0f38926b 834 strcpy(dirp->dirstr.d_name, dirp->curr);
68dc0745 835 dirp->dirstr.d_namlen = len;
0a753a76 836
68dc0745 837 /* Fake an inode */
0f38926b 838 dirp->dirstr.d_ino = dirp->curr - dirp->start;
0a753a76 839
95136add 840 /* Now set up for the next call to readdir */
68dc0745 841 dirp->curr += len + 1;
95136add 842 if (dirp->curr >= dirp->end) {
acfe0abc 843 dTHX;
95136add
GS
844 char* ptr;
845 BOOL res;
846 WIN32_FIND_DATAW wFindData;
847 WIN32_FIND_DATAA aFindData;
848 char buffer[MAX_PATH*2];
849
850 /* finding the next file that matches the wildcard
851 * (which should be all of them in this directory!).
95136add
GS
852 */
853 if (USING_WIDE()) {
854 res = FindNextFileW(dirp->handle, &wFindData);
855 if (res) {
856 W2AHELPER(wFindData.cFileName, buffer, sizeof(buffer));
857 ptr = buffer;
858 }
859 }
860 else {
861 res = FindNextFileA(dirp->handle, &aFindData);
862 if (res)
863 ptr = aFindData.cFileName;
864 }
865 if (res) {
0f38926b
GS
866 long endpos = dirp->end - dirp->start;
867 long newsize = endpos + strlen(ptr) + 1;
95136add 868 /* bump the string table size by enough for the
022735b4 869 * new name and its null terminator */
0f38926b
GS
870 while (newsize > dirp->size) {
871 long curpos = dirp->curr - dirp->start;
95136add
GS
872 dirp->size *= 2;
873 Renew(dirp->start, dirp->size, char);
0f38926b 874 dirp->curr = dirp->start + curpos;
95136add 875 }
0f38926b
GS
876 strcpy(dirp->start + endpos, ptr);
877 dirp->end = dirp->start + newsize;
95136add
GS
878 dirp->nfiles++;
879 }
880 else
881 dirp->curr = NULL;
68dc0745 882 }
68dc0745 883 return &(dirp->dirstr);
884 }
885 else
886 return NULL;
0a753a76 887}
888
68dc0745 889/* Telldir returns the current string pointer position */
c5be433b 890DllExport long
ce2e26e5 891win32_telldir(DIR *dirp)
0a753a76 892{
95136add 893 return (dirp->curr - dirp->start);
0a753a76 894}
895
896
68dc0745 897/* Seekdir moves the string pointer to a previously saved position
95136add 898 * (returned by telldir).
68dc0745 899 */
c5be433b 900DllExport void
ce2e26e5 901win32_seekdir(DIR *dirp, long loc)
0a753a76 902{
95136add 903 dirp->curr = dirp->start + loc;
0a753a76 904}
905
68dc0745 906/* Rewinddir resets the string pointer to the start */
c5be433b 907DllExport void
ce2e26e5 908win32_rewinddir(DIR *dirp)
0a753a76 909{
910 dirp->curr = dirp->start;
911}
912
68dc0745 913/* free the memory allocated by opendir */
c5be433b 914DllExport int
ce2e26e5 915win32_closedir(DIR *dirp)
0a753a76 916{
acfe0abc 917 dTHX;
95136add 918 if (dirp->handle != INVALID_HANDLE_VALUE)
0f38926b 919 FindClose(dirp->handle);
0a753a76 920 Safefree(dirp->start);
921 Safefree(dirp);
68dc0745 922 return 1;
0a753a76 923}
924
925
68dc0745 926/*
927 * various stubs
928 */
0a753a76 929
930
68dc0745 931/* Ownership
932 *
933 * Just pretend that everyone is a superuser. NT will let us know if
934 * we don\'t really have permission to do something.
935 */
0a753a76 936
937#define ROOT_UID ((uid_t)0)
938#define ROOT_GID ((gid_t)0)
939
68dc0745 940uid_t
941getuid(void)
0a753a76 942{
68dc0745 943 return ROOT_UID;
0a753a76 944}
945
68dc0745 946uid_t
947geteuid(void)
0a753a76 948{
68dc0745 949 return ROOT_UID;
0a753a76 950}
951
68dc0745 952gid_t
953getgid(void)
0a753a76 954{
68dc0745 955 return ROOT_GID;
0a753a76 956}
957
68dc0745 958gid_t
959getegid(void)
0a753a76 960{
68dc0745 961 return ROOT_GID;
0a753a76 962}
963
68dc0745 964int
22239a37 965setuid(uid_t auid)
0a753a76 966{
22239a37 967 return (auid == ROOT_UID ? 0 : -1);
0a753a76 968}
969
68dc0745 970int
22239a37 971setgid(gid_t agid)
0a753a76 972{
22239a37 973 return (agid == ROOT_GID ? 0 : -1);
0a753a76 974}
975
e34ffe5a
GS
976char *
977getlogin(void)
978{
acfe0abc 979 dTHX;
3352bfcb
GS
980 char *buf = w32_getlogin_buffer;
981 DWORD size = sizeof(w32_getlogin_buffer);
e34ffe5a
GS
982 if (GetUserName(buf,&size))
983 return buf;
984 return (char*)NULL;
985}
986
b990f8c8
GS
987int
988chown(const char *path, uid_t owner, gid_t group)
989{
990 /* XXX noop */
1c1c7f20 991 return 0;
b990f8c8
GS
992}
993
00b02797
JH
994/*
995 * XXX this needs strengthening (for PerlIO)
996 * -- BKS, 11-11-200
997*/
998int mkstemp(const char *path)
999{
1000 dTHX;
1001 char buf[MAX_PATH+1];
1002 int i = 0, fd = -1;
1003
1004retry:
1005 if (i++ > 10) { /* give up */
1006 errno = ENOENT;
1007 return -1;
1008 }
1009 if (!GetTempFileNameA((LPCSTR)path, "plr", 1, buf)) {
1010 errno = ENOENT;
1011 return -1;
1012 }
1013 fd = PerlLIO_open3(buf, O_CREAT|O_RDWR|O_EXCL, 0600);
1014 if (fd == -1)
1015 goto retry;
1016 return fd;
1017}
1018
0aaad0ff
GS
1019static long
1020find_pid(int pid)
0a753a76 1021{
acfe0abc 1022 dTHX;
7766f137
GS
1023 long child = w32_num_children;
1024 while (--child >= 0) {
0aaad0ff
GS
1025 if (w32_child_pids[child] == pid)
1026 return child;
1027 }
1028 return -1;
1029}
1030
1031static void
1032remove_dead_process(long child)
1033{
1034 if (child >= 0) {
acfe0abc 1035 dTHX;
0aaad0ff 1036 CloseHandle(w32_child_handles[child]);
c00206c8 1037 Move(&w32_child_handles[child+1], &w32_child_handles[child],
0aaad0ff 1038 (w32_num_children-child-1), HANDLE);
c00206c8 1039 Move(&w32_child_pids[child+1], &w32_child_pids[child],
0aaad0ff
GS
1040 (w32_num_children-child-1), DWORD);
1041 w32_num_children--;
f55ee38a 1042 }
f55ee38a
GS
1043}
1044
7766f137
GS
1045#ifdef USE_ITHREADS
1046static long
1047find_pseudo_pid(int pid)
1048{
acfe0abc 1049 dTHX;
7766f137
GS
1050 long child = w32_num_pseudo_children;
1051 while (--child >= 0) {
1052 if (w32_pseudo_child_pids[child] == pid)
1053 return child;
1054 }
1055 return -1;
1056}
1057
1058static void
1059remove_dead_pseudo_process(long child)
1060{
1061 if (child >= 0) {
acfe0abc 1062 dTHX;
7766f137 1063 CloseHandle(w32_pseudo_child_handles[child]);
c00206c8 1064 Move(&w32_pseudo_child_handles[child+1], &w32_pseudo_child_handles[child],
7766f137 1065 (w32_num_pseudo_children-child-1), HANDLE);
c00206c8 1066 Move(&w32_pseudo_child_pids[child+1], &w32_pseudo_child_pids[child],
7766f137
GS
1067 (w32_num_pseudo_children-child-1), DWORD);
1068 w32_num_pseudo_children--;
1069 }
1070}
1071#endif
1072
f55ee38a
GS
1073DllExport int
1074win32_kill(int pid, int sig)
1075{
acfe0abc 1076 dTHX;
0aaad0ff 1077 HANDLE hProcess;
c66b022d 1078 long child;
7766f137
GS
1079#ifdef USE_ITHREADS
1080 if (pid < 0) {
1081 /* it is a pseudo-forked child */
c66b022d 1082 child = find_pseudo_pid(-pid);
7766f137
GS
1083 if (child >= 0) {
1084 hProcess = w32_pseudo_child_handles[child];
7e5f34c0
NIS
1085 switch (sig) {
1086 case 0:
c843839f 1087 /* "Does process exist?" use of kill */
7766f137 1088 return 0;
7e5f34c0
NIS
1089 case 9:
1090 /* kill -9 style un-graceful exit */
1091 if (TerminateThread(hProcess, sig)) {
1092 remove_dead_pseudo_process(child);
1093 return 0;
1094 }
1095 break;
1096 default:
c843839f 1097 /* We fake signals to pseudo-processes using Win32 message queue */
7e5f34c0
NIS
1098 if (PostThreadMessage(-pid,WM_USER,sig,0)) {
1099 /* It might be us ... */
1100 PERL_ASYNC_CHECK();
1101 return 0;
1102 }
1103 break;
1104 }
7766f137 1105 }
922b1888
GS
1106 else if (IsWin95()) {
1107 pid = -pid;
1108 goto alien_process;
1109 }
68dc0745 1110 }
7766f137
GS
1111 else
1112#endif
1113 {
c66b022d 1114 child = find_pid(pid);
7766f137 1115 if (child >= 0) {
7e5f34c0
NIS
1116 hProcess = w32_child_handles[child];
1117 switch(sig) {
1118 case 0:
c843839f 1119 /* "Does process exist?" use of kill */
7766f137 1120 return 0;
7e5f34c0
NIS
1121 case 2:
1122 if (GenerateConsoleCtrlEvent(CTRL_C_EVENT,pid))
1123 return 0;
1124 break;
c843839f 1125 default: /* For now be backwards compatible with perl5.6 */
7e5f34c0
NIS
1126 case 9:
1127 if (TerminateProcess(hProcess, sig)) {
1128 remove_dead_process(child);
1129 return 0;
1130 }
1131 break;
1132 }
7766f137
GS
1133 }
1134 else {
922b1888
GS
1135alien_process:
1136 hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE,
1137 (IsWin95() ? -pid : pid));
42b8b86c 1138 if (hProcess) {
7e5f34c0
NIS
1139 switch(sig) {
1140 case 0:
c843839f 1141 /* "Does process exist?" use of kill */
42b8b86c 1142 return 0;
7e5f34c0
NIS
1143 case 2:
1144 if (GenerateConsoleCtrlEvent(CTRL_C_EVENT,pid))
1145 return 0;
1146 break;
c843839f 1147 default: /* For now be backwards compatible with perl5.6 */
7e5f34c0
NIS
1148 case 9:
1149 if (TerminateProcess(hProcess, sig)) {
1150 CloseHandle(hProcess);
1151 return 0;
1152 }
42b8b86c 1153 }
7766f137
GS
1154 }
1155 }
1156 }
1157 errno = EINVAL;
1158 return -1;
0a753a76 1159}
fbbbcc48 1160
68dc0745 1161DllExport int
426c1a18 1162win32_stat(const char *path, struct stat *sbuf)
0a753a76 1163{
acfe0abc 1164 dTHX;
426c1a18 1165 char buffer[MAX_PATH+1];
68dc0745 1166 int l = strlen(path);
67fbe06e 1167 int res;
82867ecf 1168 WCHAR wbuffer[MAX_PATH+1];
e9ff6d27 1169 WCHAR* pwbuffer;
6b980173
JD
1170 HANDLE handle;
1171 int nlink = 1;
0a753a76 1172
68dc0745 1173 if (l > 1) {
1174 switch(path[l - 1]) {
e1dbac94
GS
1175 /* FindFirstFile() and stat() are buggy with a trailing
1176 * backslash, so change it to a forward slash :-( */
68dc0745 1177 case '\\':
426c1a18
GS
1178 strncpy(buffer, path, l-1);
1179 buffer[l - 1] = '/';
1180 buffer[l] = '\0';
1181 path = buffer;
e1dbac94 1182 break;
23db2e2d 1183 /* FindFirstFile() is buggy with "x:", so add a dot :-( */
e1dbac94
GS
1184 case ':':
1185 if (l == 2 && isALPHA(path[0])) {
426c1a18
GS
1186 buffer[0] = path[0];
1187 buffer[1] = ':';
1188 buffer[2] = '.';
1189 buffer[3] = '\0';
e1dbac94 1190 l = 3;
426c1a18 1191 path = buffer;
e1dbac94
GS
1192 }
1193 break;
68dc0745 1194 }
1195 }
6b980173
JD
1196
1197 /* We *must* open & close the file once; otherwise file attribute changes */
1198 /* might not yet have propagated to "other" hard links of the same file. */
1199 /* This also gives us an opportunity to determine the number of links. */
7fac1903 1200 if (USING_WIDE()) {
0cb96387 1201 A2WHELPER(path, wbuffer, sizeof(wbuffer));
e9ff6d27
GS
1202 pwbuffer = PerlDir_mapW(wbuffer);
1203 handle = CreateFileW(pwbuffer, 0, 0, NULL, OPEN_EXISTING, 0, NULL);
7fac1903
GS
1204 }
1205 else {
e9ff6d27
GS
1206 path = PerlDir_mapA(path);
1207 l = strlen(path);
1208 handle = CreateFileA(path, 0, 0, NULL, OPEN_EXISTING, 0, NULL);
6b980173
JD
1209 }
1210 if (handle != INVALID_HANDLE_VALUE) {
1211 BY_HANDLE_FILE_INFORMATION bhi;
1212 if (GetFileInformationByHandle(handle, &bhi))
1213 nlink = bhi.nNumberOfLinks;
1214 CloseHandle(handle);
7fac1903 1215 }
6b980173 1216
e9ff6d27 1217 /* pwbuffer or path will be mapped correctly above */
7766f137 1218 if (USING_WIDE()) {
e9ff6d27 1219 res = _wstat(pwbuffer, (struct _stat *)sbuf);
7766f137
GS
1220 }
1221 else {
e9ff6d27 1222 res = stat(path, sbuf);
7766f137 1223 }
426c1a18 1224 sbuf->st_nlink = nlink;
6b980173 1225
24caa93f
GS
1226 if (res < 0) {
1227 /* CRT is buggy on sharenames, so make sure it really isn't.
1228 * XXX using GetFileAttributesEx() will enable us to set
426c1a18 1229 * sbuf->st_*time (but note that's not available on the
24caa93f 1230 * Windows of 1995) */
7fac1903
GS
1231 DWORD r;
1232 if (USING_WIDE()) {
e9ff6d27 1233 r = GetFileAttributesW(pwbuffer);
7fac1903
GS
1234 }
1235 else {
e9ff6d27 1236 r = GetFileAttributesA(path);
7fac1903 1237 }
24caa93f 1238 if (r != 0xffffffff && (r & FILE_ATTRIBUTE_DIRECTORY)) {
426c1a18
GS
1239 /* sbuf may still contain old garbage since stat() failed */
1240 Zero(sbuf, 1, struct stat);
1241 sbuf->st_mode = S_IFDIR | S_IREAD;
24caa93f
GS
1242 errno = 0;
1243 if (!(r & FILE_ATTRIBUTE_READONLY))
426c1a18 1244 sbuf->st_mode |= S_IWRITE | S_IEXEC;
24caa93f
GS
1245 return 0;
1246 }
1247 }
24caa93f 1248 else {
e1dbac94
GS
1249 if (l == 3 && isALPHA(path[0]) && path[1] == ':'
1250 && (path[2] == '\\' || path[2] == '/'))
2293b0e9
AB
1251 {
1252 /* The drive can be inaccessible, some _stat()s are buggy */
7fac1903 1253 if (USING_WIDE()
e9ff6d27
GS
1254 ? !GetVolumeInformationW(pwbuffer,NULL,0,NULL,NULL,NULL,NULL,0)
1255 : !GetVolumeInformationA(path,NULL,0,NULL,NULL,NULL,NULL,0)) {
2293b0e9
AB
1256 errno = ENOENT;
1257 return -1;
1258 }
1259 }
1260#ifdef __BORLANDC__
426c1a18
GS
1261 if (S_ISDIR(sbuf->st_mode))
1262 sbuf->st_mode |= S_IWRITE | S_IEXEC;
1263 else if (S_ISREG(sbuf->st_mode)) {
d0650a05 1264 int perms;
67fbe06e
GS
1265 if (l >= 4 && path[l-4] == '.') {
1266 const char *e = path + l - 3;
1267 if (strnicmp(e,"exe",3)
1268 && strnicmp(e,"bat",3)
1269 && strnicmp(e,"com",3)
1270 && (IsWin95() || strnicmp(e,"cmd",3)))
426c1a18 1271 sbuf->st_mode &= ~S_IEXEC;
67fbe06e 1272 else
426c1a18 1273 sbuf->st_mode |= S_IEXEC;
67fbe06e
GS
1274 }
1275 else
426c1a18 1276 sbuf->st_mode &= ~S_IEXEC;
d0650a05
GS
1277 /* Propagate permissions to _group_ and _others_ */
1278 perms = sbuf->st_mode & (S_IREAD|S_IWRITE|S_IEXEC);
1279 sbuf->st_mode |= (perms>>3) | (perms>>6);
67fbe06e 1280 }
67fbe06e 1281#endif
2293b0e9 1282 }
67fbe06e 1283 return res;
0a753a76 1284}
1285
8ac9c18d
GS
1286/* Find the longname of a given path. path is destructively modified.
1287 * It should have space for at least MAX_PATH characters. */
1288DllExport char *
1289win32_longpath(char *path)
1290{
1291 WIN32_FIND_DATA fdata;
1292 HANDLE fhand;
1293 char tmpbuf[MAX_PATH+1];
1294 char *tmpstart = tmpbuf;
1295 char *start = path;
1296 char sep;
1297 if (!path)
1298 return Nullch;
1299
1300 /* drive prefix */
1301 if (isALPHA(path[0]) && path[1] == ':' &&
1302 (path[2] == '/' || path[2] == '\\'))
1303 {
1304 start = path + 2;
1305 *tmpstart++ = path[0];
1306 *tmpstart++ = ':';
1307 }
1308 /* UNC prefix */
1309 else if ((path[0] == '/' || path[0] == '\\') &&
1310 (path[1] == '/' || path[1] == '\\'))
1311 {
1312 start = path + 2;
52fcf7ee
GS
1313 *tmpstart++ = path[0];
1314 *tmpstart++ = path[1];
8ac9c18d
GS
1315 /* copy machine name */
1316 while (*start && *start != '/' && *start != '\\')
1317 *tmpstart++ = *start++;
1318 if (*start) {
52fcf7ee 1319 *tmpstart++ = *start;
8ac9c18d
GS
1320 start++;
1321 /* copy share name */
1322 while (*start && *start != '/' && *start != '\\')
1323 *tmpstart++ = *start++;
1324 }
1325 }
1326 sep = *start++;
1327 if (sep == '/' || sep == '\\')
52fcf7ee 1328 *tmpstart++ = sep;
8ac9c18d
GS
1329 *tmpstart = '\0';
1330 while (sep) {
1331 /* walk up to slash */
1332 while (*start && *start != '/' && *start != '\\')
1333 ++start;
1334
1335 /* discard doubled slashes */
1336 while (*start && (start[1] == '/' || start[1] == '\\'))
1337 ++start;
1338 sep = *start;
1339
1340 /* stop and find full name of component */
1341 *start = '\0';
1342 fhand = FindFirstFile(path,&fdata);
1343 if (fhand != INVALID_HANDLE_VALUE) {
1344 strcpy(tmpstart, fdata.cFileName);
1345 tmpstart += strlen(fdata.cFileName);
1346 if (sep)
52fcf7ee 1347 *tmpstart++ = sep;
8ac9c18d
GS
1348 *tmpstart = '\0';
1349 *start++ = sep;
1350 FindClose(fhand);
1351 }
1352 else {
1353 /* failed a step, just return without side effects */
bf49b057 1354 /*PerlIO_printf(Perl_debug_log, "Failed to find %s\n", path);*/
8ac9c18d
GS
1355 *start = sep;
1356 return Nullch;
1357 }
1358 }
1359 strcpy(path,tmpbuf);
1360 return path;
1361}
1362
0551aaa8
GS
1363DllExport char *
1364win32_getenv(const char *name)
1365{
acfe0abc 1366 dTHX;
82867ecf 1367 WCHAR wBuffer[MAX_PATH+1];
0551aaa8 1368 DWORD needlen;
51371543 1369 SV *curitem = Nullsv;
58a50f62 1370
7fac1903 1371 if (USING_WIDE()) {
0cb96387 1372 A2WHELPER(name, wBuffer, sizeof(wBuffer));
51371543 1373 needlen = GetEnvironmentVariableW(wBuffer, NULL, 0);
7fac1903
GS
1374 }
1375 else
51371543 1376 needlen = GetEnvironmentVariableA(name,NULL,0);
58a50f62 1377 if (needlen != 0) {
51371543 1378 curitem = sv_2mortal(newSVpvn("", 0));
7fac1903 1379 if (USING_WIDE()) {
51371543
GS
1380 SV *acuritem;
1381 do {
1382 SvGROW(curitem, (needlen+1)*sizeof(WCHAR));
1383 needlen = GetEnvironmentVariableW(wBuffer,
1384 (WCHAR*)SvPVX(curitem),
1385 needlen);
1386 } while (needlen >= SvLEN(curitem)/sizeof(WCHAR));
c5be433b 1387 SvCUR_set(curitem, (needlen*sizeof(WCHAR))+1);
51371543
GS
1388 acuritem = sv_2mortal(newSVsv(curitem));
1389 W2AHELPER((WCHAR*)SvPVX(acuritem), SvPVX(curitem), SvCUR(curitem));
7fac1903
GS
1390 }
1391 else {
51371543
GS
1392 do {
1393 SvGROW(curitem, needlen+1);
1394 needlen = GetEnvironmentVariableA(name,SvPVX(curitem),
1395 needlen);
1396 } while (needlen >= SvLEN(curitem));
1397 SvCUR_set(curitem, needlen);
58a50f62 1398 }
0551aaa8 1399 }
c934e9d4 1400 else {
7a5f8e82 1401 /* allow any environment variables that begin with 'PERL'
c934e9d4 1402 to be stored in the registry */
51371543 1403 if (strncmp(name, "PERL", 4) == 0)
c5be433b 1404 (void)get_regstr(name, &curitem);
c69f6586 1405 }
51371543
GS
1406 if (curitem && SvCUR(curitem))
1407 return SvPVX(curitem);
58a50f62 1408
51371543 1409 return Nullch;
0551aaa8
GS
1410}
1411
ac5c734f
GS
1412DllExport int
1413win32_putenv(const char *name)
1414{
acfe0abc 1415 dTHX;
ac5c734f
GS
1416 char* curitem;
1417 char* val;
7fac1903
GS
1418 WCHAR* wCuritem;
1419 WCHAR* wVal;
1420 int length, relval = -1;
51371543 1421
73c4f7a1 1422 if (name) {
7fac1903
GS
1423 if (USING_WIDE()) {
1424 length = strlen(name)+1;
1425 New(1309,wCuritem,length,WCHAR);
c5be433b 1426 A2WHELPER(name, wCuritem, length*sizeof(WCHAR));
7fac1903 1427 wVal = wcschr(wCuritem, '=');
7766f137 1428 if (wVal) {
7fac1903 1429 *wVal++ = '\0';
7766f137 1430 if (SetEnvironmentVariableW(wCuritem, *wVal ? wVal : NULL))
7fac1903
GS
1431 relval = 0;
1432 }
1433 Safefree(wCuritem);
1434 }
1435 else {
1436 New(1309,curitem,strlen(name)+1,char);
1437 strcpy(curitem, name);
1438 val = strchr(curitem, '=');
7766f137 1439 if (val) {
7fac1903
GS
1440 /* The sane way to deal with the environment.
1441 * Has these advantages over putenv() & co.:
1442 * * enables us to store a truly empty value in the
1443 * environment (like in UNIX).
1444 * * we don't have to deal with RTL globals, bugs and leaks.
1445 * * Much faster.
1446 * Why you may want to enable USE_WIN32_RTL_ENV:
1447 * * environ[] and RTL functions will not reflect changes,
1448 * which might be an issue if extensions want to access
1449 * the env. via RTL. This cuts both ways, since RTL will
1450 * not see changes made by extensions that call the Win32
1451 * functions directly, either.
1452 * GSAR 97-06-07
1453 */
1454 *val++ = '\0';
7766f137 1455 if (SetEnvironmentVariableA(curitem, *val ? val : NULL))
7fac1903
GS
1456 relval = 0;
1457 }
1458 Safefree(curitem);
ac5c734f 1459 }
ac5c734f
GS
1460 }
1461 return relval;
1462}
1463
d55594ae 1464static long
2d7a9237 1465filetime_to_clock(PFILETIME ft)
d55594ae 1466{
7766f137
GS
1467 __int64 qw = ft->dwHighDateTime;
1468 qw <<= 32;
1469 qw |= ft->dwLowDateTime;
1470 qw /= 10000; /* File time ticks at 0.1uS, clock at 1mS */
1471 return (long) qw;
d55594ae
GS
1472}
1473
f3986ebb
GS
1474DllExport int
1475win32_times(struct tms *timebuf)
0a753a76 1476{
d55594ae
GS
1477 FILETIME user;
1478 FILETIME kernel;
1479 FILETIME dummy;
1480 if (GetProcessTimes(GetCurrentProcess(), &dummy, &dummy,
1481 &kernel,&user)) {
2d7a9237
GS
1482 timebuf->tms_utime = filetime_to_clock(&user);
1483 timebuf->tms_stime = filetime_to_clock(&kernel);
d55594ae
GS
1484 timebuf->tms_cutime = 0;
1485 timebuf->tms_cstime = 0;
1486
1487 } else {
1488 /* That failed - e.g. Win95 fallback to clock() */
1489 clock_t t = clock();
1490 timebuf->tms_utime = t;
1491 timebuf->tms_stime = 0;
1492 timebuf->tms_cutime = 0;
1493 timebuf->tms_cstime = 0;
1494 }
68dc0745 1495 return 0;
0a753a76 1496}
1497
9c51cf4c 1498/* fix utime() so it works on directories in NT */
ad0751ec
GS
1499static BOOL
1500filetime_from_time(PFILETIME pFileTime, time_t Time)
1501{
9c51cf4c 1502 struct tm *pTM = localtime(&Time);
ad0751ec 1503 SYSTEMTIME SystemTime;
9c51cf4c 1504 FILETIME LocalTime;
ad0751ec
GS
1505
1506 if (pTM == NULL)
1507 return FALSE;
1508
1509 SystemTime.wYear = pTM->tm_year + 1900;
1510 SystemTime.wMonth = pTM->tm_mon + 1;
1511 SystemTime.wDay = pTM->tm_mday;
1512 SystemTime.wHour = pTM->tm_hour;
1513 SystemTime.wMinute = pTM->tm_min;
1514 SystemTime.wSecond = pTM->tm_sec;
1515 SystemTime.wMilliseconds = 0;
1516
9c51cf4c
GS
1517 return SystemTimeToFileTime(&SystemTime, &LocalTime) &&
1518 LocalFileTimeToFileTime(&LocalTime, pFileTime);
ad0751ec
GS
1519}
1520
1521DllExport int
7766f137
GS
1522win32_unlink(const char *filename)
1523{
acfe0abc 1524 dTHX;
7766f137
GS
1525 int ret;
1526 DWORD attrs;
1527
1528 if (USING_WIDE()) {
82867ecf 1529 WCHAR wBuffer[MAX_PATH+1];
e9ff6d27 1530 WCHAR* pwBuffer;
7766f137
GS
1531
1532 A2WHELPER(filename, wBuffer, sizeof(wBuffer));
e9ff6d27
GS
1533 pwBuffer = PerlDir_mapW(wBuffer);
1534 attrs = GetFileAttributesW(pwBuffer);
c00206c8
GS
1535 if (attrs == 0xFFFFFFFF)
1536 goto fail;
7766f137 1537 if (attrs & FILE_ATTRIBUTE_READONLY) {
e9ff6d27
GS
1538 (void)SetFileAttributesW(pwBuffer, attrs & ~FILE_ATTRIBUTE_READONLY);
1539 ret = _wunlink(pwBuffer);
7766f137 1540 if (ret == -1)
e9ff6d27 1541 (void)SetFileAttributesW(pwBuffer, attrs);
7766f137
GS
1542 }
1543 else
e9ff6d27 1544 ret = _wunlink(pwBuffer);
7766f137
GS
1545 }
1546 else {
e9ff6d27
GS
1547 filename = PerlDir_mapA(filename);
1548 attrs = GetFileAttributesA(filename);
c00206c8
GS
1549 if (attrs == 0xFFFFFFFF)
1550 goto fail;
7766f137 1551 if (attrs & FILE_ATTRIBUTE_READONLY) {
e9ff6d27
GS
1552 (void)SetFileAttributesA(filename, attrs & ~FILE_ATTRIBUTE_READONLY);
1553 ret = unlink(filename);
7766f137 1554 if (ret == -1)
e9ff6d27 1555 (void)SetFileAttributesA(filename, attrs);
7766f137
GS
1556 }
1557 else
e9ff6d27 1558 ret = unlink(filename);
7766f137
GS
1559 }
1560 return ret;
c00206c8
GS
1561fail:
1562 errno = ENOENT;
1563 return -1;
7766f137
GS
1564}
1565
1566DllExport int
3b405fc5 1567win32_utime(const char *filename, struct utimbuf *times)
ad0751ec 1568{
acfe0abc 1569 dTHX;
ad0751ec
GS
1570 HANDLE handle;
1571 FILETIME ftCreate;
1572 FILETIME ftAccess;
1573 FILETIME ftWrite;
1574 struct utimbuf TimeBuffer;
82867ecf 1575 WCHAR wbuffer[MAX_PATH+1];
e9ff6d27 1576 WCHAR* pwbuffer;
ad0751ec 1577
7fac1903
GS
1578 int rc;
1579 if (USING_WIDE()) {
0cb96387 1580 A2WHELPER(filename, wbuffer, sizeof(wbuffer));
e9ff6d27
GS
1581 pwbuffer = PerlDir_mapW(wbuffer);
1582 rc = _wutime(pwbuffer, (struct _utimbuf*)times);
7fac1903
GS
1583 }
1584 else {
e9ff6d27
GS
1585 filename = PerlDir_mapA(filename);
1586 rc = utime(filename, times);
7fac1903 1587 }
ad0751ec
GS
1588 /* EACCES: path specifies directory or readonly file */
1589 if (rc == 0 || errno != EACCES /* || !IsWinNT() */)
1590 return rc;
1591
1592 if (times == NULL) {
1593 times = &TimeBuffer;
1594 time(&times->actime);
1595 times->modtime = times->actime;
1596 }
1597
1598 /* This will (and should) still fail on readonly files */
7fac1903 1599 if (USING_WIDE()) {
e9ff6d27 1600 handle = CreateFileW(pwbuffer, GENERIC_READ | GENERIC_WRITE,
7fac1903
GS
1601 FILE_SHARE_READ | FILE_SHARE_DELETE, NULL,
1602 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
1603 }
1604 else {
e9ff6d27 1605 handle = CreateFileA(filename, GENERIC_READ | GENERIC_WRITE,
7fac1903
GS
1606 FILE_SHARE_READ | FILE_SHARE_DELETE, NULL,
1607 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
1608 }
ad0751ec
GS
1609 if (handle == INVALID_HANDLE_VALUE)
1610 return rc;
1611
1612 if (GetFileTime(handle, &ftCreate, &ftAccess, &ftWrite) &&
1613 filetime_from_time(&ftAccess, times->actime) &&
1614 filetime_from_time(&ftWrite, times->modtime) &&
1615 SetFileTime(handle, &ftCreate, &ftAccess, &ftWrite))
1616 {
1617 rc = 0;
1618 }
1619
1620 CloseHandle(handle);
1621 return rc;
1622}
1623
2d7a9237 1624DllExport int
b2af26b1
GS
1625win32_uname(struct utsname *name)
1626{
1627 struct hostent *hep;
1628 STRLEN nodemax = sizeof(name->nodename)-1;
1629 OSVERSIONINFO osver;
1630
1631 memset(&osver, 0, sizeof(OSVERSIONINFO));
1632 osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
1633 if (GetVersionEx(&osver)) {
1634 /* sysname */
1635 switch (osver.dwPlatformId) {
1636 case VER_PLATFORM_WIN32_WINDOWS:
1637 strcpy(name->sysname, "Windows");
1638 break;
1639 case VER_PLATFORM_WIN32_NT:
1640 strcpy(name->sysname, "Windows NT");
1641 break;
1642 case VER_PLATFORM_WIN32s:
1643 strcpy(name->sysname, "Win32s");
1644 break;
1645 default:
1646 strcpy(name->sysname, "Win32 Unknown");
1647 break;
1648 }
1649
cf6cacac
GS
1650 /* release */
1651 sprintf(name->release, "%d.%d",
b2af26b1
GS
1652 osver.dwMajorVersion, osver.dwMinorVersion);
1653
cf6cacac
GS
1654 /* version */
1655 sprintf(name->version, "Build %d",
b2af26b1
GS
1656 osver.dwPlatformId == VER_PLATFORM_WIN32_NT
1657 ? osver.dwBuildNumber : (osver.dwBuildNumber & 0xffff));
1658 if (osver.szCSDVersion[0]) {
cf6cacac 1659 char *buf = name->version + strlen(name->version);
b2af26b1
GS
1660 sprintf(buf, " (%s)", osver.szCSDVersion);
1661 }
1662 }
1663 else {
1664 *name->sysname = '\0';
1665 *name->version = '\0';
1666 *name->release = '\0';
1667 }
1668
1669 /* nodename */
1670 hep = win32_gethostbyname("localhost");
1671 if (hep) {
1672 STRLEN len = strlen(hep->h_name);
1673 if (len <= nodemax) {
1674 strcpy(name->nodename, hep->h_name);
1675 }
1676 else {
1677 strncpy(name->nodename, hep->h_name, nodemax);
1678 name->nodename[nodemax] = '\0';
1679 }
1680 }
1681 else {
1682 DWORD sz = nodemax;
1683 if (!GetComputerName(name->nodename, &sz))
1684 *name->nodename = '\0';
1685 }
1686
1687 /* machine (architecture) */
1688 {
1689 SYSTEM_INFO info;
1690 char *arch;
1691 GetSystemInfo(&info);
a6c40364 1692
6f24f39d
JK
1693#if (defined(__BORLANDC__)&&(__BORLANDC__<=0x520)) \
1694 || (defined(__MINGW32__) && !defined(_ANONYMOUS_UNION))
a6c40364
GS
1695 switch (info.u.s.wProcessorArchitecture) {
1696#else
b2af26b1 1697 switch (info.wProcessorArchitecture) {
a6c40364 1698#endif
b2af26b1
GS
1699 case PROCESSOR_ARCHITECTURE_INTEL:
1700 arch = "x86"; break;
1701 case PROCESSOR_ARCHITECTURE_MIPS:
1702 arch = "mips"; break;
1703 case PROCESSOR_ARCHITECTURE_ALPHA:
1704 arch = "alpha"; break;
1705 case PROCESSOR_ARCHITECTURE_PPC:
1706 arch = "ppc"; break;
1707 default:
1708 arch = "unknown"; break;
1709 }
1710 strcpy(name->machine, arch);
1711 }
1712 return 0;
1713}
1714
8fb3fcfb
NIS
1715/* Timing related stuff */
1716
1717DllExport int
1718win32_async_check(pTHX)
1719{
1720 MSG msg;
1721 int ours = 1;
7e5f34c0
NIS
1722 /* Passing PeekMessage -1 as HWND (2nd arg) only get PostThreadMessage() messages
1723 * and ignores window messages - should co-exist better with windows apps e.g. Tk
1724 */
1725 while (PeekMessage(&msg, (HWND)-1, 0, 0, PM_REMOVE)) {
8fb3fcfb
NIS
1726 switch(msg.message) {
1727
7e5f34c0
NIS
1728#if 0
1729 /* Perhaps some other messages could map to signals ? ... */
1730 case WM_CLOSE:
1731 case WM_QUIT:
1732 /* Treat WM_QUIT like SIGHUP? */
1733 CALL_FPTR(PL_sighandlerp)(1);
1734 break;
1735#endif
1736
c843839f 1737 /* We use WM_USER to fake kill() with other signals */
8fb3fcfb 1738 case WM_USER: {
c843839f 1739 CALL_FPTR(PL_sighandlerp)(msg.wParam);
8fb3fcfb
NIS
1740 break;
1741 }
1742
1743 case WM_TIMER: {
1744 /* alarm() is a one-shot but SetTimer() repeats so kill it */
1745 KillTimer(NULL,w32_timerid);
1746 w32_timerid=0;
1747 /* Now fake a call to signal handler */
1748 CALL_FPTR(PL_sighandlerp)(14);
1749 break;
1750 }
1751
1752 /* Otherwise do normal Win32 thing - in case it is useful */
1753 default:
1754 TranslateMessage(&msg);
1755 DispatchMessage(&msg);
1756 ours = 0;
1757 break;
1758 }
1759 }
1760
7e5f34c0 1761 /* Above or other stuff may have set a signal flag */
8fb3fcfb
NIS
1762 if (PL_sig_pending) {
1763 despatch_signals();
1764 }
1765 return ours;
1766}
1767
1768DllExport DWORD
1769win32_msgwait(pTHX_ DWORD count, LPHANDLE handles, DWORD timeout, LPDWORD resultp)
1770{
1771 /* We may need several goes at this - so compute when we stop */
1772 DWORD ticks = 0;
1773 if (timeout != INFINITE) {
1774 ticks = GetTickCount();
1775 timeout += ticks;
1776 }
1777 while (1) {
1778 DWORD result = MsgWaitForMultipleObjects(count,handles,FALSE,timeout-ticks, QS_ALLEVENTS);
1779 if (resultp)
1780 *resultp = result;
1781 if (result == WAIT_TIMEOUT) {
1782 /* Ran out of time - explicit return of zero to avoid -ve if we
1783 have scheduling issues
1784 */
1785 return 0;
1786 }
1787 if (timeout != INFINITE) {
1788 ticks = GetTickCount();
1789 }
1790 if (result == WAIT_OBJECT_0 + count) {
1791 /* Message has arrived - check it */
1792 if (win32_async_check(aTHX)) {
1793 /* was one of ours */
1794 break;
1795 }
1796 }
1797 else {
1798 /* Not timeout or message - one of handles is ready */
1799 break;
1800 }
1801 }
1802 /* compute time left to wait */
1803 ticks = timeout - ticks;
1804 /* If we are past the end say zero */
1805 return (ticks > 0) ? ticks : 0;
1806}
1807
932b7487
RC
1808int
1809win32_internal_wait(int *status, DWORD timeout)
1810{
1811 /* XXX this wait emulation only knows about processes
1812 * spawned via win32_spawnvp(P_NOWAIT, ...).
1813 */
1814 dTHX;
1815 int i, retval;
1816 DWORD exitcode, waitcode;
1817
1818#ifdef USE_ITHREADS
1819 if (w32_num_pseudo_children) {
8fb3fcfb
NIS
1820 win32_msgwait(aTHX_ w32_num_pseudo_children, w32_pseudo_child_handles,
1821 timeout, &waitcode);
932b7487
RC
1822 /* Time out here if there are no other children to wait for. */
1823 if (waitcode == WAIT_TIMEOUT) {
1824 if (!w32_num_children) {
1825 return 0;
1826 }
1827 }
1828 else if (waitcode != WAIT_FAILED) {
1829 if (waitcode >= WAIT_ABANDONED_0
1830 && waitcode < WAIT_ABANDONED_0 + w32_num_pseudo_children)
1831 i = waitcode - WAIT_ABANDONED_0;
1832 else
1833 i = waitcode - WAIT_OBJECT_0;
1834 if (GetExitCodeThread(w32_pseudo_child_handles[i], &exitcode)) {
1835 *status = (int)((exitcode & 0xff) << 8);
1836 retval = (int)w32_pseudo_child_pids[i];
1837 remove_dead_pseudo_process(i);
1838 return -retval;
1839 }
1840 }
1841 }
1842#endif
1843
1844 if (!w32_num_children) {
1845 errno = ECHILD;
1846 return -1;
1847 }
1848
1849 /* if a child exists, wait for it to die */
8fb3fcfb 1850 win32_msgwait(aTHX_ w32_num_children, w32_child_handles, timeout, &waitcode);
932b7487
RC
1851 if (waitcode == WAIT_TIMEOUT) {
1852 return 0;
1853 }
1854 if (waitcode != WAIT_FAILED) {
1855 if (waitcode >= WAIT_ABANDONED_0
1856 && waitcode < WAIT_ABANDONED_0 + w32_num_children)
1857 i = waitcode - WAIT_ABANDONED_0;
1858 else
1859 i = waitcode - WAIT_OBJECT_0;
1860 if (GetExitCodeProcess(w32_child_handles[i], &exitcode) ) {
1861 *status = (int)((exitcode & 0xff) << 8);
1862 retval = (int)w32_child_pids[i];
1863 remove_dead_process(i);
1864 return retval;
1865 }
1866 }
1867
1868FAILED:
1869 errno = GetLastError();
1870 return -1;
1871}
1872
b2af26b1 1873DllExport int
f55ee38a
GS
1874win32_waitpid(int pid, int *status, int flags)
1875{
acfe0abc 1876 dTHX;
922b1888 1877 DWORD timeout = (flags & WNOHANG) ? 0 : INFINITE;
0aaad0ff 1878 int retval = -1;
c66b022d 1879 long child;
7766f137 1880 if (pid == -1) /* XXX threadid == 1 ? */
932b7487 1881 return win32_internal_wait(status, timeout);
7766f137
GS
1882#ifdef USE_ITHREADS
1883 else if (pid < 0) {
c66b022d 1884 child = find_pseudo_pid(-pid);
7766f137
GS
1885 if (child >= 0) {
1886 HANDLE hThread = w32_pseudo_child_handles[child];
8fb3fcfb
NIS
1887 DWORD waitcode;
1888 win32_msgwait(aTHX_ 1, &hThread, timeout, &waitcode);
2f67576d
BC
1889 if (waitcode == WAIT_TIMEOUT) {
1890 return 0;
1891 }
8fb3fcfb 1892 else if (waitcode == WAIT_OBJECT_0) {
7766f137
GS
1893 if (GetExitCodeThread(hThread, &waitcode)) {
1894 *status = (int)((waitcode & 0xff) << 8);
1895 retval = (int)w32_pseudo_child_pids[child];
1896 remove_dead_pseudo_process(child);
68a29c53 1897 return -retval;
7766f137
GS
1898 }
1899 }
1900 else
1901 errno = ECHILD;
1902 }
922b1888
GS
1903 else if (IsWin95()) {
1904 pid = -pid;
1905 goto alien_process;
1906 }
7766f137
GS
1907 }
1908#endif
f55ee38a 1909 else {
922b1888
GS
1910 HANDLE hProcess;
1911 DWORD waitcode;
c66b022d 1912 child = find_pid(pid);
0aaad0ff 1913 if (child >= 0) {
922b1888 1914 hProcess = w32_child_handles[child];
8fb3fcfb 1915 win32_msgwait(aTHX_ 1, &hProcess, timeout, &waitcode);
a7867d0a
GS
1916 if (waitcode == WAIT_TIMEOUT) {
1917 return 0;
1918 }
8fb3fcfb 1919 else if (waitcode == WAIT_OBJECT_0) {
922b1888
GS
1920 if (GetExitCodeProcess(hProcess, &waitcode)) {
1921 *status = (int)((waitcode & 0xff) << 8);
1922 retval = (int)w32_child_pids[child];
1923 remove_dead_process(child);
1924 return retval;
1925 }
a7867d0a 1926 }
0aaad0ff
GS
1927 else
1928 errno = ECHILD;
1929 }
1930 else {
922b1888
GS
1931alien_process:
1932 hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE,
1933 (IsWin95() ? -pid : pid));
1934 if (hProcess) {
8fb3fcfb 1935 win32_msgwait(aTHX_ 1, &hProcess, timeout, &waitcode);
922b1888
GS
1936 if (waitcode == WAIT_TIMEOUT) {
1937 return 0;
1938 }
8fb3fcfb 1939 else if (waitcode == WAIT_OBJECT_0) {
922b1888
GS
1940 if (GetExitCodeProcess(hProcess, &waitcode)) {
1941 *status = (int)((waitcode & 0xff) << 8);
1942 CloseHandle(hProcess);
1943 return pid;
1944 }
1945 }
1946 CloseHandle(hProcess);
1947 }
1948 else
1949 errno = ECHILD;
0aaad0ff 1950 }
f55ee38a 1951 }
0aaad0ff 1952 return retval >= 0 ? pid : retval;
f55ee38a
GS
1953}
1954
1955DllExport int
2d7a9237
GS
1956win32_wait(int *status)
1957{
932b7487 1958 return win32_internal_wait(status, INFINITE);
2d7a9237 1959}
d55594ae 1960
8fb3fcfb
NIS
1961DllExport unsigned int
1962win32_sleep(unsigned int t)
d55594ae 1963{
acfe0abc 1964 dTHX;
8fb3fcfb
NIS
1965 /* Win32 times are in ms so *1000 in and /1000 out */
1966 return win32_msgwait(aTHX_ 0, NULL, t*1000, NULL)/1000;
d55594ae
GS
1967}
1968
f3986ebb
GS
1969DllExport unsigned int
1970win32_alarm(unsigned int sec)
0a753a76 1971{
d55594ae
GS
1972 /*
1973 * the 'obvious' implentation is SetTimer() with a callback
1974 * which does whatever receiving SIGALRM would do
1975 * we cannot use SIGALRM even via raise() as it is not
1976 * one of the supported codes in <signal.h>
d55594ae 1977 */
acfe0abc 1978 dTHX;
8fb3fcfb
NIS
1979 if (sec) {
1980 w32_timerid = SetTimer(NULL,w32_timerid,sec*1000,NULL);
1981 }
1982 else {
1983 if (w32_timerid) {
1984 KillTimer(NULL,w32_timerid);
1985 w32_timerid=0;
1986 }
1987 }
afe91769 1988 return 0;
0a753a76 1989}
1990
26618a56 1991#ifdef HAVE_DES_FCRYPT
2d77217b 1992extern char * des_fcrypt(const char *txt, const char *salt, char *cbuf);
ff95b63e 1993#endif
26618a56
GS
1994
1995DllExport char *
1996win32_crypt(const char *txt, const char *salt)
1997{
acfe0abc 1998 dTHX;
ff95b63e 1999#ifdef HAVE_DES_FCRYPT
3352bfcb 2000 return des_fcrypt(txt, salt, w32_crypt_buffer);
ff95b63e 2001#else
25dbdbbc 2002 Perl_croak(aTHX_ "The crypt() function is unimplemented due to excessive paranoia.");
b8957cf1 2003 return Nullch;
ff95b63e 2004#endif
26618a56 2005}
26618a56 2006
9e5f57de 2007#ifdef USE_FIXED_OSFHANDLE
390b85e7
GS
2008
2009#define FOPEN 0x01 /* file handle open */
b181b6fb 2010#define FNOINHERIT 0x10 /* file handle opened O_NOINHERIT */
390b85e7
GS
2011#define FAPPEND 0x20 /* file handle opened O_APPEND */
2012#define FDEV 0x40 /* file handle refers to device */
2013#define FTEXT 0x80 /* file handle is in text mode */
2014
390b85e7
GS
2015/***
2016*int my_open_osfhandle(long osfhandle, int flags) - open C Runtime file handle
2017*
2018*Purpose:
2019* This function allocates a free C Runtime file handle and associates
2020* it with the Win32 HANDLE specified by the first parameter. This is a
9e5f57de
GS
2021* temperary fix for WIN95's brain damage GetFileType() error on socket
2022* we just bypass that call for socket
2023*
2024* This works with MSVC++ 4.0+ or GCC/Mingw32
390b85e7
GS
2025*
2026*Entry:
2027* long osfhandle - Win32 HANDLE to associate with C Runtime file handle.
2028* int flags - flags to associate with C Runtime file handle.
2029*
2030*Exit:
2031* returns index of entry in fh, if successful
2032* return -1, if no free entry is found
2033*
2034*Exceptions:
2035*
2036*******************************************************************************/
2037
9e5f57de
GS
2038/*
2039 * we fake up some parts of the CRT that aren't exported by MSVCRT.dll
2040 * this lets sockets work on Win9X with GCC and should fix the problems
2041 * with perl95.exe
2042 * -- BKS, 1-23-2000
2043*/
2044
9e5f57de
GS
2045/* create an ioinfo entry, kill its handle, and steal the entry */
2046
b181b6fb
GS
2047static int
2048_alloc_osfhnd(void)
9e5f57de
GS
2049{
2050 HANDLE hF = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
dd8f4818 2051 int fh = _open_osfhandle((long)hF, 0);
9e5f57de
GS
2052 CloseHandle(hF);
2053 if (fh == -1)
2054 return fh;
2055 EnterCriticalSection(&(_pioinfo(fh)->lock));
2056 return fh;
2057}
2058
390b85e7
GS
2059static int
2060my_open_osfhandle(long osfhandle, int flags)
2061{
2062 int fh;
2063 char fileflags; /* _osfile flags */
2064
2065 /* copy relevant flags from second parameter */
2066 fileflags = FDEV;
2067
9404a519 2068 if (flags & O_APPEND)
390b85e7
GS
2069 fileflags |= FAPPEND;
2070
9404a519 2071 if (flags & O_TEXT)
390b85e7
GS
2072 fileflags |= FTEXT;
2073
b181b6fb
GS
2074 if (flags & O_NOINHERIT)
2075 fileflags |= FNOINHERIT;
2076
390b85e7 2077 /* attempt to allocate a C Runtime file handle */
9404a519 2078 if ((fh = _alloc_osfhnd()) == -1) {
390b85e7
GS
2079 errno = EMFILE; /* too many open files */
2080 _doserrno = 0L; /* not an OS error */
2081 return -1; /* return error to caller */
2082 }
2083
2084 /* the file is open. now, set the info in _osfhnd array */
2085 _set_osfhnd(fh, osfhandle);
2086
2087 fileflags |= FOPEN; /* mark as open */
2088
390b85e7 2089 _osfile(fh) = fileflags; /* set osfile entry */
dd8f4818 2090 LeaveCriticalSection(&_pioinfo(fh)->lock);
390b85e7
GS
2091
2092 return fh; /* return handle */
2093}
2094
f3986ebb 2095#endif /* USE_FIXED_OSFHANDLE */
390b85e7
GS
2096
2097/* simulate flock by locking a range on the file */
2098
2099#define LK_ERR(f,i) ((f) ? (i = 0) : (errno = GetLastError()))
2100#define LK_LEN 0xffff0000
2101
f3986ebb
GS
2102DllExport int
2103win32_flock(int fd, int oper)
390b85e7
GS
2104{
2105 OVERLAPPED o;
2106 int i = -1;
2107 HANDLE fh;
2108
f3986ebb 2109 if (!IsWinNT()) {
acfe0abc 2110 dTHX;
4f63d024 2111 Perl_croak_nocontext("flock() unimplemented on this platform");
f3986ebb
GS
2112 return -1;
2113 }
390b85e7
GS
2114 fh = (HANDLE)_get_osfhandle(fd);
2115 memset(&o, 0, sizeof(o));
2116
2117 switch(oper) {
2118 case LOCK_SH: /* shared lock */
2119 LK_ERR(LockFileEx(fh, 0, 0, LK_LEN, 0, &o),i);
2120 break;
2121 case LOCK_EX: /* exclusive lock */
2122 LK_ERR(LockFileEx(fh, LOCKFILE_EXCLUSIVE_LOCK, 0, LK_LEN, 0, &o),i);
2123 break;
2124 case LOCK_SH|LOCK_NB: /* non-blocking shared lock */
2125 LK_ERR(LockFileEx(fh, LOCKFILE_FAIL_IMMEDIATELY, 0, LK_LEN, 0, &o),i);
2126 break;
2127 case LOCK_EX|LOCK_NB: /* non-blocking exclusive lock */
2128 LK_ERR(LockFileEx(fh,
2129 LOCKFILE_EXCLUSIVE_LOCK|LOCKFILE_FAIL_IMMEDIATELY,
2130 0, LK_LEN, 0, &o),i);
2131 break;
2132 case LOCK_UN: /* unlock lock */
2133 LK_ERR(UnlockFileEx(fh, 0, LK_LEN, 0, &o),i);
2134 break;
2135 default: /* unknown */
2136 errno = EINVAL;
2137 break;
2138 }
2139 return i;
2140}
2141
2142#undef LK_ERR
2143#undef LK_LEN
2144
68dc0745 2145/*
2146 * redirected io subsystem for all XS modules
2147 *
2148 */
0a753a76 2149
68dc0745 2150DllExport int *
2151win32_errno(void)
0a753a76 2152{
390b85e7 2153 return (&errno);
0a753a76 2154}
2155
dcb2879a
GS
2156DllExport char ***
2157win32_environ(void)
2158{
390b85e7 2159 return (&(_environ));
dcb2879a
GS
2160}
2161
68dc0745 2162/* the rest are the remapped stdio routines */
2163DllExport FILE *
2164win32_stderr(void)
0a753a76 2165{
390b85e7 2166 return (stderr);
0a753a76 2167}
2168
68dc0745 2169DllExport FILE *
2170win32_stdin(void)
0a753a76 2171{
390b85e7 2172 return (stdin);
0a753a76 2173}
2174
68dc0745 2175DllExport FILE *
2176win32_stdout()
0a753a76 2177{
390b85e7 2178 return (stdout);
0a753a76 2179}
2180
68dc0745 2181DllExport int
2182win32_ferror(FILE *fp)
0a753a76 2183{
390b85e7 2184 return (ferror(fp));
0a753a76 2185}
2186
2187
68dc0745 2188DllExport int
2189win32_feof(FILE *fp)
0a753a76 2190{
390b85e7 2191 return (feof(fp));
0a753a76 2192}
2193
68dc0745 2194/*
2195 * Since the errors returned by the socket error function
2196 * WSAGetLastError() are not known by the library routine strerror
2197 * we have to roll our own.
2198 */
0a753a76 2199
68dc0745 2200DllExport char *
2201win32_strerror(int e)
0a753a76 2202{
6f24f39d 2203#if !defined __BORLANDC__ && !defined __MINGW32__ /* compiler intolerance */
68dc0745 2204 extern int sys_nerr;
3e3baf6d 2205#endif
68dc0745 2206 DWORD source = 0;
0a753a76 2207
9404a519 2208 if (e < 0 || e > sys_nerr) {
acfe0abc 2209 dTHX;
9404a519 2210 if (e < 0)
68dc0745 2211 e = GetLastError();
0a753a76 2212
9404a519 2213 if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, &source, e, 0,
3352bfcb
GS
2214 w32_strerror_buffer,
2215 sizeof(w32_strerror_buffer), NULL) == 0)
2216 strcpy(w32_strerror_buffer, "Unknown Error");
0a753a76 2217
3352bfcb 2218 return w32_strerror_buffer;
68dc0745 2219 }
390b85e7 2220 return strerror(e);
0a753a76 2221}
2222
22fae026 2223DllExport void
c5be433b 2224win32_str_os_error(void *sv, DWORD dwErr)
22fae026
TM
2225{
2226 DWORD dwLen;
2227 char *sMsg;
2228 dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER
2229 |FORMAT_MESSAGE_IGNORE_INSERTS
2230 |FORMAT_MESSAGE_FROM_SYSTEM, NULL,
2231 dwErr, 0, (char *)&sMsg, 1, NULL);
2ce77adf 2232 /* strip trailing whitespace and period */
22fae026 2233 if (0 < dwLen) {
2ce77adf
GS
2234 do {
2235 --dwLen; /* dwLen doesn't include trailing null */
2236 } while (0 < dwLen && isSPACE(sMsg[dwLen]));
22fae026
TM
2237 if ('.' != sMsg[dwLen])
2238 dwLen++;
2ce77adf 2239 sMsg[dwLen] = '\0';
22fae026
TM
2240 }
2241 if (0 == dwLen) {
c69f6586 2242 sMsg = (char*)LocalAlloc(0, 64/**sizeof(TCHAR)*/);
db7c17d7
GS
2243 if (sMsg)
2244 dwLen = sprintf(sMsg,
2245 "Unknown error #0x%lX (lookup 0x%lX)",
2246 dwErr, GetLastError());
2247 }
2248 if (sMsg) {
acfe0abc 2249 dTHX;
db7c17d7
GS
2250 sv_setpvn((SV*)sv, sMsg, dwLen);
2251 LocalFree(sMsg);
22fae026 2252 }
22fae026
TM
2253}
2254
68dc0745 2255DllExport int
2256win32_fprintf(FILE *fp, const char *format, ...)
0a753a76 2257{
68dc0745 2258 va_list marker;
2259 va_start(marker, format); /* Initialize variable arguments. */
0a753a76 2260
390b85e7 2261 return (vfprintf(fp, format, marker));
0a753a76 2262}
2263
68dc0745 2264DllExport int
2265win32_printf(const char *format, ...)
0a753a76 2266{
68dc0745 2267 va_list marker;
2268 va_start(marker, format); /* Initialize variable arguments. */
0a753a76 2269
390b85e7 2270 return (vprintf(format, marker));
0a753a76 2271}
2272
68dc0745 2273DllExport int
2274win32_vfprintf(FILE *fp, const char *format, va_list args)
0a753a76 2275{
390b85e7 2276 return (vfprintf(fp, format, args));
0a753a76 2277}
2278
96e4d5b1 2279DllExport int
2280win32_vprintf(const char *format, va_list args)
2281{
390b85e7 2282 return (vprintf(format, args));
96e4d5b1 2283}
2284
68dc0745 2285DllExport size_t
2286win32_fread(void *buf, size_t size, size_t count, FILE *fp)
0a753a76 2287{
390b85e7 2288 return fread(buf, size, count, fp);
0a753a76 2289}
2290
68dc0745 2291DllExport size_t
2292win32_fwrite(const void *buf, size_t size, size_t count, FILE *fp)
0a753a76 2293{
390b85e7 2294 return fwrite(buf, size, count, fp);
0a753a76 2295}
2296
7fac1903
GS
2297#define MODE_SIZE 10
2298
68dc0745 2299DllExport FILE *
2300win32_fopen(const char *filename, const char *mode)
0a753a76 2301{
acfe0abc 2302 dTHX;
82867ecf 2303 WCHAR wMode[MODE_SIZE], wBuffer[MAX_PATH+1];
1c5905c2 2304 FILE *f;
c5be433b
GS
2305
2306 if (!*filename)
2307 return NULL;
2308
68dc0745 2309 if (stricmp(filename, "/dev/null")==0)
7fac1903
GS
2310 filename = "NUL";
2311
2312 if (USING_WIDE()) {
0cb96387
GS
2313 A2WHELPER(mode, wMode, sizeof(wMode));
2314 A2WHELPER(filename, wBuffer, sizeof(wBuffer));
1c5905c2 2315 f = _wfopen(PerlDir_mapW(wBuffer), wMode);
7fac1903 2316 }
1c5905c2
GS
2317 else
2318 f = fopen(PerlDir_mapA(filename), mode);
2319 /* avoid buffering headaches for child processes */
2320 if (f && *mode == 'a')
2321 win32_fseek(f, 0, SEEK_END);
2322 return f;
0a753a76 2323}
2324
f3986ebb
GS
2325#ifndef USE_SOCKETS_AS_HANDLES
2326#undef fdopen
2327#define fdopen my_fdopen
2328#endif
2329
68dc0745 2330DllExport FILE *
7fac1903 2331win32_fdopen(int handle, const char *mode)
0a753a76 2332{
acfe0abc 2333 dTHX;
51371543 2334 WCHAR wMode[MODE_SIZE];
1c5905c2 2335 FILE *f;
7fac1903 2336 if (USING_WIDE()) {
0cb96387 2337 A2WHELPER(mode, wMode, sizeof(wMode));
1c5905c2 2338 f = _wfdopen(handle, wMode);
7fac1903 2339 }
1c5905c2
GS
2340 else
2341 f = fdopen(handle, (char *) mode);
2342 /* avoid buffering headaches for child processes */
2343 if (f && *mode == 'a')
2344 win32_fseek(f, 0, SEEK_END);
2345 return f;
0a753a76 2346}
2347
68dc0745 2348DllExport FILE *
7fac1903 2349win32_freopen(const char *path, const char *mode, FILE *stream)
0a753a76 2350{
acfe0abc 2351 dTHX;
82867ecf 2352 WCHAR wMode[MODE_SIZE], wBuffer[MAX_PATH+1];
68dc0745 2353 if (stricmp(path, "/dev/null")==0)
7fac1903
GS
2354 path = "NUL";
2355
2356 if (USING_WIDE()) {
0cb96387
GS
2357 A2WHELPER(mode, wMode, sizeof(wMode));
2358 A2WHELPER(path, wBuffer, sizeof(wBuffer));
7766f137 2359 return _wfreopen(PerlDir_mapW(wBuffer), wMode, stream);
7fac1903 2360 }
7766f137 2361 return freopen(PerlDir_mapA(path), mode, stream);
0a753a76 2362}
2363
68dc0745 2364DllExport int
2365win32_fclose(FILE *pf)
0a753a76 2366{
f3986ebb 2367 return my_fclose(pf); /* defined in win32sck.c */
0a753a76 2368}
2369
68dc0745 2370DllExport int
2371win32_fputs(const char *s,FILE *pf)
0a753a76 2372{
390b85e7 2373 return fputs(s, pf);
0a753a76 2374}
2375
68dc0745 2376DllExport int
2377win32_fputc(int c,FILE *pf)
0a753a76 2378{
390b85e7 2379 return fputc(c,pf);
0a753a76 2380}
2381
68dc0745 2382DllExport int
2383win32_ungetc(int c,FILE *pf)
0a753a76 2384{
390b85e7 2385 return ungetc(c,pf);
0a753a76 2386}
2387
68dc0745 2388DllExport int
2389win32_getc(FILE *pf)
0a753a76 2390{
390b85e7 2391 return getc(pf);
0a753a76 2392}
2393
68dc0745 2394DllExport int
2395win32_fileno(FILE *pf)
0a753a76 2396{
390b85e7 2397 return fileno(pf);
0a753a76 2398}
2399
68dc0745 2400DllExport void
2401win32_clearerr(FILE *pf)
0a753a76 2402{
390b85e7 2403 clearerr(pf);
68dc0745 2404 return;
0a753a76 2405}
2406
68dc0745 2407DllExport int
2408win32_fflush(FILE *pf)
0a753a76 2409{
390b85e7 2410 return fflush(pf);
0a753a76 2411}
2412
68dc0745 2413DllExport long
2414win32_ftell(FILE *pf)
0a753a76 2415{
390b85e7 2416 return ftell(pf);
0a753a76 2417}
2418
68dc0745 2419DllExport int
2420win32_fseek(FILE *pf,long offset,int origin)
0a753a76 2421{
390b85e7 2422 return fseek(pf, offset, origin);
0a753a76 2423}
2424
68dc0745 2425DllExport int
2426win32_fgetpos(FILE *pf,fpos_t *p)
0a753a76 2427{
390b85e7 2428 return fgetpos(pf, p);
0a753a76 2429}
2430
68dc0745 2431DllExport int
2432win32_fsetpos(FILE *pf,const fpos_t *p)
0a753a76 2433{
390b85e7 2434 return fsetpos(pf, p);
0a753a76 2435}
2436
68dc0745 2437DllExport void
2438win32_rewind(FILE *pf)
0a753a76 2439{
390b85e7 2440 rewind(pf);
68dc0745 2441 return;
0a753a76 2442}
2443
68dc0745 2444DllExport FILE*
2445win32_tmpfile(void)
0a753a76 2446{
b3122bc4
JH
2447 dTHX;
2448 char prefix[MAX_PATH+1];
2449 char filename[MAX_PATH+1];
2450 DWORD len = GetTempPath(MAX_PATH, prefix);
2451 if (len && len < MAX_PATH) {
2452 if (GetTempFileName(prefix, "plx", 0, filename)) {
2453 HANDLE fh = CreateFile(filename,
2454 DELETE | GENERIC_READ | GENERIC_WRITE,
2455 0,
2456 NULL,
2457 CREATE_ALWAYS,
2458 FILE_ATTRIBUTE_NORMAL
2459 | FILE_FLAG_DELETE_ON_CLOSE,
2460 NULL);
2461 if (fh != INVALID_HANDLE_VALUE) {
2462 int fd = win32_open_osfhandle((long)fh, 0);
2463 if (fd >= 0) {
2464 DEBUG_p(PerlIO_printf(Perl_debug_log,
2465 "Created tmpfile=%s\n",filename));
2466 return fdopen(fd, "w+b");
2467 }
2468 }
2469 }
2470 }
2471 return NULL;
0a753a76 2472}
2473
68dc0745 2474DllExport void
2475win32_abort(void)
0a753a76 2476{
390b85e7 2477 abort();
68dc0745 2478 return;
0a753a76 2479}
2480
68dc0745 2481DllExport int
22239a37 2482win32_fstat(int fd,struct stat *sbufptr)
0a753a76 2483{
2a07f407
VK
2484#ifdef __BORLANDC__
2485 /* A file designated by filehandle is not shown as accessible
2486 * for write operations, probably because it is opened for reading.
2487 * --Vadim Konovalov
2488 */
2489 int rc = fstat(fd,sbufptr);
2490 BY_HANDLE_FILE_INFORMATION bhfi;
2491 if (GetFileInformationByHandle((HANDLE)_get_osfhandle(fd), &bhfi)) {
2492 sbufptr->st_mode &= 0xFE00;
2493 if (bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
2494 sbufptr->st_mode |= (S_IREAD + (S_IREAD >> 3) + (S_IREAD >> 6));
2495 else
2496 sbufptr->st_mode |= ((S_IREAD|S_IWRITE) + ((S_IREAD|S_IWRITE) >> 3)
2497 + ((S_IREAD|S_IWRITE) >> 6));
2498 }
2499 return rc;
2500#else
ed59ec62 2501 return my_fstat(fd,sbufptr);
2a07f407 2502#endif
0a753a76 2503}
2504
68dc0745 2505DllExport int
2506win32_pipe(int *pfd, unsigned int size, int mode)
0a753a76 2507{
390b85e7 2508 return _pipe(pfd, size, mode);
0a753a76 2509}
2510
8c0134a8
NIS
2511DllExport PerlIO*
2512win32_popenlist(const char *mode, IV narg, SV **args)
2513{
2514 dTHX;
2515 Perl_croak(aTHX_ "List form of pipe open not implemented");
2516 return NULL;
2517}
2518
50892819
GS
2519/*
2520 * a popen() clone that respects PERL5SHELL
00b02797
JH
2521 *
2522 * changed to return PerlIO* rather than FILE * by BKS, 11-11-2000
50892819
GS
2523 */
2524
00b02797 2525DllExport PerlIO*
68dc0745 2526win32_popen(const char *command, const char *mode)
0a753a76 2527{
4b556e6c 2528#ifdef USE_RTL_POPEN
390b85e7 2529 return _popen(command, mode);
50892819
GS
2530#else
2531 int p[2];
2532 int parent, child;
2533 int stdfd, oldfd;
2534 int ourmode;
2535 int childpid;
2536
2537 /* establish which ends read and write */
2538 if (strchr(mode,'w')) {
2539 stdfd = 0; /* stdin */
2540 parent = 1;
2541 child = 0;
2542 }
2543 else if (strchr(mode,'r')) {
2544 stdfd = 1; /* stdout */
2545 parent = 0;
2546 child = 1;
2547 }
2548 else
2549 return NULL;
2550
2551 /* set the correct mode */
2552 if (strchr(mode,'b'))
2553 ourmode = O_BINARY;
2554 else if (strchr(mode,'t'))
2555 ourmode = O_TEXT;
2556 else
2557 ourmode = _fmode & (O_TEXT | O_BINARY);
2558
2559 /* the child doesn't inherit handles */
2560 ourmode |= O_NOINHERIT;
2561
2562 if (win32_pipe( p, 512, ourmode) == -1)
2563 return NULL;
2564
2565 /* save current stdfd */
2566 if ((oldfd = win32_dup(stdfd)) == -1)
2567 goto cleanup;
2568
2569 /* make stdfd go to child end of pipe (implicitly closes stdfd) */
2570 /* stdfd will be inherited by the child */
2571 if (win32_dup2(p[child], stdfd) == -1)
2572 goto cleanup;
2573
2574 /* close the child end in parent */
2575 win32_close(p[child]);
2576
2577 /* start the child */
4f63d024 2578 {
acfe0abc 2579 dTHX;
c5be433b 2580 if ((childpid = do_spawn_nowait((char*)command)) == -1)
4f63d024 2581 goto cleanup;
50892819 2582
4f63d024
GS
2583 /* revert stdfd to whatever it was before */
2584 if (win32_dup2(oldfd, stdfd) == -1)
2585 goto cleanup;
50892819 2586
4f63d024
GS
2587 /* close saved handle */
2588 win32_close(oldfd);
50892819 2589
4755096e 2590 LOCK_FDPID_MUTEX;
4f63d024 2591 sv_setiv(*av_fetch(w32_fdpid, p[parent], TRUE), childpid);
4755096e 2592 UNLOCK_FDPID_MUTEX;
d91d68c1
RS
2593
2594 /* set process id so that it can be returned by perl's open() */
2595 PL_forkprocess = childpid;
4f63d024 2596 }
50892819
GS
2597
2598 /* we have an fd, return a file stream */
00b02797 2599 return (PerlIO_fdopen(p[parent], (char *)mode));
50892819
GS
2600
2601cleanup:
2602 /* we don't need to check for errors here */
2603 win32_close(p[0]);
2604 win32_close(p[1]);
2605 if (oldfd != -1) {
2606 win32_dup2(oldfd, stdfd);
2607 win32_close(oldfd);
2608 }
2609 return (NULL);
2610
4b556e6c 2611#endif /* USE_RTL_POPEN */
0a753a76 2612}
2613
50892819
GS
2614/*
2615 * pclose() clone
2616 */
2617
68dc0745 2618DllExport int
00b02797 2619win32_pclose(PerlIO *pf)
0a753a76 2620{
4b556e6c 2621#ifdef USE_RTL_POPEN
390b85e7 2622 return _pclose(pf);
50892819 2623#else
acfe0abc 2624 dTHX;
e17cb2a9
JD
2625 int childpid, status;
2626 SV *sv;
2627
4755096e 2628 LOCK_FDPID_MUTEX;
00b02797 2629 sv = *av_fetch(w32_fdpid, PerlIO_fileno(pf), TRUE);
4755096e 2630
e17cb2a9
JD
2631 if (SvIOK(sv))
2632 childpid = SvIVX(sv);
2633 else
2634 childpid = 0;
50892819
GS
2635
2636 if (!childpid) {
2637 errno = EBADF;
2638 return -1;
2639 }
2640
00b02797
JH
2641#ifdef USE_PERLIO
2642 PerlIO_close(pf);
2643#else
2644 fclose(pf);
2645#endif
e17cb2a9 2646 SvIVX(sv) = 0;
4755096e 2647 UNLOCK_FDPID_MUTEX;
e17cb2a9 2648
0aaad0ff
GS
2649 if (win32_waitpid(childpid, &status, 0) == -1)
2650 return -1;
50892819 2651
0aaad0ff 2652 return status;
50892819 2653
4b556e6c 2654#endif /* USE_RTL_POPEN */
0a753a76 2655}
6b980173
JD
2656
2657static BOOL WINAPI
2658Nt4CreateHardLinkW(
2659 LPCWSTR lpFileName,
2660 LPCWSTR lpExistingFileName,
2661 LPSECURITY_ATTRIBUTES lpSecurityAttributes)
2662{
2663 HANDLE handle;
2664 WCHAR wFullName[MAX_PATH+1];
2665 LPVOID lpContext = NULL;
2666 WIN32_STREAM_ID StreamId;
2667 DWORD dwSize = (char*)&StreamId.cStreamName - (char*)&StreamId;
2668 DWORD dwWritten;
2669 DWORD dwLen;
2670 BOOL bSuccess;
2671
2672 BOOL (__stdcall *pfnBackupWrite)(HANDLE, LPBYTE, DWORD, LPDWORD,
2673 BOOL, BOOL, LPVOID*) =
2674 (BOOL (__stdcall *)(HANDLE, LPBYTE, DWORD, LPDWORD,
2675 BOOL, BOOL, LPVOID*))
2676 GetProcAddress(GetModuleHandle("kernel32.dll"), "BackupWrite");
2677 if (pfnBackupWrite == NULL)
2678 return 0;
2679
2680 dwLen = GetFullPathNameW(lpFileName, MAX_PATH, wFullName, NULL);
2681 if (dwLen == 0)
2682 return 0;
2683 dwLen = (dwLen+1)*sizeof(WCHAR);
2684
2685 handle = CreateFileW(lpExistingFileName, FILE_WRITE_ATTRIBUTES,
2686 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2687 NULL, OPEN_EXISTING, 0, NULL);
2688 if (handle == INVALID_HANDLE_VALUE)
2689 return 0;
2690
2691 StreamId.dwStreamId = BACKUP_LINK;
2692 StreamId.dwStreamAttributes = 0;
2693 StreamId.dwStreamNameSize = 0;
6f24f39d
JK
2694#if defined(__BORLANDC__) \
2695 ||(defined(__MINGW32__) && !defined(_ANONYMOUS_UNION))
4ce4f76e
GS
2696 StreamId.Size.u.HighPart = 0;
2697 StreamId.Size.u.LowPart = dwLen;
2698#else
6b980173
JD
2699 StreamId.Size.HighPart = 0;
2700 StreamId.Size.LowPart = dwLen;
4ce4f76e 2701#endif
6b980173
JD
2702
2703 bSuccess = pfnBackupWrite(handle, (LPBYTE)&StreamId, dwSize, &dwWritten,
2704 FALSE, FALSE, &lpContext);
2705 if (bSuccess) {
2706 bSuccess = pfnBackupWrite(handle, (LPBYTE)wFullName, dwLen, &dwWritten,
2707 FALSE, FALSE, &lpContext);
2708 pfnBackupWrite(handle, NULL, 0, &dwWritten, TRUE, FALSE, &lpContext);
2709 }
2710
2711 CloseHandle(handle);
2712 return bSuccess;
2713}
2714
2715DllExport int
2716win32_link(const char *oldname, const char *newname)
2717{
acfe0abc 2718 dTHX;
6b980173 2719 BOOL (__stdcall *pfnCreateHardLinkW)(LPCWSTR,LPCWSTR,LPSECURITY_ATTRIBUTES);
82867ecf
GS
2720 WCHAR wOldName[MAX_PATH+1];
2721 WCHAR wNewName[MAX_PATH+1];
6b980173
JD
2722
2723 if (IsWin95())
1be9d9c6 2724 Perl_croak(aTHX_ PL_no_func, "link");
6b980173
JD
2725
2726 pfnCreateHardLinkW =
2727 (BOOL (__stdcall *)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES))
2728 GetProcAddress(GetModuleHandle("kernel32.dll"), "CreateHardLinkW");
2729 if (pfnCreateHardLinkW == NULL)
2730 pfnCreateHardLinkW = Nt4CreateHardLinkW;
2731
2732 if ((A2WHELPER(oldname, wOldName, sizeof(wOldName))) &&
2733 (A2WHELPER(newname, wNewName, sizeof(wNewName))) &&
7766f137
GS
2734 (wcscpy(wOldName, PerlDir_mapW(wOldName)),
2735 pfnCreateHardLinkW(PerlDir_mapW(wNewName), wOldName, NULL)))
6b980173
JD
2736 {
2737 return 0;
2738 }
2739 errno = (GetLastError() == ERROR_FILE_NOT_FOUND) ? ENOENT : EINVAL;
2740 return -1;
2741}
0a753a76 2742
68dc0745 2743DllExport int
8d9b2e3c 2744win32_rename(const char *oname, const char *newname)
e24c7c18 2745{
65cb15a1
GS
2746 WCHAR wOldName[MAX_PATH+1];
2747 WCHAR wNewName[MAX_PATH+1];
2748 char szOldName[MAX_PATH+1];
2749 char szNewName[MAX_PATH+1];
7fac1903 2750 BOOL bResult;
acfe0abc 2751 dTHX;
65cb15a1 2752
80252599
GS
2753 /* XXX despite what the documentation says about MoveFileEx(),
2754 * it doesn't work under Windows95!
2755 */
2756 if (IsWinNT()) {
65cb15a1 2757 DWORD dwFlags = MOVEFILE_COPY_ALLOWED;
7fac1903 2758 if (USING_WIDE()) {
0cb96387
GS
2759 A2WHELPER(oname, wOldName, sizeof(wOldName));
2760 A2WHELPER(newname, wNewName, sizeof(wNewName));
65cb15a1
GS
2761 if (wcsicmp(wNewName, wOldName))
2762 dwFlags |= MOVEFILE_REPLACE_EXISTING;
7766f137 2763 wcscpy(wOldName, PerlDir_mapW(wOldName));
65cb15a1 2764 bResult = MoveFileExW(wOldName,PerlDir_mapW(wNewName), dwFlags);
7fac1903
GS
2765 }
2766 else {
65cb15a1
GS
2767 if (stricmp(newname, oname))
2768 dwFlags |= MOVEFILE_REPLACE_EXISTING;
2769 strcpy(szOldName, PerlDir_mapA(oname));
2770 bResult = MoveFileExA(szOldName,PerlDir_mapA(newname), dwFlags);
7fac1903
GS
2771 }
2772 if (!bResult) {
80252599
GS
2773 DWORD err = GetLastError();
2774 switch (err) {
2775 case ERROR_BAD_NET_NAME:
2776 case ERROR_BAD_NETPATH:
2777 case ERROR_BAD_PATHNAME:
2778 case ERROR_FILE_NOT_FOUND:
2779 case ERROR_FILENAME_EXCED_RANGE:
2780 case ERROR_INVALID_DRIVE:
2781 case ERROR_NO_MORE_FILES:
2782 case ERROR_PATH_NOT_FOUND:
2783 errno = ENOENT;
2784 break;
2785 default:
2786 errno = EACCES;
2787 break;
2788 }
2789 return -1;
2790 }
2791 return 0;
e24c7c18 2792 }
80252599
GS
2793 else {
2794 int retval = 0;
65cb15a1 2795 char szTmpName[MAX_PATH+1];
80252599
GS
2796 char dname[MAX_PATH+1];
2797 char *endname = Nullch;
2798 STRLEN tmplen = 0;
2799 DWORD from_attr, to_attr;
2800
65cb15a1
GS
2801 strcpy(szOldName, PerlDir_mapA(oname));
2802 strcpy(szNewName, PerlDir_mapA(newname));
2803
80252599 2804 /* if oname doesn't exist, do nothing */
65cb15a1 2805 from_attr = GetFileAttributes(szOldName);
80252599
GS
2806 if (from_attr == 0xFFFFFFFF) {
2807 errno = ENOENT;
2808 return -1;
2809 }
2810
2811 /* if newname exists, rename it to a temporary name so that we
2812 * don't delete it in case oname happens to be the same file
2813 * (but perhaps accessed via a different path)
2814 */
65cb15a1 2815 to_attr = GetFileAttributes(szNewName);
80252599
GS
2816 if (to_attr != 0xFFFFFFFF) {
2817 /* if newname is a directory, we fail
2818 * XXX could overcome this with yet more convoluted logic */
2819 if (to_attr & FILE_ATTRIBUTE_DIRECTORY) {
2820 errno = EACCES;
2821 return -1;
2822 }
65cb15a1
GS
2823 tmplen = strlen(szNewName);
2824 strcpy(szTmpName,szNewName);
2825 endname = szTmpName+tmplen;
2826 for (; endname > szTmpName ; --endname) {
80252599
GS
2827 if (*endname == '/' || *endname == '\\') {
2828 *endname = '\0';
2829 break;
2830 }
2831 }
65cb15a1
GS
2832 if (endname > szTmpName)
2833 endname = strcpy(dname,szTmpName);
e24c7c18 2834 else
80252599
GS
2835 endname = ".";
2836
2837 /* get a temporary filename in same directory
2838 * XXX is this really the best we can do? */
65cb15a1 2839 if (!GetTempFileName((LPCTSTR)endname, "plr", 0, szTmpName)) {
80252599
GS
2840 errno = ENOENT;
2841 return -1;
2842 }
65cb15a1 2843 DeleteFile(szTmpName);
80252599 2844
65cb15a1 2845 retval = rename(szNewName, szTmpName);
80252599
GS
2846 if (retval != 0) {
2847 errno = EACCES;
2848 return retval;
e24c7c18
GS
2849 }
2850 }
80252599
GS
2851
2852 /* rename oname to newname */
65cb15a1 2853 retval = rename(szOldName, szNewName);
80252599
GS
2854
2855 /* if we created a temporary file before ... */
2856 if (endname != Nullch) {
2857 /* ...and rename succeeded, delete temporary file/directory */
2858 if (retval == 0)
65cb15a1 2859 DeleteFile(szTmpName);
80252599
GS
2860 /* else restore it to what it was */
2861 else
65cb15a1 2862 (void)rename(szTmpName, szNewName);
80252599
GS
2863 }
2864 return retval;
e24c7c18 2865 }
e24c7c18
GS
2866}
2867
2868DllExport int
68dc0745 2869win32_setmode(int fd, int mode)
0a753a76 2870{
390b85e7 2871 return setmode(fd, mode);
0a753a76 2872}
2873
96e4d5b1 2874DllExport long
2875win32_lseek(int fd, long offset, int origin)
2876{
390b85e7 2877 return lseek(fd, offset, origin);
96e4d5b1 2878}
2879
2880DllExport long
2881win32_tell(int fd)
2882{
390b85e7 2883 return tell(fd);
96e4d5b1 2884}
2885
68dc0745 2886DllExport int
2887win32_open(const char *path, int flag, ...)
0a753a76 2888{
acfe0abc 2889 dTHX;
68dc0745 2890 va_list ap;
2891 int pmode;
82867ecf 2892 WCHAR wBuffer[MAX_PATH+1];
0a753a76 2893
2894 va_start(ap, flag);
2895 pmode = va_arg(ap, int);
2896 va_end(ap);
2897
68dc0745 2898 if (stricmp(path, "/dev/null")==0)
7fac1903
GS
2899 path = "NUL";
2900
2901 if (USING_WIDE()) {
0cb96387 2902 A2WHELPER(path, wBuffer, sizeof(wBuffer));
7766f137 2903 return _wopen(PerlDir_mapW(wBuffer), flag, pmode);
7fac1903 2904 }
7766f137 2905 return open(PerlDir_mapA(path), flag, pmode);
0a753a76 2906}
2907
00b02797
JH
2908/* close() that understands socket */
2909extern int my_close(int); /* in win32sck.c */
2910
68dc0745 2911DllExport int
2912win32_close(int fd)
0a753a76 2913{
00b02797 2914 return my_close(fd);
0a753a76 2915}
2916
68dc0745 2917DllExport int
96e4d5b1 2918win32_eof(int fd)
2919{
390b85e7 2920 return eof(fd);
96e4d5b1 2921}
2922
2923DllExport int
68dc0745 2924win32_dup(int fd)
0a753a76 2925{
390b85e7 2926 return dup(fd);
0a753a76 2927}
2928
68dc0745 2929DllExport int
2930win32_dup2(int fd1,int fd2)
0a753a76 2931{
390b85e7 2932 return dup2(fd1,fd2);
0a753a76 2933}
2934
f7aeb604
GS
2935#ifdef PERL_MSVCRT_READFIX
2936
2937#define LF 10 /* line feed */
2938#define CR 13 /* carriage return */
2939#define CTRLZ 26 /* ctrl-z means eof for text */
2940#define FOPEN 0x01 /* file handle open */
2941#define FEOFLAG 0x02 /* end of file has been encountered */
2942#define FCRLF 0x04 /* CR-LF across read buffer (in text mode) */
2943#define FPIPE 0x08 /* file handle refers to a pipe */
2944#define FAPPEND 0x20 /* file handle opened O_APPEND */
2945#define FDEV 0x40 /* file handle refers to device */
2946#define FTEXT 0x80 /* file handle is in text mode */
2947#define MAX_DESCRIPTOR_COUNT (64*32) /* this is the maximun that MSVCRT can handle */
2948
b181b6fb
GS
2949int __cdecl
2950_fixed_read(int fh, void *buf, unsigned cnt)
f7aeb604
GS
2951{
2952 int bytes_read; /* number of bytes read */
2953 char *buffer; /* buffer to read to */
2954 int os_read; /* bytes read on OS call */
2955 char *p, *q; /* pointers into buffer */
2956 char peekchr; /* peek-ahead character */
2957 ULONG filepos; /* file position after seek */
2958 ULONG dosretval; /* o.s. return value */
2959
2960 /* validate handle */
2961 if (((unsigned)fh >= (unsigned)MAX_DESCRIPTOR_COUNT) ||
2962 !(_osfile(fh) & FOPEN))
2963 {
2964 /* out of range -- return error */
2965 errno = EBADF;
2966 _doserrno = 0; /* not o.s. error */
2967 return -1;
2968 }
2969
635bbe87
GS
2970 /*
2971 * If lockinitflag is FALSE, assume fd is device
2972 * lockinitflag is set to TRUE by open.
2973 */
2974 if (_pioinfo(fh)->lockinitflag)
2975 EnterCriticalSection(&(_pioinfo(fh)->lock)); /* lock file */
f7aeb604
GS
2976
2977 bytes_read = 0; /* nothing read yet */
2978 buffer = (char*)buf;
2979
2980 if (cnt == 0 || (_osfile(fh) & FEOFLAG)) {
2981 /* nothing to read or at EOF, so return 0 read */
2982 goto functionexit;
2983 }
2984
2985 if ((_osfile(fh) & (FPIPE|FDEV)) && _pipech(fh) != LF) {
2986 /* a pipe/device and pipe lookahead non-empty: read the lookahead
2987 * char */
2988 *buffer++ = _pipech(fh);
2989 ++bytes_read;
2990 --cnt;
2991 _pipech(fh) = LF; /* mark as empty */
2992 }
2993
2994 /* read the data */
2995
2996 if (!ReadFile((HANDLE)_osfhnd(fh), buffer, cnt, (LPDWORD)&os_read, NULL))
2997 {
2998 /* ReadFile has reported an error. recognize two special cases.
2999 *
3000 * 1. map ERROR_ACCESS_DENIED to EBADF
3001 *
3002 * 2. just return 0 if ERROR_BROKEN_PIPE has occurred. it
3003 * means the handle is a read-handle on a pipe for which
3004 * all write-handles have been closed and all data has been
3005 * read. */
3006
3007 if ((dosretval = GetLastError()) == ERROR_ACCESS_DENIED) {
3008 /* wrong read/write mode should return EBADF, not EACCES */
3009 errno = EBADF;
3010 _doserrno = dosretval;
3011 bytes_read = -1;
3012 goto functionexit;
3013 }
3014 else if (dosretval == ERROR_BROKEN_PIPE) {
3015 bytes_read = 0;
3016 goto functionexit;
3017 }
3018 else {
3019 bytes_read = -1;
3020 goto functionexit;
3021 }
3022 }
3023
3024 bytes_read += os_read; /* update bytes read */
3025
3026 if (_osfile(fh) & FTEXT) {
3027 /* now must translate CR-LFs to LFs in the buffer */
3028
3029 /* set CRLF flag to indicate LF at beginning of buffer */
3030 /* if ((os_read != 0) && (*(char *)buf == LF)) */
3031 /* _osfile(fh) |= FCRLF; */
3032 /* else */
3033 /* _osfile(fh) &= ~FCRLF; */
3034
3035 _osfile(fh) &= ~FCRLF;
3036
3037 /* convert chars in the buffer: p is src, q is dest */
3038 p = q = (char*)buf;
3039 while (p < (char *)buf + bytes_read) {
3040 if (*p == CTRLZ) {
3041 /* if fh is not a device, set ctrl-z flag */
3042 if (!(_osfile(fh) & FDEV))
3043 _osfile(fh) |= FEOFLAG;
3044 break; /* stop translating */
3045 }
3046 else if (*p != CR)
3047 *q++ = *p++;
3048 else {
3049 /* *p is CR, so must check next char for LF */
3050 if (p < (char *)buf + bytes_read - 1) {
3051 if (*(p+1) == LF) {
3052 p += 2;
3053 *q++ = LF; /* convert CR-LF to LF */
3054 }
3055 else
3056 *q++ = *p++; /* store char normally */
3057 }
3058 else {
3059 /* This is the hard part. We found a CR at end of
3060 buffer. We must peek ahead to see if next char
3061 is an LF. */
3062 ++p;
3063
3064 dosretval = 0;
3065 if (!ReadFile((HANDLE)_osfhnd(fh), &peekchr, 1,
3066 (LPDWORD)&os_read, NULL))
3067 dosretval = GetLastError();
3068
3069 if (dosretval != 0 || os_read == 0) {
3070 /* couldn't read ahead, store CR */
3071 *q++ = CR;
3072 }
3073 else {
3074 /* peekchr now has the extra character -- we now
3075 have several possibilities:
3076 1. disk file and char is not LF; just seek back
3077 and copy CR
3078 2. disk file and char is LF; store LF, don't seek back
3079 3. pipe/device and char is LF; store LF.
3080 4. pipe/device and char isn't LF, store CR and
3081 put char in pipe lookahead buffer. */
3082 if (_osfile(fh) & (FDEV|FPIPE)) {
3083 /* non-seekable device */
3084 if (peekchr == LF)
3085 *q++ = LF;
3086 else {
3087 *q++ = CR;
3088 _pipech(fh) = peekchr;
3089 }
3090 }
3091 else {
3092 /* disk file */
3093 if (peekchr == LF) {
3094 /* nothing read yet; must make some
3095 progress */
3096 *q++ = LF;
3097 /* turn on this flag for tell routine */
3098 _osfile(fh) |= FCRLF;
3099 }
3100 else {
3101 HANDLE osHandle; /* o.s. handle value */
3102 /* seek back */
3103 if ((osHandle = (HANDLE)_get_osfhandle(fh)) != (HANDLE)-1)
3104 {
3105 if ((filepos = SetFilePointer(osHandle, -1, NULL, FILE_CURRENT)) == -1)
3106 dosretval = GetLastError();
3107 }
3108 if (peekchr != LF)
3109 *q++ = CR;
3110 }
3111 }
3112 }
3113 }
3114 }
3115 }
3116
3117 /* we now change bytes_read to reflect the true number of chars
3118 in the buffer */
3119 bytes_read = q - (char *)buf;
3120 }
3121
3122functionexit:
635bbe87
GS
3123 if (_pioinfo(fh)->lockinitflag)
3124 LeaveCriticalSection(&(_pioinfo(fh)->lock)); /* unlock file */
f7aeb604
GS
3125
3126 return bytes_read;
3127}
3128
3129#endif /* PERL_MSVCRT_READFIX */
3130
68dc0745 3131DllExport int
3e3baf6d 3132win32_read(int fd, void *buf, unsigned int cnt)
0a753a76 3133{
f7aeb604
GS
3134#ifdef PERL_MSVCRT_READFIX
3135 return _fixed_read(fd, buf, cnt);
3136#else
390b85e7 3137 return read(fd, buf, cnt);
f7aeb604 3138#endif
0a753a76 3139}
3140
68dc0745 3141DllExport int
3e3baf6d 3142win32_write(int fd, const void *buf, unsigned int cnt)
0a753a76 3143{
390b85e7 3144 return write(fd, buf, cnt);
0a753a76 3145}
3146
68dc0745 3147DllExport int
5aabfad6 3148win32_mkdir(const char *dir, int mode)
3149{
acfe0abc 3150 dTHX;
7766f137 3151 if (USING_WIDE()) {
82867ecf 3152 WCHAR wBuffer[MAX_PATH+1];
7766f137
GS
3153 A2WHELPER(dir, wBuffer, sizeof(wBuffer));
3154 return _wmkdir(PerlDir_mapW(wBuffer));
3155 }
3156 return mkdir(PerlDir_mapA(dir)); /* just ignore mode */
5aabfad6 3157}
96e4d5b1 3158
5aabfad6 3159DllExport int
3160win32_rmdir(const char *dir)
3161{
acfe0abc 3162 dTHX;
7766f137 3163 if (USING_WIDE()) {
82867ecf 3164 WCHAR wBuffer[MAX_PATH+1];
7766f137
GS
3165 A2WHELPER(dir, wBuffer, sizeof(wBuffer));
3166 return _wrmdir(PerlDir_mapW(wBuffer));
3167 }
3168 return rmdir(PerlDir_mapA(dir));
5aabfad6 3169}
96e4d5b1 3170
5aabfad6 3171DllExport int
3172win32_chdir(const char *dir)
3173{
4ae93879 3174 dTHX;
9ec3348a
JH
3175 if (!dir) {
3176 errno = ENOENT;
3177 return -1;
3178 }
7766f137 3179 if (USING_WIDE()) {
82867ecf 3180 WCHAR wBuffer[MAX_PATH+1];
7766f137
GS
3181 A2WHELPER(dir, wBuffer, sizeof(wBuffer));
3182 return _wchdir(wBuffer);
3183 }
390b85e7 3184 return chdir(dir);
5aabfad6 3185}
96e4d5b1 3186
7766f137
GS
3187DllExport int
3188win32_access(const char *path, int mode)
3189{
acfe0abc 3190 dTHX;
7766f137 3191 if (USING_WIDE()) {
82867ecf 3192 WCHAR wBuffer[MAX_PATH+1];
7766f137
GS
3193 A2WHELPER(path, wBuffer, sizeof(wBuffer));
3194 return _waccess(PerlDir_mapW(wBuffer), mode);
3195 }
3196 return access(PerlDir_mapA(path), mode);
3197}
3198
3199DllExport int
3200win32_chmod(const char *path, int mode)
3201{
acfe0abc 3202 dTHX;
7766f137 3203 if (USING_WIDE()) {
82867ecf 3204 WCHAR wBuffer[MAX_PATH+1];
7766f137
GS
3205 A2WHELPER(path, wBuffer, sizeof(wBuffer));
3206 return _wchmod(PerlDir_mapW(wBuffer), mode);
3207 }
3208 return chmod(PerlDir_mapA(path), mode);
3209}
3210
3211
0aaad0ff 3212static char *
dd7038b3 3213create_command_line(char *cname, STRLEN clen, const char * const *args)
0aaad0ff 3214{
acfe0abc 3215 dTHX;
b309b8ae
JH
3216 int index, argc;
3217 char *cmd, *ptr;
3218 const char *arg;
3219 STRLEN len = 0;
81bc1258 3220 bool bat_file = FALSE;
b309b8ae 3221 bool cmd_shell = FALSE;
7b11e424 3222 bool dumb_shell = FALSE;
b309b8ae 3223 bool extra_quotes = FALSE;
dd7038b3 3224 bool quote_next = FALSE;
81bc1258
JH
3225
3226 if (!cname)
3227 cname = (char*)args[0];
b309b8ae
JH
3228
3229 /* The NT cmd.exe shell has the following peculiarity that needs to be
3230 * worked around. It strips a leading and trailing dquote when any
3231 * of the following is true:
3232 * 1. the /S switch was used
3233 * 2. there are more than two dquotes
3234 * 3. there is a special character from this set: &<>()@^|
3235 * 4. no whitespace characters within the two dquotes
3236 * 5. string between two dquotes isn't an executable file
3237 * To work around this, we always add a leading and trailing dquote
3238 * to the string, if the first argument is either "cmd.exe" or "cmd",
3239 * and there were at least two or more arguments passed to cmd.exe
3240 * (not including switches).
dd7038b3
JH
3241 * XXX the above rules (from "cmd /?") don't seem to be applied
3242 * always, making for the convolutions below :-(
b309b8ae 3243 */
81bc1258 3244 if (cname) {
dd7038b3
JH
3245 if (!clen)
3246 clen = strlen(cname);
3247
81bc1258
JH
3248 if (clen > 4
3249 && (stricmp(&cname[clen-4], ".bat") == 0
3250 || (IsWinNT() && stricmp(&cname[clen-4], ".cmd") == 0)))
3251 {
3252 bat_file = TRUE;
3253 len += 3;
3254 }
dd7038b3
JH
3255 else {
3256 char *exe = strrchr(cname, '/');
3257 char *exe2 = strrchr(cname, '\\');
3258 if (exe2 > exe)
3259 exe = exe2;
3260 if (exe)
3261 ++exe;
3262 else
3263 exe = cname;
3264 if (stricmp(exe, "cmd.exe") == 0 || stricmp(exe, "cmd") == 0) {
3265 cmd_shell = TRUE;
3266 len += 3;
3267 }
7b11e424
JH
3268 else if (stricmp(exe, "command.com") == 0
3269 || stricmp(exe, "command") == 0)
3270 {
3271 dumb_shell = TRUE;
3272 }
81bc1258 3273 }
b309b8ae 3274 }
0aaad0ff 3275
b309b8ae
JH
3276 DEBUG_p(PerlIO_printf(Perl_debug_log, "Args "));
3277 for (index = 0; (arg = (char*)args[index]) != NULL; ++index) {
3278 STRLEN curlen = strlen(arg);
3279 if (!(arg[0] == '"' && arg[curlen-1] == '"'))
3280 len += 2; /* assume quoting needed (worst case) */
3281 len += curlen + 1;
3282 DEBUG_p(PerlIO_printf(Perl_debug_log, "[%s]",arg));
3283 }
3284 DEBUG_p(PerlIO_printf(Perl_debug_log, "\n"));
0aaad0ff 3285
b309b8ae 3286 argc = index;
0aaad0ff
GS
3287 New(1310, cmd, len, char);
3288 ptr = cmd;
0aaad0ff 3289
81bc1258
JH
3290 if (bat_file) {
3291 *ptr++ = '"';
3292 extra_quotes = TRUE;
3293 }
3294
0aaad0ff 3295 for (index = 0; (arg = (char*)args[index]) != NULL; ++index) {
b309b8ae
JH
3296 bool do_quote = 0;
3297 STRLEN curlen = strlen(arg);
3298
81bc1258
JH
3299 /* we want to protect empty arguments and ones with spaces with
3300 * dquotes, but only if they aren't already there */
7b11e424
JH
3301 if (!dumb_shell) {
3302 if (!curlen) {
3303 do_quote = 1;
3304 }
02ef22d5
JH
3305 else if (quote_next) {
3306 /* see if it really is multiple arguments pretending to
3307 * be one and force a set of quotes around it */
3308 if (*find_next_space(arg))
3309 do_quote = 1;
3310 }
7b11e424
JH
3311 else if (!(arg[0] == '"' && curlen > 1 && arg[curlen-1] == '"')) {
3312 STRLEN i = 0;
3313 while (i < curlen) {
3314 if (isSPACE(arg[i])) {
3315 do_quote = 1;
02ef22d5
JH
3316 }
3317 else if (arg[i] == '"') {
3318 do_quote = 0;
7b11e424
JH
3319 break;
3320 }
3321 i++;
b309b8ae 3322 }
b309b8ae 3323 }
dd7038b3 3324 }
b309b8ae
JH
3325
3326 if (do_quote)
3327 *ptr++ = '"';
3328
18a945d4 3329 strcpy(ptr, arg);
b309b8ae
JH
3330 ptr += curlen;
3331
3332 if (do_quote)
3333 *ptr++ = '"';
3334
3335 if (args[index+1])
3336 *ptr++ = ' ';
3337
81bc1258
JH
3338 if (!extra_quotes
3339 && cmd_shell
dd7038b3 3340 && (stricmp(arg, "/x/c") == 0 || stricmp(arg, "/c") == 0))
b309b8ae 3341 {
dd7038b3
JH
3342 /* is there a next argument? */
3343 if (args[index+1]) {
3344 /* are there two or more next arguments? */
3345 if (args[index+2]) {
3346 *ptr++ = '"';
3347 extra_quotes = TRUE;
3348 }
3349 else {
02ef22d5 3350 /* single argument, force quoting if it has spaces */
dd7038b3
JH
3351 quote_next = TRUE;
3352 }
3353 }
b309b8ae 3354 }
0aaad0ff
GS
3355 }
3356
b309b8ae
JH
3357 if (extra_quotes)
3358 *ptr++ = '"';
3359
3360 *ptr = '\0';
3361
0aaad0ff
GS
3362 return cmd;
3363}
3364
3365static char *
3366qualified_path(const char *cmd)
3367{
acfe0abc 3368 dTHX;
0aaad0ff
GS
3369 char *pathstr;
3370 char *fullcmd, *curfullcmd;
3371 STRLEN cmdlen = 0;
3372 int has_slash = 0;
3373
3374 if (!cmd)
3375 return Nullch;
3376 fullcmd = (char*)cmd;
3377 while (*fullcmd) {
3378 if (*fullcmd == '/' || *fullcmd == '\\')
3379 has_slash++;
3380 fullcmd++;
3381 cmdlen++;
3382 }
3383
3384 /* look in PATH */
2fb9ab56 3385 pathstr = PerlEnv_getenv("PATH");
0aaad0ff
GS
3386 New(0, fullcmd, MAX_PATH+1, char);
3387 curfullcmd = fullcmd;
3388
3389 while (1) {
3390 DWORD res;
3391
3392 /* start by appending the name to the current prefix */
3393 strcpy(curfullcmd, cmd);
3394 curfullcmd += cmdlen;
3395
3396 /* if it doesn't end with '.', or has no extension, try adding
3397 * a trailing .exe first */
3398 if (cmd[cmdlen-1] != '.'
3399 && (cmdlen < 4 || cmd[cmdlen-4] != '.'))
3400 {
3401 strcpy(curfullcmd, ".exe");
3402 res = GetFileAttributes(fullcmd);
3403 if (res != 0xFFFFFFFF && !(res & FILE_ATTRIBUTE_DIRECTORY))
3404 return fullcmd;
3405 *curfullcmd = '\0';
3406 }
3407
3408 /* that failed, try the bare name */
3409 res = GetFileAttributes(fullcmd);
3410 if (res != 0xFFFFFFFF && !(res & FILE_ATTRIBUTE_DIRECTORY))
3411 return fullcmd;
3412
3413 /* quit if no other path exists, or if cmd already has path */
3414 if (!pathstr || !*pathstr || has_slash)
3415 break;
3416
3417 /* skip leading semis */
3418 while (*pathstr == ';')
3419 pathstr++;
3420
3421 /* build a new prefix from scratch */
3422 curfullcmd = fullcmd;
3423 while (*pathstr && *pathstr != ';') {
3424 if (*pathstr == '"') { /* foo;"baz;etc";bar */
3425 pathstr++; /* skip initial '"' */
3426 while (*pathstr && *pathstr != '"') {
3427 if (curfullcmd-fullcmd < MAX_PATH-cmdlen-5)
3428 *curfullcmd++ = *pathstr;
3429 pathstr++;
3430 }
3431 if (*pathstr)
3432 pathstr++; /* skip trailing '"' */
3433 }
3434 else {
3435 if (curfullcmd-fullcmd < MAX_PATH-cmdlen-5)
3436 *curfullcmd++ = *pathstr;
3437 pathstr++;
3438 }
3439 }
3440 if (*pathstr)
3441 pathstr++; /* skip trailing semi */
3442 if (curfullcmd > fullcmd /* append a dir separator */
3443 && curfullcmd[-1] != '/' && curfullcmd[-1] != '\\')
3444 {
3445 *curfullcmd++ = '\\';
3446 }
3447 }
3448GIVE_UP:
3449 Safefree(fullcmd);
3450 return Nullch;
3451}
3452
3075ddba
GS
3453/* The following are just place holders.
3454 * Some hosts may provide and environment that the OS is
3455 * not tracking, therefore, these host must provide that
3456 * environment and the current directory to CreateProcess
3457 */
3458
df3728a2
JH
3459DllExport void*
3460win32_get_childenv(void)
3075ddba
GS
3461{
3462 return NULL;
3463}
3464
df3728a2
JH
3465DllExport void
3466win32_free_childenv(void* d)
3075ddba
GS
3467{
3468}
3469
df3728a2
JH
3470DllExport void
3471win32_clearenv(void)
3472{
3473 char *envv = GetEnvironmentStrings();
3474 char *cur = envv;
3475 STRLEN len;
3476 while (*cur) {
3477 char *end = strchr(cur,'=');
3478 if (end && end != cur) {
3479 *end = '\0';
3480 SetEnvironmentVariable(cur, NULL);
3481 *end = '=';
3482 cur = end + strlen(end+1)+2;
3483 }
3484 else if ((len = strlen(cur)))
3485 cur += len+1;
3486 }
3487 FreeEnvironmentStrings(envv);
3488}
3489
3490DllExport char*
3491win32_get_childdir(void)
3075ddba 3492{
acfe0abc 3493 dTHX;
7766f137
GS
3494 char* ptr;
3495 char szfilename[(MAX_PATH+1)*2];
3496 if (USING_WIDE()) {
3497 WCHAR wfilename[MAX_PATH+1];
3498 GetCurrentDirectoryW(MAX_PATH+1, wfilename);
3499 W2AHELPER(wfilename, szfilename, sizeof(szfilename));
3500 }
3501 else {
3502 GetCurrentDirectoryA(MAX_PATH+1, szfilename);
3503 }
3504
3505 New(0, ptr, strlen(szfilename)+1, char);
3506 strcpy(ptr, szfilename);
3507 return ptr;
3075ddba
GS
3508}
3509
df3728a2
JH
3510DllExport void
3511win32_free_childdir(char* d)
3075ddba 3512{
acfe0abc 3513 dTHX;
7766f137 3514 Safefree(d);
3075ddba
GS
3515}
3516
3517
0aaad0ff
GS
3518/* XXX this needs to be made more compatible with the spawnvp()
3519 * provided by the various RTLs. In particular, searching for
3520 * *.{com,bat,cmd} files (as done by the RTLs) is unimplemented.
3521 * This doesn't significantly affect perl itself, because we
3522 * always invoke things using PERL5SHELL if a direct attempt to
3523 * spawn the executable fails.
3524 *
3525 * XXX splitting and rejoining the commandline between do_aspawn()
3526 * and win32_spawnvp() could also be avoided.
3527 */
3528
5aabfad6 3529DllExport int
3e3baf6d 3530win32_spawnvp(int mode, const char *cmdname, const char *const *argv)
0a753a76 3531{
0aaad0ff
GS
3532#ifdef USE_RTL_SPAWNVP
3533 return spawnvp(mode, cmdname, (char * const *)argv);
3534#else
acfe0abc 3535 dTHX;
2b260de0 3536 int ret;
3075ddba
GS
3537 void* env;
3538 char* dir;
635bbe87 3539 child_IO_table tbl;
0aaad0ff
GS
3540 STARTUPINFO StartupInfo;
3541 PROCESS_INFORMATION ProcessInformation;
3542 DWORD create = 0;
dd7038b3 3543 char *cmd;
0aaad0ff 3544 char *fullcmd = Nullch;
dd7038b3
JH
3545 char *cname = (char *)cmdname;
3546 STRLEN clen = 0;
3547
3548 if (cname) {
3549 clen = strlen(cname);
3550 /* if command name contains dquotes, must remove them */
3551 if (strchr(cname, '"')) {
3552 cmd = cname;
3553 New(0,cname,clen+1,char);
3554 clen = 0;
3555 while (*cmd) {
3556 if (*cmd != '"') {
3557 cname[clen] = *cmd;
3558 ++clen;
3559 }
3560 ++cmd;
3561 }
3562 cname[clen] = '\0';
3563 }
3564 }
3565
3566 cmd = create_command_line(cname, clen, argv);
0aaad0ff 3567
3075ddba
GS
3568 env = PerlEnv_get_childenv();
3569 dir = PerlEnv_get_childdir();
3570
0aaad0ff
GS
3571 switch(mode) {
3572 case P_NOWAIT: /* asynch + remember result */
3573 if (w32_num_children >= MAXIMUM_WAIT_OBJECTS) {
3574 errno = EAGAIN;
3575 ret = -1;
3576 goto RETVAL;
3577 }
7e5f34c0
NIS
3578 /* Create a new process group so we can use GenerateConsoleCtrlEvent()
3579 * in win32_kill()
3580 */
3581 create |= CREATE_NEW_PROCESS_GROUP;
0aaad0ff 3582 /* FALL THROUGH */
7e5f34c0 3583
0aaad0ff
GS
3584 case P_WAIT: /* synchronous execution */
3585 break;
3586 default: /* invalid mode */
3587 errno = EINVAL;
3588 ret = -1;
3589 goto RETVAL;
3590 }
3591 memset(&StartupInfo,0,sizeof(StartupInfo));
3592 StartupInfo.cb = sizeof(StartupInfo);
f83751a7 3593 memset(&tbl,0,sizeof(tbl));
635bbe87 3594 PerlEnv_get_child_IO(&tbl);
f83751a7
GS
3595 StartupInfo.dwFlags = tbl.dwFlags;
3596 StartupInfo.dwX = tbl.dwX;
3597 StartupInfo.dwY = tbl.dwY;
3598 StartupInfo.dwXSize = tbl.dwXSize;
3599 StartupInfo.dwYSize = tbl.dwYSize;
3600 StartupInfo.dwXCountChars = tbl.dwXCountChars;
3601 StartupInfo.dwYCountChars = tbl.dwYCountChars;
3602 StartupInfo.dwFillAttribute = tbl.dwFillAttribute;
3603 StartupInfo.wShowWindow = tbl.wShowWindow;
3604 StartupInfo.hStdInput = tbl.childStdIn;
3605 StartupInfo.hStdOutput = tbl.childStdOut;
3606 StartupInfo.hStdError = tbl.childStdErr;
3ffaa937
GS
3607 if (StartupInfo.hStdInput != INVALID_HANDLE_VALUE &&
3608 StartupInfo.hStdOutput != INVALID_HANDLE_VALUE &&
3609 StartupInfo.hStdError != INVALID_HANDLE_VALUE)
3610 {
3611 StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
3612 }
3613 else {
3614 create |= CREATE_NEW_CONSOLE;
3615 }
3616
b309b8ae 3617 DEBUG_p(PerlIO_printf(Perl_debug_log, "Spawning [%s] with [%s]\n",
dd7038b3 3618 cname,cmd));
0aaad0ff 3619RETRY:
dd7038b3 3620 if (!CreateProcess(cname, /* search PATH to find executable */
0aaad0ff
GS
3621 cmd, /* executable, and its arguments */
3622 NULL, /* process attributes */
3623 NULL, /* thread attributes */
3624 TRUE, /* inherit handles */
3625 create, /* creation flags */
3075ddba
GS
3626 (LPVOID)env, /* inherit environment */
3627 dir, /* inherit cwd */
0aaad0ff
GS
3628 &StartupInfo,
3629 &ProcessInformation))
3630 {
3631 /* initial NULL argument to CreateProcess() does a PATH
3632 * search, but it always first looks in the directory
3633 * where the current process was started, which behavior
3634 * is undesirable for backward compatibility. So we
3635 * jump through our own hoops by picking out the path
3636 * we really want it to use. */
3637 if (!fullcmd) {
dd7038b3 3638 fullcmd = qualified_path(cname);
0aaad0ff 3639 if (fullcmd) {
dd7038b3
JH
3640 if (cname != cmdname)
3641 Safefree(cname);
3642 cname = fullcmd;
b309b8ae
JH
3643 DEBUG_p(PerlIO_printf(Perl_debug_log,
3644 "Retrying [%s] with same args\n",
dd7038b3 3645 cname));
0aaad0ff
GS
3646 goto RETRY;
3647 }
3648 }
3649 errno = ENOENT;
3650 ret = -1;
3651 goto RETVAL;
3652 }
2d7a9237 3653
0aaad0ff
GS
3654 if (mode == P_NOWAIT) {
3655 /* asynchronous spawn -- store handle, return PID */
2b260de0 3656 ret = (int)ProcessInformation.dwProcessId;
922b1888
GS
3657 if (IsWin95() && ret < 0)
3658 ret = -ret;
3659
3660 w32_child_handles[w32_num_children] = ProcessInformation.hProcess;
3661 w32_child_pids[w32_num_children] = (DWORD)ret;
0aaad0ff
GS
3662 ++w32_num_children;
3663 }
3664 else {
2b260de0 3665 DWORD status;
8fb3fcfb 3666 win32_msgwait(aTHX_ 1, &ProcessInformation.hProcess, INFINITE, NULL);
7e5f34c0
NIS
3667 /* FIXME: if msgwait returned due to message perhaps forward the
3668 "signal" to the process
3669 */
2b260de0
GS
3670 GetExitCodeProcess(ProcessInformation.hProcess, &status);
3671 ret = (int)status;
0aaad0ff
GS
3672 CloseHandle(ProcessInformation.hProcess);
3673 }
e17cb2a9 3674
0aaad0ff 3675 CloseHandle(ProcessInformation.hThread);
3075ddba 3676
0aaad0ff 3677RETVAL:
3075ddba
GS
3678 PerlEnv_free_childenv(env);
3679 PerlEnv_free_childdir(dir);
0aaad0ff 3680 Safefree(cmd);
dd7038b3
JH
3681 if (cname != cmdname)
3682 Safefree(cname);
2b260de0 3683 return ret;
2d7a9237 3684#endif
0a753a76 3685}
3686
6890e559 3687DllExport int
eb62e965
JD
3688win32_execv(const char *cmdname, const char *const *argv)
3689{
7766f137 3690#ifdef USE_ITHREADS
acfe0abc 3691 dTHX;
7766f137
GS
3692 /* if this is a pseudo-forked child, we just want to spawn
3693 * the new program, and return */
3694 if (w32_pseudo_id)
3695 return spawnv(P_WAIT, cmdname, (char *const *)argv);
3696#endif
eb62e965
JD
3697 return execv(cmdname, (char *const *)argv);
3698}
3699
3700DllExport int
6890e559
GS
3701win32_execvp(const char *cmdname, const char *const *argv)
3702{
7766f137 3703#ifdef USE_ITHREADS
acfe0abc 3704 dTHX;
7766f137
GS
3705 /* if this is a pseudo-forked child, we just want to spawn
3706 * the new program, and return */
190e4ad0 3707 if (w32_pseudo_id) {
ba6ce41c
GS
3708 int status = win32_spawnvp(P_WAIT, cmdname, (char *const *)argv);
3709 if (status != -1) {
3710 my_exit(status);
3711 return 0;
3712 }
3713 else
3714 return status;
190e4ad0 3715 }
7766f137 3716#endif
390b85e7 3717 return execvp(cmdname, (char *const *)argv);
6890e559
GS
3718}
3719
84902520
TB
3720DllExport void
3721win32_perror(const char *str)
3722{
390b85e7 3723 perror(str);
84902520
TB
3724}
3725
3726DllExport void
3727win32_setbuf(FILE *pf, char *buf)
3728{
390b85e7 3729 setbuf(pf, buf);
84902520
TB
3730}
3731
3732DllExport int
3733win32_setvbuf(FILE *pf, char *buf, int type, size_t size)
3734{
390b85e7 3735 return setvbuf(pf, buf, type, size);
84902520
TB
3736}
3737
3738DllExport int
3739win32_flushall(void)
3740{
390b85e7 3741 return flushall();
84902520
TB
3742}
3743
3744DllExport int
3745win32_fcloseall(void)
3746{
390b85e7 3747 return fcloseall();
84902520
TB
3748}
3749
3750DllExport char*
3751win32_fgets(char *s, int n, FILE *pf)
3752{
390b85e7 3753 return fgets(s, n, pf);
84902520
TB
3754}
3755
3756DllExport char*
3757win32_gets(char *s)
3758{
390b85e7 3759 return gets(s);
84902520
TB
3760}
3761
3762DllExport int
3763win32_fgetc(FILE *pf)
3764{
390b85e7 3765 return fgetc(pf);
84902520
TB
3766}
3767
3768DllExport int
3769win32_putc(int c, FILE *pf)
3770{
390b85e7 3771 return putc(c,pf);
84902520
TB
3772}
3773
3774DllExport int
3775win32_puts(const char *s)
3776{
390b85e7 3777 return puts(s);
84902520
TB
3778}
3779
3780DllExport int
3781win32_getchar(void)
3782{
390b85e7 3783 return getchar();
84902520
TB
3784}
3785
3786DllExport int
3787win32_putchar(int c)
3788{
390b85e7 3789 return putchar(c);
84902520
TB
3790}
3791
bbc8f9de
NIS
3792#ifdef MYMALLOC
3793
3794#ifndef USE_PERL_SBRK
3795
df3728a2
JH
3796static char *committed = NULL; /* XXX threadead */
3797static char *base = NULL; /* XXX threadead */
3798static char *reserved = NULL; /* XXX threadead */
3799static char *brk = NULL; /* XXX threadead */
3800static DWORD pagesize = 0; /* XXX threadead */
3801static DWORD allocsize = 0; /* XXX threadead */
bbc8f9de
NIS
3802
3803void *
3804sbrk(int need)
3805{
3806 void *result;
3807 if (!pagesize)
3808 {SYSTEM_INFO info;
3809 GetSystemInfo(&info);
3810 /* Pretend page size is larger so we don't perpetually
3811 * call the OS to commit just one page ...
3812 */
3813 pagesize = info.dwPageSize << 3;
3814 allocsize = info.dwAllocationGranularity;
3815 }
3816 /* This scheme fails eventually if request for contiguous
3817 * block is denied so reserve big blocks - this is only
3818 * address space not memory ...
3819 */
3820 if (brk+need >= reserved)
3821 {
3822 DWORD size = 64*1024*1024;
3823 char *addr;
3824 if (committed && reserved && committed < reserved)
3825 {
3826 /* Commit last of previous chunk cannot span allocations */
161b471a 3827 addr = (char *) VirtualAlloc(committed,reserved-committed,MEM_COMMIT,PAGE_READWRITE);
bbc8f9de
NIS
3828 if (addr)
3829 committed = reserved;
3830 }
3831 /* Reserve some (more) space
3832 * Note this is a little sneaky, 1st call passes NULL as reserved
3833 * so lets system choose where we start, subsequent calls pass
3834 * the old end address so ask for a contiguous block
3835 */
161b471a 3836 addr = (char *) VirtualAlloc(reserved,size,MEM_RESERVE,PAGE_NOACCESS);
bbc8f9de
NIS
3837 if (addr)
3838 {
3839 reserved = addr+size;
3840 if (!base)
3841 base = addr;
3842 if (!committed)
3843 committed = base;
3844 if (!brk)
3845 brk = committed;
3846 }
3847 else
3848 {
3849 return (void *) -1;
3850 }
3851 }
3852 result = brk;
3853 brk += need;
3854 if (brk > committed)
3855 {
3856 DWORD size = ((brk-committed + pagesize -1)/pagesize) * pagesize;
161b471a 3857 char *addr = (char *) VirtualAlloc(committed,size,MEM_COMMIT,PAGE_READWRITE);
bbc8f9de
NIS
3858 if (addr)
3859 {
3860 committed += size;
3861 }
3862 else
3863 return (void *) -1;
3864 }
3865 return result;
3866}
3867
3868#endif
3869#endif
3870
84902520
TB
3871DllExport void*
3872win32_malloc(size_t size)
3873{
390b85e7 3874 return malloc(size);
84902520
TB
3875}
3876
3877DllExport void*
3878win32_calloc(size_t numitems, size_t size)
3879{
390b85e7 3880 return calloc(numitems,size);
84902520
TB
3881}
3882
3883DllExport void*
3884win32_realloc(void *block, size_t size)
3885{
390b85e7 3886 return realloc(block,size);
84902520
TB
3887}
3888
3889DllExport void
3890win32_free(void *block)
3891{
390b85e7 3892 free(block);
84902520
TB
3893}
3894
bbc8f9de 3895
68dc0745 3896int
65e48ea9 3897win32_open_osfhandle(long handle, int flags)
0a753a76 3898{
9e5f57de
GS
3899#ifdef USE_FIXED_OSFHANDLE
3900 if (IsWin95())
3901 return my_open_osfhandle(handle, flags);
3902#endif
390b85e7 3903 return _open_osfhandle(handle, flags);
0a753a76 3904}
3905
68dc0745 3906long
65e48ea9 3907win32_get_osfhandle(int fd)
0a753a76 3908{
390b85e7 3909 return _get_osfhandle(fd);
0a753a76 3910}
7bac28a0 3911
0cb96387 3912DllExport void*
c5be433b 3913win32_dynaload(const char* filename)
0cb96387 3914{
acfe0abc 3915 dTHX;
51371543 3916 HMODULE hModule;
32f99636
GS
3917 char buf[MAX_PATH+1];
3918 char *first;
3919
3920 /* LoadLibrary() doesn't recognize forward slashes correctly,
3921 * so turn 'em back. */
3922 first = strchr(filename, '/');
3923 if (first) {
3924 STRLEN len = strlen(filename);
3925 if (len <= MAX_PATH) {
3926 strcpy(buf, filename);
3927 filename = &buf[first - filename];
3928 while (*filename) {
3929 if (*filename == '/')
3930 *(char*)filename = '\\';
3931 ++filename;
3932 }
3933 filename = buf;
3934 }
3935 }
0cb96387 3936 if (USING_WIDE()) {
82867ecf 3937 WCHAR wfilename[MAX_PATH+1];
0cb96387 3938 A2WHELPER(filename, wfilename, sizeof(wfilename));
7766f137 3939 hModule = LoadLibraryExW(PerlDir_mapW(wfilename), NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
0cb96387
GS
3940 }
3941 else {
7766f137 3942 hModule = LoadLibraryExA(PerlDir_mapA(filename), NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
0cb96387
GS
3943 }
3944 return hModule;
3945}
3946
7bac28a0 3947/*
3948 * Extras.
3949 */
3950
ad2e33dc
GS
3951static
3952XS(w32_GetCwd)
3953{
3954 dXSARGS;
7766f137
GS
3955 /* Make the host for current directory */
3956 char* ptr = PerlEnv_get_childdir();
ad2e33dc 3957 /*
7766f137 3958 * If ptr != Nullch
ad2e33dc 3959 * then it worked, set PV valid,
7766f137 3960 * else return 'undef'
ad2e33dc 3961 */
7766f137
GS
3962 if (ptr) {
3963 SV *sv = sv_newmortal();
3964 sv_setpv(sv, ptr);
3965 PerlEnv_free_childdir(ptr);
3966
617e632e
NK
3967#ifndef INCOMPLETE_TAINTS
3968 SvTAINTED_on(sv);
3969#endif
3970
7766f137 3971 EXTEND(SP,1);
ad2e33dc 3972 SvPOK_on(sv);
bb897dfc
JD
3973 ST(0) = sv;
3974 XSRETURN(1);
3975 }
3467312b 3976 XSRETURN_UNDEF;
ad2e33dc
GS
3977}
3978
3979static
3980XS(w32_SetCwd)
3981{
3982 dXSARGS;
3983 if (items != 1)
4f63d024 3984 Perl_croak(aTHX_ "usage: Win32::SetCurrentDirectory($cwd)");
7766f137 3985 if (!PerlDir_chdir(SvPV_nolen(ST(0))))
ad2e33dc
GS
3986 XSRETURN_YES;
3987
3988 XSRETURN_NO;
3989}
3990
3991static
3992XS(w32_GetNextAvailDrive)
3993{
3994 dXSARGS;
3995 char ix = 'C';
3996 char root[] = "_:\\";
3467312b
JD
3997
3998 EXTEND(SP,1);
ad2e33dc
GS
3999 while (ix <= 'Z') {
4000 root[0] = ix++;
4001 if (GetDriveType(root) == 1) {
4002 root[2] = '\0';
4003 XSRETURN_PV(root);
4004 }
4005 }
3467312b 4006 XSRETURN_UNDEF;
ad2e33dc
GS
4007}
4008
4009static
4010XS(w32_GetLastError)
4011{
4012 dXSARGS;
bb897dfc 4013 EXTEND(SP,1);
ad2e33dc
GS
4014 XSRETURN_IV(GetLastError());
4015}
4016
4017static
ca135624
JD
4018XS(w32_SetLastError)
4019{
4020 dXSARGS;
4021 if (items != 1)
4f63d024 4022 Perl_croak(aTHX_ "usage: Win32::SetLastError($error)");
ca135624 4023 SetLastError(SvIV(ST(0)));
bb897dfc 4024 XSRETURN_EMPTY;
ca135624
JD
4025}
4026
4027static
ad2e33dc
GS
4028XS(w32_LoginName)
4029{
4030 dXSARGS;
3352bfcb
GS
4031 char *name = w32_getlogin_buffer;
4032 DWORD size = sizeof(w32_getlogin_buffer);
3467312b 4033 EXTEND(SP,1);
ad2e33dc
GS
4034 if (GetUserName(name,&size)) {
4035 /* size includes NULL */
79cb57f6 4036 ST(0) = sv_2mortal(newSVpvn(name,size-1));
ad2e33dc
GS
4037 XSRETURN(1);
4038 }
3467312b 4039 XSRETURN_UNDEF;
ad2e33dc
GS
4040}
4041
4042static
4043XS(w32_NodeName)
4044{
4045 dXSARGS;
4046 char name[MAX_COMPUTERNAME_LENGTH+1];
4047 DWORD size = sizeof(name);
3467312b 4048 EXTEND(SP,1);
ad2e33dc
GS
4049 if (GetComputerName(name,&size)) {
4050 /* size does NOT include NULL :-( */
79cb57f6 4051 ST(0) = sv_2mortal(newSVpvn(name,size));
ad2e33dc
GS
4052 XSRETURN(1);
4053 }
3467312b 4054 XSRETURN_UNDEF;
ad2e33dc
GS
4055}
4056
4057
4058static
4059XS(w32_DomainName)
4060{
4061 dXSARGS;
da147683
JD
4062 HINSTANCE hNetApi32 = LoadLibrary("netapi32.dll");
4063 DWORD (__stdcall *pfnNetApiBufferFree)(LPVOID Buffer);
4064 DWORD (__stdcall *pfnNetWkstaGetInfo)(LPWSTR servername, DWORD level,
4065 void *bufptr);
625a29bd 4066
da147683
JD
4067 if (hNetApi32) {
4068 pfnNetApiBufferFree = (DWORD (__stdcall *)(void *))
4069 GetProcAddress(hNetApi32, "NetApiBufferFree");
4070 pfnNetWkstaGetInfo = (DWORD (__stdcall *)(LPWSTR, DWORD, void *))
4071 GetProcAddress(hNetApi32, "NetWkstaGetInfo");
d12db45c 4072 }
da147683
JD
4073 EXTEND(SP,1);
4074 if (hNetApi32 && pfnNetWkstaGetInfo && pfnNetApiBufferFree) {
4075 /* this way is more reliable, in case user has a local account. */
4076 char dname[256];
4077 DWORD dnamelen = sizeof(dname);
4078 struct {
4079 DWORD wki100_platform_id;
4080 LPWSTR wki100_computername;
4081 LPWSTR wki100_langroup;
4082 DWORD wki100_ver_major;
4083 DWORD wki100_ver_minor;
4084 } *pwi;
4085 /* NERR_Success *is* 0*/
4086 if (0 == pfnNetWkstaGetInfo(NULL, 100, &pwi)) {
4087 if (pwi->wki100_langroup && *(pwi->wki100_langroup)) {
4088 WideCharToMultiByte(CP_ACP, NULL, pwi->wki100_langroup,
4089 -1, (LPSTR)dname, dnamelen, NULL, NULL);
4090 }
4091 else {
4092 WideCharToMultiByte(CP_ACP, NULL, pwi->wki100_computername,
4093 -1, (LPSTR)dname, dnamelen, NULL, NULL);
4094 }
4095 pfnNetApiBufferFree(pwi);
4096 FreeLibrary(hNetApi32);
4097 XSRETURN_PV(dname);
4098 }
4099 FreeLibrary(hNetApi32);
ad2e33dc 4100 }
625a29bd 4101 else {
da147683
JD
4102 /* Win95 doesn't have NetWksta*(), so do it the old way */
4103 char name[256];
4104 DWORD size = sizeof(name);
4105 if (hNetApi32)
4106 FreeLibrary(hNetApi32);
4107 if (GetUserName(name,&size)) {
4108 char sid[ONE_K_BUFSIZE];
4109 DWORD sidlen = sizeof(sid);
4110 char dname[256];
4111 DWORD dnamelen = sizeof(dname);
4112 SID_NAME_USE snu;
4113 if (LookupAccountName(NULL, name, (PSID)&sid, &sidlen,
4114 dname, &dnamelen, &snu)) {
4115 XSRETURN_PV(dname); /* all that for this */
4116 }
4117 }
9404a519 4118 }
da147683 4119 XSRETURN_UNDEF;
ad2e33dc
GS
4120}
4121
4122static
4123XS(w32_FsType)
4124{
4125 dXSARGS;
4126 char fsname[256];
4127 DWORD flags, filecomplen;
4128 if (GetVolumeInformation(NULL, NULL, 0, NULL, &filecomplen,
4129 &flags, fsname, sizeof(fsname))) {
bb897dfc 4130 if (GIMME_V == G_ARRAY) {
79cb57f6 4131 XPUSHs(sv_2mortal(newSVpvn(fsname,strlen(fsname))));
ad2e33dc
GS
4132 XPUSHs(sv_2mortal(newSViv(flags)));
4133 XPUSHs(sv_2mortal(newSViv(filecomplen)));
4134 PUTBACK;
4135 return;
4136 }
bb897dfc 4137 EXTEND(SP,1);
ad2e33dc
GS
4138 XSRETURN_PV(fsname);
4139 }
bb897dfc 4140 XSRETURN_EMPTY;
ad2e33dc
GS
4141}
4142
4143static
4144XS(w32_GetOSVersion)
4145{
4146 dXSARGS;
7766f137 4147 OSVERSIONINFOA osver;
ad2e33dc 4148
7766f137
GS
4149 if (USING_WIDE()) {
4150 OSVERSIONINFOW osverw;
4151 char szCSDVersion[sizeof(osverw.szCSDVersion)];
4152 osverw.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
4153 if (!GetVersionExW(&osverw)) {
4154 XSRETURN_EMPTY;
4155 }
4156 W2AHELPER(osverw.szCSDVersion, szCSDVersion, sizeof(szCSDVersion));
4157 XPUSHs(newSVpvn(szCSDVersion, strlen(szCSDVersion)));
4158 osver.dwMajorVersion = osverw.dwMajorVersion;
4159 osver.dwMinorVersion = osverw.dwMinorVersion;
4160 osver.dwBuildNumber = osverw.dwBuildNumber;
4161 osver.dwPlatformId = osverw.dwPlatformId;
4162 }
4163 else {
4164 osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
4165 if (!GetVersionExA(&osver)) {
4166 XSRETURN_EMPTY;
4167 }
79cb57f6 4168 XPUSHs(newSVpvn(osver.szCSDVersion, strlen(osver.szCSDVersion)));
ad2e33dc 4169 }
7766f137
GS
4170 XPUSHs(newSViv(osver.dwMajorVersion));
4171 XPUSHs(newSViv(osver.dwMinorVersion));
4172 XPUSHs(newSViv(osver.dwBuildNumber));
4173 XPUSHs(newSViv(osver.dwPlatformId));
4174 PUTBACK;
ad2e33dc
GS
4175}
4176
4177static
4178XS(w32_IsWinNT)
4179{
4180 dXSARGS;
bb897dfc 4181 EXTEND(SP,1);
ad2e33dc
GS
4182 XSRETURN_IV(IsWinNT());
4183}
4184
4185static
4186XS(w32_IsWin95)
4187{
4188 dXSARGS;
bb897dfc 4189 EXTEND(SP,1);
ad2e33dc
GS
4190 XSRETURN_IV(IsWin95());
4191}
4192
4193static
4194XS(w32_FormatMessage)
4195{
4196 dXSARGS;
4197 DWORD source = 0;
7766f137 4198 char msgbuf[ONE_K_BUFSIZE];
ad2e33dc
GS
4199
4200 if (items != 1)
4f63d024 4201 Perl_croak(aTHX_ "usage: Win32::FormatMessage($errno)");
ad2e33dc 4202
7766f137
GS
4203 if (USING_WIDE()) {
4204 WCHAR wmsgbuf[ONE_K_BUFSIZE];
4205 if (FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM,
4206 &source, SvIV(ST(0)), 0,
4207 wmsgbuf, ONE_K_BUFSIZE-1, NULL))
4208 {
4209 W2AHELPER(wmsgbuf, msgbuf, sizeof(msgbuf));
4210 XSRETURN_PV(msgbuf);
4211 }
4212 }
4213 else {
4214 if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM,
4215 &source, SvIV(ST(0)), 0,
4216 msgbuf, sizeof(msgbuf)-1, NULL))
4217 XSRETURN_PV(msgbuf);
4218 }
ad2e33dc 4219
3467312b 4220 XSRETURN_UNDEF;
ad2e33dc
GS
4221}
4222
4223static
4224XS(w32_Spawn)
4225{
4226 dXSARGS;
4227 char *cmd, *args;
33005217
JD
4228 void *env;
4229 char *dir;
ad2e33dc
GS
4230 PROCESS_INFORMATION stProcInfo;
4231 STARTUPINFO stStartInfo;
4232 BOOL bSuccess = FALSE;
4233
9404a519 4234 if (items != 3)
4f63d024 4235 Perl_croak(aTHX_ "usage: Win32::Spawn($cmdName, $args, $PID)");
ad2e33dc 4236
bb897dfc
JD
4237 cmd = SvPV_nolen(ST(0));
4238 args = SvPV_nolen(ST(1));
ad2e33dc 4239
33005217
JD
4240 env = PerlEnv_get_childenv();
4241 dir = PerlEnv_get_childdir();
4242
ad2e33dc
GS
4243 memset(&stStartInfo, 0, sizeof(stStartInfo)); /* Clear the block */
4244 stStartInfo.cb = sizeof(stStartInfo); /* Set the structure size */
4245 stStartInfo.dwFlags = STARTF_USESHOWWINDOW; /* Enable wShowWindow control */
4246 stStartInfo.wShowWindow = SW_SHOWMINNOACTIVE; /* Start min (normal) */
4247
9404a519 4248 if (CreateProcess(
ad2e33dc
GS
4249 cmd, /* Image path */
4250 args, /* Arguments for command line */
4251 NULL, /* Default process security */
4252 NULL, /* Default thread security */
4253 FALSE, /* Must be TRUE to use std handles */
4254 NORMAL_PRIORITY_CLASS, /* No special scheduling */
33005217
JD
4255 env, /* Inherit our environment block */
4256 dir, /* Inherit our currrent directory */
ad2e33dc
GS
4257 &stStartInfo, /* -> Startup info */
4258 &stProcInfo)) /* <- Process info (if OK) */
4259 {
922b1888
GS
4260 int pid = (int)stProcInfo.dwProcessId;
4261 if (IsWin95() && pid < 0)
4262 pid = -pid;
4263 sv_setiv(ST(2), pid);
ad2e33dc 4264 CloseHandle(stProcInfo.hThread);/* library source code does this. */
ad2e33dc
GS
4265 bSuccess = TRUE;
4266 }
33005217
JD
4267 PerlEnv_free_childenv(env);
4268 PerlEnv_free_childdir(dir);
ad2e33dc
GS
4269 XSRETURN_IV(bSuccess);
4270}
4271
4272static
4273XS(w32_GetTickCount)
4274{
4275 dXSARGS;
fdb068fa 4276 DWORD msec = GetTickCount();
a6c40364 4277 EXTEND(SP,1);
fdb068fa
JD
4278 if ((IV)msec > 0)
4279 XSRETURN_IV(msec);
4280 XSRETURN_NV(msec);
ad2e33dc
GS
4281}
4282
4283static
4284XS(w32_GetShortPathName)
4285{
4286 dXSARGS;
4287 SV *shortpath;
e8bab181 4288 DWORD len;
ad2e33dc 4289
9404a519 4290 if (items != 1)
4f63d024 4291 Perl_croak(aTHX_ "usage: Win32::GetShortPathName($longPathName)");
ad2e33dc
GS
4292
4293 shortpath = sv_mortalcopy(ST(0));
4294 SvUPGRADE(shortpath, SVt_PV);
631c0b04
GS
4295 if (!SvPVX(shortpath) || !SvLEN(shortpath))
4296 XSRETURN_UNDEF;
4297
ad2e33dc 4298 /* src == target is allowed */
e8bab181
GS
4299 do {
4300 len = GetShortPathName(SvPVX(shortpath),
4301 SvPVX(shortpath),
4302 SvLEN(shortpath));
4303 } while (len >= SvLEN(shortpath) && sv_grow(shortpath,len+1));
4304 if (len) {
4305 SvCUR_set(shortpath,len);
ad2e33dc 4306 ST(0) = shortpath;
bb897dfc 4307 XSRETURN(1);
e8bab181 4308 }
3467312b 4309 XSRETURN_UNDEF;
ad2e33dc
GS
4310}
4311
ad0751ec 4312static
ca135624
JD
4313XS(w32_GetFullPathName)
4314{
4315 dXSARGS;
4316 SV *filename;
4317 SV *fullpath;
4318 char *filepart;
4319 DWORD len;
4320
4321 if (items != 1)
4f63d024 4322 Perl_croak(aTHX_ "usage: Win32::GetFullPathName($filename)");
ca135624
JD
4323
4324 filename = ST(0);
4325 fullpath = sv_mortalcopy(filename);
4326 SvUPGRADE(fullpath, SVt_PV);
631c0b04
GS
4327 if (!SvPVX(fullpath) || !SvLEN(fullpath))
4328 XSRETURN_UNDEF;
4329
ca135624
JD
4330 do {
4331 len = GetFullPathName(SvPVX(filename),
4332 SvLEN(fullpath),
4333 SvPVX(fullpath),
4334 &filepart);
4335 } while (len >= SvLEN(fullpath) && sv_grow(fullpath,len+1));
4336 if (len) {
4337 if (GIMME_V == G_ARRAY) {
4338 EXTEND(SP,1);
bb897dfc 4339 XST_mPV(1,filepart);
ca135624
JD
4340 len = filepart - SvPVX(fullpath);
4341 items = 2;
4342 }
4343 SvCUR_set(fullpath,len);
4344 ST(0) = fullpath;
bb897dfc 4345 XSRETURN(items);
ca135624 4346 }
bb897dfc 4347 XSRETURN_EMPTY;
ca135624
JD
4348}
4349
4350static
8ac9c18d
GS
4351XS(w32_GetLongPathName)
4352{
4353 dXSARGS;
4354 SV *path;
4355 char tmpbuf[MAX_PATH+1];
4356 char *pathstr;
4357 STRLEN len;
4358
4359 if (items != 1)
4f63d024 4360 Perl_croak(aTHX_ "usage: Win32::GetLongPathName($pathname)");
8ac9c18d
GS
4361
4362 path = ST(0);
4363 pathstr = SvPV(path,len);
4364 strcpy(tmpbuf, pathstr);
4365 pathstr = win32_longpath(tmpbuf);
4366 if (pathstr) {
4367 ST(0) = sv_2mortal(newSVpvn(pathstr, strlen(pathstr)));
4368 XSRETURN(1);
4369 }
4370 XSRETURN_EMPTY;
4371}
4372
4373static
ad0751ec
GS
4374XS(w32_Sleep)
4375{
4376 dXSARGS;
4377 if (items != 1)
4f63d024 4378 Perl_croak(aTHX_ "usage: Win32::Sleep($milliseconds)");
ad0751ec
GS
4379 Sleep(SvIV(ST(0)));
4380 XSRETURN_YES;
4381}
4382
7509b657
GS
4383static
4384XS(w32_CopyFile)
4385{
4386 dXSARGS;
7766f137 4387 BOOL bResult;
7509b657 4388 if (items != 3)
4f63d024 4389 Perl_croak(aTHX_ "usage: Win32::CopyFile($from, $to, $overwrite)");
7766f137 4390 if (USING_WIDE()) {
82867ecf
GS
4391 WCHAR wSourceFile[MAX_PATH+1];
4392 WCHAR wDestFile[MAX_PATH+1];
7766f137
GS
4393 A2WHELPER(SvPV_nolen(ST(0)), wSourceFile, sizeof(wSourceFile));
4394 wcscpy(wSourceFile, PerlDir_mapW(wSourceFile));
4395 A2WHELPER(SvPV_nolen(ST(1)), wDestFile, sizeof(wDestFile));
4396 bResult = CopyFileW(wSourceFile, PerlDir_mapW(wDestFile), !SvTRUE(ST(2)));
4397 }
4398 else {
82867ecf 4399 char szSourceFile[MAX_PATH+1];
7766f137
GS
4400 strcpy(szSourceFile, PerlDir_mapA(SvPV_nolen(ST(0))));
4401 bResult = CopyFileA(szSourceFile, PerlDir_mapA(SvPV_nolen(ST(1))), !SvTRUE(ST(2)));
4402 }
4403
4404 if (bResult)
7509b657
GS
4405 XSRETURN_YES;
4406 XSRETURN_NO;
4407}
4408
ad2e33dc 4409void
c5be433b 4410Perl_init_os_extras(void)
ad2e33dc 4411{
acfe0abc 4412 dTHX;
ad2e33dc
GS
4413 char *file = __FILE__;
4414 dXSUB_SYS;
4415
ad2e33dc
GS
4416 /* these names are Activeware compatible */
4417 newXS("Win32::GetCwd", w32_GetCwd, file);
4418 newXS("Win32::SetCwd", w32_SetCwd, file);
4419 newXS("Win32::GetNextAvailDrive", w32_GetNextAvailDrive, file);
4420 newXS("Win32::GetLastError", w32_GetLastError, file);
ca135624 4421 newXS("Win32::SetLastError", w32_SetLastError, file);
ad2e33dc
GS
4422 newXS("Win32::LoginName", w32_LoginName, file);
4423 newXS("Win32::NodeName", w32_NodeName, file);
4424 newXS("Win32::DomainName", w32_DomainName, file);
4425 newXS("Win32::FsType", w32_FsType, file);
4426 newXS("Win32::GetOSVersion", w32_GetOSVersion, file);
4427 newXS("Win32::IsWinNT", w32_IsWinNT, file);
4428 newXS("Win32::IsWin95", w32_IsWin95, file);
4429 newXS("Win32::FormatMessage", w32_FormatMessage, file);
4430 newXS("Win32::Spawn", w32_Spawn, file);
4431 newXS("Win32::GetTickCount", w32_GetTickCount, file);
4432 newXS("Win32::GetShortPathName", w32_GetShortPathName, file);
ca135624 4433 newXS("Win32::GetFullPathName", w32_GetFullPathName, file);
8ac9c18d 4434 newXS("Win32::GetLongPathName", w32_GetLongPathName, file);
7509b657 4435 newXS("Win32::CopyFile", w32_CopyFile, file);
ad0751ec 4436 newXS("Win32::Sleep", w32_Sleep, file);
ad2e33dc
GS
4437
4438 /* XXX Bloat Alert! The following Activeware preloads really
4439 * ought to be part of Win32::Sys::*, so they're not included
4440 * here.
4441 */
4442 /* LookupAccountName
4443 * LookupAccountSID
4444 * InitiateSystemShutdown
4445 * AbortSystemShutdown
4446 * ExpandEnvrironmentStrings
4447 */
4448}
4449
c843839f
NIS
4450static PerlInterpreter* win32_process_perl = NULL;
4451
4452BOOL WINAPI
4453win32_ctrlhandler(DWORD dwCtrlType)
4454{
4455 dTHX;
4456 if (!my_perl) {
4457 my_perl = win32_process_perl;
4458 if (!my_perl) {
4459 return FALSE;
4460 }
4461 PERL_SET_THX(my_perl);
4462 }
4463
4464 switch(dwCtrlType) {
4465 case CTRL_CLOSE_EVENT:
4466 /* A signal that the system sends to all processes attached to a console when
4467 the user closes the console (either by choosing the Close command from the
4468 console window's System menu, or by choosing the End Task command from the
4469 Task List
4470 */
4471 CALL_FPTR(PL_sighandlerp)(1); /* SIGHUP */
4472 return TRUE;
4473
4474 case CTRL_C_EVENT:
4475 /* A CTRL+c signal was received */
4476 CALL_FPTR(PL_sighandlerp)(2); /* SIGINT */
4477 return TRUE;
4478
4479 case CTRL_BREAK_EVENT:
4480 /* A CTRL+BREAK signal was received */
4481 CALL_FPTR(PL_sighandlerp)(3); /* SIGQUIT */
4482 return TRUE;
4483
4484 case CTRL_LOGOFF_EVENT:
4485 /* A signal that the system sends to all console processes when a user is logging
4486 off. This signal does not indicate which user is logging off, so no
4487 assumptions can be made.
4488 */
4489 break;
4490 case CTRL_SHUTDOWN_EVENT:
4491 /* A signal that the system sends to all console processes when the system is
4492 shutting down.
4493 */
4494 break;
4495 default:
4496 break;
4497 }
4498 return FALSE;
4499}
4500
4501
4502
ad2e33dc
GS
4503void
4504Perl_win32_init(int *argcp, char ***argvp)
4505{
4506 /* Disable floating point errors, Perl will trap the ones we
4507 * care about. VC++ RTL defaults to switching these off
4508 * already, but the Borland RTL doesn't. Since we don't
4509 * want to be at the vendor's whim on the default, we set
4510 * it explicitly here.
4511 */
a835ef8a 4512#if !defined(_ALPHA_) && !defined(__GNUC__)
ad2e33dc 4513 _control87(MCW_EM, MCW_EM);
3dc9191e 4514#endif
4b556e6c 4515 MALLOC_INIT;
ad2e33dc 4516}
d55594ae 4517
635bbe87
GS
4518void
4519win32_get_child_IO(child_IO_table* ptbl)
4520{
4521 ptbl->childStdIn = GetStdHandle(STD_INPUT_HANDLE);
4522 ptbl->childStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
4523 ptbl->childStdErr = GetStdHandle(STD_ERROR_HANDLE);
4524}
4525
52853b95 4526#ifdef HAVE_INTERP_INTERN
7766f137 4527
c843839f
NIS
4528
4529
7766f137 4530void
52853b95
GS
4531Perl_sys_intern_init(pTHX)
4532{
4533 w32_perlshell_tokens = Nullch;
4534 w32_perlshell_vec = (char**)NULL;
4535 w32_perlshell_items = 0;
4536 w32_fdpid = newAV();
4537 New(1313, w32_children, 1, child_tab);
4538 w32_num_children = 0;
4539# ifdef USE_ITHREADS
4540 w32_pseudo_id = 0;
4541 New(1313, w32_pseudo_children, 1, child_tab);
4542 w32_num_pseudo_children = 0;
4543# endif
4544 w32_init_socktype = 0;
c843839f
NIS
4545 if (!win32_process_perl) {
4546 win32_process_perl = my_perl;
4547 /* Force C runtime signal stuff to set its console handler */
4548 signal(SIGINT,SIG_DFL);
4549 /* Push our handler on top */
4550 SetConsoleCtrlHandler(win32_ctrlhandler,TRUE);
4551 }
52853b95
GS
4552}
4553
3dbbd0f5
GS
4554void
4555Perl_sys_intern_clear(pTHX)
4556{
4557 Safefree(w32_perlshell_tokens);
4558 Safefree(w32_perlshell_vec);
4559 /* NOTE: w32_fdpid is freed by sv_clean_all() */
4560 Safefree(w32_children);
c843839f
NIS
4561 if (my_perl == win32_process_perl) {
4562 SetConsoleCtrlHandler(win32_ctrlhandler,FALSE);
4563 win32_process_perl = NULL;
4564 }
3dbbd0f5
GS
4565# ifdef USE_ITHREADS
4566 Safefree(w32_pseudo_children);
4567# endif
4568}
4569
52853b95
GS
4570# ifdef USE_ITHREADS
4571
4572void
7766f137
GS
4573Perl_sys_intern_dup(pTHX_ struct interp_intern *src, struct interp_intern *dst)
4574{
4575 dst->perlshell_tokens = Nullch;
4576 dst->perlshell_vec = (char**)NULL;
4577 dst->perlshell_items = 0;
4578 dst->fdpid = newAV();
4579 Newz(1313, dst->children, 1, child_tab);
7766f137 4580 dst->pseudo_id = 0;
52853b95 4581 Newz(1313, dst->pseudo_children, 1, child_tab);
862f1e8c 4582 dst->thr_intern.Winit_socktype = 0;
7766f137 4583}
52853b95
GS
4584# endif /* USE_ITHREADS */
4585#endif /* HAVE_INTERP_INTERN */
7766f137 4586
729a02f2 4587static void
acfe0abc 4588win32_free_argvw(pTHX_ void *ptr)
729a02f2
GS
4589{
4590 char** argv = (char**)ptr;
4591 while(*argv) {
4592 Safefree(*argv);
4593 *argv++ = Nullch;
4594 }
4595}
4596
4597void
c0932edc 4598win32_argv2utf8(int argc, char** argv)
729a02f2 4599{
acfe0abc 4600 dTHX;
729a02f2
GS
4601 char* psz;
4602 int length, wargc;
4603 LPWSTR* lpwStr = CommandLineToArgvW(GetCommandLineW(), &wargc);
4604 if (lpwStr && argc) {
4605 while (argc--) {
4606 length = WideCharToMultiByte(CP_UTF8, 0, lpwStr[--wargc], -1, NULL, 0, NULL, NULL);
4607 Newz(0, psz, length, char);
4608 WideCharToMultiByte(CP_UTF8, 0, lpwStr[wargc], -1, psz, length, NULL, NULL);
4609 argv[argc] = psz;
4610 }
4611 call_atexit(win32_free_argvw, argv);
4612 }
4613 GlobalFree((HGLOBAL)lpwStr);
4614}
4615
8fb3fcfb
NIS
4616
4617
4618