mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
Improve Python and Linenoise
This change reinvents all the GNU Readline features I discovered that I couldn't live without, e.g. UTF-8, CTRL-R search and CTRL-Y yanking. It now feels just as good in terms of user interface from the subconscious workflow perspective. It's real nice to finally have an embeddable line reader that's actually good with a 30 kb footprint and a bsd-2 license. This change adds a directory to the examples folder, explaining how the new Python compiler may be used. Some of the bugs with Python binaries have been addressed but overall it's still a work in progress.
This commit is contained in:
parent
ad52387b74
commit
51904e2687
35 changed files with 3541 additions and 8587 deletions
1
Makefile
1
Makefile
|
@ -152,6 +152,7 @@ include tool/build/emucrt/emucrt.mk
|
|||
include tool/build/emubin/emubin.mk
|
||||
include tool/build/build.mk
|
||||
include examples/examples.mk
|
||||
include examples/pyapp/pyapp.mk
|
||||
include tool/decode/lib/decodelib.mk
|
||||
include tool/decode/decode.mk
|
||||
include tool/hash/hash.mk
|
||||
|
|
|
@ -168,7 +168,7 @@ DEFAULT_LDFLAGS = \
|
|||
--gc-sections \
|
||||
--build-id=none \
|
||||
--no-dynamic-linker \
|
||||
-z max-page-size=0x1000 --cref -Map=$@.map
|
||||
-z max-page-size=0x1000
|
||||
|
||||
ZIPOBJ_FLAGS = \
|
||||
-b$(IMAGE_BASE_VIRTUAL)
|
||||
|
|
|
@ -63,6 +63,7 @@ o/$(MODE)/%.o: o/$(MODE)/%.cc ; @$(COMPILE) -AOBJECTIFY.cxx $(OBJECTIFY.cxx
|
|||
o/$(MODE)/%.lds: %.lds ; @$(COMPILE) -APREPROCESS $(PREPROCESS.lds) $(OUTPUT_OPTION) $<
|
||||
o/$(MODE)/%.h.ok: %.h ; @$(COMPILE) -ACHECK.h $(COMPILE.c) -x c -g0 -o $@ $<
|
||||
o/$(MODE)/%.h.okk: %.h ; @$(COMPILE) -ACHECK.h $(COMPILE.cxx) -x c++ -g0 -o $@ $<
|
||||
o/$(MODE)/%.cxx.o: %.c ; @$(COMPILE) -AOBJECTIFY.cxx $(OBJECTIFY.cxx) -x c++ $(OUTPUT_OPTION) $<
|
||||
o/$(MODE)/%.o: %.greg.c ; @$(COMPILE) -AOBJECTIFY.greg $(OBJECTIFY.greg.c) $(OUTPUT_OPTION) $<
|
||||
o/$(MODE)/%.greg.o: %.greg.c ; @$(COMPILE) -AOBJECTIFY.greg $(OBJECTIFY.greg.c) $(OUTPUT_OPTION) $<
|
||||
o/$(MODE)/%.ansi.o: %.ansi.c ; @$(COMPILE) -AOBJECTIFY.ansi $(OBJECTIFY.ansi.c) $(OUTPUT_OPTION) $<
|
||||
|
|
116
examples/pyapp/pyapp.mk
Normal file
116
examples/pyapp/pyapp.mk
Normal file
|
@ -0,0 +1,116 @@
|
|||
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
|
||||
#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# Actually Portable Python Tutorial
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# This tutorial demonstrates how to compile Python apps as tiny
|
||||
# static multiplatform APE executables as small as 1.9m in size
|
||||
# using Cosmopolitan, which is a BSD-style multitenant codebase
|
||||
#
|
||||
# GETTING STARTED
|
||||
#
|
||||
# # run these commands after cloning the cosmo repo on linux
|
||||
# $ make -j8 o//examples/pyapp/pyapp.com
|
||||
# $ o//examples/pyapp/pyapp.com
|
||||
# cosmopolitan is cool!
|
||||
#
|
||||
# HOW IT WORKS
|
||||
#
|
||||
# $ pyobj.com -m -o pyapp.o pyapp.py
|
||||
# $ ld -static -nostdlib -T o//ape/ape.lds ape.o crt.o \
|
||||
# pyapp.o \
|
||||
# cosmopolitan-python-stage2.a \
|
||||
# cosmopolitan-sqlite3.a \
|
||||
# cosmopolitan-linenoise.a \
|
||||
# cosmopolitan-bzip2.a \
|
||||
# cosmopolitan-python-stage1.a \
|
||||
# cosmopolitan.a
|
||||
# $ ./pyapp.com
|
||||
# cosmopolitan is cool!
|
||||
#
|
||||
# NOTES
|
||||
#
|
||||
# If you enjoy this tutorial, let us know jtunney@gmail.com. If
|
||||
# you're building something cool, then we can we can add you to
|
||||
# our .gitowners file which grants you commit access so you can
|
||||
# indepnedently maintain your package, as part of the mono-repo
|
||||
|
||||
PKGS += PYAPP
|
||||
PYAPP = $(PYAPP_DEPS) o/$(MODE)/examples/pyapp/pyapp.a
|
||||
PYAPP_COMS = o/$(MODE)/examples/pyapp/pyapp.com
|
||||
PYAPP_BINS = $(PYAPP_COMS) $(PYAPP_COMS:%=%.dbg)
|
||||
|
||||
# Specify our Cosmopolitan library dependencies
|
||||
#
|
||||
# - THIRD_PARTY_PYTHON_STAGE1 plus THIRD_PARTY_PYTHON_STAGE2 will
|
||||
# define the Python CAPI and supported standard library modules
|
||||
#
|
||||
PYAPP_DIRECTDEPS = \
|
||||
THIRD_PARTY_PYTHON_STAGE2
|
||||
|
||||
# Compute the transitive closure of dependencies. There's dozens of
|
||||
# other static libraries we need, in order to build a static binary
|
||||
# such as fmt.a, runtime.a, str.a etc. This magic statement figures
|
||||
# them all out and arranges them in the correct order.
|
||||
PYAPP_DEPS := $(call uniq,$(foreach x,$(PYAPP_DIRECTDEPS),$($(x))))
|
||||
|
||||
# # Asks PYOBJ.COM to turn our Python source into an ELF object which
|
||||
# # contains (a) embedded zip file artifacts of our .py file and .pyc
|
||||
# # which it it compiled; and (b) statically analyzed listings of our
|
||||
# # python namespaces and imports that GNU ld will use for tree shake
|
||||
# # NOTE: This code can be commented out since it's in build/rules.mk
|
||||
# o/$(MODE)/examples/pyapp/pyapp.o: examples/pyapp/pyapp.py o/$(MODE)/third_party/python/pyobj
|
||||
# o/$(MODE)/third_party/python/pyobj $(PYFLAGS) -o $@ $<
|
||||
|
||||
# We need to define how the repository source code path gets mapped
|
||||
# into an APE ZIP file path. By convention, we place Python modules
|
||||
# in `.python/` (which is accessible via open() system calls, using
|
||||
# the synthetic path `"/zip/.python/"`) which means that if we want
|
||||
# to turn `pyapp/pyapp.py` into `.python/pyapp.py` so it's imported
|
||||
# using `import pyapp` then we can simply append to PYOBJ.COM flags
|
||||
# flags above asking it to strip one directory component and prefix
|
||||
# Lastly be sure that whenever you use this variable override trick
|
||||
# you only do it to .o files, since otherwise it'll ruin everything
|
||||
# Passing -m to PYOBJ.COM causes a C main() function to get yoinked
|
||||
# and it means our Python module can no longer be used as a library
|
||||
o/$(MODE)/examples/pyapp/pyapp.o: PYFLAGS += -m -C2 -P.python
|
||||
|
||||
# Asks PACKAGE.COM to sanity check our DIRECTDEPS and symbol graph.
|
||||
# This program functions as an incremental linker. It also provides
|
||||
# enhancements to the object code that GCC generated similar to LTO
|
||||
# so be certain that your .com.dbg rule depends on the .pkg output!
|
||||
o/$(MODE)/examples/pyapp/pyapp.pkg: \
|
||||
o/$(MODE)/examples/pyapp/pyapp.o \
|
||||
$(foreach x,$(PYAPP_DIRECTDEPS),$($(x)_A).pkg)
|
||||
|
||||
# Ask GNU LD to link our APE executable within an ELF binary shell.
|
||||
# The CRT and APE dependencies are special dependencies that define
|
||||
# your _start() / WinMain() entrpoints as well as APE linker script
|
||||
o/$(MODE)/examples/pyapp/pyapp.com.dbg: \
|
||||
$(PYAPP_DEPS) \
|
||||
o/$(MODE)/examples/pyapp/pyapp.pkg \
|
||||
o/$(MODE)/examples/pyapp/pyapp.o \
|
||||
$(CRT) \
|
||||
$(APE)
|
||||
$(LINK) $(LINKARGS) -o $@
|
||||
|
||||
# # Unwrap the APE .COM binary, that's embedded within the linked file
|
||||
# # NOTE: This line can be commented out, since it's in build/rules.mk
|
||||
# o/$(MODE)/examples/pyapp/pyapp.com: \
|
||||
# o/$(MODE)/examples/pyapp/pyapp.com.dbg
|
||||
# $(OBJCOPY) -S -O binary $< $@
|
||||
|
||||
# Ensure that build config changes will invalidate build artifacts.
|
||||
o/$(MODE)/examples/pyapp/pyapp.o: \
|
||||
examples/pyapp/pyapp.mk
|
||||
|
||||
# By convention we want to be able to say `make -j8 o//examples/pyapp`
|
||||
# and have it build all targets the package defines.
|
||||
.PHONY: o/$(MODE)/examples/pyapp
|
||||
o/$(MODE)/examples/pyapp: \
|
||||
o/$(MODE)/examples/pyapp/pyapp.com \
|
||||
o/$(MODE)/examples/pyapp/pyapp.com.dbg
|
4
examples/pyapp/pyapp.py
Normal file
4
examples/pyapp/pyapp.py
Normal file
|
@ -0,0 +1,4 @@
|
|||
def main():
|
||||
print('cosmopolitan is cool!')
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -5745,7 +5745,7 @@ retry:
|
|||
linenoiseSetFreeHintsCallback(free);
|
||||
linenoiseSetHintsCallback(ShellHint);
|
||||
linenoiseSetCompletionCallback(ShellCompletion);
|
||||
if ((p = ezlinenoise(getprompt(NULL), "unbourne"))) {
|
||||
if ((p = ezlinenoise("$ ", "unbourne"))) {
|
||||
nr = min(strlen(p), IBUFSIZ - 2);
|
||||
memcpy(buf, p, nr);
|
||||
buf[nr++] = '\n';
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/log/backtrace.internal.h"
|
||||
#include "libc/zipos/zipos.internal.h"
|
||||
|
||||
/**
|
||||
|
|
|
@ -31,9 +31,16 @@
|
|||
*
|
||||
* "a" ALFA
|
||||
* "\316\261" ALPHA
|
||||
* "\033[A" CURSOR UP
|
||||
* "\033[38;5;202m" ORANGERED
|
||||
* "\e[38;5;202m" ORANGERED
|
||||
* "\e[A" UP
|
||||
* "\e\e[A" ALT-UP
|
||||
* "\001" CTRL-ALFA
|
||||
* "\e\001" ALT-CTRL-ALFA
|
||||
* "\eOP" PF1
|
||||
* "\000" NUL
|
||||
* "\e]rm -rf /\e\\" OSC
|
||||
* "\302\233A" UP
|
||||
* "\300\200" NUL
|
||||
*
|
||||
* This routine generalizes to ascii, utf-8, chorded modifier keys,
|
||||
* function keys, color codes, c0/c1 control codes, cursor movement,
|
||||
|
@ -44,15 +51,7 @@
|
|||
* can cause the stream to go out of sync. This function recovers such
|
||||
* events by ignoring continuation bytes at the beginning of each read.
|
||||
*
|
||||
* String control sequences, e.g. "\e_hello\e\\" currently are not
|
||||
* tokenized as a single read. Lastly note, this function has limited
|
||||
* support for UNICODE representations of C0/C1 control codes, e.g.
|
||||
*
|
||||
* "\000" NUL
|
||||
* "\300\200" NUL
|
||||
* "\302\233A" CURSOR UP
|
||||
*
|
||||
* @param buf is guaranteed to receive a NUL terminator if size>0
|
||||
* @param p is guaranteed to receive a NUL terminator if n>0
|
||||
* @return number of bytes read (helps differentiate "\0" vs. "")
|
||||
* @see examples/ttyinfo.c
|
||||
* @see ANSI X3.64-1979
|
||||
|
@ -60,81 +59,198 @@
|
|||
* @see FIPS-86
|
||||
* @see ECMA-48
|
||||
*/
|
||||
ssize_t readansi(int fd, char *buf, size_t size) {
|
||||
ssize_t readansi(int fd, char *p, size_t n) {
|
||||
wint_t x;
|
||||
uint8_t c;
|
||||
int i, j, rc;
|
||||
enum { kAscii, kUtf8, kEsc, kCsi, kSs } t;
|
||||
if (size) buf[0] = 0;
|
||||
for (j = i = 0, t = kAscii;;) {
|
||||
if (i + 2 >= size) return enomem();
|
||||
if ((rc = read(fd, &c, 1)) != 1) return rc;
|
||||
buf[i++] = c;
|
||||
buf[i] = 0;
|
||||
ssize_t rc;
|
||||
int e, i, j;
|
||||
unsigned char c;
|
||||
enum { kAscii, kUtf8, kEsc, kCsi1, kCsi2, kSs, kNf, kStr, kStr2, kDone } t;
|
||||
e = errno;
|
||||
t = kAscii;
|
||||
x = i = j = 0;
|
||||
if (n) p[0] = 0;
|
||||
do {
|
||||
for (;;) {
|
||||
if (n) {
|
||||
rc = read(fd, &c, 1);
|
||||
} else {
|
||||
rc = read(fd, 0, 0);
|
||||
}
|
||||
if (rc == -1 && errno == EINTR) {
|
||||
if (!i) {
|
||||
return -1;
|
||||
}
|
||||
} else if (rc == -1) {
|
||||
return -1;
|
||||
} else if (!rc) {
|
||||
if (!i) {
|
||||
errno = e;
|
||||
return 0;
|
||||
} else {
|
||||
return eilseq();
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i + 1 < n) {
|
||||
p[i] = c;
|
||||
p[i + 1] = 0;
|
||||
} else if (i < n) {
|
||||
p[i] = 0;
|
||||
}
|
||||
++i;
|
||||
switch (t) {
|
||||
Whoopsie:
|
||||
if (n) p[0] = c;
|
||||
t = kAscii;
|
||||
i = 1;
|
||||
/* fallthrough */
|
||||
case kAscii:
|
||||
if (c < 0200) {
|
||||
if (c == '\e') {
|
||||
t = kEsc;
|
||||
} else {
|
||||
return i;
|
||||
t = kDone;
|
||||
}
|
||||
} else if (c >= 0300) {
|
||||
t = kUtf8;
|
||||
x = ThomPikeByte(c);
|
||||
j = ThomPikeLen(c) - 1;
|
||||
} else {
|
||||
/* ignore overlong sequences */
|
||||
}
|
||||
break;
|
||||
case kUtf8:
|
||||
x = ThomPikeMerge(x, c);
|
||||
if (!--j) {
|
||||
switch (x) {
|
||||
case '\e':
|
||||
t = kEsc;
|
||||
break;
|
||||
case 0x9b:
|
||||
t = kCsi;
|
||||
break;
|
||||
default:
|
||||
return i;
|
||||
if ((c & 0300) == 0200) {
|
||||
x = ThomPikeMerge(x, c);
|
||||
if (!--j) {
|
||||
switch (x) {
|
||||
case '\e':
|
||||
t = kEsc; /* parsed but not canonicalized */
|
||||
break;
|
||||
case 0x9b:
|
||||
t = kCsi1; /* unusual but legal */
|
||||
break;
|
||||
case 0x8e:
|
||||
case 0x8f:
|
||||
t = kSs; /* unusual but legal */
|
||||
break;
|
||||
case 0x90: /* DCS (Device Control String) */
|
||||
case 0x98: /* SOS (Start of String) */
|
||||
case 0x9d: /* OSC (Operating System Command) */
|
||||
case 0x9e: /* PM (Privacy Message) */
|
||||
case 0x9f: /* APC (Application Program Command) */
|
||||
t = kStr;
|
||||
break;
|
||||
default:
|
||||
t = kDone;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
goto Whoopsie; /* ignore underlong sequences if not eof */
|
||||
}
|
||||
break;
|
||||
case kEsc:
|
||||
switch (c) {
|
||||
case '[':
|
||||
t = kCsi;
|
||||
break;
|
||||
case 'N':
|
||||
case 'O':
|
||||
t = kSs;
|
||||
break;
|
||||
case '\e':
|
||||
case 0x20 ... 0x2F:
|
||||
break;
|
||||
default:
|
||||
return i;
|
||||
}
|
||||
break;
|
||||
case kCsi:
|
||||
switch (c) {
|
||||
case '[':
|
||||
case ':':
|
||||
case ';':
|
||||
case '<':
|
||||
case '=':
|
||||
case '>':
|
||||
case '?':
|
||||
case '0' ... '9':
|
||||
break;
|
||||
default:
|
||||
return i;
|
||||
if (0x20 <= c && c <= 0x2f) {
|
||||
t = kNf;
|
||||
} else if (0x30 <= c && c <= 0x3f) { /* Fp */
|
||||
t = kDone;
|
||||
} else if (0x20 <= c && c <= 0x5F) { /* Fe */
|
||||
switch (c) {
|
||||
case '[':
|
||||
t = kCsi1;
|
||||
break;
|
||||
case 'N': /* SS2 */
|
||||
case 'O': /* SS3 */
|
||||
t = kSs;
|
||||
break;
|
||||
case 'P': /* DCS (Device Control String) */
|
||||
case 'X': /* SOS (Start of String) */
|
||||
case ']': /* DCS (Operating System Command) */
|
||||
case '^': /* PM (Privacy Message) */
|
||||
case '_': /* DCS (Application Program Command) */
|
||||
t = kStr;
|
||||
break;
|
||||
case '\\':
|
||||
goto Whoopsie;
|
||||
default:
|
||||
t = kDone;
|
||||
break;
|
||||
}
|
||||
} else if (0x60 <= c && c <= 0x7e) { /* Fs */
|
||||
t = kDone;
|
||||
} else if (c == '\e') {
|
||||
if (i < 3) {
|
||||
t = kEsc; /* alt chording */
|
||||
} else {
|
||||
t = kDone; /* esc mashing */
|
||||
i = 1;
|
||||
}
|
||||
} else {
|
||||
t = kDone;
|
||||
}
|
||||
break;
|
||||
case kSs:
|
||||
return i;
|
||||
t = kDone;
|
||||
break;
|
||||
case kNf:
|
||||
if (0x30 <= c && c <= 0x7e) {
|
||||
t = kDone;
|
||||
} else if (!(0x20 <= c && c <= 0x2f)) {
|
||||
goto Whoopsie;
|
||||
}
|
||||
break;
|
||||
case kCsi1:
|
||||
if (0x20 <= c && c <= 0x2f) {
|
||||
t = kCsi2;
|
||||
} else if (c == '[' && i == 3) {
|
||||
/* linux function keys */
|
||||
} else if (0x40 <= c && c <= 0x7e) {
|
||||
t = kDone;
|
||||
} else if (!(0x30 <= c && c <= 0x3f)) {
|
||||
goto Whoopsie;
|
||||
}
|
||||
break;
|
||||
case kCsi2:
|
||||
if (0x40 <= c && c <= 0x7e) {
|
||||
t = kDone;
|
||||
} else if (!(0x20 <= c && c <= 0x2f)) {
|
||||
goto Whoopsie;
|
||||
}
|
||||
break;
|
||||
case kStr:
|
||||
switch (c) {
|
||||
case '\a':
|
||||
t = kDone;
|
||||
break;
|
||||
case '\e': /* ESC */
|
||||
case 0302: /* C1 (UTF-8) */
|
||||
t = kStr2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case kStr2:
|
||||
switch (c) {
|
||||
case '\a':
|
||||
t = kDone;
|
||||
break;
|
||||
case '\\': /* ST (ASCII) */
|
||||
case 0234: /* ST (UTF-8) */
|
||||
t = kDone;
|
||||
break;
|
||||
default:
|
||||
t = kStr;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
unreachable;
|
||||
}
|
||||
}
|
||||
} while (t != kDone);
|
||||
errno = e;
|
||||
return i;
|
||||
}
|
||||
|
|
|
@ -104,6 +104,7 @@ privileged noasan void ftracer(void) {
|
|||
p = mempcpy(p, symbol, symbolsize);
|
||||
*p++ = ' ';
|
||||
p += uint64toarray_radix10((stamp - laststamp) / 3.3, p);
|
||||
*p++ = '\r';
|
||||
*p++ = '\n';
|
||||
write(2, g_buf, p - g_buf);
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ ssize_t getdelim(char **s, size_t *n, int delim, FILE *f) {
|
|||
f->state = errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
if (f->beg > f->end || f->bufmode == _IONBF) {
|
||||
if (f->beg > f->end) {
|
||||
f->state = errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
|
2622
libc/str/iswlower.c
2622
libc/str/iswlower.c
File diff suppressed because it is too large
Load diff
423
libc/str/iswseparator.c
Normal file
423
libc/str/iswseparator.c
Normal file
|
@ -0,0 +1,423 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/str/str.h"
|
||||
|
||||
static const unsigned short kCodes[][2] = {
|
||||
{0x00aa, 0x00aa}, /* 1x English */
|
||||
{0x00b2, 0x00b3}, /* 2x English Arabic */
|
||||
{0x00b5, 0x00b5}, /* 1x Greek */
|
||||
{0x00b9, 0x00ba}, /* 2x English Arabic */
|
||||
{0x00bc, 0x00be}, /* 3x Vulgar English Arabic */
|
||||
{0x00c0, 0x00d6}, /* 23x Watin */
|
||||
{0x00d8, 0x00f6}, /* 31x Watin */
|
||||
{0x0100, 0x02c1}, /* 450x Watin-AB,IPA,Spacemod */
|
||||
{0x02c6, 0x02d1}, /* 12x Spacemod */
|
||||
{0x02e0, 0x02e4}, /* 5x Spacemod */
|
||||
{0x02ec, 0x02ec}, /* 1x Spacemod */
|
||||
{0x02ee, 0x02ee}, /* 1x Spacemod */
|
||||
{0x0370, 0x0374}, /* 5x Greek */
|
||||
{0x0376, 0x0377}, /* 2x Greek */
|
||||
{0x037a, 0x037d}, /* 4x Greek */
|
||||
{0x037f, 0x037f}, /* 1x Greek */
|
||||
{0x0386, 0x0386}, /* 1x Greek */
|
||||
{0x0388, 0x038a}, /* 3x Greek */
|
||||
{0x038c, 0x038c}, /* 1x Greek */
|
||||
{0x038e, 0x03a1}, /* 20x Greek */
|
||||
{0x03a3, 0x03f5}, /* 83x Greek */
|
||||
{0x03f7, 0x0481}, /* 139x Greek */
|
||||
{0x048a, 0x052f}, /* 166x Cyrillic */
|
||||
{0x0531, 0x0556}, /* 38x Armenian */
|
||||
{0x0560, 0x0588}, /* 41x Armenian */
|
||||
{0x05d0, 0x05ea}, /* 27x Hebrew */
|
||||
{0x0620, 0x064a}, /* 43x Arabic */
|
||||
{0x0660, 0x0669}, /* 10x Arabic */
|
||||
{0x0671, 0x06d3}, /* 99x Arabic */
|
||||
{0x06ee, 0x06fc}, /* 15x Arabic */
|
||||
{0x0712, 0x072f}, /* 30x Syriac */
|
||||
{0x074d, 0x07a5}, /* 89x Syriac,Arabic2,Thaana */
|
||||
{0x07c0, 0x07ea}, /* 43x NKo */
|
||||
{0x0800, 0x0815}, /* 22x Samaritan */
|
||||
{0x0840, 0x0858}, /* 25x Mandaic */
|
||||
{0x0904, 0x0939}, /* 54x Devanagari */
|
||||
{0x0993, 0x09a8}, /* 22x Bengali */
|
||||
{0x09e6, 0x09f1}, /* 12x Bengali */
|
||||
{0x0a13, 0x0a28}, /* 22x Gurmukhi */
|
||||
{0x0a66, 0x0a6f}, /* 10x Gurmukhi */
|
||||
{0x0a93, 0x0aa8}, /* 22x Gujarati */
|
||||
{0x0b13, 0x0b28}, /* 22x Oriya */
|
||||
{0x0c92, 0x0ca8}, /* 23x Kannada */
|
||||
{0x0caa, 0x0cb3}, /* 10x Kannada */
|
||||
{0x0ce6, 0x0cef}, /* 10x Kannada */
|
||||
{0x0d12, 0x0d3a}, /* 41x Malayalam */
|
||||
{0x0d85, 0x0d96}, /* 18x Sinhala */
|
||||
{0x0d9a, 0x0db1}, /* 24x Sinhala */
|
||||
{0x0de6, 0x0def}, /* 10x Sinhala */
|
||||
{0x0e01, 0x0e30}, /* 48x Thai */
|
||||
{0x0e8c, 0x0ea3}, /* 24x Lao */
|
||||
{0x0f20, 0x0f33}, /* 20x Tibetan */
|
||||
{0x0f49, 0x0f6c}, /* 36x Tibetan */
|
||||
{0x109e, 0x10c5}, /* 40x Myanmar,Georgian */
|
||||
{0x10d0, 0x10fa}, /* 43x Georgian */
|
||||
{0x10fc, 0x1248}, /* 333x Georgian,Hangul,Ethiopic */
|
||||
{0x13a0, 0x13f5}, /* 86x Cherokee */
|
||||
{0x1401, 0x166d}, /* 621x Aboriginal */
|
||||
{0x16a0, 0x16ea}, /* 75x Runic */
|
||||
{0x1700, 0x170c}, /* 13x Tagalog */
|
||||
{0x1780, 0x17b3}, /* 52x Khmer */
|
||||
{0x1820, 0x1878}, /* 89x Mongolian */
|
||||
{0x1a00, 0x1a16}, /* 23x Buginese */
|
||||
{0x1a20, 0x1a54}, /* 53x Tai Tham */
|
||||
{0x1a80, 0x1a89}, /* 10x Tai Tham */
|
||||
{0x1a90, 0x1a99}, /* 10x Tai Tham */
|
||||
{0x1b05, 0x1b33}, /* 47x Balinese */
|
||||
{0x1b50, 0x1b59}, /* 10x Balinese */
|
||||
{0x1b83, 0x1ba0}, /* 30x Sundanese */
|
||||
{0x1bae, 0x1be5}, /* 56x Sundanese */
|
||||
{0x1c90, 0x1cba}, /* 43x Georgian2 */
|
||||
{0x1cbd, 0x1cbf}, /* 3x Georgian2 */
|
||||
{0x1e00, 0x1f15}, /* 278x Watin-C,Greek2 */
|
||||
{0x2070, 0x2071}, /* 2x Supersub */
|
||||
{0x2074, 0x2079}, /* 6x Supersub */
|
||||
{0x207f, 0x2089}, /* 11x Supersub */
|
||||
{0x2090, 0x209c}, /* 13x Supersub */
|
||||
{0x2100, 0x2117}, /* 24x Letterlike */
|
||||
{0x2119, 0x213f}, /* 39x Letterlike */
|
||||
{0x2145, 0x214a}, /* 6x Letterlike */
|
||||
{0x214c, 0x218b}, /* 64x Letterlike,Numbery */
|
||||
{0x21af, 0x21cd}, /* 31x Arrows */
|
||||
{0x21d5, 0x21f3}, /* 31x Arrows */
|
||||
{0x230c, 0x231f}, /* 20x Technical */
|
||||
{0x232b, 0x237b}, /* 81x Technical */
|
||||
{0x237d, 0x239a}, /* 30x Technical */
|
||||
{0x23b4, 0x23db}, /* 40x Technical */
|
||||
{0x23e2, 0x2426}, /* 69x Technical,ControlPictures */
|
||||
{0x2460, 0x25b6}, /* 343x Enclosed,Boxes,Blocks,Shapes */
|
||||
{0x25c2, 0x25f7}, /* 54x Shapes */
|
||||
{0x2600, 0x266e}, /* 111x Symbols */
|
||||
{0x2670, 0x2767}, /* 248x Symbols,Dingbats */
|
||||
{0x2776, 0x27bf}, /* 74x Dingbats */
|
||||
{0x2800, 0x28ff}, /* 256x Braille */
|
||||
{0x2c00, 0x2c2e}, /* 47x Glagolitic */
|
||||
{0x2c30, 0x2c5e}, /* 47x Glagolitic */
|
||||
{0x2c60, 0x2ce4}, /* 133x Watin-D */
|
||||
{0x2d00, 0x2d25}, /* 38x Georgian2 */
|
||||
{0x2d30, 0x2d67}, /* 56x Tifinagh */
|
||||
{0x2d80, 0x2d96}, /* 23x Ethiopic2 */
|
||||
{0x2e2f, 0x2e2f}, /* 1x Punctuation2 */
|
||||
{0x3005, 0x3007}, /* 3x CJK Symbols & Punctuation */
|
||||
{0x3021, 0x3029}, /* 9x CJK Symbols & Punctuation */
|
||||
{0x3031, 0x3035}, /* 5x CJK Symbols & Punctuation */
|
||||
{0x3038, 0x303c}, /* 5x CJK Symbols & Punctuation */
|
||||
{0x3041, 0x3096}, /* 86x Hiragana */
|
||||
{0x30a1, 0x30fa}, /* 90x Katakana */
|
||||
{0x3105, 0x312f}, /* 43x Bopomofo */
|
||||
{0x3131, 0x318e}, /* 94x Hangul Compatibility Jamo */
|
||||
{0x31a0, 0x31ba}, /* 27x Bopomofo Extended */
|
||||
{0x31f0, 0x31ff}, /* 16x Katakana Phonetic Extensions */
|
||||
{0x3220, 0x3229}, /* 10x Enclosed CJK Letters & Months */
|
||||
{0x3248, 0x324f}, /* 8x Enclosed CJK Letters & Months */
|
||||
{0x3251, 0x325f}, /* 15x Enclosed CJK Letters & Months */
|
||||
{0x3280, 0x3289}, /* 10x Enclosed CJK Letters & Months */
|
||||
{0x32b1, 0x32bf}, /* 15x Enclosed CJK Letters & Months */
|
||||
{0x3400, 0x4db5}, /* 6582x CJK Unified Ideographs Extension A */
|
||||
{0x4dc0, 0x9fef}, /* 21040x Yijing Hexagram, CJK Unified Ideographs */
|
||||
{0xa000, 0xa48c}, /* 1165x Yi Syllables */
|
||||
{0xa4d0, 0xa4fd}, /* 46x Lisu */
|
||||
{0xa500, 0xa60c}, /* 269x Vai */
|
||||
{0xa610, 0xa62b}, /* 28x Vai */
|
||||
{0xa6a0, 0xa6ef}, /* 80x Bamum */
|
||||
{0xa80c, 0xa822}, /* 23x Syloti Nagri */
|
||||
{0xa840, 0xa873}, /* 52x Phags-pa */
|
||||
{0xa882, 0xa8b3}, /* 50x Saurashtra */
|
||||
{0xa8d0, 0xa8d9}, /* 10x Saurashtra */
|
||||
{0xa900, 0xa925}, /* 38x Kayah Li */
|
||||
{0xa930, 0xa946}, /* 23x Rejang */
|
||||
{0xa960, 0xa97c}, /* 29x Hangul Jamo Extended-A */
|
||||
{0xa984, 0xa9b2}, /* 47x Javanese */
|
||||
{0xa9cf, 0xa9d9}, /* 11x Javanese */
|
||||
{0xaa00, 0xaa28}, /* 41x Cham */
|
||||
{0xaa50, 0xaa59}, /* 10x Cham */
|
||||
{0xabf0, 0xabf9}, /* 10x Meetei Mayek */
|
||||
{0xac00, 0xd7a3}, /* 11172x Hangul Syllables */
|
||||
{0xf900, 0xfa6d}, /* 366x CJK Compatibility Ideographs */
|
||||
{0xfa70, 0xfad9}, /* 106x CJK Compatibility Ideographs */
|
||||
{0xfb1f, 0xfb28}, /* 10x Alphabetic Presentation Forms */
|
||||
{0xfb2a, 0xfb36}, /* 13x Alphabetic Presentation Forms */
|
||||
{0xfb46, 0xfbb1}, /* 108x Alphabetic Presentation Forms */
|
||||
{0xfbd3, 0xfd3d}, /* 363x Arabic Presentation Forms-A */
|
||||
{0xfe76, 0xfefc}, /* 135x Arabic Presentation Forms-B */
|
||||
{0xff10, 0xff19}, /* 10x Dubs */
|
||||
{0xff21, 0xff3a}, /* 26x Dubs */
|
||||
{0xff41, 0xff5a}, /* 26x Dubs */
|
||||
{0xff66, 0xffbe}, /* 89x Dubs */
|
||||
{0xffc2, 0xffc7}, /* 6x Dubs */
|
||||
{0xffca, 0xffcf}, /* 6x Dubs */
|
||||
{0xffd2, 0xffd7}, /* 6x Dubs */
|
||||
{0xffda, 0xffdc}, /* 3x Dubs */
|
||||
};
|
||||
|
||||
static const unsigned kAstralCodes[][2] = {
|
||||
{0x10107, 0x10133}, /* 45x Aegean */
|
||||
{0x10140, 0x10178}, /* 57x Ancient Greek Numbers */
|
||||
{0x1018a, 0x1018b}, /* 2x Ancient Greek Numbers */
|
||||
{0x10280, 0x1029c}, /* 29x Lycian */
|
||||
{0x102a0, 0x102d0}, /* 49x Carian */
|
||||
{0x102e1, 0x102fb}, /* 27x Coptic Epact Numbers */
|
||||
{0x10300, 0x10323}, /* 36x Old Italic */
|
||||
{0x1032d, 0x1034a}, /* 30x Old Italic, Gothic */
|
||||
{0x10350, 0x10375}, /* 38x Old Permic */
|
||||
{0x10380, 0x1039d}, /* 30x Ugaritic */
|
||||
{0x103a0, 0x103c3}, /* 36x Old Persian */
|
||||
{0x103c8, 0x103cf}, /* 8x Old Persian */
|
||||
{0x103d1, 0x103d5}, /* 5x Old Persian */
|
||||
{0x10400, 0x1049d}, /* 158x Deseret, Shavian, Osmanya */
|
||||
{0x104b0, 0x104d3}, /* 36x Osage */
|
||||
{0x104d8, 0x104fb}, /* 36x Osage */
|
||||
{0x10500, 0x10527}, /* 40x Elbasan */
|
||||
{0x10530, 0x10563}, /* 52x Caucasian Albanian */
|
||||
{0x10600, 0x10736}, /* 311x Linear A */
|
||||
{0x10800, 0x10805}, /* 6x Cypriot Syllabary */
|
||||
{0x1080a, 0x10835}, /* 44x Cypriot Syllabary */
|
||||
{0x10837, 0x10838}, /* 2x Cypriot Syllabary */
|
||||
{0x1083f, 0x1089e}, /* 86x Cypriot,ImperialAramaic,Palmyrene,Nabataean */
|
||||
{0x108e0, 0x108f2}, /* 19x Hatran */
|
||||
{0x108f4, 0x108f5}, /* 2x Hatran */
|
||||
{0x108fb, 0x1091b}, /* 33x Hatran */
|
||||
{0x10920, 0x10939}, /* 26x Lydian */
|
||||
{0x10980, 0x109b7}, /* 56x Meroitic Hieromarks */
|
||||
{0x109bc, 0x109cf}, /* 20x Meroitic Cursive */
|
||||
{0x109d2, 0x10a00}, /* 47x Meroitic Cursive */
|
||||
{0x10a10, 0x10a13}, /* 4x Kharoshthi */
|
||||
{0x10a15, 0x10a17}, /* 3x Kharoshthi */
|
||||
{0x10a19, 0x10a35}, /* 29x Kharoshthi */
|
||||
{0x10a40, 0x10a48}, /* 9x Kharoshthi */
|
||||
{0x10a60, 0x10a7e}, /* 31x Old South Arabian */
|
||||
{0x10a80, 0x10a9f}, /* 32x Old North Arabian */
|
||||
{0x10ac0, 0x10ac7}, /* 8x Manichaean */
|
||||
{0x10ac9, 0x10ae4}, /* 28x Manichaean */
|
||||
{0x10aeb, 0x10aef}, /* 5x Manichaean */
|
||||
{0x10b00, 0x10b35}, /* 54x Avestan */
|
||||
{0x10b40, 0x10b55}, /* 22x Inscriptional Parthian */
|
||||
{0x10b58, 0x10b72}, /* 27x Inscriptional Parthian and Pahlavi */
|
||||
{0x10b78, 0x10b91}, /* 26x Inscriptional Pahlavi, Psalter Pahlavi */
|
||||
{0x10c00, 0x10c48}, /* 73x Old Turkic */
|
||||
{0x10c80, 0x10cb2}, /* 51x Old Hungarian */
|
||||
{0x10cc0, 0x10cf2}, /* 51x Old Hungarian */
|
||||
{0x10cfa, 0x10d23}, /* 42x Old Hungarian, Hanifi Rohingya */
|
||||
{0x10d30, 0x10d39}, /* 10x Hanifi Rohingya */
|
||||
{0x10e60, 0x10e7e}, /* 31x Rumi Numeral Symbols */
|
||||
{0x10f00, 0x10f27}, /* 40x Old Sogdian */
|
||||
{0x10f30, 0x10f45}, /* 22x Sogdian */
|
||||
{0x10f51, 0x10f54}, /* 4x Sogdian */
|
||||
{0x10fe0, 0x10ff6}, /* 23x Elymaic */
|
||||
{0x11003, 0x11037}, /* 53x Brahmi */
|
||||
{0x11052, 0x1106f}, /* 30x Brahmi */
|
||||
{0x11083, 0x110af}, /* 45x Kaithi */
|
||||
{0x110d0, 0x110e8}, /* 25x Sora Sompeng */
|
||||
{0x110f0, 0x110f9}, /* 10x Sora Sompeng */
|
||||
{0x11103, 0x11126}, /* 36x Chakma */
|
||||
{0x11136, 0x1113f}, /* 10x Chakma */
|
||||
{0x11144, 0x11144}, /* 1x Chakma */
|
||||
{0x11150, 0x11172}, /* 35x Mahajani */
|
||||
{0x11176, 0x11176}, /* 1x Mahajani */
|
||||
{0x11183, 0x111b2}, /* 48x Sharada */
|
||||
{0x111c1, 0x111c4}, /* 4x Sharada */
|
||||
{0x111d0, 0x111da}, /* 11x Sharada */
|
||||
{0x111dc, 0x111dc}, /* 1x Sharada */
|
||||
{0x111e1, 0x111f4}, /* 20x Sinhala Archaic Numbers */
|
||||
{0x11200, 0x11211}, /* 18x Khojki */
|
||||
{0x11213, 0x1122b}, /* 25x Khojki */
|
||||
{0x11280, 0x11286}, /* 7x Multani */
|
||||
{0x11288, 0x11288}, /* 1x Multani */
|
||||
{0x1128a, 0x1128d}, /* 4x Multani */
|
||||
{0x1128f, 0x1129d}, /* 15x Multani */
|
||||
{0x1129f, 0x112a8}, /* 10x Multani */
|
||||
{0x112b0, 0x112de}, /* 47x Khudawadi */
|
||||
{0x112f0, 0x112f9}, /* 10x Khudawadi */
|
||||
{0x11305, 0x1130c}, /* 8x Grantha */
|
||||
{0x1130f, 0x11310}, /* 2x Grantha */
|
||||
{0x11313, 0x11328}, /* 22x Grantha */
|
||||
{0x1132a, 0x11330}, /* 7x Grantha */
|
||||
{0x11332, 0x11333}, /* 2x Grantha */
|
||||
{0x11335, 0x11339}, /* 5x Grantha */
|
||||
{0x1133d, 0x1133d}, /* 1x Grantha */
|
||||
{0x11350, 0x11350}, /* 1x Grantha */
|
||||
{0x1135d, 0x11361}, /* 5x Grantha */
|
||||
{0x11400, 0x11434}, /* 53x Newa */
|
||||
{0x11447, 0x1144a}, /* 4x Newa */
|
||||
{0x11450, 0x11459}, /* 10x Newa */
|
||||
{0x1145f, 0x1145f}, /* 1x Newa */
|
||||
{0x11480, 0x114af}, /* 48x Tirhuta */
|
||||
{0x114c4, 0x114c5}, /* 2x Tirhuta */
|
||||
{0x114c7, 0x114c7}, /* 1x Tirhuta */
|
||||
{0x114d0, 0x114d9}, /* 10x Tirhuta */
|
||||
{0x11580, 0x115ae}, /* 47x Siddham */
|
||||
{0x115d8, 0x115db}, /* 4x Siddham */
|
||||
{0x11600, 0x1162f}, /* 48x Modi */
|
||||
{0x11644, 0x11644}, /* 1x Modi */
|
||||
{0x11650, 0x11659}, /* 10x Modi */
|
||||
{0x11680, 0x116aa}, /* 43x Takri */
|
||||
{0x116b8, 0x116b8}, /* 1x Takri */
|
||||
{0x116c0, 0x116c9}, /* 10x Takri */
|
||||
{0x11700, 0x1171a}, /* 27x Ahom */
|
||||
{0x11730, 0x1173b}, /* 12x Ahom */
|
||||
{0x11800, 0x1182b}, /* 44x Dogra */
|
||||
{0x118a0, 0x118f2}, /* 83x Warang Citi */
|
||||
{0x118ff, 0x118ff}, /* 1x Warang Citi */
|
||||
{0x119a0, 0x119a7}, /* 8x Nandinagari */
|
||||
{0x119aa, 0x119d0}, /* 39x Nandinagari */
|
||||
{0x119e1, 0x119e1}, /* 1x Nandinagari */
|
||||
{0x119e3, 0x119e3}, /* 1x Nandinagari */
|
||||
{0x11a00, 0x11a00}, /* 1x Zanabazar Square */
|
||||
{0x11a0b, 0x11a32}, /* 40x Zanabazar Square */
|
||||
{0x11a3a, 0x11a3a}, /* 1x Zanabazar Square */
|
||||
{0x11a50, 0x11a50}, /* 1x Soyombo */
|
||||
{0x11a5c, 0x11a89}, /* 46x Soyombo */
|
||||
{0x11a9d, 0x11a9d}, /* 1x Soyombo */
|
||||
{0x11ac0, 0x11af8}, /* 57x Pau Cin Hau */
|
||||
{0x11c00, 0x11c08}, /* 9x Bhaiksuki */
|
||||
{0x11c0a, 0x11c2e}, /* 37x Bhaiksuki */
|
||||
{0x11c40, 0x11c40}, /* 1x Bhaiksuki */
|
||||
{0x11c50, 0x11c6c}, /* 29x Bhaiksuki */
|
||||
{0x11c72, 0x11c8f}, /* 30x Marchen */
|
||||
{0x11d00, 0x11d06}, /* 7x Masaram Gondi */
|
||||
{0x11d08, 0x11d09}, /* 2x Masaram Gondi */
|
||||
{0x11d0b, 0x11d30}, /* 38x Masaram Gondi */
|
||||
{0x11d46, 0x11d46}, /* 1x Masaram Gondi */
|
||||
{0x11d50, 0x11d59}, /* 10x Masaram Gondi */
|
||||
{0x11d60, 0x11d65}, /* 6x Gunjala Gondi */
|
||||
{0x11d67, 0x11d68}, /* 2x Gunjala Gondi */
|
||||
{0x11d6a, 0x11d89}, /* 32x Gunjala Gondi */
|
||||
{0x11d98, 0x11d98}, /* 1x Gunjala Gondi */
|
||||
{0x11da0, 0x11da9}, /* 10x Gunjala Gondi */
|
||||
{0x11ee0, 0x11ef2}, /* 19x Makasar */
|
||||
{0x11fc0, 0x11fd4}, /* 21x Tamil Supplement */
|
||||
{0x12000, 0x12399}, /* 922x Cuneiform */
|
||||
{0x12400, 0x1246e}, /* 111x Cuneiform Numbers & Punctuation */
|
||||
{0x12480, 0x12543}, /* 196x Early Dynastic Cuneiform */
|
||||
{0x13000, 0x1342e}, /* 1071x Egyptian Hieromarks */
|
||||
{0x14400, 0x14646}, /* 583x Anatolian Hieromarks */
|
||||
{0x16800, 0x16a38}, /* 569x Bamum Supplement */
|
||||
{0x16a40, 0x16a5e}, /* 31x Mro */
|
||||
{0x16a60, 0x16a69}, /* 10x Mro */
|
||||
{0x16ad0, 0x16aed}, /* 30x Bassa Vah */
|
||||
{0x16b00, 0x16b2f}, /* 48x Pahawh Hmong */
|
||||
{0x16b40, 0x16b43}, /* 4x Pahawh Hmong */
|
||||
{0x16b50, 0x16b59}, /* 10x Pahawh Hmong */
|
||||
{0x16b5b, 0x16b61}, /* 7x Pahawh Hmong */
|
||||
{0x16b63, 0x16b77}, /* 21x Pahawh Hmong */
|
||||
{0x16b7d, 0x16b8f}, /* 19x Pahawh Hmong */
|
||||
{0x16e40, 0x16e96}, /* 87x Medefaidrin */
|
||||
{0x16f00, 0x16f4a}, /* 75x Miao */
|
||||
{0x16f50, 0x16f50}, /* 1x Miao */
|
||||
{0x16f93, 0x16f9f}, /* 13x Miao */
|
||||
{0x16fe0, 0x16fe1}, /* 2x Ideographic Symbols & Punctuation */
|
||||
{0x16fe3, 0x16fe3}, /* 1x Ideographic Symbols & Punctuation */
|
||||
{0x17000, 0x187f7}, /* 6136x Tangut */
|
||||
{0x18800, 0x18af2}, /* 755x Tangut Components */
|
||||
{0x1b000, 0x1b11e}, /* 287x Kana Supplement */
|
||||
{0x1b150, 0x1b152}, /* 3x Small Kana Extension */
|
||||
{0x1b164, 0x1b167}, /* 4x Small Kana Extension */
|
||||
{0x1b170, 0x1b2fb}, /* 396x Nushu */
|
||||
{0x1bc00, 0x1bc6a}, /* 107x Duployan */
|
||||
{0x1bc70, 0x1bc7c}, /* 13x Duployan */
|
||||
{0x1bc80, 0x1bc88}, /* 9x Duployan */
|
||||
{0x1bc90, 0x1bc99}, /* 10x Duployan */
|
||||
{0x1d2e0, 0x1d2f3}, /* 20x Mayan Numerals */
|
||||
{0x1d360, 0x1d378}, /* 25x Counting Rod Numerals */
|
||||
{0x1d400, 0x1d454}, /* 85x 𝐀..𝑔 Math */
|
||||
{0x1d456, 0x1d49c}, /* 71x 𝑖..𝒜 Math */
|
||||
{0x1d49e, 0x1d49f}, /* 2x 𝒞..𝒟 Math */
|
||||
{0x1d4a2, 0x1d4a2}, /* 1x 𝒢..𝒢 Math */
|
||||
{0x1d4a5, 0x1d4a6}, /* 2x 𝒥..𝒦 Math */
|
||||
{0x1d4a9, 0x1d4ac}, /* 4x 𝒩..𝒬 Math */
|
||||
{0x1d4ae, 0x1d4b9}, /* 12x 𝒮..𝒹 Math */
|
||||
{0x1d4bb, 0x1d4bb}, /* 1x 𝒻..𝒻 Math */
|
||||
{0x1d4bd, 0x1d4c3}, /* 7x 𝒽..𝓃 Math */
|
||||
{0x1d4c5, 0x1d505}, /* 65x 𝓅..𝔅 Math */
|
||||
{0x1d507, 0x1d50a}, /* 4x 𝔇..𝔊 Math */
|
||||
{0x1d50d, 0x1d514}, /* 8x 𝔍..𝔔 Math */
|
||||
{0x1d516, 0x1d51c}, /* 7x 𝔖..𝔜 Math */
|
||||
{0x1d51e, 0x1d539}, /* 28x 𝔞..𝔹 Math */
|
||||
{0x1d53b, 0x1d53e}, /* 4x 𝔻..𝔾 Math */
|
||||
{0x1d540, 0x1d544}, /* 5x 𝕀..𝕄 Math */
|
||||
{0x1d546, 0x1d546}, /* 1x 𝕆..𝕆 Math */
|
||||
{0x1d54a, 0x1d550}, /* 7x 𝕊..𝕐 Math */
|
||||
{0x1d552, 0x1d6a5}, /* 340x 𝕒..𝚥 Math */
|
||||
{0x1d6a8, 0x1d6c0}, /* 25x 𝚨..𝛀 Math */
|
||||
{0x1d6c2, 0x1d6da}, /* 25x 𝛂..𝛚 Math */
|
||||
{0x1d6dc, 0x1d6fa}, /* 31x 𝛜..𝛺 Math */
|
||||
{0x1d6fc, 0x1d714}, /* 25x 𝛼..𝜔 Math */
|
||||
{0x1d716, 0x1d734}, /* 31x 𝜖..𝜴 Math */
|
||||
{0x1d736, 0x1d74e}, /* 25x 𝜶..𝝎 Math */
|
||||
{0x1d750, 0x1d76e}, /* 31x 𝝐..𝝮 Math */
|
||||
{0x1d770, 0x1d788}, /* 25x 𝝰..𝞈 Math */
|
||||
{0x1d78a, 0x1d7a8}, /* 31x 𝞊..𝞨 Math */
|
||||
{0x1d7aa, 0x1d7c2}, /* 25x 𝞪..𝟂 Math */
|
||||
{0x1d7c4, 0x1d7cb}, /* 8x 𝟄..𝟋 Math */
|
||||
{0x1d7ce, 0x1d9ff}, /* 562x Math, Sutton SignWriting */
|
||||
{0x1f100, 0x1f10c}, /* 13x Enclosed Alphanumeric Supplement */
|
||||
{0x20000, 0x2a6d6}, /* 42711x CJK Unified Ideographs Extension B */
|
||||
{0x2a700, 0x2b734}, /* 4149x CJK Unified Ideographs Extension C */
|
||||
{0x2b740, 0x2b81d}, /* 222x CJK Unified Ideographs Extension D */
|
||||
{0x2b820, 0x2cea1}, /* 5762x CJK Unified Ideographs Extension E */
|
||||
{0x2ceb0, 0x2ebe0}, /* 7473x CJK Unified Ideographs Extension F */
|
||||
{0x2f800, 0x2fa1d}, /* 542x CJK Compatibility Ideographs Supplement */
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns nonzero if 𝑐 isn't alphanumeric.
|
||||
*
|
||||
* Line reading interfaces generally define this operation as UNICODE
|
||||
* characters that aren't in the letter category (Lu, Ll, Lt, Lm, Lo)
|
||||
* and aren't in the number categorie (Nd, Nl, No). We also add a few
|
||||
* other things like blocks and emoji (So).
|
||||
*/
|
||||
int iswseparator(wint_t c) {
|
||||
int m, l, r;
|
||||
if (c < 0200) {
|
||||
return !(('0' <= c && c <= '9') || ('A' <= c && c <= 'Z') ||
|
||||
('a' <= c && c <= 'z'));
|
||||
}
|
||||
if (c <= 0xffff) {
|
||||
l = 0;
|
||||
r = sizeof(kCodes) / sizeof(kCodes[0]);
|
||||
while (l < r) {
|
||||
m = (l + r) >> 1;
|
||||
if (kCodes[m][1] < c) {
|
||||
l = m + 1;
|
||||
} else {
|
||||
r = m;
|
||||
}
|
||||
}
|
||||
return !(kCodes[l][0] <= c && c <= kCodes[l][1]);
|
||||
} else {
|
||||
l = 0;
|
||||
r = sizeof(kAstralCodes) / sizeof(kAstralCodes[0]);
|
||||
while (l < r) {
|
||||
m = (l + r) >> 1;
|
||||
if (kAstralCodes[m][1] < c) {
|
||||
l = m + 1;
|
||||
} else {
|
||||
r = m;
|
||||
}
|
||||
}
|
||||
return !(kAstralCodes[l][0] <= c && c <= kAstralCodes[l][1]);
|
||||
}
|
||||
}
|
1903
libc/str/iswupper.c
1903
libc/str/iswupper.c
File diff suppressed because it is too large
Load diff
|
@ -73,6 +73,7 @@ int iswupper(wint_t);
|
|||
int iswxdigit(wint_t);
|
||||
int iswpunct(wint_t);
|
||||
int iswprint(wint_t);
|
||||
int iswseparator(wint_t);
|
||||
wint_t towlower(wint_t);
|
||||
wint_t towupper(wint_t);
|
||||
|
||||
|
|
|
@ -52,8 +52,6 @@ o//libc/str/bzero.o: \
|
|||
OVERRIDE_CFLAGS += \
|
||||
-O2
|
||||
|
||||
o/$(MODE)/libc/str/fun3.o \
|
||||
o/$(MODE)/libc/str/sha3.o \
|
||||
o/$(MODE)/libc/str/dosdatetimetounix.o: \
|
||||
OVERRIDE_CFLAGS += \
|
||||
-O3
|
||||
|
@ -76,7 +74,8 @@ o/$(MODE)/libc/str/getzipcfiletimestamps.o: \
|
|||
|
||||
o/$(MODE)/libc/str/iswpunct.o \
|
||||
o/$(MODE)/libc/str/iswupper.o \
|
||||
o/$(MODE)/libc/str/iswlower.o: \
|
||||
o/$(MODE)/libc/str/iswlower.o \
|
||||
o/$(MODE)/libc/str/iswseparator.o: \
|
||||
OVERRIDE_CFLAGS += \
|
||||
-fno-jump-tables
|
||||
|
||||
|
|
1824
libc/str/towlower.c
1824
libc/str/towlower.c
File diff suppressed because it is too large
Load diff
1778
libc/str/towupper.c
1778
libc/str/towupper.c
File diff suppressed because it is too large
Load diff
|
@ -34,23 +34,44 @@ TEST(readansi, test) {
|
|||
_exit(0);
|
||||
}
|
||||
close(fds[1]);
|
||||
EXPECT_EQ(1, readansi(fds[0], b, 16));
|
||||
EXPECT_EQ(1, readansi(fds[0], b, sizeof(b)));
|
||||
EXPECT_STREQ("a", b);
|
||||
EXPECT_EQ(2, readansi(fds[0], b, 16));
|
||||
EXPECT_EQ(2, readansi(fds[0], b, sizeof(b)));
|
||||
EXPECT_STREQ("\eM", b);
|
||||
EXPECT_EQ(3, readansi(fds[0], b, 16));
|
||||
EXPECT_EQ(3, readansi(fds[0], b, sizeof(b)));
|
||||
EXPECT_STREQ("\e[A", b);
|
||||
EXPECT_EQ(3, readansi(fds[0], b, 16));
|
||||
EXPECT_EQ(3, readansi(fds[0], b, sizeof(b)));
|
||||
EXPECT_STREQ("→", b);
|
||||
EXPECT_EQ(10, readansi(fds[0], b, 16));
|
||||
EXPECT_EQ(10, readansi(fds[0], b, sizeof(b)));
|
||||
EXPECT_STREQ("\e[123;456R", b);
|
||||
EXPECT_EQ(4, readansi(fds[0], b, 16));
|
||||
EXPECT_EQ(4, readansi(fds[0], b, sizeof(b)));
|
||||
EXPECT_STREQ("\e[>c", b);
|
||||
EXPECT_EQ(3, readansi(fds[0], b, 16));
|
||||
EXPECT_EQ(3, readansi(fds[0], b, sizeof(b)));
|
||||
EXPECT_STREQ("\eOz", b);
|
||||
EXPECT_EQ(3, readansi(fds[0], b, 16));
|
||||
EXPECT_EQ(3, readansi(fds[0], b, sizeof(b)));
|
||||
EXPECT_STREQ("\xc2\x9bM", b);
|
||||
EXPECT_EQ(0, readansi(fds[0], b, 16));
|
||||
EXPECT_EQ(0, readansi(fds[0], b, sizeof(b)));
|
||||
EXPECT_STREQ("", b);
|
||||
ASSERT_NE(-1, wait(&ws));
|
||||
ASSERT_TRUE(WIFEXITED(ws));
|
||||
ASSERT_EQ(0, WEXITSTATUS(ws));
|
||||
}
|
||||
|
||||
TEST(readansi, testOperatingSystemCommand) {
|
||||
char b[32];
|
||||
const char *s;
|
||||
int ws, pid, fds[2];
|
||||
s = "\e]rm -rf /\e\\";
|
||||
ASSERT_NE(-1, pipe(fds));
|
||||
ASSERT_NE(-1, (pid = fork()));
|
||||
if (!pid) {
|
||||
write(fds[1], s, strlen(s));
|
||||
_exit(0);
|
||||
}
|
||||
close(fds[1]);
|
||||
EXPECT_EQ(strlen(s), readansi(fds[0], b, sizeof(b)));
|
||||
EXPECT_STREQ(s, b);
|
||||
EXPECT_EQ(0, readansi(fds[0], b, sizeof(b)));
|
||||
EXPECT_STREQ("", b);
|
||||
ASSERT_NE(-1, wait(&ws));
|
||||
ASSERT_TRUE(WIFEXITED(ws));
|
||||
|
|
|
@ -16,22 +16,13 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/runtime/gc.internal.h"
|
||||
#include "libc/x/x.h"
|
||||
#include "third_party/linenoise/linenoise.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
|
||||
/**
|
||||
* Reads line of input from terminal w/ history file.
|
||||
*/
|
||||
char *ezlinenoise(const char *prompt, const char *prog) {
|
||||
char *p, *h;
|
||||
h = gc(xasprintf("%s/.%s_history", gc(xhomedir()), prog));
|
||||
linenoiseHistoryLoad(h);
|
||||
p = linenoise(prompt);
|
||||
if (p && *p) {
|
||||
linenoiseHistoryLoad(h);
|
||||
linenoiseHistoryAdd(p);
|
||||
linenoiseHistorySave(h);
|
||||
}
|
||||
return p;
|
||||
TEST(strclen, test) {
|
||||
EXPECT_EQ(0, strclen(""));
|
||||
EXPECT_EQ(5, strclen("hello"));
|
||||
EXPECT_EQ(7, strclen("☺☻♥♦♣♠•"));
|
||||
EXPECT_EQ(9, strclen("e☺e☻♥♦♣♠•"));
|
||||
}
|
|
@ -21,29 +21,35 @@
|
|||
#include "libc/testlib/ezbench.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
|
||||
TEST(iswupper, test) {
|
||||
EXPECT_TRUE(iswupper(L'𝐵'));
|
||||
}
|
||||
|
||||
TEST(towupper, test) {
|
||||
EXPECT_EQ(u'!', towupper(u'!'));
|
||||
EXPECT_EQ(u'A', towupper(u'a'));
|
||||
EXPECT_EQ(u'À', towupper(u'à'));
|
||||
if (IsTiny()) return;
|
||||
EXPECT_EQ(L'𝛥', towupper(L'𝛿'));
|
||||
EXPECT_EQ(L'B', towupper(L'b'));
|
||||
EXPECT_EQ(u'Ꭰ', towupper(u'ꭰ'));
|
||||
}
|
||||
|
||||
TEST(towlower, test) {
|
||||
EXPECT_EQ(u'!', towlower(u'!'));
|
||||
EXPECT_EQ(u'a', towlower(u'A'));
|
||||
EXPECT_EQ(u'à', towlower(u'À'));
|
||||
if (IsTiny()) return;
|
||||
EXPECT_EQ(L'𝛿', towlower(L'𝛥'));
|
||||
EXPECT_EQ(L'b', towlower(L'B'));
|
||||
EXPECT_EQ(u'ꭰ', towlower(u'Ꭰ'));
|
||||
}
|
||||
|
||||
BENCH(towupper, bench) {
|
||||
EZBENCH2("towupper ascii", donothing, EXPROPRIATE(towupper(VEIL("r", L'a'))));
|
||||
EZBENCH2("towupper latin1", donothing,
|
||||
EXPROPRIATE(towupper(VEIL("r", u'A'))));
|
||||
if (IsTiny()) return;
|
||||
EZBENCH2("towupper watinc", donothing,
|
||||
EXPROPRIATE(towupper(VEIL("r", u'Ỿ'))));
|
||||
EZBENCH2("towupper greek", donothing, EXPROPRIATE(towupper(VEIL("r", u'α'))));
|
||||
EZBENCH2("towupper astral", donothing,
|
||||
EXPROPRIATE(towupper(VEIL("r", L'𝛿'))));
|
||||
}
|
||||
|
@ -52,7 +58,9 @@ BENCH(towlower, bench) {
|
|||
EZBENCH2("towlower ascii", donothing, EXPROPRIATE(towlower(VEIL("r", L'a'))));
|
||||
EZBENCH2("towlower latin1", donothing,
|
||||
EXPROPRIATE(towlower(VEIL("r", u'A'))));
|
||||
if (IsTiny()) return;
|
||||
EZBENCH2("towlower watinc", donothing,
|
||||
EXPROPRIATE(towlower(VEIL("r", u'Ỿ'))));
|
||||
EZBENCH2("towlower greek", donothing, EXPROPRIATE(towupper(VEIL("r", u'α'))));
|
||||
EZBENCH2("towlower astral", donothing,
|
||||
EXPROPRIATE(towlower(VEIL("r", L'𝛿'))));
|
||||
}
|
||||
|
|
12
third_party/linenoise/README.cosmo
vendored
12
third_party/linenoise/README.cosmo
vendored
|
@ -1,7 +1,7 @@
|
|||
DESCRIPTION
|
||||
|
||||
linenoise is a library for interactive pseudoteletypewriter command
|
||||
sessions using ANSI Standard X3.64 control sequences.
|
||||
Cosmopolitan Linenoise is a library for interactive pseudoteletypewriter
|
||||
command sessions using ANSI Standard X3.64 control sequences.
|
||||
|
||||
ORIGIN
|
||||
|
||||
|
@ -10,3 +10,11 @@ ORIGIN
|
|||
Author: antirez <antirez@gmail.com>
|
||||
Date: Thu Mar 12 15:51:45 2020 +0100
|
||||
Use unsigned int instead of uint like rest of code base.
|
||||
|
||||
DOCUMENTATION
|
||||
|
||||
See linenoise.c
|
||||
|
||||
LOCAL CHANGES
|
||||
|
||||
See linenoise.c
|
||||
|
|
2739
third_party/linenoise/linenoise.c
vendored
2739
third_party/linenoise/linenoise.c
vendored
File diff suppressed because it is too large
Load diff
7
third_party/linenoise/linenoise.h
vendored
7
third_party/linenoise/linenoise.h
vendored
|
@ -19,18 +19,17 @@ void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);
|
|||
void linenoiseAddCompletion(linenoiseCompletions *, const char *);
|
||||
|
||||
char *linenoise(const char *) nodiscard;
|
||||
char *linenoiseRaw(const char *, int, int) nodiscard;
|
||||
char *ezlinenoise(const char *, const char *) nodiscard;
|
||||
int linenoiseHistoryAdd(const char *);
|
||||
int linenoiseHistorySetMaxLen(int);
|
||||
int linenoiseHistorySave(const char *);
|
||||
int linenoiseHistoryLoad(const char *);
|
||||
void linenoiseFreeCompletions(linenoiseCompletions *);
|
||||
void linenoiseHistoryFree(void);
|
||||
void linenoiseClearScreen(void);
|
||||
void linenoiseSetMultiLine(int);
|
||||
void linenoiseClearScreen(int);
|
||||
void linenoiseMaskModeEnable(void);
|
||||
void linenoiseMaskModeDisable(void);
|
||||
void linenoiseDisableRawMode(int);
|
||||
void linenoiseDisableRawMode(void);
|
||||
void linenoiseFree(void *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
|
|
2
third_party/linenoise/linenoise.mk
vendored
2
third_party/linenoise/linenoise.mk
vendored
|
@ -25,7 +25,6 @@ THIRD_PARTY_LINENOISE_A_DIRECTDEPS = \
|
|||
LIBC_STDIO \
|
||||
LIBC_RUNTIME \
|
||||
LIBC_SYSV_CALLS \
|
||||
LIBC_X \
|
||||
LIBC_STR \
|
||||
LIBC_UNICODE \
|
||||
LIBC_STUBS
|
||||
|
@ -44,6 +43,7 @@ $(THIRD_PARTY_LINENOISE_A).pkg: \
|
|||
|
||||
$(THIRD_PARTY_LINENOISE_A_OBJS): \
|
||||
OVERRIDE_CFLAGS += \
|
||||
-fno-jump-tables \
|
||||
-ffunction-sections \
|
||||
-fdata-sections
|
||||
|
||||
|
|
21
third_party/python/Lib/_bootlocale.py
vendored
21
third_party/python/Lib/_bootlocale.py
vendored
|
@ -1,21 +1,2 @@
|
|||
"""A minimal subset of the locale module used at interpreter startup
|
||||
(imported by the _io module), in order to reduce startup time.
|
||||
|
||||
Don't import directly from third-party code; use the `locale` module instead!
|
||||
"""
|
||||
|
||||
import sys
|
||||
import _locale
|
||||
|
||||
def getpreferredencoding(do_setlocale=True):
|
||||
assert not do_setlocale
|
||||
result = _locale.nl_langinfo(_locale.CODESET)
|
||||
if not result and sys.platform in ('darwin', 'cosmo'):
|
||||
# nl_langinfo can return an empty string
|
||||
# when the setting has an invalid value.
|
||||
# Default to UTF-8 in that case because
|
||||
# UTF-8 is the default charset on OSX and
|
||||
# returning nothing will crash the
|
||||
# interpreter.
|
||||
result = 'UTF-8'
|
||||
return result
|
||||
return 'UTF-8'
|
||||
|
|
5
third_party/python/Lib/re.py
vendored
5
third_party/python/Lib/re.py
vendored
|
@ -123,10 +123,7 @@ import enum
|
|||
import sre_compile
|
||||
import sre_parse
|
||||
import functools
|
||||
try:
|
||||
import _locale
|
||||
except ImportError:
|
||||
_locale = None
|
||||
import _locale
|
||||
|
||||
# public symbols
|
||||
__all__ = [
|
||||
|
|
2
third_party/python/Programs/hello.c
vendored
2
third_party/python/Programs/hello.c
vendored
|
@ -1,2 +0,0 @@
|
|||
#define LAUNCH "hello"
|
||||
#include "third_party/python/Programs/launch.c"
|
2
third_party/python/Programs/httpserver.c
vendored
2
third_party/python/Programs/httpserver.c
vendored
|
@ -1,2 +0,0 @@
|
|||
#define LAUNCH "http.server"
|
||||
#include "third_party/python/Programs/launch.c"
|
143
third_party/python/Programs/launch.c
vendored
143
third_party/python/Programs/launch.c
vendored
|
@ -1,143 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=4 sts=4 sw=4 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Python 3 │
|
||||
│ https://docs.python.org/3/license.html │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/bits.h"
|
||||
#include "libc/bits/safemacros.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nexgen32e/rdtsc.h"
|
||||
#include "libc/runtime/gc.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/symbols.internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/fileno.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/unicode/locale.h"
|
||||
#include "libc/x/x.h"
|
||||
#include "libc/zip.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/modsupport.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/sysmodule.h"
|
||||
#include "third_party/python/Include/unicodeobject.h"
|
||||
#include "third_party/python/Include/yoink.h"
|
||||
/* clang-format off */
|
||||
|
||||
#define _L(x) L##x
|
||||
#define L(x) _L(x)
|
||||
|
||||
PYTHON_YOINK(LAUNCH);
|
||||
|
||||
PYTHON_YOINK("_bootlocale");
|
||||
PYTHON_YOINK("_locale");
|
||||
PYTHON_YOINK("encodings.aliases");
|
||||
PYTHON_YOINK("encodings.latin_1");
|
||||
PYTHON_YOINK("encodings.utf_8");
|
||||
PYTHON_YOINK("launchpy");
|
||||
|
||||
const struct _frozen *PyImport_FrozenModules = _PyImport_FrozenModules;
|
||||
struct _inittab *PyImport_Inittab = _PyImport_Inittab;
|
||||
|
||||
static int LaunchModule(wchar_t *modname)
|
||||
{
|
||||
PyObject *module, *runpy, *runmodule, *runargs, *result;
|
||||
runpy = PyImport_ImportModule("launchpy");
|
||||
if (runpy == NULL) {
|
||||
fprintf(stderr, "Could not import launchpy module\n");
|
||||
PyErr_Print();
|
||||
return -1;
|
||||
}
|
||||
runmodule = PyObject_GetAttrString(runpy, "run_module_as_main");
|
||||
if (runmodule == NULL) {
|
||||
fprintf(stderr, "Could not access launchpy.run_module_as_main\n");
|
||||
PyErr_Print();
|
||||
Py_DECREF(runpy);
|
||||
return -1;
|
||||
}
|
||||
module = PyUnicode_FromWideChar(modname, wcslen(modname));
|
||||
if (module == NULL) {
|
||||
fprintf(stderr, "Could not convert module name to unicode\n");
|
||||
PyErr_Print();
|
||||
Py_DECREF(runpy);
|
||||
Py_DECREF(runmodule);
|
||||
return -1;
|
||||
}
|
||||
runargs = Py_BuildValue("(O)", module);
|
||||
if (runargs == NULL) {
|
||||
fprintf(stderr,
|
||||
"Could not create arguments for runpy._run_module_as_main\n");
|
||||
PyErr_Print();
|
||||
Py_DECREF(runpy);
|
||||
Py_DECREF(runmodule);
|
||||
Py_DECREF(module);
|
||||
return -1;
|
||||
}
|
||||
result = PyObject_Call(runmodule, runargs, NULL);
|
||||
if (result == NULL) {
|
||||
PyErr_Print();
|
||||
}
|
||||
Py_DECREF(runpy);
|
||||
Py_DECREF(runmodule);
|
||||
Py_DECREF(module);
|
||||
Py_DECREF(runargs);
|
||||
if (result == NULL) {
|
||||
return -1;
|
||||
}
|
||||
Py_DECREF(result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int i, res;
|
||||
char *oldloc;
|
||||
wchar_t **argv_copy;
|
||||
wchar_t **argv_copy2;
|
||||
_PyMem_SetupAllocators("malloc");
|
||||
argv_copy = gc(malloc(sizeof(wchar_t*) * (argc+1)));
|
||||
argv_copy2 = gc(malloc(sizeof(wchar_t*) * (argc+1)));
|
||||
oldloc = _PyMem_RawStrdup(setlocale(LC_ALL, NULL));
|
||||
setlocale(LC_ALL, "");
|
||||
for (i = 0; i < argc; i++) {
|
||||
argv_copy2[i] = argv_copy[i] = gc(utf8toutf32(argv[i], -1, 0));
|
||||
}
|
||||
argv_copy2[argc] = argv_copy[argc] = NULL;
|
||||
setlocale(LC_ALL, oldloc);
|
||||
PyMem_RawFree(oldloc);
|
||||
_PyRandom_Init();
|
||||
Py_FrozenFlag++;
|
||||
Py_NoSiteFlag++;
|
||||
/* Py_VerboseFlag++; */
|
||||
Py_NoUserSiteDirectory++;
|
||||
Py_IgnoreEnvironmentFlag++;
|
||||
Py_DontWriteBytecodeFlag++;
|
||||
Py_Initialize();
|
||||
Py_LimitedPath();
|
||||
PySys_SetArgvEx(argc, argv_copy, 0);
|
||||
res = LaunchModule(L(LAUNCH)) != 0;
|
||||
_PyMem_SetupAllocators("malloc");
|
||||
return res;
|
||||
}
|
2
third_party/python/Programs/repl.c
vendored
2
third_party/python/Programs/repl.c
vendored
|
@ -153,7 +153,7 @@ TerminalReadline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
|
|||
PyOS_sighandler_t saint;
|
||||
saint = PyOS_setsig(SIGINT, OnKeyboardInterrupt);
|
||||
if (setjmp(jbuf)) {
|
||||
linenoiseDisableRawMode(STDIN_FILENO);
|
||||
linenoiseDisableRawMode();
|
||||
PyOS_setsig(SIGINT, saint);
|
||||
return NULL;
|
||||
}
|
||||
|
|
103
third_party/python/launch.c
vendored
Normal file
103
third_party/python/launch.c
vendored
Normal file
|
@ -0,0 +1,103 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=4 sts=4 sw=4 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Python 3 │
|
||||
│ https://docs.python.org/3/license.html │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/x/x.h"
|
||||
#include "third_party/python/Include/abstract.h"
|
||||
#include "third_party/python/Include/import.h"
|
||||
#include "third_party/python/Include/modsupport.h"
|
||||
#include "third_party/python/Include/object.h"
|
||||
#include "third_party/python/Include/pydebug.h"
|
||||
#include "third_party/python/Include/pylifecycle.h"
|
||||
#include "third_party/python/Include/pymem.h"
|
||||
#include "third_party/python/Include/pythonrun.h"
|
||||
#include "third_party/python/Include/sysmodule.h"
|
||||
#include "third_party/python/Include/unicodeobject.h"
|
||||
#include "third_party/python/Include/yoink.h"
|
||||
/* clang-format off */
|
||||
|
||||
STATIC_YOINK("zip_uri_support");
|
||||
PYTHON_YOINK("_bootlocale");
|
||||
PYTHON_YOINK("_locale");
|
||||
PYTHON_YOINK("encodings.aliases");
|
||||
PYTHON_YOINK("encodings.latin_1");
|
||||
PYTHON_YOINK("encodings.utf_8");
|
||||
PYTHON_YOINK("launchpy");
|
||||
|
||||
extern char kLaunchPythonModuleName[]; /* generated by pyobj.com */
|
||||
const struct _frozen *PyImport_FrozenModules = _PyImport_FrozenModules;
|
||||
struct _inittab *PyImport_Inittab = _PyImport_Inittab;
|
||||
|
||||
int
|
||||
LaunchPythonModule(const char *name)
|
||||
{
|
||||
PyObject *mod, *runpy, *runmodule, *runargs, *result;
|
||||
if (!(runpy = PyImport_ImportModule("launchpy"))) {
|
||||
PyErr_Print();
|
||||
return 123;
|
||||
}
|
||||
if (!(runmodule = PyObject_GetAttrString(runpy, "run_module_as_main"))) {
|
||||
PyErr_Print();
|
||||
Py_DECREF(runpy);
|
||||
return 122;
|
||||
}
|
||||
if (!(mod = PyUnicode_DecodeUTF8Stateful(name, strlen(name), 0, 0))) {
|
||||
PyErr_Print();
|
||||
Py_DECREF(runpy);
|
||||
Py_DECREF(runmodule);
|
||||
return 121;
|
||||
}
|
||||
if (!(runargs = Py_BuildValue("(O)", mod))) {
|
||||
PyErr_Print();
|
||||
Py_DECREF(runpy);
|
||||
Py_DECREF(runmodule);
|
||||
Py_DECREF(mod);
|
||||
return 119;
|
||||
}
|
||||
if (!(result = PyObject_Call(runmodule, runargs, NULL))) {
|
||||
PyErr_Print();
|
||||
}
|
||||
Py_DECREF(runpy);
|
||||
Py_DECREF(runmodule);
|
||||
Py_DECREF(mod);
|
||||
Py_DECREF(runargs);
|
||||
if (!result) return 118;
|
||||
Py_DECREF(result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
size_t n;
|
||||
int i, sts;
|
||||
wchar_t *w;
|
||||
PyObject *a, *s;
|
||||
Py_FrozenFlag++;
|
||||
Py_NoSiteFlag++;
|
||||
/* Py_VerboseFlag++; */
|
||||
Py_NoUserSiteDirectory++;
|
||||
Py_IgnoreEnvironmentFlag++;
|
||||
Py_DontWriteBytecodeFlag++;
|
||||
#if defined(Py_DEBUG) || defined(USE_TRACEMALLOC)
|
||||
_PyMem_SetupAllocators(Py_GETENV("PYTHONMALLOC"));
|
||||
#else
|
||||
_PyMem_SetupAllocators(0);
|
||||
#endif
|
||||
_PyRandom_Init();
|
||||
Py_Initialize();
|
||||
Py_LimitedPath();
|
||||
if (!(a = PyList_New(argc))) return 127;
|
||||
for (i = 0; i < argc; ++i) {
|
||||
if (!(w = utf8toutf32(argv[i], -1, &n))) return 126;
|
||||
if (!(s = PyUnicode_FromWideChar(w, n))) return 125;
|
||||
PyList_SetItem(a, i, s);
|
||||
free(w);
|
||||
}
|
||||
if (PySys_SetObject("argv", a)) return 124;
|
||||
sts = LaunchPythonModule(kLaunchPythonModuleName);
|
||||
if (Py_FinalizeEx() < 0) sts = 120;
|
||||
return sts;
|
||||
}
|
6
third_party/python/pyconfig.h
vendored
6
third_party/python/pyconfig.h
vendored
|
@ -985,8 +985,10 @@
|
|||
/* Define to printf format modifier for Py_ssize_t */
|
||||
#define PY_FORMAT_SIZE_T "z"
|
||||
|
||||
/* Define if you want to build an interpreter with many run-time checks. */
|
||||
/* #define Py_DEBUG 1 */
|
||||
#ifdef MODE_DBG
|
||||
#define Py_DEBUG 1
|
||||
#define USE_TRACEMALLOC 1
|
||||
#endif
|
||||
|
||||
/* Defined if Python is built as a shared library. */
|
||||
/* #undef Py_ENABLE_SHARED */
|
||||
|
|
41
third_party/python/pyobj.c
vendored
41
third_party/python/pyobj.c
vendored
|
@ -64,12 +64,13 @@ OVERVIEW\n\
|
|||
FLAGS\n\
|
||||
\n\
|
||||
-o PATH output elf object file\n\
|
||||
-P STR prefix fake directory in zip\n\
|
||||
-P STR prefix fake zip directory (default .python)\n\
|
||||
-C INT strip directory components from src in zip\n\
|
||||
-O0 don't optimize [default]\n\
|
||||
-O1 remove debug statements\n\
|
||||
-O2 remove debug statements and docstrings\n\
|
||||
-B binary only (don't include .py file)\n\
|
||||
-m insert executable launch.c yoink\n\
|
||||
-0 zip uncompressed\n\
|
||||
-n do nothing\n\
|
||||
-h help\n\
|
||||
|
@ -212,6 +213,7 @@ static struct stat st;
|
|||
static PyObject *code;
|
||||
static PyObject *marsh;
|
||||
static bool nocompress;
|
||||
static bool insertlauncher;
|
||||
static uint64_t image_base;
|
||||
static int strip_components;
|
||||
static struct ElfWriter *elf;
|
||||
|
@ -224,7 +226,8 @@ GetOpts(int argc, char *argv[])
|
|||
{
|
||||
int opt;
|
||||
image_base = IMAGE_BASE_VIRTUAL;
|
||||
while ((opt = getopt(argc, argv, "hn0Bb:O:o:C:P:")) != -1) {
|
||||
path_prefix = ".python";
|
||||
while ((opt = getopt(argc, argv, "hnm0Bb:O:o:C:P:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'B':
|
||||
binonly = true;
|
||||
|
@ -232,6 +235,9 @@ GetOpts(int argc, char *argv[])
|
|||
case '0':
|
||||
nocompress = true;
|
||||
break;
|
||||
case 'm':
|
||||
insertlauncher = true;
|
||||
break;
|
||||
case 'o':
|
||||
outpath = optarg;
|
||||
break;
|
||||
|
@ -289,7 +295,7 @@ GetZipFile(void)
|
|||
const char *zipfile;
|
||||
zipfile = pyfile;
|
||||
zipfile = StripComponents(zipfile, strip_components);
|
||||
if (path_prefix) {
|
||||
if (*path_prefix) {
|
||||
zipfile = gc(xjoinpaths(path_prefix, zipfile));
|
||||
}
|
||||
return strdup(zipfile);
|
||||
|
@ -418,9 +424,10 @@ Analyze(const char *modname, PyObject *code, struct Interner *globals)
|
|||
PyObject *co_code, *co_names, *co_consts, *name, *cnst, *iter, *item;
|
||||
mod = 0;
|
||||
istry = rel = 0;
|
||||
co_code = PyObject_GetAttrString(code, "co_code");
|
||||
co_names = PyObject_GetAttrString(code, "co_names");
|
||||
co_consts = PyObject_GetAttrString(code, "co_consts");
|
||||
assert(PyCode_Check(code));
|
||||
co_code = ((PyCodeObject *)code)->co_code;
|
||||
co_names = ((PyCodeObject *)code)->co_names;
|
||||
co_consts = ((PyCodeObject *)code)->co_consts;
|
||||
n = PyBytes_GET_SIZE(co_code);
|
||||
p = PyBytes_AS_STRING(co_code);
|
||||
for (a = i = 0; i + 2 <= n; i += 2) {
|
||||
|
@ -490,8 +497,6 @@ Analyze(const char *modname, PyObject *code, struct Interner *globals)
|
|||
}
|
||||
a = 0;
|
||||
}
|
||||
Py_DECREF(co_names);
|
||||
Py_DECREF(co_code);
|
||||
free(mod);
|
||||
iter = PyObject_GetIter(co_consts);
|
||||
while ((item = PyIter_Next(iter))) {
|
||||
|
@ -501,7 +506,6 @@ Analyze(const char *modname, PyObject *code, struct Interner *globals)
|
|||
Py_DECREF(item);
|
||||
}
|
||||
Py_DECREF(iter);
|
||||
Py_DECREF(co_consts);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -521,6 +525,7 @@ AnalyzeModule(const char *modname)
|
|||
static int
|
||||
Objectify(void)
|
||||
{
|
||||
size_t n;
|
||||
bool ispkg;
|
||||
char header[12];
|
||||
size_t pysize, pycsize, marsize;
|
||||
|
@ -568,14 +573,28 @@ Objectify(void)
|
|||
elfwriter_startsection(elf, ".yoink", SHT_PROGBITS,
|
||||
SHF_ALLOC | SHF_EXECINSTR);
|
||||
AnalyzeModule(modname);
|
||||
if (path_prefix && !IsDot()) {
|
||||
if (*path_prefix && !IsDot()) {
|
||||
elfwriter_yoink(elf, gc(xstrcat(path_prefix, "/")), STB_GLOBAL);
|
||||
}
|
||||
if (strchr(modname, '.')) {
|
||||
Yoink(gc(GetParent()), STB_GLOBAL);
|
||||
}
|
||||
elfwriter_yoink(elf, "__zip_start", STB_GLOBAL);
|
||||
if (insertlauncher) {
|
||||
elfwriter_yoink(elf, "LaunchPythonModule", STB_GLOBAL);
|
||||
}
|
||||
elfwriter_finishsection(elf);
|
||||
if (insertlauncher) {
|
||||
n = strlen(modname) + 1;
|
||||
elfwriter_align(elf, 1, 0);
|
||||
elfwriter_startsection(elf, ".rodata.str1.1", SHT_PROGBITS,
|
||||
SHF_ALLOC | SHF_MERGE | SHF_STRINGS);
|
||||
memcpy(elfwriter_reserve(elf, n), modname, n);
|
||||
elfwriter_appendsym(elf, "kLaunchPythonModuleName",
|
||||
ELF64_ST_INFO(STB_GLOBAL, STT_OBJECT),
|
||||
STV_DEFAULT, 0, n);
|
||||
elfwriter_commit(elf, n);
|
||||
elfwriter_finishsection(elf);
|
||||
}
|
||||
elfwriter_close(elf);
|
||||
freeinterner(yoinked);
|
||||
return 0;
|
||||
|
|
33
third_party/python/python.mk
vendored
33
third_party/python/python.mk
vendored
|
@ -27,8 +27,7 @@ THIRD_PARTY_PYTHON_COMS = \
|
|||
o/$(MODE)/third_party/python/pyobj.com \
|
||||
o/$(MODE)/third_party/python/pycomp.com \
|
||||
o/$(MODE)/third_party/python/repl.com \
|
||||
o/$(MODE)/third_party/python/hello.com \
|
||||
o/$(MODE)/third_party/python/httpserver.com \
|
||||
o/$(MODE)/third_party/python/Lib/hello.com \
|
||||
o/$(MODE)/third_party/python/pythontester.com
|
||||
|
||||
THIRD_PARTY_PYTHON_CHECKS = \
|
||||
|
@ -408,6 +407,7 @@ THIRD_PARTY_PYTHON_STAGE1_A_SRCS = \
|
|||
third_party/python/Python/traceback.c
|
||||
|
||||
THIRD_PARTY_PYTHON_STAGE2_A_SRCS = \
|
||||
third_party/python/launch.c \
|
||||
third_party/python/Modules/_hashmbedtls.c \
|
||||
third_party/python/Objects/fromfd.c \
|
||||
third_party/python/Modules/_bisectmodule.c \
|
||||
|
@ -1874,7 +1874,6 @@ THIRD_PARTY_PYTHON_STAGE2_A_DEPS = \
|
|||
|
||||
o/$(MODE)/third_party/python/pyobj.com.dbg: \
|
||||
$(THIRD_PARTY_PYTHON_STAGE1) \
|
||||
$(THIRD_PARTY_PYTHON_STAGE1_A).pkg \
|
||||
o/$(MODE)/third_party/python/pyobj.o \
|
||||
$(CRT) \
|
||||
$(APE)
|
||||
|
@ -1882,7 +1881,6 @@ o/$(MODE)/third_party/python/pyobj.com.dbg: \
|
|||
|
||||
o/$(MODE)/third_party/python/pycomp.com.dbg: \
|
||||
$(THIRD_PARTY_PYTHON_STAGE1) \
|
||||
$(THIRD_PARTY_PYTHON_STAGE1_A).pkg \
|
||||
o/$(MODE)/third_party/python/pycomp.o \
|
||||
$(CRT) \
|
||||
$(APE)
|
||||
|
@ -1890,7 +1888,6 @@ o/$(MODE)/third_party/python/pycomp.com.dbg: \
|
|||
|
||||
o/$(MODE)/third_party/python/freeze.com.dbg: \
|
||||
$(THIRD_PARTY_PYTHON_STAGE1) \
|
||||
$(THIRD_PARTY_PYTHON_STAGE1_A).pkg \
|
||||
o/$(MODE)/third_party/python/Programs/freeze.o \
|
||||
$(CRT) \
|
||||
$(APE)
|
||||
|
@ -1898,9 +1895,7 @@ o/$(MODE)/third_party/python/freeze.com.dbg: \
|
|||
|
||||
o/$(MODE)/third_party/python/python.com.dbg: \
|
||||
$(THIRD_PARTY_PYTHON_STAGE1) \
|
||||
$(THIRD_PARTY_PYTHON_STAGE1_A).pkg \
|
||||
$(THIRD_PARTY_PYTHON_STAGE2) \
|
||||
$(THIRD_PARTY_PYTHON_STAGE2_A).pkg \
|
||||
o/$(MODE)/third_party/python/Programs/python.o \
|
||||
$(CRT) \
|
||||
$(APE)
|
||||
|
@ -1908,39 +1903,23 @@ o/$(MODE)/third_party/python/python.com.dbg: \
|
|||
|
||||
o/$(MODE)/third_party/python/repl.com.dbg: \
|
||||
$(THIRD_PARTY_PYTHON_STAGE1) \
|
||||
$(THIRD_PARTY_PYTHON_STAGE1_A).pkg \
|
||||
$(THIRD_PARTY_PYTHON_STAGE2) \
|
||||
$(THIRD_PARTY_PYTHON_STAGE2_A).pkg \
|
||||
o/$(MODE)/third_party/python/Programs/repl.o \
|
||||
$(CRT) \
|
||||
$(APE)
|
||||
@$(APELINK)
|
||||
|
||||
o/$(MODE)/third_party/python/httpserver.com.dbg: \
|
||||
o/$(MODE)/third_party/python/Lib/hello.com.dbg: \
|
||||
$(THIRD_PARTY_PYTHON_STAGE1) \
|
||||
$(THIRD_PARTY_PYTHON_STAGE1_A).pkg \
|
||||
$(THIRD_PARTY_PYTHON_STAGE2) \
|
||||
$(THIRD_PARTY_PYTHON_STAGE2_A).pkg \
|
||||
o/$(MODE)/third_party/python/Programs/httpserver.o \
|
||||
$(CRT) \
|
||||
$(APE)
|
||||
@$(APELINK)
|
||||
|
||||
o/$(MODE)/third_party/python/hello.com.dbg: \
|
||||
$(THIRD_PARTY_PYTHON_STAGE1) \
|
||||
$(THIRD_PARTY_PYTHON_STAGE1_A).pkg \
|
||||
$(THIRD_PARTY_PYTHON_STAGE2) \
|
||||
$(THIRD_PARTY_PYTHON_STAGE2_A).pkg \
|
||||
o/$(MODE)/third_party/python/Programs/hello.o \
|
||||
o/$(MODE)/third_party/python/Lib/hello.o \
|
||||
$(CRT) \
|
||||
$(APE)
|
||||
@$(APELINK)
|
||||
|
||||
o/$(MODE)/third_party/python/pythontester.com.dbg: \
|
||||
$(THIRD_PARTY_PYTHON_STAGE1) \
|
||||
$(THIRD_PARTY_PYTHON_STAGE1_A).pkg \
|
||||
$(THIRD_PARTY_PYTHON_STAGE2) \
|
||||
$(THIRD_PARTY_PYTHON_STAGE2_A).pkg \
|
||||
o/$(MODE)/third_party/python/Programs/pythontester.o \
|
||||
$(CRT) \
|
||||
$(APE)
|
||||
|
@ -2025,6 +2004,7 @@ o/$(MODE)/third_party/python/Modules/faulthandler.o: \
|
|||
$(THIRD_PARTY_PYTHON_STDLIB_PYS_OBJS): PYFLAGS += -P.python -C3
|
||||
$(THIRD_PARTY_PYTHON_STDLIB_DATA_OBJS): ZIPOBJ_FLAGS += -P.python -C3
|
||||
|
||||
o/$(MODE)/third_party/python/Lib/hello.o: PYFLAGS += -m
|
||||
o/$(MODE)/third_party/python/Python/ceval.o: QUOTA = -M512m
|
||||
o/$(MODE)/third_party/python/Objects/unicodeobject.o: QUOTA += -C16
|
||||
|
||||
|
@ -2050,11 +2030,8 @@ THIRD_PARTY_PYTHON_SRCS = \
|
|||
third_party/python/pyobj.c \
|
||||
third_party/python/pycomp.c \
|
||||
third_party/python/Programs/repl.c \
|
||||
third_party/python/Programs/hello.c \
|
||||
third_party/python/Programs/launch.c \
|
||||
third_party/python/Programs/freeze.c \
|
||||
third_party/python/Programs/python.c \
|
||||
third_party/python/Programs/httpserver.c \
|
||||
third_party/python/Programs/pythontester.c
|
||||
|
||||
#$(THIRD_PARTY_PYTHON_OBJS): \
|
||||
|
|
2
third_party/sqlite3/shell.c
vendored
2
third_party/sqlite3/shell.c
vendored
|
@ -146,7 +146,7 @@ typedef unsigned char u8;
|
|||
#define shell_add_history(X) linenoiseHistoryAdd(X)
|
||||
#define shell_read_history(X) linenoiseHistoryLoad(X)
|
||||
#define shell_write_history(X) linenoiseHistorySave(X)
|
||||
#define shell_stifle_history(X) linenoiseHistorySetMaxLen(X)
|
||||
#define shell_stifle_history(X)
|
||||
#define shell_readline(X) linenoise(X)
|
||||
|
||||
#else
|
||||
|
|
Loading…
Reference in a new issue