#include "Python.h"
#include "pycore_fileutils.h"
#include "pycore_runtime.h"
#include "osdefs.h"
#include <locale.h>
#include <stdlib.h>
#ifdef MS_WINDOWS
# include <malloc.h>
# include <windows.h>
# include <winioctl.h>
# include "pycore_fileutils_windows.h"
# if defined(MS_WINDOWS_GAMES) && !defined(MS_WINDOWS_DESKTOP)
# define PATHCCH_ALLOW_LONG_PATHS 0x01
# else
# include <pathcch.h>
# endif
extern int winerror_to_errno(int);
#endif
#ifdef HAVE_LANGINFO_H
#include <langinfo.h>
#endif
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#ifdef HAVE_NON_UNICODE_WCHAR_T_REPRESENTATION
#include <iconv.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef O_CLOEXEC
int _Py_open_cloexec_works = -1;
#endif
#define MAX_UNICODE 0x10ffff
static const size_t DECODE_ERROR = ((size_t)-1);
static const size_t INCOMPLETE_CHARACTER = (size_t)-2;
static int
get_surrogateescape(_Py_error_handler errors, int *surrogateescape)
{
switch (errors)
{
case _Py_ERROR_STRICT:
*surrogateescape = 0;
return 0;
case _Py_ERROR_SURROGATEESCAPE:
*surrogateescape = 1;
return 0;
default:
return -1;
}
}
PyObject *
_Py_device_encoding(int fd)
{
int valid;
Py_BEGIN_ALLOW_THREADS
_Py_BEGIN_SUPPRESS_IPH
valid = isatty(fd);
_Py_END_SUPPRESS_IPH
Py_END_ALLOW_THREADS
if (!valid)
Py_RETURN_NONE;
#ifdef MS_WINDOWS
#ifdef HAVE_WINDOWS_CONSOLE_IO
UINT cp;
if (fd == 0)
cp = GetConsoleCP();
else if (fd == 1 || fd == 2)
cp = GetConsoleOutputCP();
else
cp = 0;
if (cp == 0) {
Py_RETURN_NONE;
}
return PyUnicode_FromFormat("cp%u", (unsigned int)cp);
#else
Py_RETURN_NONE;
#endif
#else
if (_PyRuntime.preconfig.utf8_mode) {
_Py_DECLARE_STR(utf_8, "utf-8");
return Py_NewRef(&_Py_STR(utf_8));
}
return _Py_GetLocaleEncodingObject();
#endif
}
static int
is_valid_wide_char(wchar_t ch)
{
#ifdef HAVE_NON_UNICODE_WCHAR_T_REPRESENTATION
return 1;
#endif
if (Py_UNICODE_IS_SURROGATE(ch)) {
return 0;
}
if (ch > MAX_UNICODE) {
return 0;
}
return 1;
}
static size_t
_Py_mbstowcs(wchar_t *dest, const char *src, size_t n)
{
size_t count = mbstowcs(dest, src, n);
if (dest != NULL && count != DECODE_ERROR) {
for (size_t i=0; i < count; i++) {
wchar_t ch = dest[i];
if (!is_valid_wide_char(ch)) {
return DECODE_ERROR;
}
}
}
return count;
}
#ifdef HAVE_MBRTOWC
static size_t
_Py_mbrtowc(wchar_t *pwc, const char *str, size_t len, mbstate_t *pmbs)
{
assert(pwc != NULL);
size_t count = mbrtowc(pwc, str, len, pmbs);
if (count != 0 && count != DECODE_ERROR && count != INCOMPLETE_CHARACTER) {
if (!is_valid_wide_char(*pwc)) {
return DECODE_ERROR;
}
}
return count;
}
#endif
#if !defined(_Py_FORCE_UTF8_FS_ENCODING) && !defined(MS_WINDOWS)
#define USE_FORCE_ASCII
extern int _Py_normalize_encoding(const char *, char *, size_t);
#define force_ascii (_PyRuntime.fileutils.force_ascii)
static int
check_force_ascii(void)
{
char *loc = setlocale(LC_CTYPE, NULL);
if (loc == NULL) {
goto error;
}
if (strcmp(loc, "C") != 0 && strcmp(loc, "POSIX") != 0) {
return 0;
}
#if defined(HAVE_LANGINFO_H) && defined(CODESET)
const char *codeset = nl_langinfo(CODESET);
if (!codeset || codeset[0] == '\0') {
goto error;
}
char encoding[20];
if (!_Py_normalize_encoding(codeset, encoding, sizeof(encoding))) {
goto error;
}
#ifdef __hpux
if (strcmp(encoding, "roman8") == 0) {
unsigned char ch;
wchar_t wch;
size_t res;
ch = (unsigned char)0xA7;
res = _Py_mbstowcs(&wch, (char*)&ch, 1);
if (res != DECODE_ERROR && wch == L'\xA7') {
return 1;
}
}
#else
const char* ascii_aliases[] = {
"ascii",
"646",
"ansi_x3.4_1968",
"ansi_x3.4_1986",
"ansi_x3_4_1968",
"cp367",
"csascii",
"ibm367",
"iso646_us",
"iso_646.irv_1991",
"iso_ir_6",
"us",
"us_ascii",
NULL
};
int is_ascii = 0;
for (const char **alias=ascii_aliases; *alias != NULL; alias++) {
if (strcmp(encoding, *alias) == 0) {
is_ascii = 1;
break;
}
}
if (!is_ascii) {
return 0;
}
for (unsigned int i=0x80; i<=0xff; i++) {
char ch[1];
wchar_t wch[1];
size_t res;
unsigned uch = (unsigned char)i;
ch[0] = (char)uch;
res = _Py_mbstowcs(wch, ch, 1);
if (res != DECODE_ERROR) {
return 1;
}
}
#endif
return 0;
#else
return 1;
#endif
error:
return 1;
}
int
_Py_GetForceASCII(void)
{
if (force_ascii == -1) {
force_ascii = check_force_ascii();
}
return force_ascii;
}
void
_Py_ResetForceASCII(void)
{
force_ascii = -1;
}
static int
encode_ascii(const wchar_t *text, char **str,
size_t *error_pos, const char **reason,
int raw_malloc, _Py_error_handler errors)
{
char *result = NULL, *out;
size_t len, i;
wchar_t ch;
int surrogateescape;
if (get_surrogateescape(errors, &surrogateescape) < 0) {
return -3;
}
len = wcslen(text);
if (raw_malloc) {
result = PyMem_RawMalloc(len + 1);
}
else {
result = PyMem_Malloc(len + 1);
}
if (result == NULL) {
return -1;
}
out = result;
for (i=0; i<len; i++) {
ch = text[i];
if (ch <= 0x7f) {
*out++ = (char)ch;
}
else if (surrogateescape && 0xdc80 <= ch && ch <= 0xdcff) {
*out++ = (char)(ch - 0xdc00);
}
else {
if (raw_malloc) {
PyMem_RawFree(result);
}
else {
PyMem_Free(result);
}
if (error_pos != NULL) {
*error_pos = i;
}
if (reason) {
*reason = "encoding error";
}
return -2;
}
}
*out = '\0';
*str = result;
return 0;
}
#else
int
_Py_GetForceASCII(void)
{
return 0;
}
void
_Py_ResetForceASCII(void)
{
}
#endif
#if !defined(HAVE_MBRTOWC) || defined(USE_FORCE_ASCII)
static int
decode_ascii(const char *arg, wchar_t **wstr, size_t *wlen,
const char **reason, _Py_error_handler errors)
{
wchar_t *res;
unsigned char *in;
wchar_t *out;
size_t argsize = strlen(arg) + 1;
int surrogateescape;
if (get_surrogateescape(errors, &surrogateescape) < 0) {
return -3;
}
if (argsize > PY_SSIZE_T_MAX / sizeof(wchar_t)) {
return -1;
}
res = PyMem_RawMalloc(argsize * sizeof(wchar_t));
if (!res) {
return -1;
}
out = res;
for (in = (unsigned char*)arg; *in; in++) {
unsigned char ch = *in;
if (ch < 128) {
*out++ = ch;
}
else {
if (!surrogateescape) {
PyMem_RawFree(res);
if (wlen) {
*wlen = in - (unsigned char*)arg;
}
if (reason) {
*reason = "decoding error";
}
return -2;
}
*out++ = 0xdc00 + ch;
}
}
*out = 0;
if (wlen != NULL) {
*wlen = out - res;
}
*wstr = res;
return 0;
}
#endif
static int
decode_current_locale(const char* arg, wchar_t **wstr, size_t *wlen,
const char **reason, _Py_error_handler errors)
{
wchar_t *res;
size_t argsize;
size_t count;
#ifdef HAVE_MBRTOWC
unsigned char *in;
wchar_t *out;
mbstate_t mbs;
#endif
int surrogateescape;
if (get_surrogateescape(errors, &surrogateescape) < 0) {
return -3;
}
#ifdef HAVE_BROKEN_MBSTOWCS
argsize = strlen(arg);
#else
argsize = _Py_mbstowcs(NULL, arg, 0);
#endif
if (argsize != DECODE_ERROR) {
if (argsize > PY_SSIZE_T_MAX / sizeof(wchar_t) - 1) {
return -1;
}
res = (wchar_t *)PyMem_RawMalloc((argsize + 1) * sizeof(wchar_t));
if (!res) {
return -1;
}
count = _Py_mbstowcs(res, arg, argsize + 1);
if (count != DECODE_ERROR) {
*wstr = res;
if (wlen != NULL) {
*wlen = count;
}
return 0;
}
PyMem_RawFree(res);
}
#ifdef HAVE_MBRTOWC
argsize = strlen(arg) + 1;
if (argsize > PY_SSIZE_T_MAX / sizeof(wchar_t)) {
return -1;
}
res = (wchar_t*)PyMem_RawMalloc(argsize * sizeof(wchar_t));
if (!res) {
return -1;
}
in = (unsigned char*)arg;
out = res;
memset(&mbs, 0, sizeof mbs);
while (argsize) {
size_t converted = _Py_mbrtowc(out, (char*)in, argsize, &mbs);
if (converted == 0) {
break;
}
if (converted == INCOMPLETE_CHARACTER) {
goto decode_error;
}
if (converted == DECODE_ERROR) {
if (!surrogateescape) {
goto decode_error;
}
*out++ = 0xdc00 + *in++;
argsize--;
memset(&mbs, 0, sizeof mbs);
continue;
}
assert(!Py_UNICODE_IS_SURROGATE(*out));
in += converted;
argsize -= converted;
out++;
}
if (wlen != NULL) {
*wlen = out - res;
}
*wstr = res;
return 0;
decode_error:
PyMem_RawFree(res);
if (wlen) {
*wlen = in - (unsigned char*)arg;
}
if (reason) {
*reason = "decoding error";
}
return -2;
#else
return decode_ascii(arg, wstr, wlen, reason, errors);
#endif
}
int
_Py_DecodeLocaleEx(const char* arg, wchar_t **wstr, size_t *wlen,
const char **reason,
int current_locale, _Py_error_handler errors)
{
if (current_locale) {
#ifdef _Py_FORCE_UTF8_LOCALE
return _Py_DecodeUTF8Ex(arg, strlen(arg), wstr, wlen, reason,
errors);
#else
return decode_current_locale(arg, wstr, wlen, reason, errors);
#endif
}
#ifdef _Py_FORCE_UTF8_FS_ENCODING
return _Py_DecodeUTF8Ex(arg, strlen(arg), wstr, wlen, reason,
errors);
#else
int use_utf8 = (_PyRuntime.preconfig.utf8_mode >= 1);
#ifdef MS_WINDOWS
use_utf8 |= (_PyRuntime.preconfig.legacy_windows_fs_encoding == 0);
#endif
if (use_utf8) {
return _Py_DecodeUTF8Ex(arg, strlen(arg), wstr, wlen, reason,
errors);
}
#ifdef USE_FORCE_ASCII
if (force_ascii == -1) {
force_ascii = check_force_ascii();
}
if (force_ascii) {
return decode_ascii(arg, wstr, wlen, reason, errors);
}
#endif
return decode_current_locale(arg, wstr, wlen, reason, errors);
#endif
}
wchar_t*
Py_DecodeLocale(const char* arg, size_t *wlen)
{
wchar_t *wstr;
int res = _Py_DecodeLocaleEx(arg, &wstr, wlen,
NULL, 0,
_Py_ERROR_SURROGATEESCAPE);
if (res != 0) {
assert(res != -3);
if (wlen != NULL) {
*wlen = (size_t)res;
}
return NULL;
}
return wstr;
}
static int
encode_current_locale(const wchar_t *text, char **str,
size_t *error_pos, const char **reason,
int raw_malloc, _Py_error_handler errors)
{
const size_t len = wcslen(text);
char *result = NULL, *bytes = NULL;
size_t i, size, converted;
wchar_t c, buf[2];
int surrogateescape;
if (get_surrogateescape(errors, &surrogateescape) < 0) {
return -3;
}
size = 0;
buf[1] = 0;
while (1) {
for (i=0; i < len; i++) {
c = text[i];
if (c >= 0xdc80 && c <= 0xdcff) {
if (!surrogateescape) {
goto encode_error;
}
if (bytes != NULL) {
*bytes++ = c - 0xdc00;
size--;
}
else {
size++;
}
continue;
}
else {
buf[0] = c;
if (bytes != NULL) {
converted = wcstombs(bytes, buf, size);
}
else {
converted = wcstombs(NULL, buf, 0);
}
if (converted == DECODE_ERROR) {
goto encode_error;
}
if (bytes != NULL) {
bytes += converted;
size -= converted;
}
else {
size += converted;
}
}
}
if (result != NULL) {
*bytes = '\0';
break;
}
size += 1;
if (raw_malloc) {
result = PyMem_RawMalloc(size);
}
else {
result = PyMem_Malloc(size);
}
if (result == NULL) {
return -1;
}
bytes = result;
}
*str = result;
return 0;
encode_error:
if (raw_malloc) {
PyMem_RawFree(result);
}
else {
PyMem_Free(result);
}
if (error_pos != NULL) {
*error_pos = i;
}
if (reason) {
*reason = "encoding error";
}
return -2;
}
static int
encode_locale_ex(const wchar_t *text, char **str, size_t *error_pos,
const char **reason,
int raw_malloc, int current_locale, _Py_error_handler errors)
{
if (current_locale) {
#ifdef _Py_FORCE_UTF8_LOCALE
return _Py_EncodeUTF8Ex(text, str, error_pos, reason,
raw_malloc, errors);
#else
return encode_current_locale(text, str, error_pos, reason,
raw_malloc, errors);
#endif
}
#ifdef _Py_FORCE_UTF8_FS_ENCODING
return _Py_EncodeUTF8Ex(text, str, error_pos, reason,
raw_malloc, errors);
#else
int use_utf8 = (_PyRuntime.preconfig.utf8_mode >= 1);
#ifdef MS_WINDOWS
use_utf8 |= (_PyRuntime.preconfig.legacy_windows_fs_encoding == 0);
#endif
if (use_utf8) {
return _Py_EncodeUTF8Ex(text, str, error_pos, reason,
raw_malloc, errors);
}
#ifdef USE_FORCE_ASCII
if (force_ascii == -1) {
force_ascii = check_force_ascii();
}
if (force_ascii) {
return encode_ascii(text, str, error_pos, reason,
raw_malloc, errors);
}
#endif
return encode_current_locale(text, str, error_pos, reason,
raw_malloc, errors);
#endif
}
static char*
encode_locale(const wchar_t *text, size_t *error_pos,
int raw_malloc, int current_locale)
{
char *str;
int res = encode_locale_ex(text, &str, error_pos, NULL,
raw_malloc, current_locale,
_Py_ERROR_SURROGATEESCAPE);
if (res != -2 && error_pos) {
*error_pos = (size_t)-1;
}
if (res != 0) {
return NULL;
}
return str;
}
char*
Py_EncodeLocale(const wchar_t *text, size_t *error_pos)
{
return encode_locale(text, error_pos, 0, 0);
}
char*
_Py_EncodeLocaleRaw(const wchar_t *text, size_t *error_pos)
{
return encode_locale(text, error_pos, 1, 0);
}
int
_Py_EncodeLocaleEx(const wchar_t *text, char **str,
size_t *error_pos, const char **reason,
int current_locale, _Py_error_handler errors)
{
return encode_locale_ex(text, str, error_pos, reason, 1,
current_locale, errors);
}
wchar_t*
_Py_GetLocaleEncoding(void)
{
#ifdef _Py_FORCE_UTF8_LOCALE
return _PyMem_RawWcsdup(L"utf-8");
#else
#ifdef MS_WINDOWS
wchar_t encoding[23];
unsigned int ansi_codepage = GetACP();
swprintf(encoding, Py_ARRAY_LENGTH(encoding), L"cp%u", ansi_codepage);
encoding[Py_ARRAY_LENGTH(encoding) - 1] = 0;
return _PyMem_RawWcsdup(encoding);
#else
const char *encoding = nl_langinfo(CODESET);
if (!encoding || encoding[0] == '\0') {
return _PyMem_RawWcsdup(L"utf-8");
}
wchar_t *wstr;
int res = decode_current_locale(encoding, &wstr, NULL,
NULL, _Py_ERROR_SURROGATEESCAPE);
if (res < 0) {
return NULL;
}
return wstr;
#endif
#endif
}
PyObject *
_Py_GetLocaleEncodingObject(void)
{
wchar_t *encoding = _Py_GetLocaleEncoding();
if (encoding == NULL) {
PyErr_NoMemory();
return NULL;
}
PyObject *str = PyUnicode_FromWideChar(encoding, -1);
PyMem_RawFree(encoding);
return str;
}
#ifdef HAVE_NON_UNICODE_WCHAR_T_REPRESENTATION
int
_Py_LocaleUsesNonUnicodeWchar(void)
{
char* codeset = nl_langinfo(CODESET);
if (!codeset) {
return 0;
}
return (strcmp(codeset, "UTF-8") != 0 && strcmp(codeset, "646") != 0);
}
static wchar_t *
_Py_ConvertWCharForm(const wchar_t *source, Py_ssize_t size,
const char *tocode, const char *fromcode)
{
static_assert(sizeof(wchar_t) == 4, "wchar_t must be 32-bit");
if (size > (PY_SSIZE_T_MAX / (Py_ssize_t)sizeof(wchar_t))) {
PyErr_NoMemory();
return NULL;
}
wchar_t* target = PyMem_Malloc(size * sizeof(wchar_t));
if (target == NULL) {
PyErr_NoMemory();
return NULL;
}
iconv_t cd = iconv_open(tocode, fromcode);
if (cd == (iconv_t)-1) {
PyErr_Format(PyExc_ValueError, "iconv_open() failed");
PyMem_Free(target);
return NULL;
}
char *inbuf = (char *) source;
char *outbuf = (char *) target;
size_t inbytesleft = sizeof(wchar_t) * size;
size_t outbytesleft = inbytesleft;
size_t ret = iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft);
if (ret == DECODE_ERROR) {
PyErr_Format(PyExc_ValueError, "iconv() failed");
PyMem_Free(target);
iconv_close(cd);
return NULL;
}
iconv_close(cd);
return target;
}
wchar_t *
_Py_DecodeNonUnicodeWchar(const wchar_t *native, Py_ssize_t size)
{
return _Py_ConvertWCharForm(native, size, "UCS-4-INTERNAL", "wchar_t");
}
int
_Py_EncodeNonUnicodeWchar_InPlace(wchar_t *unicode, Py_ssize_t size)
{
wchar_t* result = _Py_ConvertWCharForm(unicode, size, "wchar_t", "UCS-4-INTERNAL");
if (!result) {
return -1;
}
memcpy(unicode, result, size * sizeof(wchar_t));
PyMem_Free(result);
return 0;
}
#endif
#ifdef MS_WINDOWS
static __int64 secs_between_epochs = 11644473600;
static void
FILE_TIME_to_time_t_nsec(FILETIME *in_ptr, time_t *time_out, int* nsec_out)
{
__int64 in;
memcpy(&in, in_ptr, sizeof(in));
*nsec_out = (int)(in % 10000000) * 100;
*time_out = Py_SAFE_DOWNCAST((in / 10000000) - secs_between_epochs, __int64, time_t);
}
static void
LARGE_INTEGER_to_time_t_nsec(LARGE_INTEGER *in_ptr, time_t *time_out, int* nsec_out)
{
*nsec_out = (int)(in_ptr->QuadPart % 10000000) * 100;
*time_out = Py_SAFE_DOWNCAST((in_ptr->QuadPart / 10000000) - secs_between_epochs, __int64, time_t);
}
void
_Py_time_t_to_FILE_TIME(time_t time_in, int nsec_in, FILETIME *out_ptr)
{
__int64 out;
out = time_in + secs_between_epochs;
out = out * 10000000 + nsec_in / 100;
memcpy(out_ptr, &out, sizeof(out));
}
#if _S_IREAD != 0400
#error Unsupported C library
#endif
static int
attributes_to_mode(DWORD attr)
{
int m = 0;
if (attr & FILE_ATTRIBUTE_DIRECTORY)
m |= _S_IFDIR | 0111;
else
m |= _S_IFREG;
if (attr & FILE_ATTRIBUTE_READONLY)
m |= 0444;
else
m |= 0666;
return m;
}
typedef union {
FILE_ID_128 id;
struct {
uint64_t st_ino;
uint64_t st_ino_high;
};
} id_128_to_ino;
void
_Py_attribute_data_to_stat(BY_HANDLE_FILE_INFORMATION *info, ULONG reparse_tag,
FILE_BASIC_INFO *basic_info, FILE_ID_INFO *id_info,
struct _Py_stat_struct *result)
{
memset(result, 0, sizeof(*result));
result->st_mode = attributes_to_mode(info->dwFileAttributes);
result->st_size = (((__int64)info->nFileSizeHigh)<<32) + info->nFileSizeLow;
result->st_dev = id_info ? id_info->VolumeSerialNumber : info->dwVolumeSerialNumber;
result->st_rdev = 0;
if (basic_info) {
LARGE_INTEGER_to_time_t_nsec(&basic_info->CreationTime, &result->st_birthtime, &result->st_birthtime_nsec);
LARGE_INTEGER_to_time_t_nsec(&basic_info->ChangeTime, &result->st_ctime, &result->st_ctime_nsec);
LARGE_INTEGER_to_time_t_nsec(&basic_info->LastWriteTime, &result->st_mtime, &result->st_mtime_nsec);
LARGE_INTEGER_to_time_t_nsec(&basic_info->LastAccessTime, &result->st_atime, &result->st_atime_nsec);
} else {
FILE_TIME_to_time_t_nsec(&info->ftCreationTime, &result->st_birthtime, &result->st_birthtime_nsec);
FILE_TIME_to_time_t_nsec(&info->ftLastWriteTime, &result->st_mtime, &result->st_mtime_nsec);
FILE_TIME_to_time_t_nsec(&info->ftLastAccessTime, &result->st_atime, &result->st_atime_nsec);
}
result->st_nlink = info->nNumberOfLinks;
if (id_info) {
id_128_to_ino file_id;
file_id.id = id_info->FileId;
result->st_ino = file_id.st_ino;
result->st_ino_high = file_id.st_ino_high;
}
if (!result->st_ino && !result->st_ino_high) {
result->st_ino = (((uint64_t)info->nFileIndexHigh) << 32) + info->nFileIndexLow;
}
result->st_reparse_tag = reparse_tag;
if (info->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT &&
reparse_tag == IO_REPARSE_TAG_SYMLINK) {
result->st_mode = (result->st_mode & ~S_IFMT) | S_IFLNK;
}
result->st_file_attributes = info->dwFileAttributes;
}
void
_Py_stat_basic_info_to_stat(FILE_STAT_BASIC_INFORMATION *info,
struct _Py_stat_struct *result)
{
memset(result, 0, sizeof(*result));
result->st_mode = attributes_to_mode(info->FileAttributes);
result->st_size = info->EndOfFile.QuadPart;
LARGE_INTEGER_to_time_t_nsec(&info->CreationTime, &result->st_birthtime, &result->st_birthtime_nsec);
LARGE_INTEGER_to_time_t_nsec(&info->ChangeTime, &result->st_ctime, &result->st_ctime_nsec);
LARGE_INTEGER_to_time_t_nsec(&info->LastWriteTime, &result->st_mtime, &result->st_mtime_nsec);
LARGE_INTEGER_to_time_t_nsec(&info->LastAccessTime, &result->st_atime, &result->st_atime_nsec);
result->st_nlink = info->NumberOfLinks;
result->st_dev = info->VolumeSerialNumber.QuadPart;
id_128_to_ino file_id;
file_id.id = info->FileId128;
result->st_ino = file_id.st_ino;
result->st_ino_high = file_id.st_ino_high;
result->st_reparse_tag = info->ReparseTag;
if (info->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT &&
info->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
result->st_mode = (result->st_mode & ~S_IFMT) | S_IFLNK;
}
result->st_file_attributes = info->FileAttributes;
switch (info->DeviceType) {
case FILE_DEVICE_DISK:
case FILE_DEVICE_VIRTUAL_DISK:
case FILE_DEVICE_DFS:
case FILE_DEVICE_CD_ROM:
case FILE_DEVICE_CONTROLLER:
case FILE_DEVICE_DATALINK:
break;
case FILE_DEVICE_DISK_FILE_SYSTEM:
case FILE_DEVICE_CD_ROM_FILE_SYSTEM:
case FILE_DEVICE_NETWORK_FILE_SYSTEM:
result->st_mode = (result->st_mode & ~S_IFMT) | 0x6000;
break;
case FILE_DEVICE_CONSOLE:
case FILE_DEVICE_NULL:
case FILE_DEVICE_KEYBOARD:
case FILE_DEVICE_MODEM:
case FILE_DEVICE_MOUSE:
case FILE_DEVICE_PARALLEL_PORT:
case FILE_DEVICE_PRINTER:
case FILE_DEVICE_SCREEN:
case FILE_DEVICE_SERIAL_PORT:
case FILE_DEVICE_SOUND:
result->st_mode = (result->st_mode & ~S_IFMT) | _S_IFCHR;
break;
case FILE_DEVICE_NAMED_PIPE:
result->st_mode = (result->st_mode & ~S_IFMT) | _S_IFIFO;
break;
default:
if (info->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
result->st_mode = (result->st_mode & ~S_IFMT) | _S_IFDIR;
}
break;
}
}
#endif
int
_Py_fstat_noraise(int fd, struct _Py_stat_struct *status)
{
#ifdef MS_WINDOWS
BY_HANDLE_FILE_INFORMATION info;
FILE_BASIC_INFO basicInfo;
FILE_ID_INFO idInfo;
HANDLE h;
int type;
h = _Py_get_osfhandle_noraise(fd);
if (h == INVALID_HANDLE_VALUE) {
SetLastError(ERROR_INVALID_HANDLE);
return -1;
}
memset(status, 0, sizeof(*status));
type = GetFileType(h);
if (type == FILE_TYPE_UNKNOWN) {
DWORD error = GetLastError();
if (error != 0) {
errno = winerror_to_errno(error);
return -1;
}
}
if (type != FILE_TYPE_DISK) {
if (type == FILE_TYPE_CHAR)
status->st_mode = _S_IFCHR;
else if (type == FILE_TYPE_PIPE)
status->st_mode = _S_IFIFO;
return 0;
}
if (!GetFileInformationByHandle(h, &info) ||
!GetFileInformationByHandleEx(h, FileBasicInfo, &basicInfo, sizeof(basicInfo)) ||
!GetFileInformationByHandleEx(h, FileIdInfo, &idInfo, sizeof(idInfo))) {
errno = winerror_to_errno(GetLastError());
return -1;
}
_Py_attribute_data_to_stat(&info, 0, &basicInfo, &idInfo, status);
return 0;
#else
return fstat(fd, status);
#endif
}
int
_Py_fstat(int fd, struct _Py_stat_struct *status)
{
int res;
assert(PyGILState_Check());
Py_BEGIN_ALLOW_THREADS
res = _Py_fstat_noraise(fd, status);
Py_END_ALLOW_THREADS
if (res != 0) {
#ifdef MS_WINDOWS
PyErr_SetFromWindowsErr(0);
#else
PyErr_SetFromErrno(PyExc_OSError);
#endif
return -1;
}
return 0;
}
int
_Py_wstat(const wchar_t* path, struct stat *buf)
{
int err;
#ifdef MS_WINDOWS
struct _stat wstatbuf;
err = _wstat(path, &wstatbuf);
if (!err) {
buf->st_mode = wstatbuf.st_mode;
}
#else
char *fname;
fname = _Py_EncodeLocaleRaw(path, NULL);
if (fname == NULL) {
errno = EINVAL;
return -1;
}
err = stat(fname, buf);
PyMem_RawFree(fname);
#endif
return err;
}
int
_Py_stat(PyObject *path, struct stat *statbuf)
{
#ifdef MS_WINDOWS
int err;
wchar_t *wpath = PyUnicode_AsWideCharString(path, NULL);
if (wpath == NULL)
return -2;
err = _Py_wstat(wpath, statbuf);
PyMem_Free(wpath);
return err;
#else
int ret;
PyObject *bytes;
char *cpath;
bytes = PyUnicode_EncodeFSDefault(path);
if (bytes == NULL)
return -2;
if (PyBytes_AsStringAndSize(bytes, &cpath, NULL) == -1) {
Py_DECREF(bytes);
return -2;
}
ret = stat(cpath, statbuf);
Py_DECREF(bytes);
return ret;
#endif
}
#ifdef MS_WINDOWS
#ifndef HANDLE_FLAG_INHERIT
#define HANDLE_FLAG_INHERIT 0x00000001
#endif
#endif
static int
get_inheritable(int fd, int raise)
{
#ifdef MS_WINDOWS
HANDLE handle;
DWORD flags;
handle = _Py_get_osfhandle_noraise(fd);
if (handle == INVALID_HANDLE_VALUE) {
if (raise)
PyErr_SetFromErrno(PyExc_OSError);
return -1;
}
if (!GetHandleInformation(handle, &flags)) {
if (raise)
PyErr_SetFromWindowsErr(0);
return -1;
}
return (flags & HANDLE_FLAG_INHERIT);
#else
int flags;
flags = fcntl(fd, F_GETFD, 0);
if (flags == -1) {
if (raise)
PyErr_SetFromErrno(PyExc_OSError);
return -1;
}
return !(flags & FD_CLOEXEC);
#endif
}
int
_Py_get_inheritable(int fd)
{
return get_inheritable(fd, 1);
}
static int
set_inheritable(int fd, int inheritable, int raise, int *atomic_flag_works)
{
#ifdef MS_WINDOWS
HANDLE handle;
DWORD flags;
#else
#if defined(HAVE_SYS_IOCTL_H) && defined(FIOCLEX) && defined(FIONCLEX)
static int ioctl_works = -1;
int request;
int err;
#endif
int flags, new_flags;
int res;
#endif
assert(!(atomic_flag_works != NULL && inheritable));
if (atomic_flag_works != NULL && !inheritable) {
if (*atomic_flag_works == -1) {
int isInheritable = get_inheritable(fd, raise);
if (isInheritable == -1)
return -1;
*atomic_flag_works = !isInheritable;
}
if (*atomic_flag_works)
return 0;
}
#ifdef MS_WINDOWS
handle = _Py_get_osfhandle_noraise(fd);
if (handle == INVALID_HANDLE_VALUE) {
if (raise)
PyErr_SetFromErrno(PyExc_OSError);
return -1;
}
if (inheritable)
flags = HANDLE_FLAG_INHERIT;
else
flags = 0;
if (!SetHandleInformation(handle, HANDLE_FLAG_INHERIT, flags)) {
if (raise)
PyErr_SetFromWindowsErr(0);
return -1;
}
return 0;
#else
#if defined(HAVE_SYS_IOCTL_H) && defined(FIOCLEX) && defined(FIONCLEX)
if (ioctl_works != 0 && raise != 0) {
if (inheritable)
request = FIONCLEX;
else
request = FIOCLEX;
err = ioctl(fd, request, NULL);
if (!err) {
ioctl_works = 1;
return 0;
}
#ifdef O_PATH
if (errno == EBADF) {
}
else
#endif
if (errno != ENOTTY && errno != EACCES) {
if (raise)
PyErr_SetFromErrno(PyExc_OSError);
return -1;
}
else {
ioctl_works = 0;
}
}
#endif
flags = fcntl(fd, F_GETFD);
if (flags < 0) {
if (raise)
PyErr_SetFromErrno(PyExc_OSError);
return -1;
}
if (inheritable) {
new_flags = flags & ~FD_CLOEXEC;
}
else {
new_flags = flags | FD_CLOEXEC;
}
if (new_flags == flags) {
return 0;
}
res = fcntl(fd, F_SETFD, new_flags);
if (res < 0) {
if (raise)
PyErr_SetFromErrno(PyExc_OSError);
return -1;
}
return 0;
#endif
}
static int
make_non_inheritable(int fd)
{
return set_inheritable(fd, 0, 0, NULL);
}
int
_Py_set_inheritable(int fd, int inheritable, int *atomic_flag_works)
{
return set_inheritable(fd, inheritable, 1, atomic_flag_works);
}
int
_Py_set_inheritable_async_safe(int fd, int inheritable, int *atomic_flag_works)
{
return set_inheritable(fd, inheritable, 0, atomic_flag_works);
}
static int
_Py_open_impl(const char *pathname, int flags, int gil_held)
{
int fd;
int async_err = 0;
#ifndef MS_WINDOWS
int *atomic_flag_works;
#endif
#ifdef MS_WINDOWS
flags |= O_NOINHERIT;
#elif defined(O_CLOEXEC)
atomic_flag_works = &_Py_open_cloexec_works;
flags |= O_CLOEXEC;
#else
atomic_flag_works = NULL;
#endif
if (gil_held) {
PyObject *pathname_obj = PyUnicode_DecodeFSDefault(pathname);
if (pathname_obj == NULL) {
return -1;
}
if (PySys_Audit("open", "OOi", pathname_obj, Py_None, flags) < 0) {
Py_DECREF(pathname_obj);
return -1;
}
do {
Py_BEGIN_ALLOW_THREADS
fd = open(pathname, flags);
Py_END_ALLOW_THREADS
} while (fd < 0
&& errno == EINTR && !(async_err = PyErr_CheckSignals()));
if (async_err) {
Py_DECREF(pathname_obj);
return -1;
}
if (fd < 0) {
PyErr_SetFromErrnoWithFilenameObjects(PyExc_OSError, pathname_obj, NULL);
Py_DECREF(pathname_obj);
return -1;
}
Py_DECREF(pathname_obj);
}
else {
fd = open(pathname, flags);
if (fd < 0)
return -1;
}
#ifndef MS_WINDOWS
if (set_inheritable(fd, 0, gil_held, atomic_flag_works) < 0) {
close(fd);
return -1;
}
#endif
return fd;
}
int
_Py_open(const char *pathname, int flags)
{
assert(PyGILState_Check());
return _Py_open_impl(pathname, flags, 1);
}
int
_Py_open_noraise(const char *pathname, int flags)
{
return _Py_open_impl(pathname, flags, 0);
}
FILE *
_Py_wfopen(const wchar_t *path, const wchar_t *mode)
{
FILE *f;
if (PySys_Audit("open", "uui", path, mode, 0) < 0) {
return NULL;
}
#ifndef MS_WINDOWS
char *cpath;
char cmode[10];
size_t r;
r = wcstombs(cmode, mode, 10);
if (r == DECODE_ERROR || r >= 10) {
errno = EINVAL;
return NULL;
}
cpath = _Py_EncodeLocaleRaw(path, NULL);
if (cpath == NULL) {
return NULL;
}
f = fopen(cpath, cmode);
PyMem_RawFree(cpath);
#else
f = _wfopen(path, mode);
#endif
if (f == NULL)
return NULL;
if (make_non_inheritable(fileno(f)) < 0) {
fclose(f);
return NULL;
}
return f;
}
FILE*
_Py_fopen_obj(PyObject *path, const char *mode)
{
FILE *f;
int async_err = 0;
#ifdef MS_WINDOWS
wchar_t wmode[10];
int usize;
assert(PyGILState_Check());
if (PySys_Audit("open", "Osi", path, mode, 0) < 0) {
return NULL;
}
if (!PyUnicode_Check(path)) {
PyErr_Format(PyExc_TypeError,
"str file path expected under Windows, got %R",
Py_TYPE(path));
return NULL;
}
wchar_t *wpath = PyUnicode_AsWideCharString(path, NULL);
if (wpath == NULL)
return NULL;
usize = MultiByteToWideChar(CP_ACP, 0, mode, -1,
wmode, Py_ARRAY_LENGTH(wmode));
if (usize == 0) {
PyErr_SetFromWindowsErr(0);
PyMem_Free(wpath);
return NULL;
}
do {
Py_BEGIN_ALLOW_THREADS
f = _wfopen(wpath, wmode);
Py_END_ALLOW_THREADS
} while (f == NULL
&& errno == EINTR && !(async_err = PyErr_CheckSignals()));
PyMem_Free(wpath);
#else
PyObject *bytes;
const char *path_bytes;
assert(PyGILState_Check());
if (!PyUnicode_FSConverter(path, &bytes))
return NULL;
path_bytes = PyBytes_AS_STRING(bytes);
if (PySys_Audit("open", "Osi", path, mode, 0) < 0) {
Py_DECREF(bytes);
return NULL;
}
do {
Py_BEGIN_ALLOW_THREADS
f = fopen(path_bytes, mode);
Py_END_ALLOW_THREADS
} while (f == NULL
&& errno == EINTR && !(async_err = PyErr_CheckSignals()));
Py_DECREF(bytes);
#endif
if (async_err)
return NULL;
if (f == NULL) {
PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path);
return NULL;
}
if (set_inheritable(fileno(f), 0, 1, NULL) < 0) {
fclose(f);
return NULL;
}
return f;
}
Py_ssize_t
_Py_read(int fd, void *buf, size_t count)
{
Py_ssize_t n;
int err;
int async_err = 0;
assert(PyGILState_Check());
assert(!PyErr_Occurred());
if (count > _PY_READ_MAX) {
count = _PY_READ_MAX;
}
_Py_BEGIN_SUPPRESS_IPH
do {
Py_BEGIN_ALLOW_THREADS
errno = 0;
#ifdef MS_WINDOWS
_doserrno = 0;
n = read(fd, buf, (int)count);
if (n < 0 && errno == EINVAL) {
if (_doserrno == ERROR_NO_DATA) {
errno = EAGAIN;
}
}
#else
n = read(fd, buf, count);
#endif
err = errno;
Py_END_ALLOW_THREADS
} while (n < 0 && err == EINTR &&
!(async_err = PyErr_CheckSignals()));
_Py_END_SUPPRESS_IPH
if (async_err) {
errno = err;
assert(errno == EINTR && PyErr_Occurred());
return -1;
}
if (n < 0) {
PyErr_SetFromErrno(PyExc_OSError);
errno = err;
return -1;
}
return n;
}
static Py_ssize_t
_Py_write_impl(int fd, const void *buf, size_t count, int gil_held)
{
Py_ssize_t n;
int err;
int async_err = 0;
_Py_BEGIN_SUPPRESS_IPH
#ifdef MS_WINDOWS
if (count > 32767) {
if (gil_held) {
Py_BEGIN_ALLOW_THREADS
if (isatty(fd)) {
count = 32767;
}
Py_END_ALLOW_THREADS
} else {
if (isatty(fd)) {
count = 32767;
}
}
}
#endif
if (count > _PY_WRITE_MAX) {
count = _PY_WRITE_MAX;
}
if (gil_held) {
do {
Py_BEGIN_ALLOW_THREADS
errno = 0;
#ifdef MS_WINDOWS
int c = (int)count;
do {
_doserrno = 0;
n = write(fd, buf, c);
if (n >= 0 || errno != ENOSPC || _doserrno != 0) {
break;
}
errno = EAGAIN;
c /= 2;
} while (c > 0);
#else
n = write(fd, buf, count);
#endif
err = errno;
Py_END_ALLOW_THREADS
} while (n < 0 && err == EINTR &&
!(async_err = PyErr_CheckSignals()));
}
else {
do {
errno = 0;
#ifdef MS_WINDOWS
int c = (int)count;
do {
_doserrno = 0;
n = write(fd, buf, c);
if (n >= 0 || errno != ENOSPC || _doserrno != 0) {
break;
}
errno = EAGAIN;
c /= 2;
} while (c > 0);
#else
n = write(fd, buf, count);
#endif
err = errno;
} while (n < 0 && err == EINTR);
}
_Py_END_SUPPRESS_IPH
if (async_err) {
errno = err;
assert(errno == EINTR && (!gil_held || PyErr_Occurred()));
return -1;
}
if (n < 0) {
if (gil_held)
PyErr_SetFromErrno(PyExc_OSError);
errno = err;
return -1;
}
return n;
}
Py_ssize_t
_Py_write(int fd, const void *buf, size_t count)
{
assert(PyGILState_Check());
assert(!PyErr_Occurred());
return _Py_write_impl(fd, buf, count, 1);
}
Py_ssize_t
_Py_write_noraise(int fd, const void *buf, size_t count)
{
return _Py_write_impl(fd, buf, count, 0);
}
#ifdef HAVE_READLINK
int
_Py_wreadlink(const wchar_t *path, wchar_t *buf, size_t buflen)
{
char *cpath;
char cbuf[MAXPATHLEN];
size_t cbuf_len = Py_ARRAY_LENGTH(cbuf);
wchar_t *wbuf;
Py_ssize_t res;
size_t r1;
cpath = _Py_EncodeLocaleRaw(path, NULL);
if (cpath == NULL) {
errno = EINVAL;
return -1;
}
res = readlink(cpath, cbuf, cbuf_len);
PyMem_RawFree(cpath);
if (res == -1) {
return -1;
}
if ((size_t)res == cbuf_len) {
errno = EINVAL;
return -1;
}
cbuf[res] = '\0';
wbuf = Py_DecodeLocale(cbuf, &r1);
if (wbuf == NULL) {
errno = EINVAL;
return -1;
}
if (buflen <= r1) {
PyMem_RawFree(wbuf);
errno = EINVAL;
return -1;
}
wcsncpy(buf, wbuf, buflen);
PyMem_RawFree(wbuf);
return (int)r1;
}
#endif
#ifdef HAVE_REALPATH
wchar_t*
_Py_wrealpath(const wchar_t *path,
wchar_t *resolved_path, size_t resolved_path_len)
{
char *cpath;
char cresolved_path[MAXPATHLEN];
wchar_t *wresolved_path;
char *res;
size_t r;
cpath = _Py_EncodeLocaleRaw(path, NULL);
if (cpath == NULL) {
errno = EINVAL;
return NULL;
}
res = realpath(cpath, cresolved_path);
PyMem_RawFree(cpath);
if (res == NULL)
return NULL;
wresolved_path = Py_DecodeLocale(cresolved_path, &r);
if (wresolved_path == NULL) {
errno = EINVAL;
return NULL;
}
if (resolved_path_len <= r) {
PyMem_RawFree(wresolved_path);
errno = EINVAL;
return NULL;
}
wcsncpy(resolved_path, wresolved_path, resolved_path_len);
PyMem_RawFree(wresolved_path);
return resolved_path;
}
#endif
int
_Py_isabs(const wchar_t *path)
{
#ifdef MS_WINDOWS
const wchar_t *tail;
HRESULT hr = PathCchSkipRoot(path, &tail);
if (FAILED(hr) || path == tail) {
return 0;
}
if (tail == &path[1] && (path[0] == SEP || path[0] == ALTSEP)) {
return 0;
}
if (tail == &path[2] && path[1] == L':') {
return 0;
}
return 1;
#else
return (path[0] == SEP);
#endif
}
int
_Py_abspath(const wchar_t *path, wchar_t **abspath_p)
{
if (path[0] == '\0' || !wcscmp(path, L".")) {
wchar_t cwd[MAXPATHLEN + 1];
cwd[Py_ARRAY_LENGTH(cwd) - 1] = 0;
if (!_Py_wgetcwd(cwd, Py_ARRAY_LENGTH(cwd) - 1)) {
return -1;
}
*abspath_p = _PyMem_RawWcsdup(cwd);
return 0;
}
if (_Py_isabs(path)) {
*abspath_p = _PyMem_RawWcsdup(path);
return 0;
}
#ifdef MS_WINDOWS
return _PyOS_getfullpathname(path, abspath_p);
#else
wchar_t cwd[MAXPATHLEN + 1];
cwd[Py_ARRAY_LENGTH(cwd) - 1] = 0;
if (!_Py_wgetcwd(cwd, Py_ARRAY_LENGTH(cwd) - 1)) {
return -1;
}
size_t cwd_len = wcslen(cwd);
size_t path_len = wcslen(path);
size_t len = cwd_len + 1 + path_len + 1;
if (len <= (size_t)PY_SSIZE_T_MAX / sizeof(wchar_t)) {
*abspath_p = PyMem_RawMalloc(len * sizeof(wchar_t));
}
else {
*abspath_p = NULL;
}
if (*abspath_p == NULL) {
return 0;
}
wchar_t *abspath = *abspath_p;
memcpy(abspath, cwd, cwd_len * sizeof(wchar_t));
abspath += cwd_len;
*abspath = (wchar_t)SEP;
abspath++;
memcpy(abspath, path, path_len * sizeof(wchar_t));
abspath += path_len;
*abspath = 0;
return 0;
#endif
}
#if defined(MS_WINDOWS_GAMES) && !defined(MS_WINDOWS_DESKTOP)
HRESULT
PathCchSkipRoot(const wchar_t *path, const wchar_t **rootEnd)
{
static int initialized = 0;
typedef HRESULT(__stdcall *PPathCchSkipRoot) (PCWSTR pszPath,
PCWSTR *ppszRootEnd);
static PPathCchSkipRoot _PathCchSkipRoot;
if (initialized == 0) {
HMODULE pathapi = LoadLibraryExW(L"api-ms-win-core-path-l1-1-0.dll", NULL,
LOAD_LIBRARY_SEARCH_SYSTEM32);
if (pathapi) {
_PathCchSkipRoot = (PPathCchSkipRoot)GetProcAddress(
pathapi, "PathCchSkipRoot");
}
else {
_PathCchSkipRoot = NULL;
}
initialized = 1;
}
if (!_PathCchSkipRoot) {
return E_NOINTERFACE;
}
return _PathCchSkipRoot(path, rootEnd);
}
static HRESULT
PathCchCombineEx(wchar_t *buffer, size_t bufsize, const wchar_t *dirname,
const wchar_t *relfile, unsigned long flags)
{
static int initialized = 0;
typedef HRESULT(__stdcall *PPathCchCombineEx) (PWSTR pszPathOut,
size_t cchPathOut,
PCWSTR pszPathIn,
PCWSTR pszMore,
unsigned long dwFlags);
static PPathCchCombineEx _PathCchCombineEx;
if (initialized == 0) {
HMODULE pathapi = LoadLibraryExW(L"api-ms-win-core-path-l1-1-0.dll", NULL,
LOAD_LIBRARY_SEARCH_SYSTEM32);
if (pathapi) {
_PathCchCombineEx = (PPathCchCombineEx)GetProcAddress(
pathapi, "PathCchCombineEx");
}
else {
_PathCchCombineEx = NULL;
}
initialized = 1;
}
if (!_PathCchCombineEx) {
return E_NOINTERFACE;
}
return _PathCchCombineEx(buffer, bufsize, dirname, relfile, flags);
}
#endif
static int
join_relfile(wchar_t *buffer, size_t bufsize,
const wchar_t *dirname, const wchar_t *relfile)
{
#ifdef MS_WINDOWS
if (FAILED(PathCchCombineEx(buffer, bufsize, dirname, relfile,
PATHCCH_ALLOW_LONG_PATHS))) {
return -1;
}
#else
assert(!_Py_isabs(relfile));
size_t dirlen = wcslen(dirname);
size_t rellen = wcslen(relfile);
size_t maxlen = bufsize - 1;
if (maxlen > MAXPATHLEN || dirlen >= maxlen || rellen >= maxlen - dirlen) {
return -1;
}
if (dirlen == 0) {
wcscpy(buffer, relfile);
}
else {
if (dirname != buffer) {
wcscpy(buffer, dirname);
}
size_t relstart = dirlen;
if (dirlen > 1 && dirname[dirlen - 1] != SEP) {
buffer[dirlen] = SEP;
relstart += 1;
}
wcscpy(&buffer[relstart], relfile);
}
#endif
return 0;
}
wchar_t *
_Py_join_relfile(const wchar_t *dirname, const wchar_t *relfile)
{
assert(dirname != NULL && relfile != NULL);
#ifndef MS_WINDOWS
assert(!_Py_isabs(relfile));
#endif
size_t maxlen = wcslen(dirname) + 1 + wcslen(relfile);
size_t bufsize = maxlen + 1;
wchar_t *filename = PyMem_RawMalloc(bufsize * sizeof(wchar_t));
if (filename == NULL) {
return NULL;
}
assert(wcslen(dirname) < MAXPATHLEN);
assert(wcslen(relfile) < MAXPATHLEN - wcslen(dirname));
if (join_relfile(filename, bufsize, dirname, relfile) < 0) {
PyMem_RawFree(filename);
return NULL;
}
return filename;
}
int
_Py_add_relfile(wchar_t *dirname, const wchar_t *relfile, size_t bufsize)
{
assert(dirname != NULL && relfile != NULL);
assert(bufsize > 0);
return join_relfile(dirname, bufsize, dirname, relfile);
}
size_t
_Py_find_basename(const wchar_t *filename)
{
for (size_t i = wcslen(filename); i > 0; --i) {
if (filename[i] == SEP) {
return i + 1;
}
}
return 0;
}
wchar_t *
_Py_normpath(wchar_t *path, Py_ssize_t size)
{
assert(path != NULL);
if (!path[0] || size == 0) {
return path;
}
wchar_t *pEnd = size >= 0 ? &path[size] : NULL;
wchar_t *p1 = path;
wchar_t *p2 = path;
wchar_t *minP2 = path;
wchar_t lastC = L'\0';
#define IS_END(x) (pEnd ? (x) == pEnd : !*(x))
#ifdef ALTSEP
#define IS_SEP(x) (*(x) == SEP || *(x) == ALTSEP)
#else
#define IS_SEP(x) (*(x) == SEP)
#endif
#define SEP_OR_END(x) (IS_SEP(x) || IS_END(x))
if (p1[0] == L'.' && IS_SEP(&p1[1])) {
path = &path[2];
while (IS_SEP(path) && !IS_END(path)) {
path++;
}
p1 = p2 = minP2 = path;
lastC = SEP;
}
#ifdef MS_WINDOWS
else if (p1[0] && p1[1] == L':') {
*p2++ = *p1++;
*p2++ = *p1++;
minP2 = p2;
lastC = L':';
}
else if (IS_SEP(&p1[0]) && IS_SEP(&p1[1])) {
int sepCount = 2;
*p2++ = SEP;
*p2++ = SEP;
p1 += 2;
for (; !IS_END(p1) && sepCount; ++p1) {
if (IS_SEP(p1)) {
--sepCount;
*p2++ = lastC = SEP;
} else {
*p2++ = lastC = *p1;
}
}
if (sepCount) {
minP2 = p2;
} else {
minP2 = p2 - 1;
}
}
#else
else if (IS_SEP(&p1[0]) && IS_SEP(&p1[1]) && !IS_SEP(&p1[2])) {
*p2++ = *p1++;
*p2++ = *p1++;
minP2 = p2 - 1;
lastC = SEP;
}
#endif
for (; !IS_END(p1); ++p1) {
wchar_t c = *p1;
#ifdef ALTSEP
if (c == ALTSEP) {
c = SEP;
}
#endif
if (lastC == SEP) {
if (c == L'.') {
int sep_at_1 = SEP_OR_END(&p1[1]);
int sep_at_2 = !sep_at_1 && SEP_OR_END(&p1[2]);
if (sep_at_2 && p1[1] == L'.') {
wchar_t *p3 = p2;
while (p3 != minP2 && *--p3 == SEP) { }
while (p3 != minP2 && *(p3 - 1) != SEP) { --p3; }
if (p2 == minP2
|| (p3[0] == L'.' && p3[1] == L'.' && IS_SEP(&p3[2])))
{
*p2++ = L'.';
*p2++ = L'.';
lastC = L'.';
} else if (p3[0] == SEP) {
p2 = p3 + 1;
} else {
p2 = p3;
}
p1 += 1;
} else if (sep_at_1) {
} else {
*p2++ = lastC = c;
}
} else if (c == SEP) {
} else {
*p2++ = lastC = c;
}
} else {
*p2++ = lastC = c;
}
}
*p2 = L'\0';
if (p2 != minP2) {
while (--p2 != minP2 && *p2 == SEP) {
*p2 = L'\0';
}
}
#undef SEP_OR_END
#undef IS_SEP
#undef IS_END
return path;
}
wchar_t*
_Py_wgetcwd(wchar_t *buf, size_t buflen)
{
#ifdef MS_WINDOWS
int ibuflen = (int)Py_MIN(buflen, INT_MAX);
return _wgetcwd(buf, ibuflen);
#else
char fname[MAXPATHLEN];
wchar_t *wname;
size_t len;
if (getcwd(fname, Py_ARRAY_LENGTH(fname)) == NULL)
return NULL;
wname = Py_DecodeLocale(fname, &len);
if (wname == NULL)
return NULL;
if (buflen <= len) {
PyMem_RawFree(wname);
return NULL;
}
wcsncpy(buf, wname, buflen);
PyMem_RawFree(wname);
return buf;
#endif
}
int
_Py_dup(int fd)
{
#ifdef MS_WINDOWS
HANDLE handle;
#endif
assert(PyGILState_Check());
#ifdef MS_WINDOWS
handle = _Py_get_osfhandle(fd);
if (handle == INVALID_HANDLE_VALUE)
return -1;
Py_BEGIN_ALLOW_THREADS
_Py_BEGIN_SUPPRESS_IPH
fd = dup(fd);
_Py_END_SUPPRESS_IPH
Py_END_ALLOW_THREADS
if (fd < 0) {
PyErr_SetFromErrno(PyExc_OSError);
return -1;
}
if (_Py_set_inheritable(fd, 0, NULL) < 0) {
_Py_BEGIN_SUPPRESS_IPH
close(fd);
_Py_END_SUPPRESS_IPH
return -1;
}
#elif defined(HAVE_FCNTL_H) && defined(F_DUPFD_CLOEXEC)
Py_BEGIN_ALLOW_THREADS
_Py_BEGIN_SUPPRESS_IPH
fd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
_Py_END_SUPPRESS_IPH
Py_END_ALLOW_THREADS
if (fd < 0) {
PyErr_SetFromErrno(PyExc_OSError);
return -1;
}
#elif HAVE_DUP
Py_BEGIN_ALLOW_THREADS
_Py_BEGIN_SUPPRESS_IPH
fd = dup(fd);
_Py_END_SUPPRESS_IPH
Py_END_ALLOW_THREADS
if (fd < 0) {
PyErr_SetFromErrno(PyExc_OSError);
return -1;
}
if (_Py_set_inheritable(fd, 0, NULL) < 0) {
_Py_BEGIN_SUPPRESS_IPH
close(fd);
_Py_END_SUPPRESS_IPH
return -1;
}
#else
errno = ENOTSUP;
PyErr_SetFromErrno(PyExc_OSError);
return -1;
#endif
return fd;
}
#ifndef MS_WINDOWS
int
_Py_get_blocking(int fd)
{
int flags;
_Py_BEGIN_SUPPRESS_IPH
flags = fcntl(fd, F_GETFL, 0);
_Py_END_SUPPRESS_IPH
if (flags < 0) {
PyErr_SetFromErrno(PyExc_OSError);
return -1;
}
return !(flags & O_NONBLOCK);
}
int
_Py_set_blocking(int fd, int blocking)
{
#if defined(HAVE_SYS_IOCTL_H) && defined(FIONBIO) && !defined(__VXWORKS__)
int arg = !blocking;
if (ioctl(fd, FIONBIO, &arg) < 0)
goto error;
#else
int flags, res;
_Py_BEGIN_SUPPRESS_IPH
flags = fcntl(fd, F_GETFL, 0);
if (flags >= 0) {
if (blocking)
flags = flags & (~O_NONBLOCK);
else
flags = flags | O_NONBLOCK;
res = fcntl(fd, F_SETFL, flags);
} else {
res = -1;
}
_Py_END_SUPPRESS_IPH
if (res < 0)
goto error;
#endif
return 0;
error:
PyErr_SetFromErrno(PyExc_OSError);
return -1;
}
#else
int
_Py_get_blocking(int fd)
{
HANDLE handle;
DWORD mode;
BOOL success;
handle = _Py_get_osfhandle(fd);
if (handle == INVALID_HANDLE_VALUE) {
return -1;
}
Py_BEGIN_ALLOW_THREADS
success = GetNamedPipeHandleStateW(handle, &mode,
NULL, NULL, NULL, NULL, 0);
Py_END_ALLOW_THREADS
if (!success) {
PyErr_SetFromWindowsErr(0);
return -1;
}
return !(mode & PIPE_NOWAIT);
}
int
_Py_set_blocking(int fd, int blocking)
{
HANDLE handle;
DWORD mode;
BOOL success;
handle = _Py_get_osfhandle(fd);
if (handle == INVALID_HANDLE_VALUE) {
return -1;
}
Py_BEGIN_ALLOW_THREADS
success = GetNamedPipeHandleStateW(handle, &mode,
NULL, NULL, NULL, NULL, 0);
if (success) {
if (blocking) {
mode &= ~PIPE_NOWAIT;
}
else {
mode |= PIPE_NOWAIT;
}
success = SetNamedPipeHandleState(handle, &mode, NULL, NULL);
}
Py_END_ALLOW_THREADS
if (!success) {
PyErr_SetFromWindowsErr(0);
return -1;
}
return 0;
}
void*
_Py_get_osfhandle_noraise(int fd)
{
void *handle;
_Py_BEGIN_SUPPRESS_IPH
handle = (void*)_get_osfhandle(fd);
_Py_END_SUPPRESS_IPH
return handle;
}
void*
_Py_get_osfhandle(int fd)
{
void *handle = _Py_get_osfhandle_noraise(fd);
if (handle == INVALID_HANDLE_VALUE)
PyErr_SetFromErrno(PyExc_OSError);
return handle;
}
int
_Py_open_osfhandle_noraise(void *handle, int flags)
{
int fd;
_Py_BEGIN_SUPPRESS_IPH
fd = _open_osfhandle((intptr_t)handle, flags);
_Py_END_SUPPRESS_IPH
return fd;
}
int
_Py_open_osfhandle(void *handle, int flags)
{
int fd = _Py_open_osfhandle_noraise(handle, flags);
if (fd == -1)
PyErr_SetFromErrno(PyExc_OSError);
return fd;
}
#endif
int
_Py_GetLocaleconvNumeric(struct lconv *lc,
PyObject **decimal_point, PyObject **thousands_sep)
{
assert(decimal_point != NULL);
assert(thousands_sep != NULL);
#ifndef MS_WINDOWS
int change_locale = 0;
if ((strlen(lc->decimal_point) > 1 || ((unsigned char)lc->decimal_point[0]) > 127)) {
change_locale = 1;
}
if ((strlen(lc->thousands_sep) > 1 || ((unsigned char)lc->thousands_sep[0]) > 127)) {
change_locale = 1;
}
char *oldloc = NULL, *loc = NULL;
if (change_locale) {
oldloc = setlocale(LC_CTYPE, NULL);
if (!oldloc) {
PyErr_SetString(PyExc_RuntimeWarning,
"failed to get LC_CTYPE locale");
return -1;
}
oldloc = _PyMem_Strdup(oldloc);
if (!oldloc) {
PyErr_NoMemory();
return -1;
}
loc = setlocale(LC_NUMERIC, NULL);
if (loc != NULL && strcmp(loc, oldloc) == 0) {
loc = NULL;
}
if (loc != NULL) {
setlocale(LC_CTYPE, loc);
}
}
#define GET_LOCALE_STRING(ATTR) PyUnicode_DecodeLocale(lc->ATTR, NULL)
#else
#define GET_LOCALE_STRING(ATTR) PyUnicode_FromWideChar(lc->_W_ ## ATTR, -1)
#endif
int res = -1;
*decimal_point = GET_LOCALE_STRING(decimal_point);
if (*decimal_point == NULL) {
goto done;
}
*thousands_sep = GET_LOCALE_STRING(thousands_sep);
if (*thousands_sep == NULL) {
goto done;
}
res = 0;
done:
#ifndef MS_WINDOWS
if (loc != NULL) {
setlocale(LC_CTYPE, oldloc);
}
PyMem_Free(oldloc);
#endif
return res;
#undef GET_LOCALE_STRING
}
#ifdef __FreeBSD__
# define USE_CLOSEFROM
#endif
#ifdef HAVE_FDWALK
# define USE_FDWALK
#endif
#ifdef USE_FDWALK
static int
_fdwalk_close_func(void *lohi, int fd)
{
int lo = ((int *)lohi)[0];
int hi = ((int *)lohi)[1];
if (fd >= hi) {
return 1;
}
else if (fd >= lo) {
(void)close(fd);
}
return 0;
}
#endif
void
_Py_closerange(int first, int last)
{
first = Py_MAX(first, 0);
_Py_BEGIN_SUPPRESS_IPH
#ifdef HAVE_CLOSE_RANGE
if (close_range(first, last, 0) == 0) {
}
else
#endif
#ifdef USE_CLOSEFROM
if (last >= sysconf(_SC_OPEN_MAX)) {
closefrom(first);
}
else
#endif
#ifdef USE_FDWALK
{
int lohi[2];
lohi[0] = first;
lohi[1] = last + 1;
fdwalk(_fdwalk_close_func, lohi);
}
#else
{
for (int i = first; i <= last; i++) {
(void)close(i);
}
}
#endif
_Py_END_SUPPRESS_IPH
}