mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 11:37:35 +00:00
a4b455185b
Renaming gc() to _gc() was a mistake since the better thing to do is put it behind the _COSMO_SOURCE macro. We need this change because I haven't wanted to use my amazing garbage collector ever since we renamed it. You now need to define _COSMO_SOURCE yourself when using amalgamation header and cosmocc users need to pass the -mcosmo flag to get the gc() function Some other issues relating to cancelation have been fixed along the way. We're also now putting cosmocc in a folder named `.cosmocc` so it can be more safely excluded by grep --exclude-dir=.cosmocc --exclude-dir=o etc.
357 lines
11 KiB
C
357 lines
11 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 │
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
#define PY_SSIZE_T_CLEAN
|
|
#include "libc/calls/calls.h"
|
|
#include "libc/calls/internal.h"
|
|
#include "libc/calls/struct/siginfo.h"
|
|
#include "libc/calls/struct/sigset.h"
|
|
#include "libc/calls/ucontext.h"
|
|
#include "libc/dce.h"
|
|
#include "libc/errno.h"
|
|
#include "libc/intrin/safemacros.internal.h"
|
|
#include "libc/intrin/weaken.h"
|
|
#include "libc/log/check.h"
|
|
#include "libc/log/log.h"
|
|
#include "libc/macros.internal.h"
|
|
#include "libc/mem/gc.h"
|
|
#include "libc/mem/mem.h"
|
|
#include "libc/runtime/runtime.h"
|
|
#include "libc/runtime/symbols.internal.h"
|
|
#include "libc/stdio/stdio.h"
|
|
#include "libc/str/locale.h"
|
|
#include "libc/str/str.h"
|
|
#include "libc/sysv/consts/fileno.h"
|
|
#include "libc/sysv/consts/prot.h"
|
|
#include "libc/sysv/consts/sig.h"
|
|
#include "libc/time/time.h"
|
|
#include "libc/x/x.h"
|
|
#include "libc/x/xasprintf.h"
|
|
#include "third_party/linenoise/linenoise.h"
|
|
#include "third_party/python/Include/abstract.h"
|
|
#include "third_party/python/Include/ceval.h"
|
|
#include "third_party/python/Include/dictobject.h"
|
|
#include "third_party/python/Include/fileutils.h"
|
|
#include "third_party/python/Include/funcobject.h"
|
|
#include "third_party/python/Include/import.h"
|
|
#include "third_party/python/Include/listobject.h"
|
|
#include "third_party/python/Include/moduleobject.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/pylifecycle.h"
|
|
#include "third_party/python/Include/pymem.h"
|
|
#include "third_party/python/Include/pyport.h"
|
|
#include "third_party/python/Include/pythonrun.h"
|
|
#include "third_party/python/Include/unicodeobject.h"
|
|
#include "third_party/python/Include/yoink.h"
|
|
#include "third_party/xed/x86.h"
|
|
|
|
STATIC_STACK_SIZE(0x100000);
|
|
|
|
__static_yoink("__die");
|
|
__static_yoink("zipos");
|
|
|
|
PYTHON_YOINK("cosmo");
|
|
PYTHON_YOINK("_locale");
|
|
PYTHON_YOINK("_bootlocale");
|
|
PYTHON_YOINK("encodings.aliases");
|
|
PYTHON_YOINK("encodings.latin_1");
|
|
PYTHON_YOINK("encodings.utf_8");
|
|
|
|
extern char kLaunchPythonModuleName[]; /* optionally generated by pyobj.com */
|
|
const struct _frozen *PyImport_FrozenModules = _PyImport_FrozenModules;
|
|
struct _inittab *PyImport_Inittab = (void *)_PyImport_Inittab;
|
|
static int g_gotint;
|
|
|
|
static void
|
|
OnKeyboardInterrupt(int sig)
|
|
{
|
|
g_gotint = sig;
|
|
}
|
|
|
|
static void
|
|
AddCompletion(linenoiseCompletions *c, char *s)
|
|
{
|
|
char **p = c->cvec;
|
|
size_t n = c->len + 1;
|
|
if ((p = realloc(p, n * sizeof(*p)))) {
|
|
p[n - 1] = s;
|
|
c->cvec = p;
|
|
c->len = n;
|
|
}
|
|
}
|
|
|
|
static void
|
|
CompleteModule(const char *s, const char *p, linenoiseCompletions *c)
|
|
{
|
|
const char *name;
|
|
struct _inittab *it;
|
|
Py_ssize_t plen, namelen;
|
|
PyObject *m, *f, *g, *i, *v, *n;
|
|
plen = strlen(p);
|
|
for (it = PyImport_Inittab; it->name; ++it) {
|
|
if (startswithi(it->name, p)) {
|
|
AddCompletion(c, xasprintf("%s%s", s, it->name + plen));
|
|
}
|
|
}
|
|
if ((m = PyImport_ImportModule("pkgutil"))) {
|
|
if ((f = PyObject_GetAttrString(m, "iter_modules"))) {
|
|
if ((g = PyObject_CallFunctionObjArgs(f, 0))) {
|
|
if ((i = PyObject_GetIter(g))) {
|
|
while ((v = PyIter_Next(i))) {
|
|
if ((n = PyObject_GetAttrString(v, "name"))) {
|
|
if (((name = PyUnicode_AsUTF8AndSize(n, &namelen)) &&
|
|
namelen >= plen && !memcasecmp(name, p, plen))) {
|
|
AddCompletion(c, xasprintf("%s%s", s, name + plen));
|
|
}
|
|
Py_DECREF(n);
|
|
}
|
|
Py_DECREF(v);
|
|
}
|
|
Py_DECREF(i);
|
|
}
|
|
Py_DECREF(g);
|
|
}
|
|
Py_DECREF(f);
|
|
}
|
|
Py_DECREF(m);
|
|
}
|
|
}
|
|
|
|
static void
|
|
CompleteDict(const char *b, const char *q, const char *p,
|
|
linenoiseCompletions *c, PyObject *o)
|
|
{
|
|
const char *s;
|
|
PyObject *k, *v;
|
|
Py_ssize_t i, m;
|
|
for (i = 0; PyDict_Next(o, &i, &k, &v);) {
|
|
if ((v != Py_None && PyUnicode_Check(k) &&
|
|
(s = PyUnicode_AsUTF8AndSize(k, &m)) &&
|
|
m >= q - p && !memcasecmp(s, p, q - p))) {
|
|
AddCompletion(c, xasprintf("%.*s%.*s", p - b, b, m, s));
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
CompleteDir(const char *b, const char *q, const char *p,
|
|
linenoiseCompletions *c, PyObject *o)
|
|
{
|
|
Py_ssize_t m;
|
|
const char *s;
|
|
PyObject *d, *i, *k;
|
|
if ((d = PyObject_Dir(o))) {
|
|
if ((i = PyObject_GetIter(d))) {
|
|
while ((k = PyIter_Next(i))) {
|
|
if (((s = PyUnicode_AsUTF8AndSize(k, &m)) &&
|
|
m >= q - p && !memcasecmp(s, p, q - p) &&
|
|
!(q - p == 0 && m > 4 &&
|
|
(s[0+0] == '_' && s[0+1] == '_' &&
|
|
s[m-1] == '_' && s[m-2] == '_')))) {
|
|
AddCompletion(c, xasprintf("%.*s%.*s", p - b, b, m, s));
|
|
}
|
|
Py_DECREF(k);
|
|
}
|
|
Py_DECREF(i);
|
|
}
|
|
Py_DECREF(d);
|
|
}
|
|
}
|
|
|
|
static void
|
|
Complete(const char *p, linenoiseCompletions *c)
|
|
{
|
|
char *s;
|
|
PyObject *o, *t;
|
|
const char *q, *b;
|
|
if (startswith(p, "import ")) {
|
|
for (q = p + 7; *q; ++q) {
|
|
if (!isalnum(*q) && *q != '_') {
|
|
return;
|
|
}
|
|
}
|
|
CompleteModule(p, p + 7, c);
|
|
return;
|
|
}
|
|
for (b = p, p += strlen(p); p > b; --p) {
|
|
if (!isalnum(p[-1]) && p[-1] != '.' && p[-1] != '_') {
|
|
break;
|
|
}
|
|
}
|
|
o = PyModule_GetDict(PyImport_AddModule("__main__"));
|
|
if (!*(q = strchrnul(p, '.'))) {
|
|
CompleteDict(b, q, p, c, o);
|
|
CompleteDir(b, q, p, c, PyDict_GetItemString(o, "__builtins__"));
|
|
} else {
|
|
s = strndup(p, q - p);
|
|
if ((t = PyDict_GetItemString(o, s))) {
|
|
Py_INCREF(t);
|
|
} else {
|
|
o = PyDict_GetItemString(o, "__builtins__");
|
|
if (PyObject_HasAttrString(o, s)) {
|
|
t = PyObject_GetAttrString(o, s);
|
|
}
|
|
}
|
|
while ((p = q + 1), (o = t)) {
|
|
if (*(q = strchrnul(p, '.'))) {
|
|
t = PyObject_GetAttrString(o, gc(strndup(p, q - p)));
|
|
Py_DECREF(o);
|
|
} else {
|
|
CompleteDir(b, q, p, c, o);
|
|
Py_DECREF(o);
|
|
break;
|
|
}
|
|
}
|
|
free((void *)s);
|
|
}
|
|
}
|
|
|
|
static void
|
|
TerminalCompletion(const char *p, linenoiseCompletions *c)
|
|
{
|
|
PyGILState_STATE gilstate;
|
|
gilstate = PyGILState_Ensure();
|
|
Complete(p, c);
|
|
PyGILState_Release(gilstate);
|
|
if (PyErr_Occurred()) {
|
|
PyErr_Clear();
|
|
}
|
|
}
|
|
|
|
static char *
|
|
TerminalHint(const char *p, const char **ansi1, const char **ansi2)
|
|
{
|
|
char *h = 0;
|
|
linenoiseCompletions c = {0};
|
|
TerminalCompletion(p, &c);
|
|
if (c.len == 1) h = strdup(c.cvec[0] + strlen(p));
|
|
linenoiseFreeCompletions(&c);
|
|
return h;
|
|
}
|
|
|
|
static char *
|
|
ReinterpretCommand(const char *line)
|
|
{
|
|
size_t n;
|
|
n = strlen(line);
|
|
if (n && line[n - 1] == '?') {
|
|
return xstrcat("help(", gc(strndup(gc(line), n - 1)), ')');
|
|
} else {
|
|
return xstrdup(line);
|
|
}
|
|
}
|
|
|
|
static char *
|
|
TerminalReadline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
|
|
{
|
|
size_t n;
|
|
char *p, *q;
|
|
PyOS_sighandler_t saint;
|
|
saint = PyOS_setsig(SIGINT, OnKeyboardInterrupt);
|
|
p = linenoiseWithHistory(prompt, "python");
|
|
PyOS_setsig(SIGINT, saint);
|
|
if (g_gotint) {
|
|
PyOS_setsig(SIGINT, saint);
|
|
g_gotint = 0;
|
|
q = 0;
|
|
} else if (p) {
|
|
p = ReinterpretCommand(p);
|
|
n = strlen(p);
|
|
q = PyMem_RawMalloc(n + 2);
|
|
strcpy(mempcpy(q, p, n), "\n");
|
|
clearerr(sys_stdin);
|
|
} else if ((q = PyMem_RawMalloc(1))) {
|
|
*q = 0;
|
|
}
|
|
free(p);
|
|
return q;
|
|
}
|
|
|
|
int
|
|
RunPythonModule(int argc, char **argv)
|
|
{
|
|
char *launchargs[4];
|
|
wchar_t **argv_copy;
|
|
/* We need a second copy, as Python might modify the first one. */
|
|
wchar_t **argv_copy2;
|
|
int i, res;
|
|
char *oldloc;
|
|
|
|
if (argc == 1 && _weaken(kLaunchPythonModuleName)) {
|
|
launchargs[0] = argv[0];
|
|
launchargs[1] = strdup("-m");
|
|
launchargs[2] = strdup(kLaunchPythonModuleName);
|
|
launchargs[3] = 0;
|
|
argc = 3;
|
|
argv = launchargs;
|
|
}
|
|
|
|
PyOS_ReadlineFunctionPointer = TerminalReadline;
|
|
linenoiseSetCompletionCallback(TerminalCompletion);
|
|
linenoiseSetHintsCallback(TerminalHint);
|
|
linenoiseSetFreeHintsCallback(free);
|
|
|
|
#if IsModeDbg()
|
|
/* Force malloc() allocator to bootstrap Python */
|
|
_PyMem_SetupAllocators("malloc");
|
|
#endif
|
|
|
|
argv_copy = (wchar_t **)PyMem_RawMalloc(sizeof(wchar_t*) * (argc+1));
|
|
argv_copy2 = (wchar_t **)PyMem_RawMalloc(sizeof(wchar_t*) * (argc+1));
|
|
if (!argv_copy || !argv_copy2) {
|
|
fprintf(stderr, "out of memory\n");
|
|
return 1;
|
|
}
|
|
|
|
/* 754 requires that FP exceptions run in "no stop" mode by default,
|
|
* and until C vendors implement C99's ways to control FP exceptions,
|
|
* Python requires non-stop mode. Alas, some platforms enable FP
|
|
* exceptions by default. Here we disable them.
|
|
*/
|
|
#ifdef __FreeBSD__
|
|
fedisableexcept(FE_OVERFLOW);
|
|
#endif
|
|
|
|
oldloc = _PyMem_RawStrdup(setlocale(LC_ALL, NULL));
|
|
if (!oldloc) {
|
|
fprintf(stderr, "out of memory\n");
|
|
return 1;
|
|
}
|
|
|
|
setlocale(LC_ALL, "");
|
|
for (i = 0; i < argc; i++) {
|
|
argv_copy[i] = Py_DecodeLocale(argv[i], NULL);
|
|
if (!argv_copy[i]) {
|
|
PyMem_RawFree(oldloc);
|
|
fprintf(stderr, "Fatal Python error: "
|
|
"unable to decode the command line argument #%i\n",
|
|
i + 1);
|
|
return 1;
|
|
}
|
|
argv_copy2[i] = argv_copy[i];
|
|
}
|
|
argv_copy2[argc] = argv_copy[argc] = NULL;
|
|
|
|
setlocale(LC_ALL, oldloc);
|
|
PyMem_RawFree(oldloc);
|
|
|
|
res = Py_Main(argc, argv_copy);
|
|
|
|
#if IsModeDbg()
|
|
/* Force again malloc() allocator to release memory blocks allocated
|
|
before Py_Main() */
|
|
_PyMem_SetupAllocators("malloc");
|
|
#endif
|
|
|
|
for (i = 0; i < argc; i++) {
|
|
PyMem_RawFree(argv_copy2[i]);
|
|
}
|
|
PyMem_RawFree(argv_copy);
|
|
PyMem_RawFree(argv_copy2);
|
|
return res;
|
|
}
|