X-Git-Url: https://perl5.git.perl.org/perl5.git/blobdiff_plain/bc10eb633bcfd88cfe28820712157caa4e11a418..72e6b643d040f276d474b709b942bdd8d03bc2a2:/win32/win32.c diff --git a/win32/win32.c b/win32/win32.c index a7c409f..56dbd15 100644 --- a/win32/win32.c +++ b/win32/win32.c @@ -1,7 +1,7 @@ /* WIN32.C * * (c) 1995 Microsoft Corporation. All rights reserved. - * Developed by hip communications inc., http://info.hip.com/info/ + * Developed by hip communications inc. * Portions (c) 1993 Intergraph Corporation. All rights reserved. * * You may distribute under the terms of either the GNU General Public @@ -11,53 +11,61 @@ #define WIN32_LEAN_AND_MEAN #define WIN32IO_IS_STDIO #include + #ifdef __GNUC__ -#define Win32_Winsock +# define Win32_Winsock +#endif + +#ifndef _WIN32_WINNT +# define _WIN32_WINNT 0x0500 /* needed for CreateHardlink() etc. */ #endif + #include + #ifndef HWND_MESSAGE -# define HWND_MESSAGE ((HWND)-3) +# define HWND_MESSAGE ((HWND)-3) +#endif + +#ifndef PROCESSOR_ARCHITECTURE_AMD64 +# define PROCESSOR_ARCHITECTURE_AMD64 9 +#endif + +#ifndef WC_NO_BEST_FIT_CHARS +# define WC_NO_BEST_FIT_CHARS 0x00000400 #endif + #include +#include +#include #include #include /* #include "config.h" */ #if !defined(PERLIO_IS_STDIO) && !defined(USE_SFIO) -#define PerlIO FILE +# define PerlIO FILE #endif #include #include "EXTERN.h" #include "perl.h" -/* GCC-2.95.2/Mingw32-1.1 forgot the WINAPI on CommandLineToArgvW() */ -#if defined(__MINGW32__) && (__MINGW32_MAJOR_VERSION==1) -# include -#else -EXTERN_C LPWSTR* WINAPI CommandLineToArgvW(LPCWSTR lpCommandLine, int * pNumArgs); -#endif - #define NO_XSLOCKS #define PERL_NO_GET_CONTEXT #include "XSUB.h" -#include "Win32iop.h" #include #ifndef __GNUC__ /* assert.h conflicts with #define of assert in perl.h */ -#include +# include #endif + #include #include #include #include -#if defined(_MSC_VER) || defined(__MINGW32__) #include -#else -#include -#endif + #ifdef __GNUC__ /* Mingw32 defaults to globing command line * So we turn it off like this: @@ -76,11 +84,6 @@ int _fcloseall(); END_EXTERN_C #endif -#if defined(__BORLANDC__) -# define _stat stat -# define _utimbuf utimbuf -#endif - #define EXECF_EXEC 1 #define EXECF_SPAWN 2 #define EXECF_SPAWN_NOWAIT 3 @@ -96,22 +99,62 @@ END_EXTERN_C # define getlogin g_getlogin #endif -static void get_shell(void); -static long tokenize(const char *str, char **dest, char ***destv); -static int do_spawn2(pTHX_ const char *cmd, int exectype); -static BOOL has_shell_metachars(const char *ptr); -static long filetime_to_clock(PFILETIME ft); -static BOOL filetime_from_time(PFILETIME ft, time_t t); -static char * get_emd_part(SV **leading, char *trailing, ...); -static void remove_dead_process(long deceased); -static long find_pid(int pid); -static char * qualified_path(const char *cmd); -static char * win32_get_xlib(const char *pl, const char *xlib, - const char *libname); +/* VS2005 (MSC version 14) provides a mechanism to set an invalid + * parameter handler. This functionality is not available in the + * 64-bit compiler from the Platform SDK, which unfortunately also + * believes itself to be MSC version 14. + * + * There is no #define related to _set_invalid_parameter_handler(), + * but we can check for one of the constants defined for + * _set_abort_behavior(), which was introduced into stdlib.h at + * the same time. + */ + +#if _MSC_VER >= 1400 && defined(_WRITE_ABORT_MSG) +# define SET_INVALID_PARAMETER_HANDLER +#endif + +#ifdef SET_INVALID_PARAMETER_HANDLER +static BOOL set_silent_invalid_parameter_handler(BOOL newvalue); +static void my_invalid_parameter_handler(const wchar_t* expression, + const wchar_t* function, const wchar_t* file, + unsigned int line, uintptr_t pReserved); +#endif + +static char* get_regstr_from(HKEY hkey, const char *valuename, SV **svp); +static char* get_regstr(const char *valuename, SV **svp); +static char* get_emd_part(SV **prev_pathp, STRLEN *const len, + char *trailing, ...); +static char* win32_get_xlib(const char *pl, const char *xlib, + const char *libname, STRLEN *const len); +static BOOL has_shell_metachars(const char *ptr); +static long tokenize(const char *str, char **dest, char ***destv); +static void get_shell(void); +static char* find_next_space(const char *s); +static int do_spawn2(pTHX_ const char *cmd, int exectype); +static long find_pid(int pid); +static void remove_dead_process(long child); +static int terminate_process(DWORD pid, HANDLE process_handle, int sig); +static int my_kill(int pid, int sig); +static void out_of_memory(void); +static char* wstr_to_str(const wchar_t* wstr); +static long filetime_to_clock(PFILETIME ft); +static BOOL filetime_from_time(PFILETIME ft, time_t t); +static char* create_command_line(char *cname, STRLEN clen, + const char * const *args); +static char* qualified_path(const char *cmd); +static void ansify_path(void); +static LRESULT win32_process_message(HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam); #ifdef USE_ITHREADS -static void remove_dead_pseudo_process(long child); -static long find_pseudo_pid(int pid); +static long find_pseudo_pid(int pid); +static void remove_dead_pseudo_process(long child); +static HWND get_hwnd_delay(pTHX, long child, DWORD tries); +#endif + +#ifdef HAVE_INTERP_INTERN +static void win32_csighandler(int sig); #endif START_EXTERN_C @@ -121,63 +164,102 @@ END_EXTERN_C static OSVERSIONINFO g_osver = {0, 0, 0, 0, 0, ""}; -#define ONE_K_BUFSIZE 1024 +#ifdef SET_INVALID_PARAMETER_HANDLER +static BOOL silent_invalid_parameter_handler = FALSE; -#ifdef __BORLANDC__ -/* Silence STDERR grumblings from Borland's math library. */ -DllExport int -_matherr(struct _exception *a) +static BOOL +set_silent_invalid_parameter_handler(BOOL newvalue) { - PERL_UNUSED_VAR(a); - return 1; + BOOL oldvalue = silent_invalid_parameter_handler; +# ifdef _DEBUG + silent_invalid_parameter_handler = newvalue; +# endif + return oldvalue; } -#endif -#if _MSC_VER >= 1400 -void my_invalid_parameter_handler(const wchar_t* expression, +static void +my_invalid_parameter_handler(const wchar_t* expression, const wchar_t* function, const wchar_t* file, unsigned int line, uintptr_t pReserved) { # ifdef _DEBUG - wprintf(L"Invalid parameter detected in function %s." - L" File: %s Line: %d\n", function, file, line); - wprintf(L"Expression: %s\n", expression); + char* ansi_expression; + char* ansi_function; + char* ansi_file; + if (silent_invalid_parameter_handler) + return; + ansi_expression = wstr_to_str(expression); + ansi_function = wstr_to_str(function); + ansi_file = wstr_to_str(file); + fprintf(stderr, "Invalid parameter detected in function %s. " + "File: %s, line: %d\n", ansi_function, ansi_file, line); + fprintf(stderr, "Expression: %s\n", ansi_expression); + free(ansi_expression); + free(ansi_function); + free(ansi_file); # endif } #endif -int -IsWin95(void) -{ - return (g_osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS); -} - -int -IsWinNT(void) -{ - return (g_osver.dwPlatformId == VER_PLATFORM_WIN32_NT); -} - EXTERN_C void set_w32_module_name(void) { + /* this function may be called at DLL_PROCESS_ATTACH time */ char* ptr; - GetModuleFileName((HMODULE)((w32_perldll_handle == INVALID_HANDLE_VALUE) - ? GetModuleHandle(NULL) - : w32_perldll_handle), - w32_module_name, sizeof(w32_module_name)); + HMODULE module = (HMODULE)((w32_perldll_handle == INVALID_HANDLE_VALUE) + ? GetModuleHandle(NULL) + : w32_perldll_handle); + + OSVERSIONINFO osver; /* g_osver may not yet be initialized */ + osver.dwOSVersionInfoSize = sizeof(osver); + GetVersionEx(&osver); + + if (osver.dwMajorVersion > 4) { + WCHAR modulename[MAX_PATH]; + WCHAR fullname[MAX_PATH]; + char *ansi; + + DWORD (__stdcall *pfnGetLongPathNameW)(LPCWSTR, LPWSTR, DWORD) = + (DWORD (__stdcall *)(LPCWSTR, LPWSTR, DWORD)) + GetProcAddress(GetModuleHandle("kernel32.dll"), "GetLongPathNameW"); - /* remove \\?\ prefix */ - if (memcmp(w32_module_name, "\\\\?\\", 4) == 0) - memmove(w32_module_name, w32_module_name+4, strlen(w32_module_name+4)+1); + GetModuleFileNameW(module, modulename, sizeof(modulename)/sizeof(WCHAR)); - /* try to get full path to binary (which may be mangled when perl is - * run from a 16-bit app) */ - /*PerlIO_printf(Perl_debug_log, "Before %s\n", w32_module_name);*/ - (void)win32_longpath(w32_module_name); - /*PerlIO_printf(Perl_debug_log, "After %s\n", w32_module_name);*/ + /* Make sure we get an absolute pathname in case the module was loaded + * explicitly by LoadLibrary() with a relative path. */ + GetFullPathNameW(modulename, sizeof(fullname)/sizeof(WCHAR), fullname, NULL); + + /* Make sure we start with the long path name of the module because we + * later scan for pathname components to match "5.xx" to locate + * compatible sitelib directories, and the short pathname might mangle + * this path segment (e.g. by removing the dot on NTFS to something + * like "5xx~1.yy") */ + if (pfnGetLongPathNameW) + pfnGetLongPathNameW(fullname, fullname, sizeof(fullname)/sizeof(WCHAR)); + + /* remove \\?\ prefix */ + if (memcmp(fullname, L"\\\\?\\", 4*sizeof(WCHAR)) == 0) + memmove(fullname, fullname+4, (wcslen(fullname+4)+1)*sizeof(WCHAR)); + + ansi = win32_ansipath(fullname); + my_strlcpy(w32_module_name, ansi, sizeof(w32_module_name)); + win32_free(ansi); + } + else { + GetModuleFileName(module, w32_module_name, sizeof(w32_module_name)); + + /* remove \\?\ prefix */ + if (memcmp(w32_module_name, "\\\\?\\", 4) == 0) + memmove(w32_module_name, w32_module_name+4, strlen(w32_module_name+4)+1); + + /* try to get full path to binary (which may be mangled when perl is + * run from a 16-bit app) */ + /*PerlIO_printf(Perl_debug_log, "Before %s\n", w32_module_name);*/ + win32_longpath(w32_module_name); + /*PerlIO_printf(Perl_debug_log, "After %s\n", w32_module_name);*/ + } /* normalize to forward slashes */ ptr = w32_module_name; @@ -196,7 +278,7 @@ get_regstr_from(HKEY hkey, const char *valuename, SV **svp) HKEY handle; DWORD type; const char *subkey = "Software\\Perl"; - char *str = Nullch; + char *str = NULL; long retval; retval = RegOpenKeyEx(hkey, subkey, 0, KEY_READ, &handle); @@ -234,7 +316,7 @@ get_regstr(const char *valuename, SV **svp) /* *prev_pathp (if non-NULL) is expected to be POK (valid allocated SvPVX(sv)) */ static char * -get_emd_part(SV **prev_pathp, char *trailing_path, ...) +get_emd_part(SV **prev_pathp, STRLEN *const len, char *trailing_path, ...) { char base[10]; va_list ap; @@ -291,19 +373,21 @@ get_emd_part(SV **prev_pathp, char *trailing_path, ...) else if (SvPVX(*prev_pathp)) sv_catpvn(*prev_pathp, ";", 1); sv_catpv(*prev_pathp, mod_name); + if(len) + *len = SvCUR(*prev_pathp); return SvPVX(*prev_pathp); } - return Nullch; + return NULL; } char * -win32_get_privlib(const char *pl) +win32_get_privlib(const char *pl, STRLEN *const len) { dTHX; char *stdlib = "lib"; char buffer[MAX_PATH+1]; - SV *sv = Nullsv; + SV *sv = NULL; /* $stdlib = $HKCU{"lib-$]"} || $HKLM{"lib-$]"} || $HKCU{"lib"} || $HKLM{"lib"} || ""; */ sprintf(buffer, "%s-%s", stdlib, pl); @@ -311,17 +395,18 @@ win32_get_privlib(const char *pl) (void)get_regstr(stdlib, &sv); /* $stdlib .= ";$EMD/../../lib" */ - return get_emd_part(&sv, stdlib, ARCHNAME, "bin", Nullch); + return get_emd_part(&sv, len, stdlib, ARCHNAME, "bin", NULL); } static char * -win32_get_xlib(const char *pl, const char *xlib, const char *libname) +win32_get_xlib(const char *pl, const char *xlib, const char *libname, + STRLEN *const len) { dTHX; char regstr[40]; char pathstr[MAX_PATH+1]; - SV *sv1 = Nullsv; - SV *sv2 = Nullsv; + SV *sv1 = NULL; + SV *sv2 = NULL; /* $HKCU{"$xlib-$]"} || $HKLM{"$xlib-$]"} . ---; */ sprintf(regstr, "%s-%s", xlib, pl); @@ -330,7 +415,7 @@ win32_get_xlib(const char *pl, const char *xlib, const char *libname) /* $xlib .= * ";$EMD/" . ((-d $EMD/../../../$]) ? "../../.." : "../.."). "/$libname/$]/lib"; */ sprintf(pathstr, "%s/%s/lib", libname, pl); - (void)get_emd_part(&sv1, pathstr, ARCHNAME, "bin", pl, Nullch); + (void)get_emd_part(&sv1, NULL, pathstr, ARCHNAME, "bin", pl, NULL); /* $HKCU{$xlib} || $HKLM{$xlib} . ---; */ (void)get_regstr(xlib, &sv2); @@ -338,25 +423,26 @@ win32_get_xlib(const char *pl, const char *xlib, const char *libname) /* $xlib .= * ";$EMD/" . ((-d $EMD/../../../$]) ? "../../.." : "../.."). "/$libname/lib"; */ sprintf(pathstr, "%s/lib", libname); - (void)get_emd_part(&sv2, pathstr, ARCHNAME, "bin", pl, Nullch); + (void)get_emd_part(&sv2, NULL, pathstr, ARCHNAME, "bin", pl, NULL); if (!sv1 && !sv2) - return Nullch; - if (!sv1) - return SvPVX(sv2); - if (!sv2) - return SvPVX(sv1); - - sv_catpvn(sv1, ";", 1); - sv_catsv(sv1, sv2); + return NULL; + if (!sv1) { + sv1 = sv2; + } else if (sv2) { + sv_catpvn(sv1, ";", 1); + sv_catsv(sv1, sv2); + } + if (len) + *len = SvCUR(sv1); return SvPVX(sv1); } char * -win32_get_sitelib(const char *pl) +win32_get_sitelib(const char *pl, STRLEN *const len) { - return win32_get_xlib(pl, "sitelib", "site"); + return win32_get_xlib(pl, "sitelib", "site", len); } #ifndef PERL_VENDORLIB_NAME @@ -364,9 +450,9 @@ win32_get_sitelib(const char *pl) #endif char * -win32_get_vendorlib(const char *pl) +win32_get_vendorlib(const char *pl, STRLEN *const len) { - return win32_get_xlib(pl, "vendorlib", PERL_VENDORLIB_NAME); + return win32_get_xlib(pl, "vendorlib", PERL_VENDORLIB_NAME, len); } static BOOL @@ -417,22 +503,6 @@ has_shell_metachars(const char *ptr) PerlIO * Perl_my_popen(pTHX_ const char *cmd, const char *mode) { -#ifdef FIXCMD -#define fixcmd(x) { \ - char *pspace = strchr((x),' '); \ - if (pspace) { \ - char *p = (x); \ - while (p < pspace) { \ - if (*p == '/') \ - *p = '\\'; \ - p++; \ - } \ - } \ - } -#else -#define fixcmd(x) -#endif - fixcmd(cmd); PERL_FLUSHALL_FOR_CHILD; return win32_popen(cmd, mode); } @@ -453,19 +523,12 @@ win32_os_id(void) DllExport int win32_getpid(void) { - int pid; #ifdef USE_ITHREADS dTHX; if (w32_pseudo_id) return -((int)w32_pseudo_id); #endif - pid = _getpid(); - /* Windows 9x appears to always reports a pid for threads and processes - * that has the high bit set. So we treat the lower 31 bits as the - * "real" PID for Perl's purposes. */ - if (IsWin95() && pid < 0) - pid = -pid; - return pid; + return _getpid(); } /* Tokenize a string. Words are null-separated, and the list @@ -477,14 +540,14 @@ win32_getpid(void) static long tokenize(const char *str, char **dest, char ***destv) { - char *retstart = Nullch; + char *retstart = NULL; char **retvstart = 0; int items = -1; if (str) { dTHX; int slen = strlen(str); - register char *ret; - register char **retv; + char *ret; + char **retv; Newx(ret, slen+2, char); Newx(retv, (slen+3)/2, char*); @@ -512,7 +575,7 @@ tokenize(const char *str, char **dest, char ***destv) ++items; ret++; } - retvstart[items] = Nullch; + retvstart[items] = NULL; *ret++ = '\0'; *ret = '\0'; } @@ -533,8 +596,7 @@ get_shell(void) * interactive use (which is what most programs look in COMSPEC * for). */ - const char* defaultshell = (IsWinNT() - ? "cmd.exe /x/d/c" : "command.com /c"); + const char* defaultshell = "cmd.exe /x/d/c"; const char *usershell = PerlEnv_getenv("PERL5SHELL"); w32_perlshell_items = tokenize(usershell ? usershell : defaultshell, &w32_perlshell_tokens, @@ -551,6 +613,8 @@ Perl_do_aspawn(pTHX_ SV *really, SV **mark, SV **sp) int flag = P_WAIT; int index = 0; + PERL_ARGS_ASSERT_DO_ASPAWN; + if (sp <= mark) return -1; @@ -589,8 +653,7 @@ Perl_do_aspawn(pTHX_ SV *really, SV **mark, SV **sp) } if (flag == P_NOWAIT) { - if (IsWin95()) - PL_statusvalue = -1; /* >16bits hint for pp_system() */ + PL_statusvalue = -1; /* >16bits hint for pp_system() */ } else { if (status < 0) { @@ -656,7 +719,7 @@ do_spawn2(pTHX_ const char *cmd, int exectype) if (*s) *s++ = '\0'; } - *a = Nullch; + *a = NULL; if (argv[0]) { switch (exectype) { case EXECF_SPAWN: @@ -685,7 +748,7 @@ do_spawn2(pTHX_ const char *cmd, int exectype) while (++i < w32_perlshell_items) argv[i] = w32_perlshell_vec[i]; argv[i++] = (char *)cmd; - argv[i] = Nullch; + argv[i] = NULL; switch (exectype) { case EXECF_SPAWN: status = win32_spawnvp(P_WAIT, argv[0], @@ -703,8 +766,7 @@ do_spawn2(pTHX_ const char *cmd, int exectype) Safefree(argv); } if (exectype == EXECF_SPAWN_NOWAIT) { - if (IsWin95()) - PL_statusvalue = -1; /* >16bits hint for pp_system() */ + PL_statusvalue = -1; /* >16bits hint for pp_system() */ } else { if (status < 0) { @@ -724,18 +786,24 @@ do_spawn2(pTHX_ const char *cmd, int exectype) int Perl_do_spawn(pTHX_ char *cmd) { + PERL_ARGS_ASSERT_DO_SPAWN; + return do_spawn2(aTHX_ cmd, EXECF_SPAWN); } int Perl_do_spawn_nowait(pTHX_ char *cmd) { + PERL_ARGS_ASSERT_DO_SPAWN_NOWAIT; + return do_spawn2(aTHX_ cmd, EXECF_SPAWN_NOWAIT); } bool Perl_do_exec(pTHX_ const char *cmd) { + PERL_ARGS_ASSERT_DO_EXEC; + do_spawn2(aTHX_ cmd, EXECF_EXEC); return FALSE; } @@ -752,16 +820,20 @@ win32_opendir(const char *filename) long len; long idx; char scanname[MAX_PATH+3]; - Stat_t sbuf; - WIN32_FIND_DATAA aFindData; + WCHAR wscanname[sizeof(scanname)]; + WIN32_FIND_DATAW wFindData; + char buffer[MAX_PATH*2]; + BOOL use_default; len = strlen(filename); - if (len > MAX_PATH) + if (len == 0) { + errno = ENOENT; return NULL; - - /* check to see if filename is a directory */ - if (win32_stat(filename, &sbuf) < 0 || !S_ISDIR(sbuf.st_mode)) + } + if (len > MAX_PATH) { + errno = ENAMETOOLONG; return NULL; + } /* Get us a DIR structure */ Newxz(dirp, 1, DIR); @@ -781,7 +853,9 @@ win32_opendir(const char *filename) scanname[len] = '\0'; /* do the FindFirstFile call */ - dirp->handle = FindFirstFileA(PerlDir_mapA(scanname), &aFindData); + MultiByteToWideChar(CP_ACP, 0, scanname, -1, wscanname, sizeof(wscanname)/sizeof(WCHAR)); + dirp->handle = FindFirstFileW(PerlDir_mapW(wscanname), &wFindData); + if (dirp->handle == INVALID_HANDLE_VALUE) { DWORD err = GetLastError(); /* FindFirstFile() fails on empty drives! */ @@ -803,16 +877,26 @@ win32_opendir(const char *filename) return NULL; } + use_default = FALSE; + WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, + wFindData.cFileName, -1, + buffer, sizeof(buffer), NULL, &use_default); + if (use_default && *wFindData.cAlternateFileName) { + WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, + wFindData.cAlternateFileName, -1, + buffer, sizeof(buffer), NULL, NULL); + } + /* now allocate the first part of the string table for * the filenames that we find. */ - idx = strlen(aFindData.cFileName)+1; + idx = strlen(buffer)+1; if (idx < 256) - dirp->size = 128; + dirp->size = 256; else dirp->size = idx; Newx(dirp->start, dirp->size, char); - strcpy(dirp->start, aFindData.cFileName); + strcpy(dirp->start, buffer); dirp->nfiles++; dirp->end = dirp->curr = dirp->start; dirp->end += idx; @@ -841,16 +925,33 @@ win32_readdir(DIR *dirp) dirp->curr += len + 1; if (dirp->curr >= dirp->end) { dTHX; - BOOL res; - WIN32_FIND_DATAA aFindData; + BOOL res; + char buffer[MAX_PATH*2]; + if (dirp->handle == INVALID_HANDLE_VALUE) { + res = 0; + } /* finding the next file that matches the wildcard * (which should be all of them in this directory!). */ - res = FindNextFileA(dirp->handle, &aFindData); + else { + WIN32_FIND_DATAW wFindData; + res = FindNextFileW(dirp->handle, &wFindData); + if (res) { + BOOL use_default = FALSE; + WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, + wFindData.cFileName, -1, + buffer, sizeof(buffer), NULL, &use_default); + if (use_default && *wFindData.cAlternateFileName) { + WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, + wFindData.cAlternateFileName, -1, + buffer, sizeof(buffer), NULL, NULL); + } + } + } if (res) { long endpos = dirp->end - dirp->start; - long newsize = endpos + strlen(aFindData.cFileName) + 1; + long newsize = endpos + strlen(buffer) + 1; /* bump the string table size by enough for the * new name and its null terminator */ while (newsize > dirp->size) { @@ -859,12 +960,17 @@ win32_readdir(DIR *dirp) Renew(dirp->start, dirp->size, char); dirp->curr = dirp->start + curpos; } - strcpy(dirp->start + endpos, aFindData.cFileName); + strcpy(dirp->start + endpos, buffer); dirp->end = dirp->start + newsize; dirp->nfiles++; } - else + else { dirp->curr = NULL; + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose(dirp->handle); + dirp->handle = INVALID_HANDLE_VALUE; + } + } } return &(dirp->dirstr); } @@ -876,7 +982,7 @@ win32_readdir(DIR *dirp) DllExport long win32_telldir(DIR *dirp) { - return (dirp->curr - dirp->start); + return dirp->curr ? (dirp->curr - dirp->start) : -1; } @@ -886,7 +992,7 @@ win32_telldir(DIR *dirp) DllExport void win32_seekdir(DIR *dirp, long loc) { - dirp->curr = dirp->start + loc; + dirp->curr = loc == -1 ? NULL : dirp->start + loc; } /* Rewinddir resets the string pointer to the start */ @@ -908,6 +1014,50 @@ win32_closedir(DIR *dirp) return 1; } +/* duplicate a open DIR* for interpreter cloning */ +DllExport DIR * +win32_dirp_dup(DIR *const dirp, CLONE_PARAMS *const param) +{ + dVAR; + PerlInterpreter *const from = param->proto_perl; + PerlInterpreter *const to = PERL_GET_THX; + + long pos; + DIR *dup; + + /* switch back to original interpreter because win32_readdir() + * might Renew(dirp->start). + */ + if (from != to) { + PERL_SET_THX(from); + } + + /* mark current position; read all remaining entries into the + * cache, and then restore to current position. + */ + pos = win32_telldir(dirp); + while (win32_readdir(dirp)) { + /* read all entries into cache */ + } + win32_seekdir(dirp, pos); + + /* switch back to new interpreter to allocate new DIR structure */ + if (from != to) { + PERL_SET_THX(to); + } + + Newx(dup, 1, DIR); + memcpy(dup, dirp, sizeof(DIR)); + + Newx(dup->start, dirp->size, char); + memcpy(dup->start, dirp->start, dirp->size); + + dup->end = dup->start + (dirp->end - dirp->start); + if (dirp->curr) + dup->curr = dup->start + (dirp->curr - dirp->start); + + return dup; +} /* * various stubs @@ -1053,65 +1203,217 @@ remove_dead_pseudo_process(long child) (w32_num_pseudo_children-child-1), DWORD); Move(&w32_pseudo_child_message_hwnds[child+1], &w32_pseudo_child_message_hwnds[child], (w32_num_pseudo_children-child-1), HWND); + Move(&w32_pseudo_child_sigterm[child+1], &w32_pseudo_child_sigterm[child], + (w32_num_pseudo_children-child-1), char); w32_num_pseudo_children--; } } + +void +win32_wait_for_children(pTHX) +{ + if (w32_pseudo_children && w32_num_pseudo_children) { + long child = 0; + long count = 0; + HANDLE handles[MAXIMUM_WAIT_OBJECTS]; + + for (child = 0; child < w32_num_pseudo_children; ++child) { + if (!w32_pseudo_child_sigterm[child]) + handles[count++] = w32_pseudo_child_handles[child]; + } + /* XXX should use MsgWaitForMultipleObjects() to continue + * XXX processing messages while we wait. + */ + WaitForMultipleObjects(count, handles, TRUE, INFINITE); + + while (w32_num_pseudo_children) + CloseHandle(w32_pseudo_child_handles[--w32_num_pseudo_children]); + } +} +#endif + +static int +terminate_process(DWORD pid, HANDLE process_handle, int sig) +{ + switch(sig) { + case 0: + /* "Does process exist?" use of kill */ + return 1; + case 2: + if (GenerateConsoleCtrlEvent(CTRL_C_EVENT, pid)) + return 1; + break; + case SIGBREAK: + case SIGTERM: + if (GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, pid)) + return 1; + break; + default: /* For now be backwards compatible with perl 5.6 */ + case 9: + /* Note that we will only be able to kill processes owned by the + * current process owner, even when we are running as an administrator. + * To kill processes of other owners we would need to set the + * 'SeDebugPrivilege' privilege before obtaining the process handle. + */ + if (TerminateProcess(process_handle, sig)) + return 1; + break; + } + return 0; +} + +int +killpg(int pid, int sig) +{ + HANDLE process_handle; + HANDLE snapshot_handle; + int killed = 0; + + process_handle = OpenProcess(PROCESS_TERMINATE, FALSE, pid); + if (process_handle == NULL) + return 0; + + killed += terminate_process(pid, process_handle, sig); + + snapshot_handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (snapshot_handle != INVALID_HANDLE_VALUE) { + PROCESSENTRY32 entry; + + entry.dwSize = sizeof(entry); + if (Process32First(snapshot_handle, &entry)) { + do { + if (entry.th32ParentProcessID == (DWORD)pid) + killed += killpg(entry.th32ProcessID, sig); + entry.dwSize = sizeof(entry); + } + while (Process32Next(snapshot_handle, &entry)); + } + CloseHandle(snapshot_handle); + } + CloseHandle(process_handle); + return killed; +} + +static int +my_kill(int pid, int sig) +{ + int retval = 0; + HANDLE process_handle; + + if (sig < 0) + return killpg(pid, -sig); + + process_handle = OpenProcess(PROCESS_TERMINATE, FALSE, pid); + /* OpenProcess() returns NULL on error, *not* INVALID_HANDLE_VALUE */ + if (process_handle != NULL) { + retval = terminate_process(pid, process_handle, sig); + CloseHandle(process_handle); + } + return retval; +} + +#ifdef USE_ITHREADS +/* Get a child pseudo-process HWND, with retrying and delaying/yielding. + * The "tries" parameter is the number of retries to make, with a Sleep(1) + * (waiting and yielding the time slot) between each try. Specifying 0 causes + * only Sleep(0) (no waiting and potentially no yielding) to be used, so is not + * recommended + * Returns an hwnd != INVALID_HANDLE_VALUE (so be aware that NULL can be + * returned) or croaks if the child pseudo-process doesn't schedule and deliver + * a HWND in the time period allowed. + */ +static HWND +get_hwnd_delay(pTHX, long child, DWORD tries) +{ + HWND hwnd = w32_pseudo_child_message_hwnds[child]; + if (hwnd != INVALID_HANDLE_VALUE) return hwnd; + + /* Pseudo-process has not yet properly initialized since hwnd isn't set. + * Fast sleep: On some NT kernels/systems, a Sleep(0) won't deschedule a + * thread 100% of the time since threads are attached to a CPU for NUMA and + * caching reasons, and the child thread was attached to a different CPU + * therefore there is no workload on that CPU and Sleep(0) returns control + * without yielding the time slot. + * https://rt.perl.org/rt3/Ticket/Display.html?id=88840 + */ + Sleep(0); + win32_async_check(aTHX); + hwnd = w32_pseudo_child_message_hwnds[child]; + if (hwnd != INVALID_HANDLE_VALUE) return hwnd; + + { + unsigned int count = 0; + /* No Sleep(1) if tries==0, just fail instead if we get this far. */ + while (count++ < tries) { + Sleep(1); + win32_async_check(aTHX); + hwnd = w32_pseudo_child_message_hwnds[child]; + if (hwnd != INVALID_HANDLE_VALUE) return hwnd; + } + } + + Perl_croak(aTHX_ "panic: child pseudo-process was never scheduled"); +} #endif DllExport int win32_kill(int pid, int sig) { dTHX; - HANDLE hProcess; long child; - int retval; #ifdef USE_ITHREADS if (pid < 0) { /* it is a pseudo-forked child */ child = find_pseudo_pid(-pid); if (child >= 0) { - HWND hwnd = w32_pseudo_child_message_hwnds[child]; - hProcess = w32_pseudo_child_handles[child]; + HANDLE hProcess = w32_pseudo_child_handles[child]; switch (sig) { - case 0: - /* "Does process exist?" use of kill */ - return 0; - - case 9: - /* kill -9 style un-graceful exit */ - if (TerminateThread(hProcess, sig)) { - remove_dead_pseudo_process(child); + case 0: + /* "Does process exist?" use of kill */ return 0; - } - break; - default: { - int count = 0; - /* pseudo-process has not yet properly initialized if hwnd isn't set */ - while (hwnd == INVALID_HANDLE_VALUE && count < 5) { - /* Yield and wait for the other thread to send us its message_hwnd */ - Sleep(0); - win32_async_check(aTHX); - ++count; - } - if (hwnd != INVALID_HANDLE_VALUE) { - /* We fake signals to pseudo-processes using Win32 - * message queue. In Win9X the pids are negative already. */ - if ((hwnd != NULL && PostMessage(hwnd, WM_USER_KILL, sig, 0)) || - PostThreadMessage(IsWin95() ? pid : -pid, WM_USER_KILL, sig, 0)) - { - /* It might be us ... */ - PERL_ASYNC_CHECK(); - return 0; - } - } - break; - } - } /* switch */ - } - else if (IsWin95()) { - pid = -pid; - goto alien_process; + case 9: { + /* kill -9 style un-graceful exit */ + /* Do a wait to make sure child starts and isn't in DLL + * Loader Lock */ + HWND hwnd = get_hwnd_delay(aTHX, child, 5); + if (TerminateThread(hProcess, sig)) { + /* Allow the scheduler to finish cleaning up the other + * thread. + * Otherwise, if we ExitProcess() before another context + * switch happens we will end up with a process exit + * code of "sig" instead of our own exit status. + * https://rt.cpan.org/Ticket/Display.html?id=66016#txn-908976 + */ + Sleep(0); + remove_dead_pseudo_process(child); + return 0; + } + break; + } + + default: { + HWND hwnd = get_hwnd_delay(aTHX, child, 5); + /* We fake signals to pseudo-processes using Win32 + * message queue. */ + if ((hwnd != NULL && PostMessage(hwnd, WM_USER_KILL, sig, 0)) || + PostThreadMessage(-pid, WM_USER_KILL, sig, 0)) + { + /* Don't wait for child process to terminate after we send a + * SIGTERM because the child may be blocked in a system call + * and never receive the signal. + */ + if (sig == SIGTERM) { + Sleep(0); + w32_pseudo_child_sigterm[child] = 1; + } + /* It might be us ... */ + PERL_ASYNC_CHECK(); + return 0; + } + break; + } + } /* switch */ } } else @@ -1119,58 +1421,18 @@ win32_kill(int pid, int sig) { child = find_pid(pid); if (child >= 0) { - hProcess = w32_child_handles[child]; - switch(sig) { - case 0: - /* "Does process exist?" use of kill */ - return 0; - case 2: - if (GenerateConsoleCtrlEvent(CTRL_C_EVENT,pid)) - return 0; - break; - case SIGBREAK: - case SIGTERM: - if (GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT,pid)) - return 0; - break; - default: /* For now be backwards compatible with perl5.6 */ - case 9: - if (TerminateProcess(hProcess, sig)) { - remove_dead_process(child); - return 0; - } - break; + if (my_kill(pid, sig)) { + DWORD exitcode = 0; + if (GetExitCodeProcess(w32_child_handles[child], &exitcode) && + exitcode != STILL_ACTIVE) + { + remove_dead_process(child); + } + return 0; } } else { -alien_process: - retval = -1; - hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, - (IsWin95() ? -pid : pid)); - if (hProcess) { - switch(sig) { - case 0: - /* "Does process exist?" use of kill */ - retval = 0; - break; - case 2: - if (GenerateConsoleCtrlEvent(CTRL_C_EVENT,pid)) - retval = 0; - break; - case SIGBREAK: - case SIGTERM: - if (GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT,pid)) - retval = 0; - break; - default: /* For now be backwards compatible with perl5.6 */ - case 9: - if (TerminateProcess(hProcess, sig)) - retval = 0; - break; - } - } - CloseHandle(hProcess); - if (retval == 0) + if (my_kill(pid, sig)) return 0; } } @@ -1283,28 +1545,18 @@ win32_stat(const char *path, Stat_t *sbuf) errno = ENOTDIR; return -1; } -#ifdef __BORLANDC__ - if (S_ISDIR(sbuf->st_mode)) - sbuf->st_mode |= S_IWRITE | S_IEXEC; - else if (S_ISREG(sbuf->st_mode)) { - int perms; - if (l >= 4 && path[l-4] == '.') { - const char *e = path + l - 3; - if (strnicmp(e,"exe",3) - && strnicmp(e,"bat",3) - && strnicmp(e,"com",3) - && (IsWin95() || strnicmp(e,"cmd",3))) - sbuf->st_mode &= ~S_IEXEC; - else - sbuf->st_mode |= S_IEXEC; + if (S_ISDIR(sbuf->st_mode)) { + /* Ensure the "write" bit is switched off in the mode for + * directories with the read-only attribute set. Some compilers + * switch it on for directories, which is technically correct + * (directories are indeed always writable unless denied by DACLs), + * but we want stat() and -w to reflect the state of the read-only + * attribute for symmetry with chmod(). */ + DWORD r = GetFileAttributesA(path); + if (r != 0xffffffff && (r & FILE_ATTRIBUTE_READONLY)) { + sbuf->st_mode &= ~S_IWRITE; } - else - sbuf->st_mode &= ~S_IEXEC; - /* Propagate permissions to _group_ and _others_ */ - perms = sbuf->st_mode & (S_IREAD|S_IWRITE|S_IEXEC); - sbuf->st_mode |= (perms>>3) | (perms>>6); } -#endif } return res; } @@ -1333,7 +1585,7 @@ win32_longpath(char *path) char *start = path; char sep; if (!path) - return Nullch; + return NULL; /* drive prefix */ if (isALPHA(path[0]) && path[1] == ':') { @@ -1397,26 +1649,145 @@ win32_longpath(char *path) else { FindClose(fhand); errno = ERANGE; - return Nullch; + return NULL; } } else { /* failed a step, just return without side effects */ /*PerlIO_printf(Perl_debug_log, "Failed to find %s\n", path);*/ errno = EINVAL; - return Nullch; + return NULL; } } strcpy(path,tmpbuf); return path; } +static void +out_of_memory(void) +{ + if (PL_curinterp) { + dTHX; + /* Can't use PerlIO to write as it allocates memory */ + PerlLIO_write(PerlIO_fileno(Perl_error_log), + PL_no_mem, strlen(PL_no_mem)); + my_exit(1); + } + exit(1); +} + +/* Converts a wide character (UTF-16) string to the Windows ANSI code page, + * potentially using the system's default replacement character for any + * unrepresentable characters. The caller must free() the returned string. */ +static char* +wstr_to_str(const wchar_t* wstr) +{ + BOOL used_default = FALSE; + size_t wlen = wcslen(wstr) + 1; + int len = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wstr, wlen, + NULL, 0, NULL, NULL); + char* str = malloc(len); + if (!str) + out_of_memory(); + WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wstr, wlen, + str, len, NULL, &used_default); + return str; +} + +/* The win32_ansipath() function takes a Unicode filename and converts it + * into the current Windows codepage. If some characters cannot be mapped, + * then it will convert the short name instead. + * + * The buffer to the ansi pathname must be freed with win32_free() when it + * it no longer needed. + * + * The argument to win32_ansipath() must exist before this function is + * called; otherwise there is no way to determine the short path name. + * + * Ideas for future refinement: + * - Only convert those segments of the path that are not in the current + * codepage, but leave the other segments in their long form. + * - If the resulting name is longer than MAX_PATH, start converting + * additional path segments into short names until the full name + * is shorter than MAX_PATH. Shorten the filename part last! + */ +DllExport char * +win32_ansipath(const WCHAR *widename) +{ + char *name; + BOOL use_default = FALSE; + size_t widelen = wcslen(widename)+1; + int len = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, widename, widelen, + NULL, 0, NULL, NULL); + name = win32_malloc(len); + if (!name) + out_of_memory(); + + WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, widename, widelen, + name, len, NULL, &use_default); + if (use_default) { + DWORD shortlen = GetShortPathNameW(widename, NULL, 0); + if (shortlen) { + WCHAR *shortname = win32_malloc(shortlen*sizeof(WCHAR)); + if (!shortname) + out_of_memory(); + shortlen = GetShortPathNameW(widename, shortname, shortlen)+1; + + len = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, shortname, shortlen, + NULL, 0, NULL, NULL); + name = win32_realloc(name, len); + if (!name) + out_of_memory(); + WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, shortname, shortlen, + name, len, NULL, NULL); + win32_free(shortname); + } + } + return name; +} + +DllExport char * +win32_getenvironmentstrings(void) +{ + LPWSTR lpWStr, lpWTmp; + LPSTR lpStr, lpTmp; + DWORD env_len, wenvstrings_len = 0, aenvstrings_len = 0; + + /* Get the process environment strings */ + lpWTmp = lpWStr = (LPWSTR) GetEnvironmentStringsW(); + for (wenvstrings_len = 1; *lpWTmp != '\0'; lpWTmp += env_len + 1) { + env_len = wcslen(lpWTmp); + /* calculate the size of the environment strings */ + wenvstrings_len += env_len + 1; + } + + /* Get the number of bytes required to store the ACP encoded string */ + aenvstrings_len = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, + lpWStr, wenvstrings_len, NULL, 0, NULL, NULL); + lpTmp = lpStr = (char *)win32_calloc(aenvstrings_len, sizeof(char)); + if(!lpTmp) + out_of_memory(); + + /* Convert the string from UTF-16 encoding to ACP encoding */ + WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, lpWStr, wenvstrings_len, lpStr, + aenvstrings_len, NULL, NULL); + + return(lpStr); +} + +DllExport void +win32_freeenvironmentstrings(void* block) +{ + win32_free(block); +} + DllExport char * win32_getenv(const char *name) { dTHX; DWORD needlen; - SV *curitem = Nullsv; + SV *curitem = NULL; + DWORD last_err; needlen = GetEnvironmentVariableA(name,NULL,0); if (needlen != 0) { @@ -1429,15 +1800,42 @@ win32_getenv(const char *name) SvCUR_set(curitem, needlen); } else { - /* allow any environment variables that begin with 'PERL' - to be stored in the registry */ - if (strncmp(name, "PERL", 4) == 0) - (void)get_regstr(name, &curitem); + last_err = GetLastError(); + if (last_err == ERROR_NOT_ENOUGH_MEMORY) { + /* It appears the variable is in the env, but the Win32 API + doesn't have a canned way of getting it. So we fall back to + grabbing the whole env and pulling this value out if possible */ + char *envv = GetEnvironmentStrings(); + char *cur = envv; + STRLEN len; + while (*cur) { + char *end = strchr(cur,'='); + if (end && end != cur) { + *end = '\0'; + if (!strcmp(cur,name)) { + curitem = sv_2mortal(newSVpv(end+1,0)); + *end = '='; + break; + } + *end = '='; + cur = end + strlen(end+1)+2; + } + else if ((len = strlen(cur))) + cur += len+1; + } + FreeEnvironmentStrings(envv); + } + else { + /* last ditch: allow any environment variables that begin with 'PERL' + to be obtained from the registry, if found there */ + if (strncmp(name, "PERL", 4) == 0) + (void)get_regstr(name, &curitem); + } } if (curitem && SvCUR(curitem)) return SvPVX(curitem); - return Nullch; + return NULL; } DllExport int @@ -1457,9 +1855,11 @@ win32_putenv(const char *name) * Has these advantages over putenv() & co.: * * enables us to store a truly empty value in the * environment (like in UNIX). - * * we don't have to deal with RTL globals, bugs and leaks. + * * we don't have to deal with RTL globals, bugs and leaks + * (specifically, see http://support.microsoft.com/kb/235601). * * Much faster. - * Why you may want to enable USE_WIN32_RTL_ENV: + * Why you may want to use the RTL environment handling + * (previously enabled by USE_WIN32_RTL_ENV): * * environ[] and RTL functions will not reflect changes, * which might be an issue if extensions want to access * the env. via RTL. This cuts both ways, since RTL will @@ -1571,7 +1971,7 @@ win32_utime(const char *filename, struct utimbuf *times) rc = utime(filename, times); /* EACCES: path specifies directory or readonly file */ - if (rc == 0 || errno != EACCES /* || !IsWinNT() */) + if (rc == 0 || errno != EACCES) return rc; if (times == NULL) { @@ -1691,8 +2091,7 @@ win32_uname(struct utsname *name) char *arch; GetSystemInfo(&info); -#if (defined(__BORLANDC__)&&(__BORLANDC__<=0x520)) \ - || (defined(__MINGW32__) && !defined(_ANONYMOUS_UNION)) +#if (defined(__MINGW32__) && !defined(_ANONYMOUS_UNION) && !defined(__MINGW_EXTENSION)) procarch = info.u.s.wProcessorArchitecture; #else procarch = info.wProcessorArchitecture; @@ -1700,44 +2099,12 @@ win32_uname(struct utsname *name) switch (procarch) { case PROCESSOR_ARCHITECTURE_INTEL: arch = "x86"; break; - case PROCESSOR_ARCHITECTURE_MIPS: - arch = "mips"; break; - case PROCESSOR_ARCHITECTURE_ALPHA: - arch = "alpha"; break; - case PROCESSOR_ARCHITECTURE_PPC: - arch = "ppc"; break; -#ifdef PROCESSOR_ARCHITECTURE_SHX - case PROCESSOR_ARCHITECTURE_SHX: - arch = "shx"; break; -#endif -#ifdef PROCESSOR_ARCHITECTURE_ARM - case PROCESSOR_ARCHITECTURE_ARM: - arch = "arm"; break; -#endif -#ifdef PROCESSOR_ARCHITECTURE_IA64 case PROCESSOR_ARCHITECTURE_IA64: arch = "ia64"; break; -#endif -#ifdef PROCESSOR_ARCHITECTURE_ALPHA64 - case PROCESSOR_ARCHITECTURE_ALPHA64: - arch = "alpha64"; break; -#endif -#ifdef PROCESSOR_ARCHITECTURE_MSIL - case PROCESSOR_ARCHITECTURE_MSIL: - arch = "msil"; break; -#endif -#ifdef PROCESSOR_ARCHITECTURE_AMD64 case PROCESSOR_ARCHITECTURE_AMD64: arch = "amd64"; break; -#endif -#ifdef PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 - case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64: - arch = "ia32-64"; break; -#endif -#ifdef PROCESSOR_ARCHITECTURE_UNKNOWN case PROCESSOR_ARCHITECTURE_UNKNOWN: arch = "unknown"; break; -#endif default: sprintf(name->machine, "unknown(0x%x)", procarch); arch = name->machine; @@ -1800,70 +2167,49 @@ win32_async_check(pTHX) MSG msg; HWND hwnd = w32_message_hwnd; + /* Reset w32_poll_count before doing anything else, incase we dispatch + * messages that end up calling back into perl */ w32_poll_count = 0; - if (hwnd == INVALID_HANDLE_VALUE) { - /* Call PeekMessage() to mark all pending messages in the queue as "old". - * This is necessary when we are being called by win32_msgwait() to - * make sure MsgWaitForMultipleObjects() stops reporting the same waiting - * message over and over. An example how this can happen is when - * Perl is calling win32_waitpid() inside a GUI application and the GUI - * is generating messages before the process terminated. - */ - PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE|PM_NOYIELD); - if (PL_sig_pending) - despatch_signals(); - return 1; + if (hwnd != INVALID_HANDLE_VALUE) { + /* Passing PeekMessage -1 as HWND (2nd arg) only gets PostThreadMessage() messages + * and ignores window messages - should co-exist better with windows apps e.g. Tk + */ + if (hwnd == NULL) + hwnd = (HWND)-1; + + while (PeekMessage(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE|PM_NOYIELD) || + PeekMessage(&msg, hwnd, WM_USER_MIN, WM_USER_MAX, PM_REMOVE|PM_NOYIELD)) + { + /* re-post a WM_QUIT message (we'll mark it as read later) */ + if(msg.message == WM_QUIT) { + PostQuitMessage((int)msg.wParam); + break; + } + + if(!CallMsgFilter(&msg, MSGF_USER)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } } - /* Passing PeekMessage -1 as HWND (2nd arg) only get PostThreadMessage() messages - * and ignores window messages - should co-exist better with windows apps e.g. Tk + /* Call PeekMessage() to mark all pending messages in the queue as "old". + * This is necessary when we are being called by win32_msgwait() to + * make sure MsgWaitForMultipleObjects() stops reporting the same waiting + * message over and over. An example how this can happen is when + * Perl is calling win32_waitpid() inside a GUI application and the GUI + * is generating messages before the process terminated. */ - if (hwnd == NULL) - hwnd = (HWND)-1; - - while (PeekMessage(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE|PM_NOYIELD) || - PeekMessage(&msg, hwnd, WM_USER_MIN, WM_USER_MAX, PM_REMOVE|PM_NOYIELD)) - { - switch (msg.message) { -#ifdef USE_ITHREADS - case WM_USER_MESSAGE: { - int child = find_pseudo_pid(msg.wParam); - if (child >= 0) - w32_pseudo_child_message_hwnds[child] = (HWND)msg.lParam; - break; - } -#endif + PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE|PM_NOYIELD); - case WM_USER_KILL: { - /* We use WM_USER to fake kill() with other signals */ - int sig = msg.wParam; - if (do_raise(aTHX_ sig)) - sig_terminate(aTHX_ sig); - break; - } - - case WM_TIMER: { - /* alarm() is a one-shot but SetTimer() repeats so kill it */ - if (w32_timerid && w32_timerid==msg.wParam) { - KillTimer(w32_message_hwnd, w32_timerid); - w32_timerid=0; - - /* Now fake a call to signal handler */ - if (do_raise(aTHX_ 14)) - sig_terminate(aTHX_ 14); - } - break; - } - } /* switch */ - } - - /* Above or other stuff may have set a signal flag */ - if (PL_sig_pending) { - despatch_signals(); - } - return 1; -} + /* Above or other stuff may have set a signal flag */ + if (PL_sig_pending) + despatch_signals(); + + return 1; +} /* This function will not return until the timeout has elapsed, or until * one of the handles is ready. */ @@ -1871,13 +2217,33 @@ DllExport DWORD win32_msgwait(pTHX_ DWORD count, LPHANDLE handles, DWORD timeout, LPDWORD resultp) { /* We may need several goes at this - so compute when we stop */ - DWORD ticks = 0; + FT_t ticks = {0}; + unsigned __int64 endtime = timeout; if (timeout != INFINITE) { - ticks = GetTickCount(); - timeout += ticks; - } - while (1) { - DWORD result = MsgWaitForMultipleObjects(count,handles,FALSE,timeout-ticks, QS_ALLEVENTS); + GetSystemTimeAsFileTime(&ticks.ft_val); + ticks.ft_i64 /= 10000; + endtime += ticks.ft_i64; + } + /* This was a race condition. Do not let a non INFINITE timeout to + * MsgWaitForMultipleObjects roll under 0 creating a near + * infinity/~(UINT32)0 timeout which will appear as a deadlock to the + * user who did a CORE perl function with a non infinity timeout, + * sleep for example. This is 64 to 32 truncation minefield. + * + * This scenario can only be created if the timespan from the return of + * MsgWaitForMultipleObjects to GetSystemTimeAsFileTime exceeds 1 ms. To + * generate the scenario, manual breakpoints in a C debugger are required, + * or a context switch occured in win32_async_check in PeekMessage, or random + * messages are delivered to the *thread* message queue of the Perl thread + * from another process (msctf.dll doing IPC among its instances, VS debugger + * causes msctf.dll to be loaded into Perl by kernel), see [perl #33096]. + */ + while (ticks.ft_i64 <= endtime) { + /* if timeout's type is lengthened, remember to split 64b timeout + * into multiple non-infinity runs of MWFMO */ + DWORD result = MsgWaitForMultipleObjects(count, handles, FALSE, + (DWORD)(endtime - ticks.ft_i64), + QS_POSTMESSAGE|QS_TIMER|QS_SENDMESSAGE); if (resultp) *resultp = result; if (result == WAIT_TIMEOUT) { @@ -1887,8 +2253,9 @@ win32_msgwait(pTHX_ DWORD count, LPHANDLE handles, DWORD timeout, LPDWORD result return 0; } if (timeout != INFINITE) { - ticks = GetTickCount(); - } + GetSystemTimeAsFileTime(&ticks.ft_val); + ticks.ft_i64 /= 10000; + } if (result == WAIT_OBJECT_0 + count) { /* Message has arrived - check it */ (void)win32_async_check(aTHX); @@ -1898,10 +2265,13 @@ win32_msgwait(pTHX_ DWORD count, LPHANDLE handles, DWORD timeout, LPDWORD result break; } } - /* compute time left to wait */ - ticks = timeout - ticks; /* If we are past the end say zero */ - return (ticks > 0) ? ticks : 0; + if (!ticks.ft_i64 || ticks.ft_i64 > endtime) + return 0; + /* compute time left to wait */ + ticks.ft_i64 = endtime - ticks.ft_i64; + /* if more ms than DWORD, then return max DWORD */ + return ticks.ft_i64 <= UINT_MAX ? (DWORD)ticks.ft_i64 : UINT_MAX; } int @@ -1998,10 +2368,6 @@ win32_waitpid(int pid, int *status, int flags) else errno = ECHILD; } - else if (IsWin95()) { - pid = -pid; - goto alien_process; - } } #endif else { @@ -2026,9 +2392,7 @@ win32_waitpid(int pid, int *status, int flags) errno = ECHILD; } else { -alien_process: - hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, - (IsWin95() ? -pid : pid)); + hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, pid); if (hProcess) { win32_msgwait(aTHX_ 1, &hProcess, timeout, &waitcode); if (waitcode == WAIT_TIMEOUT) { @@ -2062,7 +2426,11 @@ win32_sleep(unsigned int t) { dTHX; /* Win32 times are in ms so *1000 in and /1000 out */ - return win32_msgwait(aTHX_ 0, NULL, t*1000, NULL)/1000; + if (t > UINT_MAX / 1000) { + Perl_ck_warner(aTHX_ packWARN(WARN_OVERFLOW), + "sleep(%lu) too large", t); + } + return win32_msgwait(aTHX_ 0, NULL, t * 1000, NULL) / 1000; } DllExport unsigned int @@ -2096,115 +2464,17 @@ win32_alarm(unsigned int sec) return 0; } -#ifdef HAVE_DES_FCRYPT extern char * des_fcrypt(const char *txt, const char *salt, char *cbuf); -#endif DllExport char * win32_crypt(const char *txt, const char *salt) { dTHX; -#ifdef HAVE_DES_FCRYPT return des_fcrypt(txt, salt, w32_crypt_buffer); -#else - Perl_croak(aTHX_ "The crypt() function is unimplemented due to excessive paranoia."); - return Nullch; -#endif } -#ifdef USE_FIXED_OSFHANDLE - -#define FOPEN 0x01 /* file handle open */ -#define FNOINHERIT 0x10 /* file handle opened O_NOINHERIT */ -#define FAPPEND 0x20 /* file handle opened O_APPEND */ -#define FDEV 0x40 /* file handle refers to device */ -#define FTEXT 0x80 /* file handle is in text mode */ - -/*** -*int my_open_osfhandle(intptr_t osfhandle, int flags) - open C Runtime file handle -* -*Purpose: -* This function allocates a free C Runtime file handle and associates -* it with the Win32 HANDLE specified by the first parameter. This is a -* temperary fix for WIN95's brain damage GetFileType() error on socket -* we just bypass that call for socket -* -* This works with MSVC++ 4.0+ or GCC/Mingw32 -* -*Entry: -* intptr_t osfhandle - Win32 HANDLE to associate with C Runtime file handle. -* int flags - flags to associate with C Runtime file handle. -* -*Exit: -* returns index of entry in fh, if successful -* return -1, if no free entry is found -* -*Exceptions: -* -*******************************************************************************/ - -/* - * we fake up some parts of the CRT that aren't exported by MSVCRT.dll - * this lets sockets work on Win9X with GCC and should fix the problems - * with perl95.exe - * -- BKS, 1-23-2000 -*/ - -/* create an ioinfo entry, kill its handle, and steal the entry */ - -static int -_alloc_osfhnd(void) -{ - HANDLE hF = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL); - int fh = _open_osfhandle((intptr_t)hF, 0); - CloseHandle(hF); - if (fh == -1) - return fh; - EnterCriticalSection(&(_pioinfo(fh)->lock)); - return fh; -} - -static int -my_open_osfhandle(intptr_t osfhandle, int flags) -{ - int fh; - char fileflags; /* _osfile flags */ - - /* copy relevant flags from second parameter */ - fileflags = FDEV; - - if (flags & O_APPEND) - fileflags |= FAPPEND; - - if (flags & O_TEXT) - fileflags |= FTEXT; - - if (flags & O_NOINHERIT) - fileflags |= FNOINHERIT; - - /* attempt to allocate a C Runtime file handle */ - if ((fh = _alloc_osfhnd()) == -1) { - errno = EMFILE; /* too many open files */ - _doserrno = 0L; /* not an OS error */ - return -1; /* return error to caller */ - } - - /* the file is open. now, set the info in _osfhnd array */ - _set_osfhnd(fh, osfhandle); - - fileflags |= FOPEN; /* mark as open */ - - _osfile(fh) = fileflags; /* set osfile entry */ - LeaveCriticalSection(&_pioinfo(fh)->lock); - - return fh; /* return handle */ -} - -#endif /* USE_FIXED_OSFHANDLE */ - /* simulate flock by locking a range on the file */ -#define LK_ERR(f,i) ((f) ? (i = 0) : (errno = GetLastError())) #define LK_LEN 0xffff0000 DllExport int @@ -2214,40 +2484,47 @@ win32_flock(int fd, int oper) int i = -1; HANDLE fh; - if (!IsWinNT()) { - dTHX; - Perl_croak_nocontext("flock() unimplemented on this platform"); - return -1; - } fh = (HANDLE)_get_osfhandle(fd); + if (fh == (HANDLE)-1) /* _get_osfhandle() already sets errno to EBADF */ + return -1; + memset(&o, 0, sizeof(o)); switch(oper) { case LOCK_SH: /* shared lock */ - LK_ERR(LockFileEx(fh, 0, 0, LK_LEN, 0, &o),i); + if (LockFileEx(fh, 0, 0, LK_LEN, 0, &o)) + i = 0; break; case LOCK_EX: /* exclusive lock */ - LK_ERR(LockFileEx(fh, LOCKFILE_EXCLUSIVE_LOCK, 0, LK_LEN, 0, &o),i); + if (LockFileEx(fh, LOCKFILE_EXCLUSIVE_LOCK, 0, LK_LEN, 0, &o)) + i = 0; break; case LOCK_SH|LOCK_NB: /* non-blocking shared lock */ - LK_ERR(LockFileEx(fh, LOCKFILE_FAIL_IMMEDIATELY, 0, LK_LEN, 0, &o),i); + if (LockFileEx(fh, LOCKFILE_FAIL_IMMEDIATELY, 0, LK_LEN, 0, &o)) + i = 0; break; case LOCK_EX|LOCK_NB: /* non-blocking exclusive lock */ - LK_ERR(LockFileEx(fh, - LOCKFILE_EXCLUSIVE_LOCK|LOCKFILE_FAIL_IMMEDIATELY, - 0, LK_LEN, 0, &o),i); + if (LockFileEx(fh, LOCKFILE_EXCLUSIVE_LOCK|LOCKFILE_FAIL_IMMEDIATELY, + 0, LK_LEN, 0, &o)) + i = 0; break; case LOCK_UN: /* unlock lock */ - LK_ERR(UnlockFileEx(fh, 0, LK_LEN, 0, &o),i); + if (UnlockFileEx(fh, 0, LK_LEN, 0, &o)) + i = 0; break; default: /* unknown */ errno = EINVAL; - break; + return -1; + } + if (i == -1) { + if (GetLastError() == ERROR_LOCK_VIOLATION) + errno = WSAEWOULDBLOCK; + else + errno = EINVAL; } return i; } -#undef LK_ERR #undef LK_LEN /* @@ -2281,7 +2558,7 @@ win32_stdin(void) } DllExport FILE * -win32_stdout() +win32_stdout(void) { return (stdout); } @@ -2308,24 +2585,27 @@ win32_feof(FILE *fp) DllExport char * win32_strerror(int e) { -#if !defined __BORLANDC__ && !defined __MINGW32__ /* compiler intolerance */ +#if !defined __MINGW32__ /* compiler intolerance */ extern int sys_nerr; #endif - DWORD source = 0; if (e < 0 || e > sys_nerr) { dTHX; if (e < 0) e = GetLastError(); - if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, &source, e, 0, - w32_strerror_buffer, - sizeof(w32_strerror_buffer), NULL) == 0) + if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM + |FORMAT_MESSAGE_IGNORE_INSERTS, NULL, e, 0, + w32_strerror_buffer, sizeof(w32_strerror_buffer), + NULL) == 0) + { strcpy(w32_strerror_buffer, "Unknown Error"); - + } return w32_strerror_buffer; } +#undef strerror return strerror(e); +#define strerror win32_strerror } DllExport void @@ -2423,11 +2703,6 @@ win32_fopen(const char *filename, const char *mode) return f; } -#ifndef USE_SOCKETS_AS_HANDLES -#undef fdopen -#define fdopen my_fdopen -#endif - DllExport FILE * win32_fdopen(int handle, const char *mode) { @@ -2503,14 +2778,10 @@ DllExport Off_t win32_ftell(FILE *pf) { #if defined(WIN64) || defined(USE_LARGE_FILES) -#if defined(__BORLANDC__) /* buk */ - return win32_tell( fileno( pf ) ); -#else fpos_t pos; if (fgetpos(pf, &pos)) return -1; return (Off_t)pos; -#endif #else return ftell(pf); #endif @@ -2520,13 +2791,6 @@ DllExport int win32_fseek(FILE *pf, Off_t offset,int origin) { #if defined(WIN64) || defined(USE_LARGE_FILES) -#if defined(__BORLANDC__) /* buk */ - return win32_lseek( - fileno(pf), - offset, - origin - ); -#else fpos_t pos; switch (origin) { case SEEK_CUR: @@ -2546,7 +2810,6 @@ win32_fseek(FILE *pf, Off_t offset,int origin) return -1; } return fsetpos(pf, &offset); -#endif #else return fseek(pf, (long)offset, origin); #endif @@ -2555,25 +2818,13 @@ win32_fseek(FILE *pf, Off_t offset,int origin) DllExport int win32_fgetpos(FILE *pf,fpos_t *p) { -#if defined(__BORLANDC__) && defined(USE_LARGE_FILES) /* buk */ - if( win32_tell(fileno(pf)) == -1L ) { - errno = EBADF; - return -1; - } - return 0; -#else return fgetpos(pf, p); -#endif } DllExport int win32_fsetpos(FILE *pf,const fpos_t *p) { -#if defined(__BORLANDC__) && defined(USE_LARGE_FILES) /* buk */ - return win32_lseek(fileno(pf), *p, SEEK_CUR); -#else return fsetpos(pf, p); -#endif } DllExport void @@ -2603,9 +2854,6 @@ win32_tmpfd(void) if (fh != INVALID_HANDLE_VALUE) { int fd = win32_open_osfhandle((intptr_t)fh, 0); if (fd >= 0) { -#if defined(__BORLANDC__) - setmode(fd,O_BINARY); -#endif DEBUG_p(PerlIO_printf(Perl_debug_log, "Created tmpfile=%s\n",filename)); return fd; @@ -2635,46 +2883,10 @@ win32_abort(void) DllExport int win32_fstat(int fd, Stat_t *sbufptr) { -#ifdef __BORLANDC__ - /* A file designated by filehandle is not shown as accessible - * for write operations, probably because it is opened for reading. - * --Vadim Konovalov - */ - BY_HANDLE_FILE_INFORMATION bhfi; -#if defined(WIN64) || defined(USE_LARGE_FILES) - /* Borland 5.5.1 has a 64-bit stat, but only a 32-bit fstat */ - struct stat tmp; - int rc = fstat(fd,&tmp); - - sbufptr->st_dev = tmp.st_dev; - sbufptr->st_ino = tmp.st_ino; - sbufptr->st_mode = tmp.st_mode; - sbufptr->st_nlink = tmp.st_nlink; - sbufptr->st_uid = tmp.st_uid; - sbufptr->st_gid = tmp.st_gid; - sbufptr->st_rdev = tmp.st_rdev; - sbufptr->st_size = tmp.st_size; - sbufptr->st_atime = tmp.st_atime; - sbufptr->st_mtime = tmp.st_mtime; - sbufptr->st_ctime = tmp.st_ctime; -#else - int rc = fstat(fd,sbufptr); -#endif - - if (GetFileInformationByHandle((HANDLE)_get_osfhandle(fd), &bhfi)) { -#if defined(WIN64) || defined(USE_LARGE_FILES) - sbufptr->st_size = (bhfi.nFileSizeHigh << 32) + bhfi.nFileSizeLow ; -#endif - sbufptr->st_mode &= 0xFE00; - if (bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY) - sbufptr->st_mode |= (S_IREAD + (S_IREAD >> 3) + (S_IREAD >> 6)); - else - sbufptr->st_mode |= ((S_IREAD|S_IWRITE) + ((S_IREAD|S_IWRITE) >> 3) - + ((S_IREAD|S_IWRITE) >> 6)); - } - return rc; +#if defined(WIN64) || defined(USE_LARGE_FILES) + return _fstati64(fd, sbufptr); #else - return my_fstat(fd,sbufptr); + return fstat(fd, sbufptr); #endif } @@ -2786,9 +2998,7 @@ win32_popen(const char *command, const char *mode) lock_held = 0; } - LOCK_FDPID_MUTEX; sv_setiv(*av_fetch(w32_fdpid, p[parent], TRUE), childpid); - UNLOCK_FDPID_MUTEX; /* set process id so that it can be returned by perl's open() */ PL_forkprocess = childpid; @@ -2829,7 +3039,6 @@ win32_pclose(PerlIO *pf) int childpid, status; SV *sv; - LOCK_FDPID_MUTEX; sv = *av_fetch(w32_fdpid, PerlIO_fileno(pf), TRUE); if (SvIOK(sv)) @@ -2838,7 +3047,6 @@ win32_pclose(PerlIO *pf) childpid = 0; if (!childpid) { - UNLOCK_FDPID_MUTEX; errno = EBADF; return -1; } @@ -2849,7 +3057,6 @@ win32_pclose(PerlIO *pf) fclose(pf); #endif SvIVX(sv) = 0; - UNLOCK_FDPID_MUTEX; if (win32_waitpid(childpid, &status, 0) == -1) return -1; @@ -2859,89 +3066,51 @@ win32_pclose(PerlIO *pf) #endif /* USE_RTL_POPEN */ } -static BOOL WINAPI -Nt4CreateHardLinkW( - LPCWSTR lpFileName, - LPCWSTR lpExistingFileName, - LPSECURITY_ATTRIBUTES lpSecurityAttributes) -{ - HANDLE handle; - WCHAR wFullName[MAX_PATH+1]; - LPVOID lpContext = NULL; - WIN32_STREAM_ID StreamId; - DWORD dwSize = (char*)&StreamId.cStreamName - (char*)&StreamId; - DWORD dwWritten; - DWORD dwLen; - BOOL bSuccess; - - BOOL (__stdcall *pfnBackupWrite)(HANDLE, LPBYTE, DWORD, LPDWORD, - BOOL, BOOL, LPVOID*) = - (BOOL (__stdcall *)(HANDLE, LPBYTE, DWORD, LPDWORD, - BOOL, BOOL, LPVOID*)) - GetProcAddress(GetModuleHandle("kernel32.dll"), "BackupWrite"); - if (pfnBackupWrite == NULL) - return 0; - - dwLen = GetFullPathNameW(lpFileName, MAX_PATH, wFullName, NULL); - if (dwLen == 0) - return 0; - dwLen = (dwLen+1)*sizeof(WCHAR); - - handle = CreateFileW(lpExistingFileName, FILE_WRITE_ATTRIBUTES, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, OPEN_EXISTING, 0, NULL); - if (handle == INVALID_HANDLE_VALUE) - return 0; - - StreamId.dwStreamId = BACKUP_LINK; - StreamId.dwStreamAttributes = 0; - StreamId.dwStreamNameSize = 0; -#if defined(__BORLANDC__) \ - ||(defined(__MINGW32__) && !defined(_ANONYMOUS_UNION)) - StreamId.Size.u.HighPart = 0; - StreamId.Size.u.LowPart = dwLen; -#else - StreamId.Size.HighPart = 0; - StreamId.Size.LowPart = dwLen; -#endif - - bSuccess = pfnBackupWrite(handle, (LPBYTE)&StreamId, dwSize, &dwWritten, - FALSE, FALSE, &lpContext); - if (bSuccess) { - bSuccess = pfnBackupWrite(handle, (LPBYTE)wFullName, dwLen, &dwWritten, - FALSE, FALSE, &lpContext); - pfnBackupWrite(handle, NULL, 0, &dwWritten, TRUE, FALSE, &lpContext); - } - - CloseHandle(handle); - return bSuccess; -} - DllExport int win32_link(const char *oldname, const char *newname) { dTHX; - BOOL (__stdcall *pfnCreateHardLinkW)(LPCWSTR,LPCWSTR,LPSECURITY_ATTRIBUTES); WCHAR wOldName[MAX_PATH+1]; WCHAR wNewName[MAX_PATH+1]; - if (IsWin95()) - Perl_croak(aTHX_ PL_no_func, "link"); - - pfnCreateHardLinkW = - (BOOL (__stdcall *)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES)) - GetProcAddress(GetModuleHandle("kernel32.dll"), "CreateHardLinkW"); - if (pfnCreateHardLinkW == NULL) - pfnCreateHardLinkW = Nt4CreateHardLinkW; - if (MultiByteToWideChar(CP_ACP, 0, oldname, -1, wOldName, MAX_PATH+1) && MultiByteToWideChar(CP_ACP, 0, newname, -1, wNewName, MAX_PATH+1) && (wcscpy(wOldName, PerlDir_mapW(wOldName)), - pfnCreateHardLinkW(PerlDir_mapW(wNewName), wOldName, NULL))) + CreateHardLinkW(PerlDir_mapW(wNewName), wOldName, NULL))) { return 0; } - errno = (GetLastError() == ERROR_FILE_NOT_FOUND) ? ENOENT : EINVAL; + /* This isn't perfect, eg. Win32 returns ERROR_ACCESS_DENIED for + both permissions errors and if the source is a directory, while + POSIX wants EACCES and EPERM respectively. + + Determined by experimentation on Windows 7 x64 SP1, since MS + don't document what error codes are returned. + */ + switch (GetLastError()) { + case ERROR_BAD_NET_NAME: + case ERROR_BAD_NETPATH: + case ERROR_BAD_PATHNAME: + case ERROR_FILE_NOT_FOUND: + case ERROR_FILENAME_EXCED_RANGE: + case ERROR_INVALID_DRIVE: + case ERROR_PATH_NOT_FOUND: + errno = ENOENT; + break; + case ERROR_ALREADY_EXISTS: + errno = EEXIST; + break; + case ERROR_ACCESS_DENIED: + errno = EACCES; + break; + case ERROR_NOT_SAME_DEVICE: + errno = EXDEV; + break; + default: + /* ERROR_INVALID_FUNCTION - eg. on a FAT volume */ + errno = EINVAL; + break; + } return -1; } @@ -2949,113 +3118,35 @@ DllExport int win32_rename(const char *oname, const char *newname) { char szOldName[MAX_PATH+1]; - char szNewName[MAX_PATH+1]; BOOL bResult; + DWORD dwFlags = MOVEFILE_COPY_ALLOWED; dTHX; - /* XXX despite what the documentation says about MoveFileEx(), - * it doesn't work under Windows95! - */ - if (IsWinNT()) { - DWORD dwFlags = MOVEFILE_COPY_ALLOWED; - if (stricmp(newname, oname)) - dwFlags |= MOVEFILE_REPLACE_EXISTING; - strcpy(szOldName, PerlDir_mapA(oname)); - bResult = MoveFileExA(szOldName,PerlDir_mapA(newname), dwFlags); - if (!bResult) { - DWORD err = GetLastError(); - switch (err) { - case ERROR_BAD_NET_NAME: - case ERROR_BAD_NETPATH: - case ERROR_BAD_PATHNAME: - case ERROR_FILE_NOT_FOUND: - case ERROR_FILENAME_EXCED_RANGE: - case ERROR_INVALID_DRIVE: - case ERROR_NO_MORE_FILES: - case ERROR_PATH_NOT_FOUND: - errno = ENOENT; - break; - default: - errno = EACCES; - break; - } - return -1; - } - return 0; - } - else { - int retval = 0; - char szTmpName[MAX_PATH+1]; - char dname[MAX_PATH+1]; - char *endname = Nullch; - STRLEN tmplen = 0; - DWORD from_attr, to_attr; - - strcpy(szOldName, PerlDir_mapA(oname)); - strcpy(szNewName, PerlDir_mapA(newname)); - - /* if oname doesn't exist, do nothing */ - from_attr = GetFileAttributes(szOldName); - if (from_attr == 0xFFFFFFFF) { - errno = ENOENT; - return -1; - } - - /* if newname exists, rename it to a temporary name so that we - * don't delete it in case oname happens to be the same file - * (but perhaps accessed via a different path) - */ - to_attr = GetFileAttributes(szNewName); - if (to_attr != 0xFFFFFFFF) { - /* if newname is a directory, we fail - * XXX could overcome this with yet more convoluted logic */ - if (to_attr & FILE_ATTRIBUTE_DIRECTORY) { - errno = EACCES; - return -1; - } - tmplen = strlen(szNewName); - strcpy(szTmpName,szNewName); - endname = szTmpName+tmplen; - for (; endname > szTmpName ; --endname) { - if (*endname == '/' || *endname == '\\') { - *endname = '\0'; - break; - } - } - if (endname > szTmpName) - endname = strcpy(dname,szTmpName); - else - endname = "."; - - /* get a temporary filename in same directory - * XXX is this really the best we can do? */ - if (!GetTempFileName((LPCTSTR)endname, "plr", 0, szTmpName)) { - errno = ENOENT; - return -1; - } - DeleteFile(szTmpName); - - retval = rename(szNewName, szTmpName); - if (retval != 0) { - errno = EACCES; - return retval; - } - } - - /* rename oname to newname */ - retval = rename(szOldName, szNewName); - - /* if we created a temporary file before ... */ - if (endname != Nullch) { - /* ...and rename succeeded, delete temporary file/directory */ - if (retval == 0) - DeleteFile(szTmpName); - /* else restore it to what it was */ - else - (void)rename(szTmpName, szNewName); - } - return retval; + if (stricmp(newname, oname)) + dwFlags |= MOVEFILE_REPLACE_EXISTING; + strcpy(szOldName, PerlDir_mapA(oname)); + + bResult = MoveFileExA(szOldName,PerlDir_mapA(newname), dwFlags); + if (!bResult) { + DWORD err = GetLastError(); + switch (err) { + case ERROR_BAD_NET_NAME: + case ERROR_BAD_NETPATH: + case ERROR_BAD_PATHNAME: + case ERROR_FILE_NOT_FOUND: + case ERROR_FILENAME_EXCED_RANGE: + case ERROR_INVALID_DRIVE: + case ERROR_NO_MORE_FILES: + case ERROR_PATH_NOT_FOUND: + errno = ENOENT; + break; + default: + errno = EACCES; + break; + } + return -1; } + return 0; } DllExport int @@ -3117,23 +3208,7 @@ DllExport Off_t win32_lseek(int fd, Off_t offset, int origin) { #if defined(WIN64) || defined(USE_LARGE_FILES) -#if defined(__BORLANDC__) /* buk */ - LARGE_INTEGER pos; - pos.QuadPart = offset; - pos.LowPart = SetFilePointer( - (HANDLE)_get_osfhandle(fd), - pos.LowPart, - &pos.HighPart, - origin - ); - if (pos.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { - pos.QuadPart = -1; - } - - return pos.QuadPart; -#else return _lseeki64(fd, offset, origin); -#endif #else return lseek(fd, (long)offset, origin); #endif @@ -3143,24 +3218,7 @@ DllExport Off_t win32_tell(int fd) { #if defined(WIN64) || defined(USE_LARGE_FILES) -#if defined(__BORLANDC__) /* buk */ - LARGE_INTEGER pos; - pos.QuadPart = 0; - pos.LowPart = SetFilePointer( - (HANDLE)_get_osfhandle(fd), - pos.LowPart, - &pos.HighPart, - FILE_CURRENT - ); - if (pos.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { - pos.QuadPart = -1; - } - - return pos.QuadPart; - /* return tell(fd); */ -#else return _telli64(fd); -#endif #else return tell(fd); #endif @@ -3199,6 +3257,27 @@ win32_eof(int fd) } DllExport int +win32_isatty(int fd) +{ + /* The Microsoft isatty() function returns true for *all* + * character mode devices, including "nul". Our implementation + * should only return true if the handle has a console buffer. + */ + DWORD mode; + HANDLE fh = (HANDLE)_get_osfhandle(fd); + if (fh == (HANDLE)-1) { + /* errno is already set to EBADF */ + return 0; + } + + if (GetConsoleMode(fh, &mode)) + return 1; + + errno = ENOTTY; + return 0; +} + +DllExport int win32_dup(int fd) { return dup(fd); @@ -3210,210 +3289,10 @@ win32_dup2(int fd1,int fd2) return dup2(fd1,fd2); } -#ifdef PERL_MSVCRT_READFIX - -#define LF 10 /* line feed */ -#define CR 13 /* carriage return */ -#define CTRLZ 26 /* ctrl-z means eof for text */ -#define FOPEN 0x01 /* file handle open */ -#define FEOFLAG 0x02 /* end of file has been encountered */ -#define FCRLF 0x04 /* CR-LF across read buffer (in text mode) */ -#define FPIPE 0x08 /* file handle refers to a pipe */ -#define FAPPEND 0x20 /* file handle opened O_APPEND */ -#define FDEV 0x40 /* file handle refers to device */ -#define FTEXT 0x80 /* file handle is in text mode */ -#define MAX_DESCRIPTOR_COUNT (64*32) /* this is the maximun that MSVCRT can handle */ - -int __cdecl -_fixed_read(int fh, void *buf, unsigned cnt) -{ - int bytes_read; /* number of bytes read */ - char *buffer; /* buffer to read to */ - int os_read; /* bytes read on OS call */ - char *p, *q; /* pointers into buffer */ - char peekchr; /* peek-ahead character */ - ULONG filepos; /* file position after seek */ - ULONG dosretval; /* o.s. return value */ - - /* validate handle */ - if (((unsigned)fh >= (unsigned)MAX_DESCRIPTOR_COUNT) || - !(_osfile(fh) & FOPEN)) - { - /* out of range -- return error */ - errno = EBADF; - _doserrno = 0; /* not o.s. error */ - return -1; - } - - /* - * If lockinitflag is FALSE, assume fd is device - * lockinitflag is set to TRUE by open. - */ - if (_pioinfo(fh)->lockinitflag) - EnterCriticalSection(&(_pioinfo(fh)->lock)); /* lock file */ - - bytes_read = 0; /* nothing read yet */ - buffer = (char*)buf; - - if (cnt == 0 || (_osfile(fh) & FEOFLAG)) { - /* nothing to read or at EOF, so return 0 read */ - goto functionexit; - } - - if ((_osfile(fh) & (FPIPE|FDEV)) && _pipech(fh) != LF) { - /* a pipe/device and pipe lookahead non-empty: read the lookahead - * char */ - *buffer++ = _pipech(fh); - ++bytes_read; - --cnt; - _pipech(fh) = LF; /* mark as empty */ - } - - /* read the data */ - - if (!ReadFile((HANDLE)_osfhnd(fh), buffer, cnt, (LPDWORD)&os_read, NULL)) - { - /* ReadFile has reported an error. recognize two special cases. - * - * 1. map ERROR_ACCESS_DENIED to EBADF - * - * 2. just return 0 if ERROR_BROKEN_PIPE has occurred. it - * means the handle is a read-handle on a pipe for which - * all write-handles have been closed and all data has been - * read. */ - - if ((dosretval = GetLastError()) == ERROR_ACCESS_DENIED) { - /* wrong read/write mode should return EBADF, not EACCES */ - errno = EBADF; - _doserrno = dosretval; - bytes_read = -1; - goto functionexit; - } - else if (dosretval == ERROR_BROKEN_PIPE) { - bytes_read = 0; - goto functionexit; - } - else { - bytes_read = -1; - goto functionexit; - } - } - - bytes_read += os_read; /* update bytes read */ - - if (_osfile(fh) & FTEXT) { - /* now must translate CR-LFs to LFs in the buffer */ - - /* set CRLF flag to indicate LF at beginning of buffer */ - /* if ((os_read != 0) && (*(char *)buf == LF)) */ - /* _osfile(fh) |= FCRLF; */ - /* else */ - /* _osfile(fh) &= ~FCRLF; */ - - _osfile(fh) &= ~FCRLF; - - /* convert chars in the buffer: p is src, q is dest */ - p = q = (char*)buf; - while (p < (char *)buf + bytes_read) { - if (*p == CTRLZ) { - /* if fh is not a device, set ctrl-z flag */ - if (!(_osfile(fh) & FDEV)) - _osfile(fh) |= FEOFLAG; - break; /* stop translating */ - } - else if (*p != CR) - *q++ = *p++; - else { - /* *p is CR, so must check next char for LF */ - if (p < (char *)buf + bytes_read - 1) { - if (*(p+1) == LF) { - p += 2; - *q++ = LF; /* convert CR-LF to LF */ - } - else - *q++ = *p++; /* store char normally */ - } - else { - /* This is the hard part. We found a CR at end of - buffer. We must peek ahead to see if next char - is an LF. */ - ++p; - - dosretval = 0; - if (!ReadFile((HANDLE)_osfhnd(fh), &peekchr, 1, - (LPDWORD)&os_read, NULL)) - dosretval = GetLastError(); - - if (dosretval != 0 || os_read == 0) { - /* couldn't read ahead, store CR */ - *q++ = CR; - } - else { - /* peekchr now has the extra character -- we now - have several possibilities: - 1. disk file and char is not LF; just seek back - and copy CR - 2. disk file and char is LF; store LF, don't seek back - 3. pipe/device and char is LF; store LF. - 4. pipe/device and char isn't LF, store CR and - put char in pipe lookahead buffer. */ - if (_osfile(fh) & (FDEV|FPIPE)) { - /* non-seekable device */ - if (peekchr == LF) - *q++ = LF; - else { - *q++ = CR; - _pipech(fh) = peekchr; - } - } - else { - /* disk file */ - if (peekchr == LF) { - /* nothing read yet; must make some - progress */ - *q++ = LF; - /* turn on this flag for tell routine */ - _osfile(fh) |= FCRLF; - } - else { - HANDLE osHandle; /* o.s. handle value */ - /* seek back */ - if ((osHandle = (HANDLE)_get_osfhandle(fh)) != (HANDLE)-1) - { - if ((filepos = SetFilePointer(osHandle, -1, NULL, FILE_CURRENT)) == -1) - dosretval = GetLastError(); - } - if (peekchr != LF) - *q++ = CR; - } - } - } - } - } - } - - /* we now change bytes_read to reflect the true number of chars - in the buffer */ - bytes_read = q - (char *)buf; - } - -functionexit: - if (_pioinfo(fh)->lockinitflag) - LeaveCriticalSection(&(_pioinfo(fh)->lock)); /* unlock file */ - - return bytes_read; -} - -#endif /* PERL_MSVCRT_READFIX */ - DllExport int win32_read(int fd, void *buf, unsigned int cnt) { -#ifdef PERL_MSVCRT_READFIX - return _fixed_read(fd, buf, cnt); -#else return read(fd, buf, cnt); -#endif } DllExport int @@ -3500,11 +3379,10 @@ create_command_line(char *cname, STRLEN clen, const char * const *args) if (clen > 4 && (stricmp(&cname[clen-4], ".bat") == 0 - || (IsWinNT() && stricmp(&cname[clen-4], ".cmd") == 0))) + || (stricmp(&cname[clen-4], ".cmd") == 0))) { bat_file = TRUE; - if (!IsWin95()) - len += 3; + len += 3; } else { char *exe = strrchr(cname, '/'); @@ -3541,7 +3419,7 @@ create_command_line(char *cname, STRLEN clen, const char * const *args) Newx(cmd, len, char); ptr = cmd; - if (bat_file && !IsWin95()) { + if (bat_file) { *ptr++ = '"'; extra_quotes = TRUE; } @@ -3628,7 +3506,7 @@ qualified_path(const char *cmd) int has_slash = 0; if (!cmd) - return Nullch; + return NULL; fullcmd = (char*)cmd; while (*fullcmd) { if (*fullcmd == '/' || *fullcmd == '\\') @@ -3702,7 +3580,7 @@ qualified_path(const char *cmd) } Safefree(fullcmd); - return Nullch; + return NULL; } /* The following are just place holders. @@ -3789,7 +3667,7 @@ win32_spawnvp(int mode, const char *cmdname, const char *const *argv) PROCESS_INFORMATION ProcessInformation; DWORD create = 0; char *cmd; - char *fullcmd = Nullch; + char *fullcmd = NULL; char *cname = (char *)cmdname; STRLEN clen = 0; @@ -3906,8 +3784,6 @@ RETRY: if (mode == P_NOWAIT) { /* asynchronous spawn -- store handle, return PID */ ret = (int)ProcessInformation.dwProcessId; - if (IsWin95() && ret < 0) - ret = -ret; w32_child_handles[w32_num_children] = ProcessInformation.hProcess; w32_child_pids[w32_num_children] = (DWORD)ret; @@ -3944,17 +3820,9 @@ win32_execv(const char *cmdname, const char *const *argv) /* if this is a pseudo-forked child, we just want to spawn * the new program, and return */ if (w32_pseudo_id) -# ifdef __BORLANDC__ - return spawnv(P_WAIT, cmdname, (char *const *)argv); -# else return spawnv(P_WAIT, cmdname, argv); -# endif #endif -#ifdef __BORLANDC__ - return execv(cmdname, (char *const *)argv); -#else return execv(cmdname, argv); -#endif } DllExport int @@ -3974,11 +3842,7 @@ win32_execvp(const char *cmdname, const char *const *argv) return status; } #endif -#ifdef __BORLANDC__ - return execvp(cmdname, (char *const *)argv); -#else return execvp(cmdname, argv); -#endif } DllExport void @@ -4179,10 +4043,6 @@ win32_free(void *block) DllExport int win32_open_osfhandle(intptr_t handle, int flags) { -#ifdef USE_FIXED_OSFHANDLE - if (IsWin95()) - return my_open_osfhandle(handle, flags); -#endif return _open_osfhandle(handle, flags); } @@ -4201,21 +4061,6 @@ win32_fdupopen(FILE *pf) int fileno = win32_dup(win32_fileno(pf)); /* open the file in the same mode */ -#ifdef __BORLANDC__ - if((pf)->flags & _F_READ) { - mode[0] = 'r'; - mode[1] = 0; - } - else if((pf)->flags & _F_WRIT) { - mode[0] = 'a'; - mode[1] = 0; - } - else if((pf)->flags & _F_RDWR) { - mode[0] = 'r'; - mode[1] = '+'; - mode[2] = 0; - } -#else if((pf)->_flag & _IOREAD) { mode[0] = 'r'; mode[1] = 0; @@ -4229,7 +4074,6 @@ win32_fdupopen(FILE *pf) mode[1] = '+'; mode[2] = 0; } -#endif /* it appears that the binmode is attached to the * file descriptor so binmode files will be handled @@ -4270,11 +4114,6 @@ win32_dynaload(const char* filename) return LoadLibraryExA(PerlDir_mapA(filename), NULL, LOAD_WITH_ALTERED_SEARCH_PATH); } -/* - * Extras. - */ - -static XS(w32_SetChildShowWindow) { dXSARGS; @@ -4300,509 +4139,19 @@ XS(w32_SetChildShowWindow) XSRETURN(1); } -static -XS(w32_GetCwd) -{ - dXSARGS; - /* Make the host for current directory */ - char* ptr = PerlEnv_get_childdir(); - /* - * If ptr != Nullch - * then it worked, set PV valid, - * else return 'undef' - */ - if (ptr) { - SV *sv = sv_newmortal(); - sv_setpv(sv, ptr); - PerlEnv_free_childdir(ptr); - -#ifndef INCOMPLETE_TAINTS - SvTAINTED_on(sv); -#endif - - EXTEND(SP,1); - SvPOK_on(sv); - ST(0) = sv; - XSRETURN(1); - } - XSRETURN_UNDEF; -} - -static -XS(w32_SetCwd) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "usage: Win32::SetCurrentDirectory($cwd)"); - if (!PerlDir_chdir(SvPV_nolen(ST(0)))) - XSRETURN_YES; - - XSRETURN_NO; -} - -static -XS(w32_GetNextAvailDrive) -{ - dXSARGS; - char ix = 'C'; - char root[] = "_:\\"; - - EXTEND(SP,1); - while (ix <= 'Z') { - root[0] = ix++; - if (GetDriveType(root) == 1) { - root[2] = '\0'; - XSRETURN_PV(root); - } - } - XSRETURN_UNDEF; -} - -static -XS(w32_GetLastError) -{ - dXSARGS; - EXTEND(SP,1); - XSRETURN_IV(GetLastError()); -} - -static -XS(w32_SetLastError) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "usage: Win32::SetLastError($error)"); - SetLastError(SvIV(ST(0))); - XSRETURN_EMPTY; -} - -static -XS(w32_LoginName) -{ - dXSARGS; - char *name = w32_getlogin_buffer; - DWORD size = sizeof(w32_getlogin_buffer); - EXTEND(SP,1); - if (GetUserName(name,&size)) { - /* size includes NULL */ - ST(0) = sv_2mortal(newSVpvn(name,size-1)); - XSRETURN(1); - } - XSRETURN_UNDEF; -} - -static -XS(w32_NodeName) -{ - dXSARGS; - char name[MAX_COMPUTERNAME_LENGTH+1]; - DWORD size = sizeof(name); - EXTEND(SP,1); - if (GetComputerName(name,&size)) { - /* size does NOT include NULL :-( */ - ST(0) = sv_2mortal(newSVpvn(name,size)); - XSRETURN(1); - } - XSRETURN_UNDEF; -} - - -static -XS(w32_DomainName) -{ - dXSARGS; - HINSTANCE hNetApi32 = LoadLibrary("netapi32.dll"); - DWORD (__stdcall *pfnNetApiBufferFree)(LPVOID Buffer); - DWORD (__stdcall *pfnNetWkstaGetInfo)(LPWSTR servername, DWORD level, - void *bufptr); - - if (hNetApi32) { - pfnNetApiBufferFree = (DWORD (__stdcall *)(void *)) - GetProcAddress(hNetApi32, "NetApiBufferFree"); - pfnNetWkstaGetInfo = (DWORD (__stdcall *)(LPWSTR, DWORD, void *)) - GetProcAddress(hNetApi32, "NetWkstaGetInfo"); - } - EXTEND(SP,1); - if (hNetApi32 && pfnNetWkstaGetInfo && pfnNetApiBufferFree) { - /* this way is more reliable, in case user has a local account. */ - char dname[256]; - DWORD dnamelen = sizeof(dname); - struct { - DWORD wki100_platform_id; - LPWSTR wki100_computername; - LPWSTR wki100_langroup; - DWORD wki100_ver_major; - DWORD wki100_ver_minor; - } *pwi; - /* NERR_Success *is* 0*/ - if (0 == pfnNetWkstaGetInfo(NULL, 100, &pwi)) { - if (pwi->wki100_langroup && *(pwi->wki100_langroup)) { - WideCharToMultiByte(CP_ACP, 0, pwi->wki100_langroup, - -1, (LPSTR)dname, dnamelen, NULL, NULL); - } - else { - WideCharToMultiByte(CP_ACP, 0, pwi->wki100_computername, - -1, (LPSTR)dname, dnamelen, NULL, NULL); - } - pfnNetApiBufferFree(pwi); - FreeLibrary(hNetApi32); - XSRETURN_PV(dname); - } - FreeLibrary(hNetApi32); - } - else { - /* Win95 doesn't have NetWksta*(), so do it the old way */ - char name[256]; - DWORD size = sizeof(name); - if (hNetApi32) - FreeLibrary(hNetApi32); - if (GetUserName(name,&size)) { - char sid[ONE_K_BUFSIZE]; - DWORD sidlen = sizeof(sid); - char dname[256]; - DWORD dnamelen = sizeof(dname); - SID_NAME_USE snu; - if (LookupAccountName(NULL, name, (PSID)&sid, &sidlen, - dname, &dnamelen, &snu)) { - XSRETURN_PV(dname); /* all that for this */ - } - } - } - XSRETURN_UNDEF; -} - -static -XS(w32_FsType) -{ - dXSARGS; - char fsname[256]; - DWORD flags, filecomplen; - if (GetVolumeInformation(NULL, NULL, 0, NULL, &filecomplen, - &flags, fsname, sizeof(fsname))) { - if (GIMME_V == G_ARRAY) { - XPUSHs(sv_2mortal(newSVpvn(fsname,strlen(fsname)))); - XPUSHs(sv_2mortal(newSViv(flags))); - XPUSHs(sv_2mortal(newSViv(filecomplen))); - PUTBACK; - return; - } - EXTEND(SP,1); - XSRETURN_PV(fsname); - } - XSRETURN_EMPTY; -} - -static -XS(w32_GetOSVersion) -{ - dXSARGS; - /* Use explicit struct definition because wSuiteMask and - * wProductType are not defined in the VC++ 6.0 headers. - * WORD type has been replaced by unsigned short because - * WORD is already used by Perl itself. - */ - struct { - DWORD dwOSVersionInfoSize; - DWORD dwMajorVersion; - DWORD dwMinorVersion; - DWORD dwBuildNumber; - DWORD dwPlatformId; - CHAR szCSDVersion[128]; - unsigned short wServicePackMajor; - unsigned short wServicePackMinor; - unsigned short wSuiteMask; - BYTE wProductType; - BYTE wReserved; - } osver; - BOOL bEx = TRUE; - - osver.dwOSVersionInfoSize = sizeof(osver); - if (!GetVersionExA((OSVERSIONINFOA*)&osver)) { - bEx = FALSE; - osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA); - if (!GetVersionExA((OSVERSIONINFOA*)&osver)) { - XSRETURN_EMPTY; - } - } - if (GIMME_V == G_SCALAR) { - XSRETURN_IV(osver.dwPlatformId); - } - XPUSHs(newSVpvn(osver.szCSDVersion, strlen(osver.szCSDVersion))); - - XPUSHs(newSViv(osver.dwMajorVersion)); - XPUSHs(newSViv(osver.dwMinorVersion)); - XPUSHs(newSViv(osver.dwBuildNumber)); - XPUSHs(newSViv(osver.dwPlatformId)); - if (bEx) { - XPUSHs(newSViv(osver.wServicePackMajor)); - XPUSHs(newSViv(osver.wServicePackMinor)); - XPUSHs(newSViv(osver.wSuiteMask)); - XPUSHs(newSViv(osver.wProductType)); - } - PUTBACK; -} - -static -XS(w32_IsWinNT) -{ - dXSARGS; - EXTEND(SP,1); - XSRETURN_IV(IsWinNT()); -} - -static -XS(w32_IsWin95) -{ - dXSARGS; - EXTEND(SP,1); - XSRETURN_IV(IsWin95()); -} - -static -XS(w32_FormatMessage) -{ - dXSARGS; - DWORD source = 0; - char msgbuf[ONE_K_BUFSIZE]; - - if (items != 1) - Perl_croak(aTHX_ "usage: Win32::FormatMessage($errno)"); - - if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, - &source, SvIV(ST(0)), 0, - msgbuf, sizeof(msgbuf)-1, NULL)) - { - XSRETURN_PV(msgbuf); - } - - XSRETURN_UNDEF; -} - -static -XS(w32_Spawn) -{ - dXSARGS; - char *cmd, *args; - void *env; - char *dir; - PROCESS_INFORMATION stProcInfo; - STARTUPINFO stStartInfo; - BOOL bSuccess = FALSE; - - if (items != 3) - Perl_croak(aTHX_ "usage: Win32::Spawn($cmdName, $args, $PID)"); - - cmd = SvPV_nolen(ST(0)); - args = SvPV_nolen(ST(1)); - - env = PerlEnv_get_childenv(); - dir = PerlEnv_get_childdir(); - - memset(&stStartInfo, 0, sizeof(stStartInfo)); /* Clear the block */ - stStartInfo.cb = sizeof(stStartInfo); /* Set the structure size */ - stStartInfo.dwFlags = STARTF_USESHOWWINDOW; /* Enable wShowWindow control */ - stStartInfo.wShowWindow = SW_SHOWMINNOACTIVE; /* Start min (normal) */ - - if (CreateProcess( - cmd, /* Image path */ - args, /* Arguments for command line */ - NULL, /* Default process security */ - NULL, /* Default thread security */ - FALSE, /* Must be TRUE to use std handles */ - NORMAL_PRIORITY_CLASS, /* No special scheduling */ - env, /* Inherit our environment block */ - dir, /* Inherit our currrent directory */ - &stStartInfo, /* -> Startup info */ - &stProcInfo)) /* <- Process info (if OK) */ - { - int pid = (int)stProcInfo.dwProcessId; - if (IsWin95() && pid < 0) - pid = -pid; - sv_setiv(ST(2), pid); - CloseHandle(stProcInfo.hThread);/* library source code does this. */ - bSuccess = TRUE; - } - PerlEnv_free_childenv(env); - PerlEnv_free_childdir(dir); - XSRETURN_IV(bSuccess); -} - -static -XS(w32_GetTickCount) -{ - dXSARGS; - DWORD msec = GetTickCount(); - EXTEND(SP,1); - if ((IV)msec > 0) - XSRETURN_IV(msec); - XSRETURN_NV(msec); -} - -static -XS(w32_GetShortPathName) -{ - dXSARGS; - SV *shortpath; - DWORD len; - - if (items != 1) - Perl_croak(aTHX_ "usage: Win32::GetShortPathName($longPathName)"); - - shortpath = sv_mortalcopy(ST(0)); - SvUPGRADE(shortpath, SVt_PV); - if (!SvPVX(shortpath) || !SvLEN(shortpath)) - XSRETURN_UNDEF; - - /* src == target is allowed */ - do { - len = GetShortPathName(SvPVX(shortpath), - SvPVX(shortpath), - SvLEN(shortpath)); - } while (len >= SvLEN(shortpath) && sv_grow(shortpath,len+1)); - if (len) { - SvCUR_set(shortpath,len); - *SvEND(shortpath) = '\0'; - ST(0) = shortpath; - XSRETURN(1); - } - XSRETURN_UNDEF; -} - -static -XS(w32_GetFullPathName) -{ - dXSARGS; - SV *filename; - SV *fullpath; - char *filepart; - DWORD len; - STRLEN filename_len; - char *filename_p; - - if (items != 1) - Perl_croak(aTHX_ "usage: Win32::GetFullPathName($filename)"); - - filename = ST(0); - filename_p = SvPV(filename, filename_len); - fullpath = sv_2mortal(newSVpvn(filename_p, filename_len)); - if (!SvPVX(fullpath) || !SvLEN(fullpath)) - XSRETURN_UNDEF; - - do { - len = GetFullPathName(SvPVX(filename), - SvLEN(fullpath), - SvPVX(fullpath), - &filepart); - } while (len >= SvLEN(fullpath) && sv_grow(fullpath,len+1)); - if (len) { - if (GIMME_V == G_ARRAY) { - EXTEND(SP,1); - if (filepart) { - XST_mPV(1,filepart); - len = filepart - SvPVX(fullpath); - } - else { - XST_mPVN(1,"",0); - } - items = 2; - } - SvCUR_set(fullpath,len); - *SvEND(fullpath) = '\0'; - ST(0) = fullpath; - XSRETURN(items); - } - XSRETURN_EMPTY; -} - -static -XS(w32_GetLongPathName) -{ - dXSARGS; - SV *path; - char tmpbuf[MAX_PATH+1]; - char *pathstr; - STRLEN len; - - if (items != 1) - Perl_croak(aTHX_ "usage: Win32::GetLongPathName($pathname)"); - - path = ST(0); - pathstr = SvPV(path,len); - strcpy(tmpbuf, pathstr); - pathstr = win32_longpath(tmpbuf); - if (pathstr) { - ST(0) = sv_2mortal(newSVpvn(pathstr, strlen(pathstr))); - XSRETURN(1); - } - XSRETURN_EMPTY; -} - -static -XS(w32_Sleep) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "usage: Win32::Sleep($milliseconds)"); - Sleep(SvIV(ST(0))); - XSRETURN_YES; -} - -static -XS(w32_CopyFile) -{ - dXSARGS; - BOOL bResult; - char szSourceFile[MAX_PATH+1]; - - if (items != 3) - Perl_croak(aTHX_ "usage: Win32::CopyFile($from, $to, $overwrite)"); - strcpy(szSourceFile, PerlDir_mapA(SvPV_nolen(ST(0)))); - bResult = CopyFileA(szSourceFile, PerlDir_mapA(SvPV_nolen(ST(1))), !SvTRUE(ST(2))); - if (bResult) - XSRETURN_YES; - XSRETURN_NO; -} - void Perl_init_os_extras(void) { dTHX; char *file = __FILE__; - dXSUB_SYS; - - /* these names are Activeware compatible */ - newXS("Win32::GetCwd", w32_GetCwd, file); - newXS("Win32::SetCwd", w32_SetCwd, file); - newXS("Win32::GetNextAvailDrive", w32_GetNextAvailDrive, file); - newXS("Win32::GetLastError", w32_GetLastError, file); - newXS("Win32::SetLastError", w32_SetLastError, file); - newXS("Win32::LoginName", w32_LoginName, file); - newXS("Win32::NodeName", w32_NodeName, file); - newXS("Win32::DomainName", w32_DomainName, file); - newXS("Win32::FsType", w32_FsType, file); - newXS("Win32::GetOSVersion", w32_GetOSVersion, file); - newXS("Win32::IsWinNT", w32_IsWinNT, file); - newXS("Win32::IsWin95", w32_IsWin95, file); - newXS("Win32::FormatMessage", w32_FormatMessage, file); - newXS("Win32::Spawn", w32_Spawn, file); - newXS("Win32::GetTickCount", w32_GetTickCount, file); - newXS("Win32::GetShortPathName", w32_GetShortPathName, file); - newXS("Win32::GetFullPathName", w32_GetFullPathName, file); - newXS("Win32::GetLongPathName", w32_GetLongPathName, file); - newXS("Win32::CopyFile", w32_CopyFile, file); - newXS("Win32::Sleep", w32_Sleep, file); - newXS("Win32::SetChildShowWindow", w32_SetChildShowWindow, file); - /* XXX Bloat Alert! The following Activeware preloads really - * ought to be part of Win32::Sys::*, so they're not included - * here. - */ - /* LookupAccountName - * LookupAccountSID - * InitiateSystemShutdown - * AbortSystemShutdown - * ExpandEnvrironmentStrings - */ + /* Initialize Win32CORE if it has been statically linked. */ + void (*pfn_init)(pTHX); + pfn_init = (void (*)(pTHX))GetProcAddress((HMODULE)w32_perldll_handle, "init_Win32CORE"); + if (pfn_init) + pfn_init(aTHX); + + newXS("Win32::SetChildShowWindow", w32_SetChildShowWindow, file); } void * @@ -4874,14 +4223,108 @@ win32_ctrlhandler(DWORD dwCtrlType) } -#if _MSC_VER >= 1400 +#ifdef SET_INVALID_PARAMETER_HANDLER # include #endif +static void +ansify_path(void) +{ + size_t len; + char *ansi_path; + WCHAR *wide_path; + WCHAR *wide_dir; + + /* fetch Unicode version of PATH */ + len = 2000; + wide_path = win32_malloc(len*sizeof(WCHAR)); + while (wide_path) { + size_t newlen = GetEnvironmentVariableW(L"PATH", wide_path, len); + if (newlen < len) + break; + len = newlen; + wide_path = win32_realloc(wide_path, len*sizeof(WCHAR)); + } + if (!wide_path) + return; + + /* convert to ANSI pathnames */ + wide_dir = wide_path; + ansi_path = NULL; + while (wide_dir) { + WCHAR *sep = wcschr(wide_dir, ';'); + char *ansi_dir; + size_t ansi_len; + size_t wide_len; + + if (sep) + *sep++ = '\0'; + + /* remove quotes around pathname */ + if (*wide_dir == '"') + ++wide_dir; + wide_len = wcslen(wide_dir); + if (wide_len && wide_dir[wide_len-1] == '"') + wide_dir[wide_len-1] = '\0'; + + /* append ansi_dir to ansi_path */ + ansi_dir = win32_ansipath(wide_dir); + ansi_len = strlen(ansi_dir); + if (ansi_path) { + size_t newlen = len + 1 + ansi_len; + ansi_path = win32_realloc(ansi_path, newlen+1); + if (!ansi_path) + break; + ansi_path[len] = ';'; + memcpy(ansi_path+len+1, ansi_dir, ansi_len+1); + len = newlen; + } + else { + len = ansi_len; + ansi_path = win32_malloc(5+len+1); + if (!ansi_path) + break; + memcpy(ansi_path, "PATH=", 5); + memcpy(ansi_path+5, ansi_dir, len+1); + len += 5; + } + win32_free(ansi_dir); + wide_dir = sep; + } + + if (ansi_path) { + /* Update C RTL environ array. This will only have full effect if + * perl_parse() is later called with `environ` as the `env` argument. + * Otherwise S_init_postdump_symbols() will overwrite PATH again. + * + * We do have to ansify() the PATH before Perl has been fully + * initialized because S_find_script() uses the PATH when perl + * is being invoked with the -S option. This happens before %ENV + * is initialized in S_init_postdump_symbols(). + * + * XXX Is this a bug? Should S_find_script() use the environment + * XXX passed in the `env` arg to parse_perl()? + */ + putenv(ansi_path); + /* Keep system environment in sync because S_init_postdump_symbols() + * will not call mg_set() if it initializes %ENV from `environ`. + */ + SetEnvironmentVariableA("PATH", ansi_path+5); + /* We are intentionally leaking the ansi_path string here because + * the some runtime libraries puts it directly into the environ + * array. The Microsoft runtime library seems to make a copy, + * but will leak the copy should it be replaced again later. + * Since this code is only called once during PERL_SYS_INIT this + * shouldn't really matter. + */ + } + win32_free(wide_path); +} + void Perl_win32_init(int *argcp, char ***argvp) { -#if _MSC_VER >= 1400 +#ifdef SET_INVALID_PARAMETER_HANDLER _invalid_parameter_handler oldHandler, newHandler; newHandler = my_invalid_parameter_handler; oldHandler = _set_invalid_parameter_handler(newHandler); @@ -4889,14 +4332,29 @@ Perl_win32_init(int *argcp, char ***argvp) #endif /* Disable floating point errors, Perl will trap the ones we * care about. VC++ RTL defaults to switching these off - * already, but the Borland RTL doesn't. Since we don't + * already, but some RTLs don't. Since we don't * want to be at the vendor's whim on the default, we set * it explicitly here. */ -#if !defined(_ALPHA_) && !defined(__GNUC__) +#if !defined(__GNUC__) _control87(MCW_EM, MCW_EM); #endif MALLOC_INIT; + + /* When the manifest resource requests Common-Controls v6 then + * user32.dll no longer registers all the Windows classes used for + * standard controls but leaves some of them to be registered by + * comctl32.dll. InitCommonControls() doesn't do anything but calling + * it makes sure comctl32.dll gets loaded into the process and registers + * the standard control classes. Without this even normal Windows APIs + * like MessageBox() can fail under some versions of Windows XP. + */ + InitCommonControls(); + + g_osver.dwOSVersionInfoSize = sizeof(g_osver); + GetVersionEx(&g_osver); + + ansify_path(); } void @@ -4923,7 +4381,16 @@ win32_signal(int sig, Sighandler_t subcode) dTHX; if (sig < SIG_SIZE) { int save_errno = errno; - Sighandler_t result = signal(sig, subcode); + Sighandler_t result; +#ifdef SET_INVALID_PARAMETER_HANDLER + /* Silence our invalid parameter handler since we expect to make some + * calls with invalid signal numbers giving a SIG_ERR result. */ + BOOL oldvalue = set_silent_invalid_parameter_handler(TRUE); +#endif + result = signal(sig, subcode); +#ifdef SET_INVALID_PARAMETER_HANDLER + set_silent_invalid_parameter_handler(oldvalue); +#endif if (result == SIG_ERR) { result = w32_sighandler[sig]; errno = save_errno; @@ -4937,9 +4404,113 @@ win32_signal(int sig, Sighandler_t subcode) } } +/* The PerlMessageWindowClass's WindowProc */ +LRESULT CALLBACK +win32_message_window_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + return win32_process_message(hwnd, msg, wParam, lParam) ? + 0 : DefWindowProc(hwnd, msg, wParam, lParam); +} -#ifdef HAVE_INTERP_INTERN +/* The real message handler. Can be called with + * hwnd == NULL to process our thread messages. Returns TRUE for any messages + * that it processes */ +static LRESULT +win32_process_message(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + /* BEWARE. The context retrieved using dTHX; is the context of the + * 'parent' thread during the CreateWindow() phase - i.e. for all messages + * up to and including WM_CREATE. If it ever happens that you need the + * 'child' context before this, then it needs to be passed into + * win32_create_message_window(), and passed to the WM_NCCREATE handler + * from the lparam of CreateWindow(). It could then be stored/retrieved + * using [GS]etWindowLongPtr(... GWLP_USERDATA ...), possibly eliminating + * the dTHX calls here. */ + /* XXX For now it is assumed that the overhead of the dTHX; for what + * are relativley infrequent code-paths, is better than the added + * complexity of getting the correct context passed into + * win32_create_message_window() */ + switch(msg) { + +#ifdef USE_ITHREADS + case WM_USER_MESSAGE: { + long child = find_pseudo_pid((int)wParam); + if (child >= 0) { + dTHX; + w32_pseudo_child_message_hwnds[child] = (HWND)lParam; + return 1; + } + break; + } +#endif + + case WM_USER_KILL: { + dTHX; + /* We use WM_USER_KILL to fake kill() with other signals */ + int sig = (int)wParam; + if (do_raise(aTHX_ sig)) + sig_terminate(aTHX_ sig); + + return 1; + } + + case WM_TIMER: { + dTHX; + /* alarm() is a one-shot but SetTimer() repeats so kill it */ + if (w32_timerid && w32_timerid==(UINT)wParam) { + KillTimer(w32_message_hwnd, w32_timerid); + w32_timerid=0; + + /* Now fake a call to signal handler */ + if (do_raise(aTHX_ 14)) + sig_terminate(aTHX_ 14); + + return 1; + } + break; + } + + default: + break; + + } /* switch */ + + /* Above or other stuff may have set a signal flag, and we may not have + * been called from win32_async_check() (e.g. some other GUI's message + * loop. BUT DON'T dispatch signals here: If someone has set a SIGALRM + * handler that die's, and the message loop that calls here is wrapped + * in an eval, then you may well end up with orphaned windows - signals + * are dispatched by win32_async_check() */ + + return 0; +} + +void +win32_create_message_window_class(void) +{ + /* create the window class for "message only" windows */ + WNDCLASS wc; + + Zero(&wc, 1, wc); + wc.lpfnWndProc = win32_message_window_proc; + wc.hInstance = (HINSTANCE)GetModuleHandle(NULL); + wc.lpszClassName = "PerlMessageWindowClass"; + + /* second and subsequent calls will fail, but class + * will already be registered */ + RegisterClass(&wc); +} + +HWND +win32_create_message_window(void) +{ + win32_create_message_window_class(); + return CreateWindow("PerlMessageWindowClass", "PerlMessageWindow", + 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL); +} + +#ifdef HAVE_INTERP_INTERN static void win32_csighandler(int sig) @@ -4951,23 +4522,6 @@ win32_csighandler(int sig) /* Does nothing */ } -HWND -win32_create_message_window() -{ - /* "message-only" windows have been implemented in Windows 2000 and later. - * On earlier versions we'll continue to post messages to a specific - * thread and use hwnd==NULL. This is brittle when either an embedding - * application or an XS module is also posting messages to hwnd=NULL - * because once removed from the queue they cannot be delivered to the - * "right" place with DispatchMessage() anymore, as there is no WindowProc - * if there is no window handle. - */ - if (g_osver.dwMajorVersion < 5) - return NULL; - - return CreateWindow("Static", "", 0, 0, 0, 0, 0, HWND_MESSAGE, 0, 0, NULL); -} - #if defined(__MINGW32__) && defined(__cplusplus) #define CAST_HWND__(x) (HWND__*)(x) #else @@ -4979,12 +4533,7 @@ Perl_sys_intern_init(pTHX) { int i; - if (g_osver.dwOSVersionInfoSize == 0) { - g_osver.dwOSVersionInfoSize = sizeof(g_osver); - GetVersionEx(&g_osver); - } - - w32_perlshell_tokens = Nullch; + w32_perlshell_tokens = NULL; w32_perlshell_vec = (char**)NULL; w32_perlshell_items = 0; w32_fdpid = newAV(); @@ -5001,7 +4550,7 @@ Perl_sys_intern_init(pTHX) for (i=0; i < SIG_SIZE; i++) { w32_sighandler[i] = SIG_DFL; } -# ifdef MULTIPLICTY +# ifdef MULTIPLICITY if (my_perl == PL_curinterp) { # else { @@ -5009,6 +4558,15 @@ Perl_sys_intern_init(pTHX) /* Force C runtime signal stuff to set its console handler */ signal(SIGINT,win32_csighandler); signal(SIGBREAK,win32_csighandler); + + /* We spawn asynchronous processes with the CREATE_NEW_PROCESS_GROUP + * flag. This has the side-effect of disabling Ctrl-C events in all + * processes in this group. + * We re-enable Ctrl-C handling by calling SetConsoleCtrlHandler() + * with a NULL handler. + */ + SetConsoleCtrlHandler(NULL,FALSE); + /* Push our handler on top */ SetConsoleCtrlHandler(win32_ctrlhandler,TRUE); } @@ -5044,7 +4602,9 @@ Perl_sys_intern_clear(pTHX) void Perl_sys_intern_dup(pTHX_ struct interp_intern *src, struct interp_intern *dst) { - dst->perlshell_tokens = Nullch; + PERL_ARGS_ASSERT_SYS_INTERN_DUP; + + dst->perlshell_tokens = NULL; dst->perlshell_vec = (char**)NULL; dst->perlshell_items = 0; dst->fdpid = newAV(); @@ -5058,32 +4618,3 @@ Perl_sys_intern_dup(pTHX_ struct interp_intern *src, struct interp_intern *dst) } # endif /* USE_ITHREADS */ #endif /* HAVE_INTERP_INTERN */ - -static void -win32_free_argvw(pTHX_ void *ptr) -{ - char** argv = (char**)ptr; - while(*argv) { - Safefree(*argv); - *argv++ = Nullch; - } -} - -void -win32_argv2utf8(int argc, char** argv) -{ - dTHX; - char* psz; - int length, wargc; - LPWSTR* lpwStr = CommandLineToArgvW(GetCommandLineW(), &wargc); - if (lpwStr && argc) { - while (argc--) { - length = WideCharToMultiByte(CP_UTF8, 0, lpwStr[--wargc], -1, NULL, 0, NULL, NULL); - Newxz(psz, length, char); - WideCharToMultiByte(CP_UTF8, 0, lpwStr[wargc], -1, psz, length, NULL, NULL); - argv[argc] = psz; - } - call_atexit(win32_free_argvw, argv); - } - GlobalFree((HGLOBAL)lpwStr); -}