From 5443ca169cb7517d394ed8c34442c7d94bef6837 Mon Sep 17 00:00:00 2001 From: Nick Wellnhofer Date: Tue, 6 Sep 2022 13:36:42 +0200 Subject: [PATCH] python: Support Python 3 on Windows Copy updated implementation of PyFileGET from libxml2. --- python/types.c | 190 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 165 insertions(+), 25 deletions(-) diff --git a/python/types.c b/python/types.c index 1617cb96..651f17dd 100644 --- a/python/types.c +++ b/python/types.c @@ -25,42 +25,182 @@ xmlParserInputPtr xmlNoNetExternalEntityLoader(const char *URL, #if PY_MAJOR_VERSION >= 3 #include +#include + +#ifdef _WIN32 + +#include +#include + +/* Taken from info on MSDN site, as we may not have the Windows WDK/DDK headers */ +typedef struct _IO_STATUS_BLOCK { + union { + NTSTATUS Status; + PVOID Pointer; + } DUMMYUNIONNAME; + ULONG_PTR Information; +} IO_STATUS_BLOCK; + +typedef struct _FILE_ACCESS_INFORMATION { + ACCESS_MASK AccessFlags; +} FILE_ACCESS_INFORMATION; + +typedef NTSTATUS (*t_NtQueryInformationFile) (HANDLE FileHandle, + IO_STATUS_BLOCK *IoStatusBlock, + PVOID FileInformation, + ULONG Length, + int FileInformationClass); /* this is an Enum */ + +#if (defined (_MSC_VER) && _MSC_VER >= 1400) +/* + * This is the (empty) invalid parameter handler + * that is used for Visual C++ 2005 (and later) builds + * so that we can use this instead of the system automatically + * aborting the process. + * + * This is necessary as we use _get_oshandle() to check the validity + * of the file descriptors as we close them, so when an invalid file + * descriptor is passed into that function as we check on it, we get + * -1 as the result, instead of the gspawn helper program aborting. + * + * Please see http://msdn.microsoft.com/zh-tw/library/ks2530z6%28v=vs.80%29.aspx + * for an explanation on this. + */ +void +myInvalidParameterHandler(const wchar_t *expression, + const wchar_t *function, + const wchar_t *file, + unsigned int line, + uintptr_t pReserved) +{ +} +#endif +#else #include #include +#endif FILE * libxml_PyFileGet(PyObject *f) { - int fd, flags; FILE *res; const char *mode; + int fd = PyObject_AsFileDescriptor(f); + +#ifdef _WIN32 + intptr_t w_fh = -1; + HMODULE hntdll = NULL; + IO_STATUS_BLOCK status_block; + FILE_ACCESS_INFORMATION ai; + t_NtQueryInformationFile NtQueryInformationFile; + BOOL is_read = FALSE; + BOOL is_write = FALSE; + BOOL is_append = FALSE; + +#if (defined (_MSC_VER) && _MSC_VER >= 1400) + /* set up our empty invalid parameter handler */ + _invalid_parameter_handler oldHandler, newHandler; + newHandler = myInvalidParameterHandler; + oldHandler = _set_invalid_parameter_handler(newHandler); + + /* Disable the message box for assertions. */ + _CrtSetReportMode(_CRT_ASSERT, 0); +#endif + + w_fh = _get_osfhandle(fd); + + if (w_fh == -1) + return(NULL); + + hntdll = GetModuleHandleW(L"ntdll.dll"); + + if (hntdll == NULL) + return(NULL); +XML_IGNORE_FPTR_CAST_WARNINGS + NtQueryInformationFile = (t_NtQueryInformationFile)GetProcAddress(hntdll, "NtQueryInformationFile"); +XML_POP_WARNINGS + + if (NtQueryInformationFile != NULL && + (NtQueryInformationFile((HANDLE)w_fh, + &status_block, + &ai, + sizeof(FILE_ACCESS_INFORMATION), + 8) == 0)) /* 8 means "FileAccessInformation" */ + { + if (ai.AccessFlags & FILE_READ_DATA) + is_read = TRUE; + if (ai.AccessFlags & FILE_WRITE_DATA) + is_write = TRUE; + if (ai.AccessFlags & FILE_APPEND_DATA) + is_append = TRUE; + + if (is_write) { + if (is_read) { + if (is_append) + mode = "a+"; + else + mode = "rw"; + } else { + if (is_append) + mode = "a"; + else + mode = "w"; + } + } else { + if (is_append) + mode = "r+"; + else + mode = "r"; + } + } + + FreeLibrary(hntdll); + + if (!is_write && !is_read) /* also happens if we did not load or run NtQueryInformationFile() successfully */ + return(NULL); +#else + int flags; - fd = PyObject_AsFileDescriptor(f); /* - * Get the flags on the fd to understand how it was opened + * macOS returns O_RDWR for standard streams, but fails to write to + * stdout or stderr when opened with fdopen(dup_fd, "rw"). */ - flags = fcntl(fd, F_GETFL, 0); - switch (flags & O_ACCMODE) { - case O_RDWR: - if (flags & O_APPEND) - mode = "a+"; - else - mode = "rw"; - break; - case O_RDONLY: - if (flags & O_APPEND) - mode = "r+"; - else - mode = "r"; - break; - case O_WRONLY: - if (flags & O_APPEND) - mode = "a"; - else - mode = "w"; - break; - default: - return(NULL); + switch (fd) { + case STDIN_FILENO: + mode = "r"; + break; + case STDOUT_FILENO: + case STDERR_FILENO: + mode = "w"; + break; + default: + /* + * Get the flags on the fd to understand how it was opened + */ + flags = fcntl(fd, F_GETFL, 0); + switch (flags & O_ACCMODE) { + case O_RDWR: + if (flags & O_APPEND) + mode = "a+"; + else + mode = "rw"; + break; + case O_RDONLY: + if (flags & O_APPEND) + mode = "r+"; + else + mode = "r"; + break; + case O_WRONLY: + if (flags & O_APPEND) + mode = "a"; + else + mode = "w"; + break; + default: + return(NULL); + } } +#endif /* * the FILE struct gets a new fd, so that it can be closed