mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 11:37:35 +00:00
e16a7d8f3b
`et` means `expandtab`. ```sh rg 'vi: .* :vi' -l -0 | \ xargs -0 sed -i '' 's/vi: \(.*\) et\(.*\) :vi/vi: \1 xoet\2:vi/' rg 'vi: .* :vi' -l -0 | \ xargs -0 sed -i '' 's/vi: \(.*\)noet\(.*\):vi/vi: \1et\2 :vi/' rg 'vi: .* :vi' -l -0 | \ xargs -0 sed -i '' 's/vi: \(.*\)xoet\(.*\):vi/vi: \1noet\2:vi/' ```
330 lines
10 KiB
C
330 lines
10 KiB
C
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8 -*-│
|
|
│ vi: set et ft=c ts=4 sts=4 sw=4 fenc=utf-8 :vi │
|
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
│ Python 3 │
|
|
│ https://docs.python.org/3/license.html │
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
#include "libc/calls/calls.h"
|
|
#include "libc/calls/internal.h"
|
|
#include "libc/calls/struct/stat.h"
|
|
#include "libc/str/locale.h"
|
|
#include "third_party/python/Include/abstract.h"
|
|
#include "third_party/python/Include/boolobject.h"
|
|
#include "third_party/python/Include/codecs.h"
|
|
#include "third_party/python/Include/import.h"
|
|
#include "third_party/python/Include/object.h"
|
|
#include "third_party/python/Include/pydebug.h"
|
|
#include "third_party/python/Include/pyerrors.h"
|
|
#include "third_party/python/Include/pymem.h"
|
|
#include "third_party/python/Include/sysmodule.h"
|
|
#include "third_party/python/Include/yoink.h"
|
|
|
|
/* PYTHON_YOINK("io"); */
|
|
/* PYTHON_YOINK("encodings.aliases"); */
|
|
/* PYTHON_YOINK("encodings.latin_1"); */
|
|
/* PYTHON_YOINK("encodings.utf_8"); */
|
|
|
|
_Py_IDENTIFIER(name);
|
|
_Py_IDENTIFIER(stdin);
|
|
_Py_IDENTIFIER(stdout);
|
|
_Py_IDENTIFIER(stderr);
|
|
|
|
char *_Py_StandardStreamEncoding;
|
|
char *_Py_StandardStreamErrors;
|
|
|
|
/* Check if a file descriptor is valid or not.
|
|
Return 0 if the file descriptor is invalid, return non-zero otherwise. */
|
|
static int
|
|
is_valid_fd(int fd)
|
|
{
|
|
if (IsWindows()) {
|
|
return __isfdopen(fd);
|
|
}
|
|
if (IsXnu()) {
|
|
/* bpo-30225: On macOS Tiger, when stdout is redirected to a pipe
|
|
and the other side of the pipe is closed, dup(1) succeed, whereas
|
|
fstat(1, &st) fails with EBADF. Prefer fstat() over dup() to detect
|
|
such error. */
|
|
struct stat st;
|
|
return fstat(fd, &st) == 0;
|
|
}
|
|
int fd2;
|
|
if (fd < 0)
|
|
return 0;
|
|
_Py_BEGIN_SUPPRESS_IPH
|
|
/* Prefer dup() over fstat(). fstat() can require input/output whereas
|
|
dup() doesn't, there is a low risk of EMFILE/ENFILE at Python
|
|
startup. */
|
|
fd2 = dup(fd);
|
|
if (fd2 >= 0)
|
|
close(fd2);
|
|
_Py_END_SUPPRESS_IPH
|
|
return fd2 >= 0;
|
|
}
|
|
|
|
/* returns Py_None if the fd is not valid */
|
|
static PyObject*
|
|
create_stdio(PyObject* io,
|
|
int fd, int write_mode, const char* name,
|
|
const char* encoding, const char* errors)
|
|
{
|
|
PyObject *buf = NULL, *stream = NULL, *text = NULL, *raw = NULL, *res;
|
|
const char* mode;
|
|
const char* newline;
|
|
PyObject *line_buffering;
|
|
int buffering, isatty;
|
|
_Py_IDENTIFIER(open);
|
|
_Py_IDENTIFIER(isatty);
|
|
_Py_IDENTIFIER(TextIOWrapper);
|
|
_Py_IDENTIFIER(mode);
|
|
|
|
if (!is_valid_fd(fd))
|
|
Py_RETURN_NONE;
|
|
|
|
/* stdin is always opened in buffered mode, first because it shouldn't
|
|
make a difference in common use cases, second because TextIOWrapper
|
|
depends on the presence of a read1() method which only exists on
|
|
buffered streams.
|
|
*/
|
|
if (Py_UnbufferedStdioFlag && write_mode)
|
|
buffering = 0;
|
|
else
|
|
buffering = -1;
|
|
if (write_mode)
|
|
mode = "wb";
|
|
else
|
|
mode = "rb";
|
|
buf = _PyObject_CallMethodId(io, &PyId_open, "isiOOOi",
|
|
fd, mode, buffering,
|
|
Py_None, Py_None, /* encoding, errors */
|
|
Py_None, 0); /* newline, closefd */
|
|
if (buf == NULL)
|
|
goto error;
|
|
|
|
if (buffering) {
|
|
_Py_IDENTIFIER(raw);
|
|
raw = _PyObject_GetAttrId(buf, &PyId_raw);
|
|
if (raw == NULL)
|
|
goto error;
|
|
}
|
|
else {
|
|
raw = buf;
|
|
Py_INCREF(raw);
|
|
}
|
|
|
|
#ifdef MS_WINDOWS
|
|
/* Windows console IO is always UTF-8 encoded */
|
|
if (PyWindowsConsoleIO_Check(raw))
|
|
encoding = "utf-8";
|
|
#endif
|
|
|
|
text = PyUnicode_FromString(name);
|
|
if (text == NULL || _PyObject_SetAttrId(raw, &PyId_name, text) < 0)
|
|
goto error;
|
|
res = _PyObject_CallMethodId(raw, &PyId_isatty, NULL);
|
|
if (res == NULL)
|
|
goto error;
|
|
isatty = PyObject_IsTrue(res);
|
|
Py_DECREF(res);
|
|
if (isatty == -1)
|
|
goto error;
|
|
if (isatty || Py_UnbufferedStdioFlag)
|
|
line_buffering = Py_True;
|
|
else
|
|
line_buffering = Py_False;
|
|
|
|
Py_CLEAR(raw);
|
|
Py_CLEAR(text);
|
|
|
|
#ifdef MS_WINDOWS
|
|
/* sys.stdin: enable universal newline mode, translate "\r\n" and "\r"
|
|
newlines to "\n".
|
|
sys.stdout and sys.stderr: translate "\n" to "\r\n". */
|
|
newline = NULL;
|
|
#else
|
|
/* sys.stdin: split lines at "\n".
|
|
sys.stdout and sys.stderr: don't translate newlines (use "\n"). */
|
|
newline = "\n";
|
|
#endif
|
|
|
|
stream = _PyObject_CallMethodId(io, &PyId_TextIOWrapper, "OsssO",
|
|
buf, encoding, errors,
|
|
newline, line_buffering);
|
|
Py_CLEAR(buf);
|
|
if (stream == NULL)
|
|
goto error;
|
|
|
|
if (write_mode)
|
|
mode = "w";
|
|
else
|
|
mode = "r";
|
|
text = PyUnicode_FromString(mode);
|
|
if (!text || _PyObject_SetAttrId(stream, &PyId_mode, text) < 0)
|
|
goto error;
|
|
Py_CLEAR(text);
|
|
return stream;
|
|
|
|
error:
|
|
Py_XDECREF(buf);
|
|
Py_XDECREF(stream);
|
|
Py_XDECREF(text);
|
|
Py_XDECREF(raw);
|
|
|
|
if (PyErr_ExceptionMatches(PyExc_OSError) && !is_valid_fd(fd)) {
|
|
/* Issue #24891: the file descriptor was closed after the first
|
|
is_valid_fd() check was called. Ignore the OSError and set the
|
|
stream to None. */
|
|
PyErr_Clear();
|
|
Py_RETURN_NONE;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* Initialize sys.stdin, stdout, stderr and builtins.open */
|
|
int
|
|
_Py_InitStdio(void)
|
|
{
|
|
PyObject *iomod = NULL, *wrapper;
|
|
PyObject *bimod = NULL;
|
|
PyObject *m;
|
|
PyObject *std = NULL;
|
|
int status = 0, fd;
|
|
PyObject * encoding_attr;
|
|
char *pythonioencoding = NULL, *encoding, *errors;
|
|
|
|
/* Hack to avoid a nasty recursion issue when Python is invoked
|
|
in verbose mode: pre-import the Latin-1 and UTF-8 codecs */
|
|
if ((m = PyImport_ImportModule("encodings.utf_8")) == NULL) {
|
|
goto error;
|
|
}
|
|
Py_DECREF(m);
|
|
|
|
if (!(m = PyImport_ImportModule("encodings.latin_1"))) {
|
|
goto error;
|
|
}
|
|
Py_DECREF(m);
|
|
|
|
if (!(bimod = PyImport_ImportModule("builtins"))) {
|
|
goto error;
|
|
}
|
|
|
|
if (!(iomod = PyImport_ImportModule("io"))) {
|
|
goto error;
|
|
}
|
|
if (!(wrapper = PyObject_GetAttrString(iomod, "OpenWrapper"))) {
|
|
goto error;
|
|
}
|
|
|
|
/* Set builtins.open */
|
|
if (PyObject_SetAttrString(bimod, "open", wrapper) == -1) {
|
|
Py_DECREF(wrapper);
|
|
goto error;
|
|
}
|
|
Py_DECREF(wrapper);
|
|
|
|
encoding = _Py_StandardStreamEncoding;
|
|
errors = _Py_StandardStreamErrors;
|
|
if (!encoding || !errors) {
|
|
pythonioencoding = Py_GETENV("PYTHONIOENCODING");
|
|
if (pythonioencoding) {
|
|
char *err;
|
|
pythonioencoding = _PyMem_Strdup(pythonioencoding);
|
|
if (pythonioencoding == NULL) {
|
|
PyErr_NoMemory();
|
|
goto error;
|
|
}
|
|
err = strchr(pythonioencoding, ':');
|
|
if (err) {
|
|
*err = '\0';
|
|
err++;
|
|
if (*err && !errors) {
|
|
errors = err;
|
|
}
|
|
}
|
|
if (*pythonioencoding && !encoding) {
|
|
encoding = pythonioencoding;
|
|
}
|
|
}
|
|
if (!errors && !(pythonioencoding && *pythonioencoding)) {
|
|
/* When the LC_CTYPE locale is the POSIX locale ("C locale"),
|
|
stdin and stdout use the surrogateescape error handler by
|
|
default, instead of the strict error handler. */
|
|
char *loc = setlocale(LC_CTYPE, NULL);
|
|
if (loc != NULL && strcmp(loc, "C") == 0)
|
|
errors = "surrogateescape";
|
|
}
|
|
}
|
|
|
|
/* Set sys.stdin */
|
|
fd = fileno(stdin);
|
|
/* Under some conditions stdin, stdout and stderr may not be connected
|
|
* and fileno() may point to an invalid file descriptor. For example
|
|
* GUI apps don't have valid standard streams by default.
|
|
*/
|
|
std = create_stdio(iomod, fd, 0, "<stdin>", encoding, errors);
|
|
if (std == NULL)
|
|
goto error;
|
|
PySys_SetObject("__stdin__", std);
|
|
_PySys_SetObjectId(&PyId_stdin, std);
|
|
Py_DECREF(std);
|
|
|
|
/* Set sys.stdout */
|
|
fd = fileno(stdout);
|
|
std = create_stdio(iomod, fd, 1, "<stdout>", encoding, errors);
|
|
if (std == NULL)
|
|
goto error;
|
|
PySys_SetObject("__stdout__", std);
|
|
_PySys_SetObjectId(&PyId_stdout, std);
|
|
Py_DECREF(std);
|
|
|
|
#if 1 /* Disable this if you have trouble debugging bootstrap stuff */
|
|
/* Set sys.stderr, replaces the preliminary stderr */
|
|
fd = fileno(stderr);
|
|
std = create_stdio(iomod, fd, 1, "<stderr>", encoding, "backslashreplace");
|
|
if (std == NULL)
|
|
goto error;
|
|
|
|
/* Same as hack above, pre-import stderr's codec to avoid recursion
|
|
when import.c tries to write to stderr in verbose mode. */
|
|
encoding_attr = PyObject_GetAttrString(std, "encoding");
|
|
if (encoding_attr != NULL) {
|
|
const char * std_encoding;
|
|
std_encoding = PyUnicode_AsUTF8(encoding_attr);
|
|
if (std_encoding != NULL) {
|
|
PyObject *codec_info = _PyCodec_Lookup(std_encoding);
|
|
Py_XDECREF(codec_info);
|
|
}
|
|
Py_DECREF(encoding_attr);
|
|
}
|
|
PyErr_Clear(); /* Not a fatal error if codec isn't available */
|
|
|
|
if (PySys_SetObject("__stderr__", std) < 0) {
|
|
Py_DECREF(std);
|
|
goto error;
|
|
}
|
|
if (_PySys_SetObjectId(&PyId_stderr, std) < 0) {
|
|
Py_DECREF(std);
|
|
goto error;
|
|
}
|
|
Py_DECREF(std);
|
|
#endif
|
|
|
|
if (0) {
|
|
error:
|
|
status = -1;
|
|
}
|
|
|
|
/* We won't need them anymore. */
|
|
if (_Py_StandardStreamEncoding) {
|
|
PyMem_RawFree(_Py_StandardStreamEncoding);
|
|
_Py_StandardStreamEncoding = NULL;
|
|
}
|
|
if (_Py_StandardStreamErrors) {
|
|
PyMem_RawFree(_Py_StandardStreamErrors);
|
|
_Py_StandardStreamErrors = NULL;
|
|
}
|
|
PyMem_Free(pythonioencoding);
|
|
Py_XDECREF(bimod);
|
|
Py_XDECREF(iomod);
|
|
return status;
|
|
}
|