X-Git-Url: https://perl5.git.perl.org/perl5.git/blobdiff_plain/b2d41e21b21c57718b8e506bc104adc20f931613..9f1eb87f94b3f331150512092da4adda55d38443:/win32/win32.c diff --git a/win32/win32.c b/win32/win32.c index 2e5db2c..3d1f460 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 @@ -15,16 +15,42 @@ #define Win32_Winsock #endif #include -/* GCC-2.95.2/Mingw32-1.1 forgot the WINAPI on CommandLineToArgvW() */ -#if defined(__MINGW32__) && (__MINGW32_MAJOR_VERSION==1) -# include -#else - LPWSTR* WINAPI CommandLineToArgvW(LPCWSTR lpCommandLine, int * pNumArgs); +#ifndef HWND_MESSAGE +# define HWND_MESSAGE ((HWND)-3) +#endif +#ifndef WC_NO_BEST_FIT_CHARS +# define WC_NO_BEST_FIT_CHARS 0x00000400 /* requires Windows 2000 or later */ #endif #include +#include +#include #include #include +#define SystemProcessesAndThreadsInformation 5 + +/* Inline some definitions from the DDK */ +typedef struct { + USHORT Length; + USHORT MaximumLength; + PWSTR Buffer; +} UNICODE_STRING; + +typedef struct { + ULONG NextEntryDelta; + ULONG ThreadCount; + ULONG Reserved1[6]; + LARGE_INTEGER CreateTime; + LARGE_INTEGER UserTime; + LARGE_INTEGER KernelTime; + UNICODE_STRING ProcessName; + LONG BasePriority; + ULONG ProcessId; + ULONG InheritedFromProcessId; + /* Remainder of the structure depends on the Windows version, + * but we don't need those additional fields anyways... */ +} SYSTEM_PROCESSES; + /* #include "config.h" */ #if !defined(PERLIO_IS_STDIO) && !defined(USE_SFIO) @@ -39,7 +65,6 @@ #define PERL_NO_GET_CONTEXT #include "XSUB.h" -#include "Win32iop.h" #include #ifndef __GNUC__ /* assert.h conflicts with #define of assert in perl.h */ @@ -63,11 +88,13 @@ int _CRT_glob = 0; #if defined(__MINGW32__) && (__MINGW32_MAJOR_VERSION==1) /* Mingw32-1.1 is missing some prototypes */ +START_EXTERN_C FILE * _wfopen(LPCWSTR wszFileName, LPCWSTR wszMode); FILE * _wfdopen(int nFd, LPCWSTR wszMode); FILE * _freopen(LPCWSTR wszFileName, LPCWSTR wszMode, FILE * pOldStream); int _flushall(); int _fcloseall(); +END_EXTERN_C #endif #if defined(__BORLANDC__) @@ -92,16 +119,19 @@ int _fcloseall(); static void get_shell(void); static long tokenize(const char *str, char **dest, char ***destv); -static int do_spawn2(pTHX_ char *cmd, int exectype); -static BOOL has_shell_metachars(char *ptr); +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 char * get_emd_part(SV **leading, STRLEN *const len, + 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); + const char *libname, STRLEN *const len); +static LRESULT win32_process_message(HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam); #ifdef USE_ITHREADS static void remove_dead_pseudo_process(long child); @@ -113,36 +143,128 @@ HANDLE w32_perldll_handle = INVALID_HANDLE_VALUE; char w32_module_name[MAX_PATH+1]; END_EXTERN_C -static DWORD w32_platform = (DWORD)-1; +static OSVERSIONINFO g_osver = {0, 0, 0, 0, 0, ""}; + +static HANDLE (WINAPI *pfnCreateToolhelp32Snapshot)(DWORD, DWORD) = NULL; +static BOOL (WINAPI *pfnProcess32First)(HANDLE, PROCESSENTRY32*) = NULL; +static BOOL (WINAPI *pfnProcess32Next)(HANDLE, PROCESSENTRY32*) = NULL; +static LONG (WINAPI *pfnZwQuerySystemInformation)(UINT, PVOID, ULONG, PULONG); + +#ifdef __BORLANDC__ +/* Silence STDERR grumblings from Borland's math library. */ +DllExport int +_matherr(struct _exception *a) +{ + PERL_UNUSED_VAR(a); + return 1; +} +#endif + +/* 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 -#define ONE_K_BUFSIZE 1024 +#ifdef SET_INVALID_PARAMETER_HANDLER +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); +# endif +} +#endif int IsWin95(void) { - return (win32_os_id() == VER_PLATFORM_WIN32_WINDOWS); + return (g_osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS); } int IsWinNT(void) { - return (win32_os_id() == VER_PLATFORM_WIN32_NT); + return (g_osver.dwPlatformId == VER_PLATFORM_WIN32_NT); +} + +int +IsWin2000(void) +{ + return (g_osver.dwMajorVersion > 4); } 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"); + + GetModuleFileNameW(module, modulename, sizeof(modulename)/sizeof(WCHAR)); + + /* 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)); - /* 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);*/ + /* 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; @@ -161,7 +283,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); @@ -199,7 +321,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; @@ -253,21 +375,24 @@ get_emd_part(SV **prev_pathp, char *trailing_path, ...) dTHX; if (!*prev_pathp) *prev_pathp = sv_2mortal(newSVpvn("",0)); - sv_catpvn(*prev_pathp, ";", 1); + 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); @@ -275,17 +400,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); @@ -294,7 +420,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); @@ -302,25 +428,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 @@ -328,13 +455,13 @@ 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 -has_shell_metachars(char *ptr) +has_shell_metachars(const char *ptr) { int inquote = 0; char quote = '\0'; @@ -379,7 +506,7 @@ has_shell_metachars(char *ptr) * the library functions will get the correct environment */ PerlIO * -Perl_my_popen(pTHX_ char *cmd, char *mode) +Perl_my_popen(pTHX_ const char *cmd, const char *mode) { #ifdef FIXCMD #define fixcmd(x) { \ @@ -411,15 +538,7 @@ Perl_my_pclose(pTHX_ PerlIO *fp) DllExport unsigned long win32_os_id(void) { - static OSVERSIONINFO osver; - - if (osver.dwPlatformId != w32_platform) { - memset(&osver, 0, sizeof(OSVERSIONINFO)); - osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - GetVersionEx(&osver); - w32_platform = osver.dwPlatformId; - } - return (unsigned long)w32_platform; + return (unsigned long)g_osver.dwPlatformId; } DllExport int @@ -449,7 +568,7 @@ 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) { @@ -457,8 +576,8 @@ tokenize(const char *str, char **dest, char ***destv) int slen = strlen(str); register char *ret; register char **retv; - New(1307, ret, slen+2, char); - New(1308, retv, (slen+3)/2, char*); + Newx(ret, slen+2, char); + Newx(retv, (slen+3)/2, char*); retstart = ret; retvstart = retv; @@ -484,7 +603,7 @@ tokenize(const char *str, char **dest, char ***destv) ++items; ret++; } - retvstart[items] = Nullch; + retvstart[items] = NULL; *ret++ = '\0'; *ret = '\0'; } @@ -523,11 +642,13 @@ 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; get_shell(); - New(1306, argv, (sp - mark) + w32_perlshell_items + 2, char*); + Newx(argv, (sp - mark) + w32_perlshell_items + 2, char*); if (SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) { ++mark; @@ -561,8 +682,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) { @@ -603,7 +723,7 @@ find_next_space(const char *s) } static int -do_spawn2(pTHX_ char *cmd, int exectype) +do_spawn2(pTHX_ const char *cmd, int exectype) { char **a; char *s; @@ -615,8 +735,8 @@ do_spawn2(pTHX_ char *cmd, int exectype) /* Save an extra exec if possible. See if there are shell * metacharacters in it */ if (!has_shell_metachars(cmd)) { - New(1301,argv, strlen(cmd) / 2 + 2, char*); - New(1302,cmd2, strlen(cmd) + 1, char); + Newx(argv, strlen(cmd) / 2 + 2, char*); + Newx(cmd2, strlen(cmd) + 1, char); strcpy(cmd2, cmd); a = argv; for (s = cmd2; *s;) { @@ -628,7 +748,7 @@ do_spawn2(pTHX_ char *cmd, int exectype) if (*s) *s++ = '\0'; } - *a = Nullch; + *a = NULL; if (argv[0]) { switch (exectype) { case EXECF_SPAWN: @@ -653,11 +773,11 @@ do_spawn2(pTHX_ char *cmd, int exectype) char **argv; int i = -1; get_shell(); - New(1306, argv, w32_perlshell_items + 2, char*); + Newx(argv, w32_perlshell_items + 2, char*); while (++i < w32_perlshell_items) argv[i] = w32_perlshell_vec[i]; - argv[i++] = cmd; - argv[i] = Nullch; + argv[i++] = (char *)cmd; + argv[i] = NULL; switch (exectype) { case EXECF_SPAWN: status = win32_spawnvp(P_WAIT, argv[0], @@ -675,8 +795,7 @@ do_spawn2(pTHX_ 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) { @@ -696,18 +815,24 @@ do_spawn2(pTHX_ 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_ char *cmd) +Perl_do_exec(pTHX_ const char *cmd) { + PERL_ARGS_ASSERT_DO_EXEC; + do_spawn2(aTHX_ cmd, EXECF_EXEC); return FALSE; } @@ -717,31 +842,41 @@ Perl_do_exec(pTHX_ char *cmd) * return the pointer to the current file name. */ DllExport DIR * -win32_opendir(char *filename) +win32_opendir(const char *filename) { dTHX; DIR *dirp; long len; long idx; char scanname[MAX_PATH+3]; - Stat_t sbuf; WIN32_FIND_DATAA aFindData; WIN32_FIND_DATAW wFindData; - HANDLE fh; + bool using_wide; char buffer[MAX_PATH*2]; - WCHAR wbuffer[MAX_PATH+1]; - char* ptr; + char *ptr; 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; + } + +#if 0 /* This call to stat is unnecessary. The FindFirstFile() below will + * fail with ERROR_PATH_NOT_FOUND if filename is not a directory. */ + { + /* check to see if filename is a directory */ + Stat_t sbuf; + if (win32_stat(filename, &sbuf) < 0 || !S_ISDIR(sbuf.st_mode)) + return NULL; + } +#endif /* Get us a DIR structure */ - Newz(1303, dirp, 1, DIR); + Newxz(dirp, 1, DIR); /* Create the search pattern */ strcpy(scanname, filename); @@ -758,15 +893,16 @@ win32_opendir(char *filename) scanname[len] = '\0'; /* do the FindFirstFile call */ - if (USING_WIDE()) { - A2WHELPER(scanname, wbuffer, sizeof(wbuffer)); - fh = FindFirstFileW(PerlDir_mapW(wbuffer), &wFindData); + if (IsWin2000()) { + WCHAR wscanname[sizeof(scanname)]; + MultiByteToWideChar(CP_ACP, 0, scanname, -1, wscanname, sizeof(wscanname)/sizeof(WCHAR)); + dirp->handle = FindFirstFileW(PerlDir_mapW(wscanname), &wFindData); + using_wide = TRUE; } else { - fh = FindFirstFileA(PerlDir_mapA(scanname), &aFindData); + dirp->handle = FindFirstFileA(PerlDir_mapA(scanname), &aFindData); } - dirp->handle = fh; - if (fh == INVALID_HANDLE_VALUE) { + if (dirp->handle == INVALID_HANDLE_VALUE) { DWORD err = GetLastError(); /* FindFirstFile() fails on empty drives! */ switch (err) { @@ -787,22 +923,30 @@ win32_opendir(char *filename) return NULL; } - /* now allocate the first part of the string table for - * the filenames that we find. - */ - if (USING_WIDE()) { - W2AHELPER(wFindData.cFileName, buffer, sizeof(buffer)); - ptr = buffer; + if (using_wide) { + 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); + } + ptr = buffer; } else { - ptr = aFindData.cFileName; + ptr = aFindData.cFileName; } + /* now allocate the first part of the string table for + * the filenames that we find. + */ idx = strlen(ptr)+1; if (idx < 256) - dirp->size = 128; + dirp->size = 256; else dirp->size = idx; - New(1304, dirp->start, dirp->size, char); + Newx(dirp->start, dirp->size, char); strcpy(dirp->start, ptr); dirp->nfiles++; dirp->end = dirp->curr = dirp->start; @@ -832,27 +976,37 @@ win32_readdir(DIR *dirp) dirp->curr += len + 1; if (dirp->curr >= dirp->end) { dTHX; - char* ptr; - BOOL res; - WIN32_FIND_DATAW wFindData; - WIN32_FIND_DATAA aFindData; - char buffer[MAX_PATH*2]; + BOOL res; + WIN32_FIND_DATAA aFindData; + char buffer[MAX_PATH*2]; + char *ptr; + 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!). */ - if (USING_WIDE()) { + else if (IsWin2000()) { + WIN32_FIND_DATAW wFindData; res = FindNextFileW(dirp->handle, &wFindData); if (res) { - W2AHELPER(wFindData.cFileName, buffer, sizeof(buffer)); - ptr = buffer; - } - } - else { - res = FindNextFileA(dirp->handle, &aFindData); - if (res) - ptr = aFindData.cFileName; - } + 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); + } + ptr = buffer; + } + } + else { + res = FindNextFileA(dirp->handle, &aFindData); + ptr = aFindData.cFileName; + } if (res) { long endpos = dirp->end - dirp->start; long newsize = endpos + strlen(ptr) + 1; @@ -868,8 +1022,13 @@ win32_readdir(DIR *dirp) 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); } @@ -881,7 +1040,7 @@ win32_readdir(DIR *dirp) DllExport long win32_telldir(DIR *dirp) { - return (dirp->curr - dirp->start); + return dirp->curr ? (dirp->curr - dirp->start) : -1; } @@ -891,7 +1050,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 */ @@ -913,6 +1072,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 @@ -1056,28 +1259,167 @@ remove_dead_pseudo_process(long child) (w32_num_pseudo_children-child-1), HANDLE); Move(&w32_pseudo_child_pids[child+1], &w32_pseudo_child_pids[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); 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; +} + +/* Traverse process tree using ToolHelp functions */ +static int +kill_process_tree_toolhelp(DWORD 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 = pfnCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (snapshot_handle != INVALID_HANDLE_VALUE) { + PROCESSENTRY32 entry; + + entry.dwSize = sizeof(entry); + if (pfnProcess32First(snapshot_handle, &entry)) { + do { + if (entry.th32ParentProcessID == pid) + killed += kill_process_tree_toolhelp(entry.th32ProcessID, sig); + entry.dwSize = sizeof(entry); + } + while (pfnProcess32Next(snapshot_handle, &entry)); + } + CloseHandle(snapshot_handle); + } + CloseHandle(process_handle); + return killed; +} + +/* Traverse process tree using undocumented system information structures. + * This is only necessary on Windows NT, which lacks the ToolHelp functions. + */ +static int +kill_process_tree_sysinfo(SYSTEM_PROCESSES *process_info, DWORD pid, int sig) +{ + HANDLE process_handle; + SYSTEM_PROCESSES *p = process_info; + int killed = 0; + + process_handle = OpenProcess(PROCESS_TERMINATE, FALSE, pid); + if (process_handle == NULL) + return 0; + + killed += terminate_process(pid, process_handle, sig); + + while (1) { + if (p->InheritedFromProcessId == (DWORD)pid) + killed += kill_process_tree_sysinfo(process_info, p->ProcessId, sig); + + if (p->NextEntryDelta == 0) + break; + + p = (SYSTEM_PROCESSES*)((char*)p + p->NextEntryDelta); + } + + CloseHandle(process_handle); + return killed; +} + +int +killpg(int pid, int sig) +{ + /* Use "documented" method whenever available */ + if (pfnCreateToolhelp32Snapshot && pfnProcess32First && pfnProcess32Next) { + return kill_process_tree_toolhelp((DWORD)pid, sig); + } + + /* Fall back to undocumented Windows internals on Windows NT */ + if (pfnZwQuerySystemInformation) { + dTHX; + char *buffer; + DWORD size = 0; + + pfnZwQuerySystemInformation(SystemProcessesAndThreadsInformation, NULL, 0, &size); + Newx(buffer, size, char); + + if (pfnZwQuerySystemInformation(SystemProcessesAndThreadsInformation, buffer, size, NULL) >= 0) { + int killed = kill_process_tree_sysinfo((SYSTEM_PROCESSES*)buffer, (DWORD)pid, sig); + Safefree(buffer); + return killed; + } + } + return 0; +} + +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; +} + 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) { - hProcess = w32_pseudo_child_handles[child]; + HWND hwnd = w32_pseudo_child_message_hwnds[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)) { @@ -1085,16 +1427,31 @@ win32_kill(int pid, int sig) return 0; } break; - default: - /* We fake signals to pseudo-processes using Win32 - * message queue. In Win9X the pids are negative already. */ - if (PostThreadMessage(IsWin95() ? pid : -pid,WM_USER,sig,0)) { - /* It might be us ... */ - PERL_ASYNC_CHECK(); - return 0; - } + + 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); + hwnd = w32_pseudo_child_message_hwnds[child]; + ++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; @@ -1106,58 +1463,19 @@ 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((IsWin95() ? -pid : pid), sig)) return 0; } } @@ -1172,25 +1490,36 @@ win32_stat(const char *path, Stat_t *sbuf) char buffer[MAX_PATH+1]; int l = strlen(path); int res; - WCHAR wbuffer[MAX_PATH+1]; - WCHAR* pwbuffer; - HANDLE handle; int nlink = 1; + BOOL expect_dir = FALSE; + + GV *gv_sloppy = gv_fetchpvs("\027IN32_SLOPPY_STAT", + GV_NOTQUAL, SVt_PV); + BOOL sloppy = gv_sloppy && SvTRUE(GvSV(gv_sloppy)); if (l > 1) { switch(path[l - 1]) { /* FindFirstFile() and stat() are buggy with a trailing - * backslash, so change it to a forward slash :-( */ + * slashes, except for the root directory of a drive */ case '\\': - if (l >= sizeof(buffer)) { + case '/': + if (l > sizeof(buffer)) { errno = ENAMETOOLONG; return -1; } - strncpy(buffer, path, l-1); - buffer[l - 1] = '/'; - buffer[l] = '\0'; - path = buffer; + --l; + strncpy(buffer, path, l); + /* remove additional trailing slashes */ + while (l > 1 && (buffer[l-1] == '/' || buffer[l-1] == '\\')) + --l; + /* add back slash if we otherwise end up with just a drive letter */ + if (l == 2 && isALPHA(buffer[0]) && buffer[1] == ':') + buffer[l++] = '\\'; + buffer[l] = '\0'; + path = buffer; + expect_dir = TRUE; break; + /* FindFirstFile() is buggy with "x:", so add a dot :-( */ case ':': if (l == 2 && isALPHA(path[0])) { @@ -1205,41 +1534,28 @@ win32_stat(const char *path, Stat_t *sbuf) } } - /* We *must* open & close the file once; otherwise file attribute changes */ - /* might not yet have propagated to "other" hard links of the same file. */ - /* This also gives us an opportunity to determine the number of links. */ - if (USING_WIDE()) { - A2WHELPER(path, wbuffer, sizeof(wbuffer)); - pwbuffer = PerlDir_mapW(wbuffer); - handle = CreateFileW(pwbuffer, 0, 0, NULL, OPEN_EXISTING, 0, NULL); - } - else { - path = PerlDir_mapA(path); - l = strlen(path); - handle = CreateFileA(path, 0, 0, NULL, OPEN_EXISTING, 0, NULL); - } - if (handle != INVALID_HANDLE_VALUE) { - BY_HANDLE_FILE_INFORMATION bhi; - if (GetFileInformationByHandle(handle, &bhi)) - nlink = bhi.nNumberOfLinks; - CloseHandle(handle); - } + path = PerlDir_mapA(path); + l = strlen(path); - /* pwbuffer or path will be mapped correctly above */ - if (USING_WIDE()) { -#if defined(WIN64) || defined(USE_LARGE_FILES) - res = _wstati64(pwbuffer, sbuf); -#else - res = _wstat(pwbuffer, (struct _stat*)sbuf); -#endif + if (!sloppy) { + /* We must open & close the file once; otherwise file attribute changes */ + /* might not yet have propagated to "other" hard links of the same file. */ + /* This also gives us an opportunity to determine the number of links. */ + HANDLE handle = CreateFileA(path, 0, 0, NULL, OPEN_EXISTING, 0, NULL); + if (handle != INVALID_HANDLE_VALUE) { + BY_HANDLE_FILE_INFORMATION bhi; + if (GetFileInformationByHandle(handle, &bhi)) + nlink = bhi.nNumberOfLinks; + CloseHandle(handle); + } } - else { + + /* path will be mapped correctly above */ #if defined(WIN64) || defined(USE_LARGE_FILES) - res = _stati64(path, sbuf); + res = _stati64(path, sbuf); #else - res = stat(path, sbuf); + res = stat(path, sbuf); #endif - } sbuf->st_nlink = nlink; if (res < 0) { @@ -1247,13 +1563,7 @@ win32_stat(const char *path, Stat_t *sbuf) * XXX using GetFileAttributesEx() will enable us to set * sbuf->st_*time (but note that's not available on the * Windows of 1995) */ - DWORD r; - if (USING_WIDE()) { - r = GetFileAttributesW(pwbuffer); - } - else { - r = GetFileAttributesA(path); - } + DWORD r = GetFileAttributesA(path); if (r != 0xffffffff && (r & FILE_ATTRIBUTE_DIRECTORY)) { /* sbuf may still contain old garbage since stat() failed */ Zero(sbuf, 1, Stat_t); @@ -1269,16 +1579,31 @@ win32_stat(const char *path, Stat_t *sbuf) && (path[2] == '\\' || path[2] == '/')) { /* The drive can be inaccessible, some _stat()s are buggy */ - if (USING_WIDE() - ? !GetVolumeInformationW(pwbuffer,NULL,0,NULL,NULL,NULL,NULL,0) - : !GetVolumeInformationA(path,NULL,0,NULL,NULL,NULL,NULL,0)) { + if (!GetVolumeInformationA(path,NULL,0,NULL,NULL,NULL,NULL,0)) { errno = ENOENT; return -1; } } + if (expect_dir && !S_ISDIR(sbuf->st_mode)) { + errno = ENOTDIR; + return -1; + } + if (S_ISDIR(sbuf->st_mode)) { + /* Ensure the "write" bit is switched off in the mode for + * directories with the read-only attribute set. Borland (at least) + * switches 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; + } + } #ifdef __BORLANDC__ - if (S_ISDIR(sbuf->st_mode)) - sbuf->st_mode |= S_IWRITE | S_IEXEC; + if (S_ISDIR(sbuf->st_mode)) { + sbuf->st_mode |= S_IEXEC; + } else if (S_ISREG(sbuf->st_mode)) { int perms; if (l >= 4 && path[l-4] == '.') { @@ -1326,7 +1651,7 @@ win32_longpath(char *path) char *start = path; char sep; if (!path) - return Nullch; + return NULL; /* drive prefix */ if (isALPHA(path[0]) && path[1] == ':') { @@ -1390,56 +1715,101 @@ 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); +} + +/* 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_getenv(const char *name) { dTHX; - WCHAR wBuffer[MAX_PATH+1]; DWORD needlen; - SV *curitem = Nullsv; + SV *curitem = NULL; - if (USING_WIDE()) { - A2WHELPER(name, wBuffer, sizeof(wBuffer)); - needlen = GetEnvironmentVariableW(wBuffer, NULL, 0); - } - else - needlen = GetEnvironmentVariableA(name,NULL,0); + needlen = GetEnvironmentVariableA(name,NULL,0); if (needlen != 0) { curitem = sv_2mortal(newSVpvn("", 0)); - if (USING_WIDE()) { - SV *acuritem; - do { - SvGROW(curitem, (needlen+1)*sizeof(WCHAR)); - needlen = GetEnvironmentVariableW(wBuffer, - (WCHAR*)SvPVX(curitem), - needlen); - } while (needlen >= SvLEN(curitem)/sizeof(WCHAR)); - SvCUR_set(curitem, (needlen*sizeof(WCHAR))+1); - acuritem = sv_2mortal(newSVsv(curitem)); - W2AHELPER((WCHAR*)SvPVX(acuritem), SvPVX(curitem), SvCUR(curitem)); - } - else { - do { - SvGROW(curitem, needlen+1); - needlen = GetEnvironmentVariableA(name,SvPVX(curitem), - needlen); - } while (needlen >= SvLEN(curitem)); - SvCUR_set(curitem, needlen); - } + do { + SvGROW(curitem, needlen+1); + needlen = GetEnvironmentVariableA(name,SvPVX(curitem), + needlen); + } while (needlen >= SvLEN(curitem)); + SvCUR_set(curitem, needlen); } else { /* allow any environment variables that begin with 'PERL' @@ -1450,7 +1820,7 @@ win32_getenv(const char *name) if (curitem && SvCUR(curitem)) return SvPVX(curitem); - return Nullch; + return NULL; } DllExport int @@ -1459,48 +1829,34 @@ win32_putenv(const char *name) dTHX; char* curitem; char* val; - WCHAR* wCuritem; - WCHAR* wVal; - int length, relval = -1; + int relval = -1; if (name) { - if (USING_WIDE()) { - length = strlen(name)+1; - New(1309,wCuritem,length,WCHAR); - A2WHELPER(name, wCuritem, length*sizeof(WCHAR)); - wVal = wcschr(wCuritem, '='); - if (wVal) { - *wVal++ = '\0'; - if (SetEnvironmentVariableW(wCuritem, *wVal ? wVal : NULL)) - relval = 0; - } - Safefree(wCuritem); - } - else { - New(1309,curitem,strlen(name)+1,char); - strcpy(curitem, name); - val = strchr(curitem, '='); - if (val) { - /* The sane way to deal with the environment. - * 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. - * * Much faster. - * Why you may want to enable 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 - * not see changes made by extensions that call the Win32 - * functions directly, either. - * GSAR 97-06-07 - */ - *val++ = '\0'; - if (SetEnvironmentVariableA(curitem, *val ? val : NULL)) - relval = 0; - } - Safefree(curitem); - } + Newx(curitem,strlen(name)+1,char); + strcpy(curitem, name); + val = strchr(curitem, '='); + if (val) { + /* The sane way to deal with the environment. + * 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 + * (specifically, see http://support.microsoft.com/kb/235601). + * * Much faster. + * 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 + * not see changes made by extensions that call the Win32 + * functions directly, either. + * GSAR 97-06-07 + */ + *val++ = '\0'; + if (SetEnvironmentVariableA(curitem, *val ? val : NULL)) + relval = 0; + } + Safefree(curitem); } return relval; } @@ -1568,42 +1924,21 @@ win32_unlink(const char *filename) int ret; DWORD attrs; - if (USING_WIDE()) { - WCHAR wBuffer[MAX_PATH+1]; - WCHAR* pwBuffer; - - A2WHELPER(filename, wBuffer, sizeof(wBuffer)); - pwBuffer = PerlDir_mapW(wBuffer); - attrs = GetFileAttributesW(pwBuffer); - if (attrs == 0xFFFFFFFF) - goto fail; - if (attrs & FILE_ATTRIBUTE_READONLY) { - (void)SetFileAttributesW(pwBuffer, attrs & ~FILE_ATTRIBUTE_READONLY); - ret = _wunlink(pwBuffer); - if (ret == -1) - (void)SetFileAttributesW(pwBuffer, attrs); - } - else - ret = _wunlink(pwBuffer); + filename = PerlDir_mapA(filename); + attrs = GetFileAttributesA(filename); + if (attrs == 0xFFFFFFFF) { + errno = ENOENT; + return -1; } - else { - filename = PerlDir_mapA(filename); - attrs = GetFileAttributesA(filename); - if (attrs == 0xFFFFFFFF) - goto fail; - if (attrs & FILE_ATTRIBUTE_READONLY) { - (void)SetFileAttributesA(filename, attrs & ~FILE_ATTRIBUTE_READONLY); - ret = unlink(filename); - if (ret == -1) - (void)SetFileAttributesA(filename, attrs); - } - else - ret = unlink(filename); + if (attrs & FILE_ATTRIBUTE_READONLY) { + (void)SetFileAttributesA(filename, attrs & ~FILE_ATTRIBUTE_READONLY); + ret = unlink(filename); + if (ret == -1) + (void)SetFileAttributesA(filename, attrs); } + else + ret = unlink(filename); return ret; -fail: - errno = ENOENT; - return -1; } DllExport int @@ -1615,19 +1950,11 @@ win32_utime(const char *filename, struct utimbuf *times) FILETIME ftAccess; FILETIME ftWrite; struct utimbuf TimeBuffer; - WCHAR wbuffer[MAX_PATH+1]; - WCHAR* pwbuffer; - int rc; - if (USING_WIDE()) { - A2WHELPER(filename, wbuffer, sizeof(wbuffer)); - pwbuffer = PerlDir_mapW(wbuffer); - rc = _wutime(pwbuffer, (struct _utimbuf*)times); - } - else { - filename = PerlDir_mapA(filename); - rc = utime(filename, times); - } + + filename = PerlDir_mapA(filename); + rc = utime(filename, times); + /* EACCES: path specifies directory or readonly file */ if (rc == 0 || errno != EACCES /* || !IsWinNT() */) return rc; @@ -1639,16 +1966,9 @@ win32_utime(const char *filename, struct utimbuf *times) } /* This will (and should) still fail on readonly files */ - if (USING_WIDE()) { - handle = CreateFileW(pwbuffer, GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); - } - else { - handle = CreateFileA(filename, GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); - } + handle = CreateFileA(filename, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (handle == INVALID_HANDLE_VALUE) return rc; @@ -1701,44 +2021,34 @@ win32_uname(struct utsname *name) { struct hostent *hep; STRLEN nodemax = sizeof(name->nodename)-1; - OSVERSIONINFO osver; - - memset(&osver, 0, sizeof(OSVERSIONINFO)); - osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - if (GetVersionEx(&osver)) { - /* sysname */ - switch (osver.dwPlatformId) { - case VER_PLATFORM_WIN32_WINDOWS: - strcpy(name->sysname, "Windows"); - break; - case VER_PLATFORM_WIN32_NT: - strcpy(name->sysname, "Windows NT"); - break; - case VER_PLATFORM_WIN32s: - strcpy(name->sysname, "Win32s"); - break; - default: - strcpy(name->sysname, "Win32 Unknown"); - break; - } - /* release */ - sprintf(name->release, "%d.%d", - osver.dwMajorVersion, osver.dwMinorVersion); - - /* version */ - sprintf(name->version, "Build %d", - osver.dwPlatformId == VER_PLATFORM_WIN32_NT - ? osver.dwBuildNumber : (osver.dwBuildNumber & 0xffff)); - if (osver.szCSDVersion[0]) { - char *buf = name->version + strlen(name->version); - sprintf(buf, " (%s)", osver.szCSDVersion); - } + /* sysname */ + switch (g_osver.dwPlatformId) { + case VER_PLATFORM_WIN32_WINDOWS: + strcpy(name->sysname, "Windows"); + break; + case VER_PLATFORM_WIN32_NT: + strcpy(name->sysname, "Windows NT"); + break; + case VER_PLATFORM_WIN32s: + strcpy(name->sysname, "Win32s"); + break; + default: + strcpy(name->sysname, "Win32 Unknown"); + break; } - else { - *name->sysname = '\0'; - *name->version = '\0'; - *name->release = '\0'; + + /* release */ + sprintf(name->release, "%d.%d", + g_osver.dwMajorVersion, g_osver.dwMinorVersion); + + /* version */ + sprintf(name->version, "Build %d", + g_osver.dwPlatformId == VER_PLATFORM_WIN32_NT + ? g_osver.dwBuildNumber : (g_osver.dwBuildNumber & 0xffff)); + if (g_osver.szCSDVersion[0]) { + char *buf = name->version + strlen(name->version); + sprintf(buf, " (%s)", g_osver.szCSDVersion); } /* nodename */ @@ -1767,7 +2077,7 @@ win32_uname(struct utsname *name) GetSystemInfo(&info); #if (defined(__BORLANDC__)&&(__BORLANDC__<=0x520)) \ - || (defined(__MINGW32__) && !defined(_ANONYMOUS_UNION)) + || (defined(__MINGW32__) && !defined(_ANONYMOUS_UNION) && !defined(__MINGW_EXTENSION)) procarch = info.u.s.wProcessorArchitecture; #else procarch = info.wProcessorArchitecture; @@ -1873,65 +2183,50 @@ DllExport int win32_async_check(pTHX) { MSG msg; - int ours = 1; - /* 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 - */ - while (PeekMessage(&msg, (HWND)-1, 0, 0, PM_REMOVE|PM_NOYIELD)) { - int sig; - switch(msg.message) { + HWND hwnd = w32_message_hwnd; -#if 0 - /* Perhaps some other messages could map to signals ? ... */ - case WM_CLOSE: - case WM_QUIT: - /* Treat WM_QUIT like SIGHUP? */ - sig = SIGHUP; - goto Raise; - break; -#endif + /* Reset w32_poll_count before doing anything else, incase we dispatch + * messages that end up calling back into perl */ + w32_poll_count = 0; - /* We use WM_USER to fake kill() with other signals */ - case WM_USER: { - sig = msg.wParam; - Raise: - 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(NULL,w32_timerid); - w32_timerid=0; - } - else - goto FallThrough; - /* Now fake a call to signal handler */ - if (do_raise(aTHX_ 14)) { - sig_terminate(aTHX_ 14); - } - break; - } + 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; + } - /* Otherwise do normal Win32 thing - in case it is useful */ - default: - FallThrough: - TranslateMessage(&msg); - DispatchMessage(&msg); - ours = 0; - break; - } + if(!CallMsgFilter(&msg, MSGF_USER)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } } - w32_poll_count = 0; + + /* 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); /* Above or other stuff may have set a signal flag */ - if (PL_sig_pending) { - despatch_signals(); - } - return ours; + if (PL_sig_pending) + despatch_signals(); + + return 1; } /* This function will not return until the timeout has elapsed, or until @@ -1946,7 +2241,7 @@ win32_msgwait(pTHX_ DWORD count, LPHANDLE handles, DWORD timeout, LPDWORD result timeout += ticks; } while (1) { - DWORD result = MsgWaitForMultipleObjects(count,handles,FALSE,timeout-ticks, QS_ALLEVENTS); + DWORD result = MsgWaitForMultipleObjects(count,handles,FALSE,timeout-ticks, QS_POSTMESSAGE|QS_TIMER|QS_SENDMESSAGE); if (resultp) *resultp = result; if (result == WAIT_TIMEOUT) { @@ -2144,13 +2439,22 @@ win32_alarm(unsigned int sec) * one of the supported codes in */ dTHX; + + if (w32_message_hwnd == INVALID_HANDLE_VALUE) + w32_message_hwnd = win32_create_message_window(); + if (sec) { - w32_timerid = SetTimer(NULL,w32_timerid,sec*1000,NULL); + if (w32_message_hwnd == NULL) + w32_timerid = SetTimer(NULL, w32_timerid, sec*1000, NULL); + else { + w32_timerid = 1; + SetTimer(w32_message_hwnd, w32_timerid, sec*1000, NULL); + } } else { if (w32_timerid) { - KillTimer(NULL,w32_timerid); - w32_timerid=0; + KillTimer(w32_message_hwnd, w32_timerid); + w32_timerid = 0; } } return 0; @@ -2168,7 +2472,7 @@ win32_crypt(const char *txt, const char *salt) return des_fcrypt(txt, salt, w32_crypt_buffer); #else Perl_croak(aTHX_ "The crypt() function is unimplemented due to excessive paranoia."); - return Nullch; + return NULL; #endif } @@ -2264,7 +2568,6 @@ my_open_osfhandle(intptr_t osfhandle, int flags) /* 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 @@ -2280,34 +2583,46 @@ win32_flock(int fd, int oper) 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 /* @@ -2341,7 +2656,7 @@ win32_stdin(void) } DllExport FILE * -win32_stdout() +win32_stdout(void) { return (stdout); } @@ -2371,21 +2686,24 @@ win32_strerror(int e) #if !defined __BORLANDC__ && !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 @@ -2468,7 +2786,6 @@ DllExport FILE * win32_fopen(const char *filename, const char *mode) { dTHX; - WCHAR wMode[MODE_SIZE], wBuffer[MAX_PATH+1]; FILE *f; if (!*filename) @@ -2477,13 +2794,7 @@ win32_fopen(const char *filename, const char *mode) if (stricmp(filename, "/dev/null")==0) filename = "NUL"; - if (USING_WIDE()) { - A2WHELPER(mode, wMode, sizeof(wMode)); - A2WHELPER(filename, wBuffer, sizeof(wBuffer)); - f = _wfopen(PerlDir_mapW(wBuffer), wMode); - } - else - f = fopen(PerlDir_mapA(filename), mode); + f = fopen(PerlDir_mapA(filename), mode); /* avoid buffering headaches for child processes */ if (f && *mode == 'a') win32_fseek(f, 0, SEEK_END); @@ -2499,14 +2810,8 @@ DllExport FILE * win32_fdopen(int handle, const char *mode) { dTHX; - WCHAR wMode[MODE_SIZE]; FILE *f; - if (USING_WIDE()) { - A2WHELPER(mode, wMode, sizeof(wMode)); - f = _wfdopen(handle, wMode); - } - else - f = fdopen(handle, (char *) mode); + f = fdopen(handle, (char *) mode); /* avoid buffering headaches for child processes */ if (f && *mode == 'a') win32_fseek(f, 0, SEEK_END); @@ -2517,15 +2822,9 @@ DllExport FILE * win32_freopen(const char *path, const char *mode, FILE *stream) { dTHX; - WCHAR wMode[MODE_SIZE], wBuffer[MAX_PATH+1]; if (stricmp(path, "/dev/null")==0) path = "NUL"; - if (USING_WIDE()) { - A2WHELPER(mode, wMode, sizeof(wMode)); - A2WHELPER(path, wBuffer, sizeof(wBuffer)); - return _wfreopen(PerlDir_mapW(wBuffer), wMode, stream); - } return freopen(PerlDir_mapA(path), mode, stream); } @@ -2582,7 +2881,7 @@ DllExport Off_t win32_ftell(FILE *pf) { #if defined(WIN64) || defined(USE_LARGE_FILES) -#if defined(__BORLAND__) /* buk */ +#if defined(__BORLANDC__) /* buk */ return win32_tell( fileno( pf ) ); #else fpos_t pos; @@ -2627,7 +2926,7 @@ win32_fseek(FILE *pf, Off_t offset,int origin) return fsetpos(pf, &offset); #endif #else - return fseek(pf, offset, origin); + return fseek(pf, (long)offset, origin); #endif } @@ -2719,9 +3018,31 @@ win32_fstat(int fd, Stat_t *sbufptr) * for write operations, probably because it is opened for reading. * --Vadim Konovalov */ - int rc = fstat(fd,sbufptr); 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 = ((__int64)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)); @@ -2801,16 +3122,16 @@ win32_popen(const char *command, const char *mode) if (win32_pipe(p, 512, ourmode) == -1) return NULL; - /* save current stdfd */ - if ((oldfd = win32_dup(stdfd)) == -1) - goto cleanup; - /* save the old std handle (this needs to happen before the * dup2(), since that might call SetStdHandle() too) */ OP_REFCNT_LOCK; lock_held = 1; old_h = GetStdHandle(nhandle); + /* save current stdfd */ + if ((oldfd = win32_dup(stdfd)) == -1) + goto cleanup; + /* make stdfd go to child end of pipe (implicitly closes stdfd) */ /* stdfd will be inherited by the child */ if (win32_dup2(p[child], stdfd) == -1) @@ -2832,6 +3153,9 @@ win32_popen(const char *command, const char *mode) if (win32_dup2(oldfd, stdfd) == -1) goto cleanup; + /* close saved handle */ + win32_close(oldfd); + /* restore the old std handle (this needs to happen after the * dup2(), since that might call SetStdHandle() too */ if (lock_held) { @@ -2840,12 +3164,7 @@ win32_popen(const char *command, const char *mode) lock_held = 0; } - /* close saved handle */ - win32_close(oldfd); - - 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; @@ -2858,15 +3177,15 @@ cleanup: /* we don't need to check for errors here */ win32_close(p[0]); win32_close(p[1]); + if (oldfd != -1) { + win32_dup2(oldfd, stdfd); + win32_close(oldfd); + } if (lock_held) { SetStdHandle(nhandle, old_h); OP_REFCNT_UNLOCK; lock_held = 0; } - if (oldfd != -1) { - win32_dup2(oldfd, stdfd); - win32_close(oldfd); - } return (NULL); #endif /* USE_RTL_POPEN */ @@ -2886,7 +3205,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)) @@ -2905,7 +3223,6 @@ win32_pclose(PerlIO *pf) fclose(pf); #endif SvIVX(sv) = 0; - UNLOCK_FDPID_MUTEX; if (win32_waitpid(childpid, &status, 0) == -1) return -1; @@ -2990,10 +3307,10 @@ win32_link(const char *oldname, const char *newname) if (pfnCreateHardLinkW == NULL) pfnCreateHardLinkW = Nt4CreateHardLinkW; - if ((A2WHELPER(oldname, wOldName, sizeof(wOldName))) && - (A2WHELPER(newname, wNewName, sizeof(wNewName))) && + 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))) + pfnCreateHardLinkW(PerlDir_mapW(wNewName), wOldName, NULL))) { return 0; } @@ -3004,8 +3321,6 @@ win32_link(const char *oldname, const char *newname) DllExport int win32_rename(const char *oname, const char *newname) { - WCHAR wOldName[MAX_PATH+1]; - WCHAR wNewName[MAX_PATH+1]; char szOldName[MAX_PATH+1]; char szNewName[MAX_PATH+1]; BOOL bResult; @@ -3016,20 +3331,10 @@ win32_rename(const char *oname, const char *newname) */ if (IsWinNT()) { DWORD dwFlags = MOVEFILE_COPY_ALLOWED; - if (USING_WIDE()) { - A2WHELPER(oname, wOldName, sizeof(wOldName)); - A2WHELPER(newname, wNewName, sizeof(wNewName)); - if (wcsicmp(wNewName, wOldName)) - dwFlags |= MOVEFILE_REPLACE_EXISTING; - wcscpy(wOldName, PerlDir_mapW(wOldName)); - bResult = MoveFileExW(wOldName,PerlDir_mapW(wNewName), dwFlags); - } - else { - if (stricmp(newname, oname)) - dwFlags |= MOVEFILE_REPLACE_EXISTING; - strcpy(szOldName, PerlDir_mapA(oname)); - bResult = MoveFileExA(szOldName,PerlDir_mapA(newname), dwFlags); - } + 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) { @@ -3055,7 +3360,7 @@ win32_rename(const char *oname, const char *newname) int retval = 0; char szTmpName[MAX_PATH+1]; char dname[MAX_PATH+1]; - char *endname = Nullch; + char *endname = NULL; STRLEN tmplen = 0; DWORD from_attr, to_attr; @@ -3114,7 +3419,7 @@ win32_rename(const char *oname, const char *newname) retval = rename(szOldName, szNewName); /* if we created a temporary file before ... */ - if (endname != Nullch) { + if (endname != NULL) { /* ...and rename succeeded, delete temporary file/directory */ if (retval == 0) DeleteFile(szTmpName); @@ -3177,7 +3482,7 @@ finish: win32_lseek(fd, cur, SEEK_SET); return retval; #else - return chsize(fd, size); + return chsize(fd, (long)size); #endif } @@ -3203,7 +3508,7 @@ win32_lseek(int fd, Off_t offset, int origin) return _lseeki64(fd, offset, origin); #endif #else - return lseek(fd, offset, origin); + return lseek(fd, (long)offset, origin); #endif } @@ -3240,7 +3545,6 @@ win32_open(const char *path, int flag, ...) dTHX; va_list ap; int pmode; - WCHAR wBuffer[MAX_PATH+1]; va_start(ap, flag); pmode = va_arg(ap, int); @@ -3249,10 +3553,6 @@ win32_open(const char *path, int flag, ...) if (stricmp(path, "/dev/null")==0) path = "NUL"; - if (USING_WIDE()) { - A2WHELPER(path, wBuffer, sizeof(wBuffer)); - return _wopen(PerlDir_mapW(wBuffer), flag, pmode); - } return open(PerlDir_mapA(path), flag, pmode); } @@ -3272,6 +3572,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); @@ -3499,11 +3820,6 @@ DllExport int win32_mkdir(const char *dir, int mode) { dTHX; - if (USING_WIDE()) { - WCHAR wBuffer[MAX_PATH+1]; - A2WHELPER(dir, wBuffer, sizeof(wBuffer)); - return _wmkdir(PerlDir_mapW(wBuffer)); - } return mkdir(PerlDir_mapA(dir)); /* just ignore mode */ } @@ -3511,11 +3827,6 @@ DllExport int win32_rmdir(const char *dir) { dTHX; - if (USING_WIDE()) { - WCHAR wBuffer[MAX_PATH+1]; - A2WHELPER(dir, wBuffer, sizeof(wBuffer)); - return _wrmdir(PerlDir_mapW(wBuffer)); - } return rmdir(PerlDir_mapA(dir)); } @@ -3527,11 +3838,6 @@ win32_chdir(const char *dir) errno = ENOENT; return -1; } - if (USING_WIDE()) { - WCHAR wBuffer[MAX_PATH+1]; - A2WHELPER(dir, wBuffer, sizeof(wBuffer)); - return _wchdir(wBuffer); - } return chdir(dir); } @@ -3539,11 +3845,6 @@ DllExport int win32_access(const char *path, int mode) { dTHX; - if (USING_WIDE()) { - WCHAR wBuffer[MAX_PATH+1]; - A2WHELPER(path, wBuffer, sizeof(wBuffer)); - return _waccess(PerlDir_mapW(wBuffer), mode); - } return access(PerlDir_mapA(path), mode); } @@ -3551,11 +3852,6 @@ DllExport int win32_chmod(const char *path, int mode) { dTHX; - if (USING_WIDE()) { - WCHAR wBuffer[MAX_PATH+1]; - A2WHELPER(path, wBuffer, sizeof(wBuffer)); - return _wchmod(PerlDir_mapW(wBuffer), mode); - } return chmod(PerlDir_mapA(path), mode); } @@ -3636,7 +3932,7 @@ create_command_line(char *cname, STRLEN clen, const char * const *args) DEBUG_p(PerlIO_printf(Perl_debug_log, "\n")); argc = index; - New(1310, cmd, len, char); + Newx(cmd, len, char); ptr = cmd; if (bat_file && !IsWin95()) { @@ -3726,7 +4022,7 @@ qualified_path(const char *cmd) int has_slash = 0; if (!cmd) - return Nullch; + return NULL; fullcmd = (char*)cmd; while (*fullcmd) { if (*fullcmd == '/' || *fullcmd == '\\') @@ -3740,7 +4036,7 @@ qualified_path(const char *cmd) /* worst case: PATH is a single directory; we need additional space * to append "/", ".exe" and trailing "\0" */ - New(0, fullcmd, (pathstr ? strlen(pathstr) : 0) + cmdlen + 6, char); + Newx(fullcmd, (pathstr ? strlen(pathstr) : 0) + cmdlen + 6, char); curfullcmd = fullcmd; while (1) { @@ -3800,7 +4096,7 @@ qualified_path(const char *cmd) } Safefree(fullcmd); - return Nullch; + return NULL; } /* The following are just place holders. @@ -3845,17 +4141,10 @@ win32_get_childdir(void) { dTHX; char* ptr; - char szfilename[(MAX_PATH+1)*2]; - if (USING_WIDE()) { - WCHAR wfilename[MAX_PATH+1]; - GetCurrentDirectoryW(MAX_PATH+1, wfilename); - W2AHELPER(wfilename, szfilename, sizeof(szfilename)); - } - else { - GetCurrentDirectoryA(MAX_PATH+1, szfilename); - } + char szfilename[MAX_PATH+1]; - New(0, ptr, strlen(szfilename)+1, char); + GetCurrentDirectoryA(MAX_PATH+1, szfilename); + Newx(ptr, strlen(szfilename)+1, char); strcpy(ptr, szfilename); return ptr; } @@ -3894,7 +4183,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; @@ -3903,7 +4192,7 @@ win32_spawnvp(int mode, const char *cmdname, const char *const *argv) /* if command name contains dquotes, must remove them */ if (strchr(cname, '"')) { cmd = cname; - New(0,cname,clen+1,char); + Newx(cname,clen+1,char); clen = 0; while (*cmd) { if (*cmd != '"') { @@ -4049,9 +4338,17 @@ 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 @@ -4071,7 +4368,11 @@ 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 @@ -4341,7 +4642,6 @@ DllExport void* win32_dynaload(const char* filename) { dTHX; - HMODULE hModule; char buf[MAX_PATH+1]; char *first; @@ -4361,22 +4661,9 @@ win32_dynaload(const char* filename) filename = buf; } } - if (USING_WIDE()) { - WCHAR wfilename[MAX_PATH+1]; - A2WHELPER(filename, wfilename, sizeof(wfilename)); - hModule = LoadLibraryExW(PerlDir_mapW(wfilename), NULL, LOAD_WITH_ALTERED_SEARCH_PATH); - } - else { - hModule = LoadLibraryExA(PerlDir_mapA(filename), NULL, LOAD_WITH_ALTERED_SEARCH_PATH); - } - return hModule; + return LoadLibraryExA(PerlDir_mapA(filename), NULL, LOAD_WITH_ALTERED_SEARCH_PATH); } -/* - * Extras. - */ - -static XS(w32_SetChildShowWindow) { dXSARGS; @@ -4402,567 +4689,24 @@ 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; - - if (USING_WIDE()) { - struct { - DWORD dwOSVersionInfoSize; - DWORD dwMajorVersion; - DWORD dwMinorVersion; - DWORD dwBuildNumber; - DWORD dwPlatformId; - WCHAR szCSDVersion[128]; - unsigned short wServicePackMajor; - unsigned short wServicePackMinor; - unsigned short wSuiteMask; - BYTE wProductType; - BYTE wReserved; - } osverw; - char szCSDVersion[sizeof(osverw.szCSDVersion)]; - osverw.dwOSVersionInfoSize = sizeof(osverw); - if (!GetVersionExW((OSVERSIONINFOW*)&osverw)) { - bEx = FALSE; - osverw.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW); - if (!GetVersionExW((OSVERSIONINFOW*)&osverw)) { - XSRETURN_EMPTY; - } - } - if (GIMME_V == G_SCALAR) { - XSRETURN_IV(osverw.dwPlatformId); - } - W2AHELPER(osverw.szCSDVersion, szCSDVersion, sizeof(szCSDVersion)); - XPUSHs(newSVpvn(szCSDVersion, strlen(szCSDVersion))); - osver.dwMajorVersion = osverw.dwMajorVersion; - osver.dwMinorVersion = osverw.dwMinorVersion; - osver.dwBuildNumber = osverw.dwBuildNumber; - osver.dwPlatformId = osverw.dwPlatformId; - osver.wServicePackMajor = osverw.wServicePackMajor; - osver.wServicePackMinor = osverw.wServicePackMinor; - osver.wSuiteMask = osverw.wSuiteMask; - osver.wProductType = osverw.wProductType; - } - else { - 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 (USING_WIDE()) { - WCHAR wmsgbuf[ONE_K_BUFSIZE]; - if (FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, - &source, SvIV(ST(0)), 0, - wmsgbuf, ONE_K_BUFSIZE-1, NULL)) - { - W2AHELPER(wmsgbuf, msgbuf, sizeof(msgbuf)); - XSRETURN_PV(msgbuf); - } - } - else { - 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; - if (items != 3) - Perl_croak(aTHX_ "usage: Win32::CopyFile($from, $to, $overwrite)"); - if (USING_WIDE()) { - WCHAR wSourceFile[MAX_PATH+1]; - WCHAR wDestFile[MAX_PATH+1]; - A2WHELPER(SvPV_nolen(ST(0)), wSourceFile, sizeof(wSourceFile)); - wcscpy(wSourceFile, PerlDir_mapW(wSourceFile)); - A2WHELPER(SvPV_nolen(ST(1)), wDestFile, sizeof(wDestFile)); - bResult = CopyFileW(wSourceFile, PerlDir_mapW(wDestFile), !SvTRUE(ST(2))); - } - else { - char szSourceFile[MAX_PATH+1]; - 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); +#if defined(__BORLANDC__) + /* makedef.pl seems to have given up on fixing this issue in the .def file */ + pfn_init = (void (*)(pTHX))GetProcAddress((HMODULE)w32_perldll_handle, "_init_Win32CORE"); +#else + pfn_init = (void (*)(pTHX))GetProcAddress((HMODULE)w32_perldll_handle, "init_Win32CORE"); +#endif + if (pfn_init) + pfn_init(aTHX); + + newXS("Win32::SetChildShowWindow", w32_SetChildShowWindow, file); } void * @@ -5034,9 +4778,119 @@ win32_ctrlhandler(DWORD dwCtrlType) } +#ifdef SET_INVALID_PARAMETER_HANDLER +# include +#endif + +static void +ansify_path(void) +{ + size_t len; + char *ansi_path; + WCHAR *wide_path; + WCHAR *wide_dir; + + /* win32_ansipath() requires Windows 2000 or later */ + if (!IsWin2000()) + return; + + /* 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 Borland runtime library 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) { + HMODULE module; + +#ifdef SET_INVALID_PARAMETER_HANDLER + _invalid_parameter_handler oldHandler, newHandler; + newHandler = my_invalid_parameter_handler; + oldHandler = _set_invalid_parameter_handler(newHandler); + _CrtSetReportMode(_CRT_ASSERT, 0); +#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 @@ -5047,12 +4901,42 @@ Perl_win32_init(int *argcp, char ***argvp) _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(); + + module = GetModuleHandle("ntdll.dll"); + if (module) { + *(FARPROC*)&pfnZwQuerySystemInformation = GetProcAddress(module, "ZwQuerySystemInformation"); + } + + module = GetModuleHandle("kernel32.dll"); + if (module) { + *(FARPROC*)&pfnCreateToolhelp32Snapshot = GetProcAddress(module, "CreateToolhelp32Snapshot"); + *(FARPROC*)&pfnProcess32First = GetProcAddress(module, "Process32First"); + *(FARPROC*)&pfnProcess32Next = GetProcAddress(module, "Process32Next"); + } + + g_osver.dwOSVersionInfoSize = sizeof(g_osver); + GetVersionEx(&g_osver); + + ansify_path(); } void Perl_win32_term(void) { + dTHX; + HINTS_REFCNT_TERM; OP_REFCNT_TERM; + PERLIO_TERM; MALLOC_TERM; } @@ -5084,9 +4968,167 @@ 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 +/* we use a message filter hook to process thread messages, passing any + * messages that we don't process on to the rest of the hook chain + * Anyone else writing a message loop that wants to play nicely with perl + * should do + * CallMsgFilter(&msg, MSGF_***); + * between their GetMessage and DispatchMessage calls. */ +LRESULT CALLBACK +win32_message_filter_proc(int code, WPARAM wParam, LPARAM lParam) { + LPMSG pmsg = (LPMSG)lParam; + + /* we'll process it if code says we're allowed, and it's a thread message */ + if (code >= 0 && pmsg->hwnd == NULL + && win32_process_message(pmsg->hwnd, pmsg->message, + pmsg->wParam, pmsg->lParam)) + { + return TRUE; + } + + /* XXX: MSDN says that hhk is ignored, but we should really use the + * return value from SetWindowsHookEx() in win32_create_message_window(). */ + return CallNextHookEx(NULL, code, wParam, lParam); +} + +/* 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) +{ + HWND hwnd = NULL; + + /* "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. + */ + /* Using HWND_MESSAGE appears to work under Win98, despite MSDN + * documentation to the contrary, however, there is some evidence that + * there may be problems with the implementation on Win98. As it is not + * officially supported we take the cautious route and stick with thread + * messages (hwnd == NULL) on platforms prior to Win2k. + */ + if (IsWin2000()) { + win32_create_message_window_class(); + + hwnd = CreateWindow("PerlMessageWindowClass", "PerlMessageWindow", + 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL); + } + + /* If we din't create a window for any reason, then we'll use thread + * messages for our signalling, so we install a hook which + * is called by CallMsgFilter in win32_async_check(), or any other + * modal loop (e.g. Win32::MsgBox or any other GUI extention, or anything + * that use OLE, etc. */ + if(!hwnd) { + SetWindowsHookEx(WH_MSGFILTER, win32_message_filter_proc, + NULL, GetCurrentThreadId()); + } + + return hwnd; +} + +#ifdef HAVE_INTERP_INTERN static void win32_csighandler(int sig) @@ -5098,35 +5140,52 @@ win32_csighandler(int sig) /* Does nothing */ } +#if defined(__MINGW32__) && defined(__cplusplus) +#define CAST_HWND__(x) (HWND__*)(x) +#else +#define CAST_HWND__(x) x +#endif + void Perl_sys_intern_init(pTHX) { int i; - w32_perlshell_tokens = Nullch; + + w32_perlshell_tokens = NULL; w32_perlshell_vec = (char**)NULL; w32_perlshell_items = 0; w32_fdpid = newAV(); - New(1313, w32_children, 1, child_tab); + Newx(w32_children, 1, child_tab); w32_num_children = 0; # ifdef USE_ITHREADS w32_pseudo_id = 0; - New(1313, w32_pseudo_children, 1, child_tab); + Newx(w32_pseudo_children, 1, pseudo_child_tab); w32_num_pseudo_children = 0; # endif - w32_init_socktype = 0; w32_timerid = 0; + w32_message_hwnd = CAST_HWND__(INVALID_HANDLE_VALUE); w32_poll_count = 0; for (i=0; i < SIG_SIZE; i++) { w32_sighandler[i] = SIG_DFL; } -# ifdef MULTIPLICTY +# ifdef MULTIPLICITY if (my_perl == PL_curinterp) { # else { # endif /* Force C runtime signal stuff to set its console handler */ - signal(SIGINT,&win32_csighandler); - signal(SIGBREAK,&win32_csighandler); + 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. At least on Windows NT and later we + * can re-enable Ctrl-C handling by calling SetConsoleCtrlHandler() + * with a NULL handler. This is not valid on Windows 9X. + */ + if (IsWinNT()) + SetConsoleCtrlHandler(NULL,FALSE); + /* Push our handler on top */ SetConsoleCtrlHandler(win32_ctrlhandler,TRUE); } @@ -5140,9 +5199,11 @@ Perl_sys_intern_clear(pTHX) /* NOTE: w32_fdpid is freed by sv_clean_all() */ Safefree(w32_children); if (w32_timerid) { - KillTimer(NULL,w32_timerid); - w32_timerid=0; + KillTimer(w32_message_hwnd, w32_timerid); + w32_timerid = 0; } + if (w32_message_hwnd != NULL && w32_message_hwnd != INVALID_HANDLE_VALUE) + DestroyWindow(w32_message_hwnd); # ifdef MULTIPLICITY if (my_perl == PL_curinterp) { # else @@ -5160,46 +5221,19 @@ 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(); - Newz(1313, dst->children, 1, child_tab); + Newxz(dst->children, 1, child_tab); dst->pseudo_id = 0; - Newz(1313, dst->pseudo_children, 1, child_tab); - dst->thr_intern.Winit_socktype = 0; - dst->timerid = 0; - dst->poll_count = 0; + Newxz(dst->pseudo_children, 1, pseudo_child_tab); + dst->timerid = 0; + dst->message_hwnd = CAST_HWND__(INVALID_HANDLE_VALUE); + dst->poll_count = 0; Copy(src->sigtable,dst->sigtable,SIG_SIZE,Sighandler_t); } # 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); - Newz(0, 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); -}