From 9e3e985ae5fdccc9acf0fc451f2a61ff0407fd04 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Sat, 10 Oct 2020 21:18:53 -0700 Subject: [PATCH] Make terminal ui binaries work well everywhere Here's some screenshots of an emulator tui program that was compiled on Linux, then scp'd it to Windows, Mac, and FreeBSD. https://justine.storage.googleapis.com/blinkenlights-cmdexe.png https://justine.storage.googleapis.com/blinkenlights-imac.png https://justine.storage.googleapis.com/blinkenlights-freebsd.png https://justine.storage.googleapis.com/blinkenlights-lisp.png How is this even possible that we have a nontrivial ui binary that just works on Mac, Windows, Linux, and BSD? Surely a first ever achievement. Fixed many bugs. Bootstrapped John McCarthy's metacircular evaluator on bare metal in half the size of Altair BASIC (about 2.5kb) and ran it in emulator for fun and profit. --- .gitignore | 1 + ape/ape.S | 4 +- dsp/core/getintegercoefficients.c | 2 +- dsp/scale/gyarados.c | 1 - dsp/tty/hidecursor.c | 3 + dsp/tty/ident.c | 2 + dsp/tty/send.c | 6 +- dsp/tty/sendtitle.c | 2 + dsp/tty/ttyraw.c | 2 + dsp/tty/write.c | 2 + examples/acid2.c | 136 ++ examples/backtrace.c | 6 +- examples/examples.mk | 4 +- examples/hellojs.c | 2 + examples/ispell.c | 2 + examples/kilo.c | 104 +- examples/printargs.c | 4 + examples/printmysymbols.c | 10 +- examples/tiny-raw-linux-tutorial.S | 3 - examples/ttyinfo.c | 189 ++- libc/calls/calls.h | 4 +- libc/{time => calls}/getitimer.c | 6 +- libc/calls/hefty/mkntcmdline.c | 2 +- libc/calls/internal.h | 5 + libc/calls/ioctl-tcsets-nt.c | 16 +- libc/calls/ioctl-tiocgwinsz-nt.c | 47 +- libc/calls/nanosleep-nt.c | 44 + .../getsymbolname.c => calls/nanosleep-xnu.c} | 21 +- libc/calls/nanosleep.c | 31 +- libc/calls/now.c | 36 +- libc/{nexgen32e/ispunct.S => calls/nowl.S} | 34 +- libc/calls/onntalarm.c | 31 + libc/calls/onntconsoleevent.c | 6 +- libc/calls/pread.c | 3 +- libc/calls/preadv.c | 2 +- libc/calls/pwritev.c | 9 +- libc/calls/readansi.c | 124 ++ libc/calls/setitimer-nt.c | 104 ++ libc/{time => calls}/setitimer.c | 33 +- libc/calls/struct/stat.h | 2 +- .../unprovable.S => calls/thunks/onntalarm.S} | 14 +- libc/calls/typedef/sigaction_f.h | 5 +- libc/calls/ucontext.h | 1 - libc/calls/writev.c | 5 + libc/conv/conv.h | 9 +- libc/conv/conv.mk | 3 +- libc/fmt/palandprintf.c | 22 +- libc/fmt/stoa.c | 61 +- libc/intrin/pcmpgtb.c | 2 + libc/intrin/pmovmskb.c | 6 + libc/intrin/pshufd.c | 6 +- libc/intrin/psubb.c | 4 +- libc/intrin/psubb.h | 2 +- libc/isystem/windows.h | 1 + libc/linux/close.h | 12 + libc/linux/exit.h | 2 +- libc/linux/fstat.h | 16 + libc/linux/mmap.h | 20 + libc/linux/munmap.h | 15 + libc/linux/open.h | 15 + libc/linux/read.h | 2 +- libc/linux/write.h | 4 +- libc/log/asan.c | 94 +- libc/log/attachdebugger.c | 12 +- libc/log/backtrace.h | 5 +- libc/log/backtrace2.c | 20 +- libc/log/backtrace3.c | 14 +- libc/log/check.h | 4 +- libc/log/checkaligned.c | 2 +- libc/log/checkfail.c | 16 +- libc/log/checkfail_ndebug.c | 2 +- libc/log/die.c | 4 +- libc/log/gdbexec.c | 2 +- libc/log/getsymboltable.c | 8 +- libc/log/log.h | 1 - libc/log/malloc_stats.c | 6 +- libc/log/meminfo.c | 4 +- libc/log/oncrash.c | 86 +- libc/log/perror.c | 6 +- libc/log/ubsan.c | 4 +- libc/log/vflogf.c | 6 +- libc/macros.h | 2 +- libc/nexgen32e/bcopy.S | 1 - libc/nexgen32e/cescapec.S | 13 +- libc/nexgen32e/ctype.S | 101 -- libc/nexgen32e/hextoint.S | 33 - libc/nexgen32e/iscntrl.S | 35 - libc/nexgen32e/isgraph.S | 38 - libc/nexgen32e/iswalnum.S | 33 - libc/nexgen32e/iswalpha.S | 34 - libc/nexgen32e/iswblank.S | 34 - libc/nexgen32e/iswcntrl.S | 35 - libc/nexgen32e/iswdigit.S | 34 - libc/nexgen32e/iswgraph.S | 38 - libc/nexgen32e/iswlower.S | 34 - libc/nexgen32e/iswprint.S | 35 - libc/nexgen32e/iswpunct.S | 39 - libc/nexgen32e/iswspace.S | 33 - libc/nexgen32e/iswupper.S | 34 - libc/nexgen32e/iswxdigit.S | 34 - libc/nexgen32e/kctype.S | 63 - libc/nexgen32e/tolower.S | 31 - libc/nexgen32e/toupper.S | 35 - libc/nexgen32e/towupper.S | 36 - libc/nt/KernelBase/CancelIo.s | 13 + libc/nt/KernelBase/CancelIoEx.s | 10 + libc/nt/KernelBase/CancelSynchronousIo.s | 13 + libc/nt/KernelBase/CreateIoCompletionPort.s | 10 + libc/nt/KernelBase/CreateThread.s | 10 + libc/nt/KernelBase/CreateWaitableTimerW.s | 10 + .../nt/KernelBase/GetQueuedCompletionStatus.s | 10 + .../KernelBase/GetQueuedCompletionStatusEx.s | 10 + .../KernelBase/PostQueuedCompletionStatus.s | 10 + libc/nt/KernelBase/ReadFileEx.s | 10 + libc/nt/KernelBase/SetWaitableTimer.s | 10 + libc/nt/KernelBase/WriteFileEx.s | 10 + libc/nt/files.h | 9 +- libc/nt/iocp.h | 79 + libc/nt/ipc.h | 32 +- .../SetFileCompletionNotificationModes.s | 10 + libc/nt/master.sh | 26 +- libc/nt/nt/time.h | 2 +- libc/nt/struct/guid.h | 15 + libc/nt/struct/overlappedentry.h | 16 + libc/nt/synchronization.h | 57 +- libc/nt/thread.h | 13 + libc/nt/thunk/msabi.h | 7 +- libc/nt/winsock.h | 84 +- libc/runtime/_exit.S | 3 - libc/runtime/closesymboltable.c | 2 +- libc/runtime/directmap.c | 1 + libc/runtime/executive.S | 38 +- libc/runtime/findcombinary.c | 18 +- libc/runtime/finddebugbinary.c | 2 +- libc/runtime/ftrace.greg.c | 47 +- libc/runtime/getdosargv.c | 33 +- libc/runtime/getdosenviron.h | 22 +- libc/runtime/gothere.greg.c | 1 + libc/runtime/internal.h | 2 +- libc/runtime/isheap.c | 18 +- libc/runtime/mapanon.c | 2 +- libc/runtime/memtrack.h | 10 +- libc/runtime/opensymboltable.c | 6 +- libc/runtime/printmemoryintervals.c | 8 +- libc/runtime/runtime.h | 1 - libc/runtime/symbols.h | 33 +- libc/runtime/winmain.greg.c | 15 +- libc/str/decodentsutf16.c | 33 + libc/str/hextoint.c | 32 + libc/str/isalnum.c | 25 + libc/{log/backtrace.c => str/isalpha.c} | 6 +- tool/build/lib/init.c => libc/str/isascii.c | 4 +- libc/str/isblank.c | 24 + libc/str/iscntrl.c | 24 + libc/str/isdigit.c | 24 + libc/str/isgraph.c | 24 + libc/str/islower.c | 24 + libc/str/isprint.c | 24 + libc/str/ispunct.c | 25 + libc/str/isspace.c | 25 + libc/str/isupper.c | 24 + libc/str/iswalnum.c | 24 + libc/str/iswalpha.c | 24 + libc/str/iswblank.c | 24 + libc/str/iswcntrl.c | 24 + libc/str/iswctype.c | 82 +- libc/str/iswdigit.c | 24 + libc/str/iswgraph.c | 24 + libc/str/iswlower.c | 24 + libc/str/iswprint.c | 24 + libc/str/iswpunct.c | 24 + libc/str/iswspace.c | 24 + libc/str/iswupper.c | 24 + libc/str/iswxdigit.c | 24 + libc/str/isxdigit.c | 25 + libc/str/str.h | 6 +- libc/str/thompike.h | 2 +- libc/str/tolower.c | 24 + libc/str/toupper.c | 24 + libc/str/towlower.c | 24 + libc/str/towupper.c | 24 + libc/str/tpenc.S | 10 +- libc/str/tpenc.h | 2 +- libc/str/tprecode16to8.c | 27 +- libc/str/tprecode8to16.c | 34 +- libc/str/utf16.h | 8 + libc/stubs/xnu.S | 2 +- libc/sysv/consts.sh | 154 +- libc/sysv/consts/O_DIRECTORY.s | 2 +- libc/sysv/consts/ipproto.h | 65 +- libc/sysv/systemfive.S | 35 +- libc/testlib/testmain.c | 5 +- libc/testlib/testmem.c | 11 +- libc/testlib/testrunner.c | 6 +- libc/time/localtime.c | 34 +- libc/time/strftime.c | 2 + libc/time/time.h | 5 +- libc/time/time.mk | 28 +- libc/tinymath/ceil.S | 19 +- libc/tzfile.h | 2 +- libc/unicode/blocks.txt | 344 +++++ libc/unicode/unicode-properties.txt | 30 + libc/unicode/wcwidth.c | 4 +- libc/x/x.h | 2 + .../quick_exit.c => x/xmemalignzero.c} | 22 +- libc/zipos/zipos.S | 4 + libc/zipos/zipos.mk | 3 +- test/libc/fmt/palandprintf_test.c | 18 +- test/libc/intrin/intrin_test.c | 2 +- test/libc/nexgen32e/cescapec_test.c | 33 + test/libc/runtime/getdosargv_test.c | 50 +- test/libc/runtime/getdosenviron_test.c | 28 +- test/libc/str/strchr_test.c | 5 + test/libc/str/tpenc_test.c | 11 +- test/libc/str/tpencode_test.c | 4 + test/libc/str/tprecode16to8_test.c | 27 + test/libc/str/tprecode8to16_test.c | 6 + test/libc/str/undeflate_test.c | 3 +- test/libc/time/strftime_test.c | 35 +- test/tool/build/lib/divmul_test.c | 1 - test/tool/build/lib/iovs_test.c | 85 + test/tool/build/lib/machine_test.c | 135 +- test/tool/build/lib/pml4t_test.c | 158 -- test/tool/build/lib/pty_test.c | 235 +++ test/tool/build/lib/test.mk | 1 + .../clamp1.S => third_party/dtoa/divmax.S | 24 +- third_party/dtoa/dtoa.c | 9 +- third_party/duktape/duktape.mk | 2 +- third_party/stb/ycbcr.c | 8 +- tool/build/{emulator.c => blinkenlights.c} | 1373 ++++++++++++----- tool/build/emubin/emubin.mk | 10 +- tool/build/emubin/linmap.c | 32 + tool/build/emubin/lisp.c | 583 ++----- tool/build/emubin/lisp.h | 182 +++ tool/build/emubin/lisp.lds | 3 +- tool/build/emubin/lisp.lisp | 40 +- tool/build/emubin/lispstart.S | 51 +- tool/build/emubin/{spiral.real.c => spiral.c} | 0 tool/build/lib/argv.h | 11 + tool/build/lib/breakpoint.c | 1 + tool/build/lib/buffer.c | 11 +- tool/build/lib/dis.c | 13 +- tool/build/lib/elfwriter.c | 9 +- tool/build/lib/elfwriter_yoink.c | 8 +- tool/build/lib/endian.h | 1 + tool/build/lib/fds.h | 5 +- tool/build/lib/instruction.c | 8 +- tool/build/lib/ioports.c | 5 +- tool/build/lib/iovs.c | 53 + tool/build/lib/iovs.h | 31 + tool/build/lib/loader.c | 48 +- tool/build/lib/machine.c | 51 +- tool/build/lib/machine.h | 120 +- tool/build/lib/memory.c | 67 +- tool/build/lib/memorymalloc.c | 172 ++- tool/build/lib/pml4t.c | 192 --- tool/build/lib/pml4t.h | 14 +- tool/build/lib/pml4tfmt.c | 16 +- tool/build/lib/pty.c | 1252 ++++++++++++--- tool/build/lib/pty.h | 51 +- tool/build/lib/reset.c | 16 +- tool/build/lib/sse.c | 1 - tool/build/lib/ssefloat.c | 48 +- tool/build/lib/ssemov.c | 16 +- tool/build/lib/syscall.c | 388 +++-- tool/build/lib/throw.c | 15 +- tool/build/lib/time.c | 7 +- tool/build/tinyemu.c | 9 +- tool/build/zipobj.c | 4 +- tool/calc/calc.c | 7 +- tool/emacs/cosmo-cpp-constants.el | 9 +- tool/net/redbean.c | 9 +- tool/viz/deathstar.c | 8 +- tool/viz/printvideo.c | 7 +- libc/log/bisectsymbol.c => tool/viz/tailf.c | 99 +- tool/viz/unicode.c | 92 ++ 276 files changed, 7026 insertions(+), 3790 deletions(-) create mode 100644 examples/acid2.c rename libc/{time => calls}/getitimer.c (94%) create mode 100644 libc/calls/nanosleep-nt.c rename libc/{log/getsymbolname.c => calls/nanosleep-xnu.c} (82%) rename libc/{nexgen32e/ispunct.S => calls/nowl.S} (82%) create mode 100644 libc/calls/onntalarm.c create mode 100644 libc/calls/readansi.c create mode 100644 libc/calls/setitimer-nt.c rename libc/{time => calls}/setitimer.c (75%) rename libc/{stubs/unprovable.S => calls/thunks/onntalarm.S} (93%) create mode 100644 libc/linux/close.h create mode 100644 libc/linux/fstat.h create mode 100644 libc/linux/mmap.h create mode 100644 libc/linux/munmap.h create mode 100644 libc/linux/open.h delete mode 100644 libc/nexgen32e/ctype.S delete mode 100644 libc/nexgen32e/hextoint.S delete mode 100644 libc/nexgen32e/iscntrl.S delete mode 100644 libc/nexgen32e/isgraph.S delete mode 100644 libc/nexgen32e/iswalnum.S delete mode 100644 libc/nexgen32e/iswalpha.S delete mode 100644 libc/nexgen32e/iswblank.S delete mode 100644 libc/nexgen32e/iswcntrl.S delete mode 100644 libc/nexgen32e/iswdigit.S delete mode 100644 libc/nexgen32e/iswgraph.S delete mode 100644 libc/nexgen32e/iswlower.S delete mode 100644 libc/nexgen32e/iswprint.S delete mode 100644 libc/nexgen32e/iswpunct.S delete mode 100644 libc/nexgen32e/iswspace.S delete mode 100644 libc/nexgen32e/iswupper.S delete mode 100644 libc/nexgen32e/iswxdigit.S delete mode 100644 libc/nexgen32e/kctype.S delete mode 100644 libc/nexgen32e/tolower.S delete mode 100644 libc/nexgen32e/toupper.S delete mode 100644 libc/nexgen32e/towupper.S create mode 100644 libc/nt/iocp.h create mode 100644 libc/nt/struct/guid.h create mode 100644 libc/nt/struct/overlappedentry.h create mode 100644 libc/str/decodentsutf16.c create mode 100644 libc/str/hextoint.c create mode 100644 libc/str/isalnum.c rename libc/{log/backtrace.c => str/isalpha.c} (94%) rename tool/build/lib/init.c => libc/str/isascii.c (96%) create mode 100644 libc/str/isblank.c create mode 100644 libc/str/iscntrl.c create mode 100644 libc/str/isdigit.c create mode 100644 libc/str/isgraph.c create mode 100644 libc/str/islower.c create mode 100644 libc/str/isprint.c create mode 100644 libc/str/ispunct.c create mode 100644 libc/str/isspace.c create mode 100644 libc/str/isupper.c create mode 100644 libc/str/iswalnum.c create mode 100644 libc/str/iswalpha.c create mode 100644 libc/str/iswblank.c create mode 100644 libc/str/iswcntrl.c create mode 100644 libc/str/iswdigit.c create mode 100644 libc/str/iswgraph.c create mode 100644 libc/str/iswlower.c create mode 100644 libc/str/iswprint.c create mode 100644 libc/str/iswpunct.c create mode 100644 libc/str/iswspace.c create mode 100644 libc/str/iswupper.c create mode 100644 libc/str/iswxdigit.c create mode 100644 libc/str/isxdigit.c create mode 100644 libc/str/tolower.c create mode 100644 libc/str/toupper.c create mode 100644 libc/str/towlower.c create mode 100644 libc/str/towupper.c create mode 100644 libc/unicode/blocks.txt create mode 100644 libc/unicode/unicode-properties.txt rename libc/{runtime/quick_exit.c => x/xmemalignzero.c} (84%) create mode 100644 test/libc/nexgen32e/cescapec_test.c create mode 100644 test/libc/str/tprecode16to8_test.c create mode 100644 test/tool/build/lib/iovs_test.c delete mode 100644 test/tool/build/lib/pml4t_test.c create mode 100644 test/tool/build/lib/pty_test.c rename libc/nexgen32e/clamp1.S => third_party/dtoa/divmax.S (89%) rename tool/build/{emulator.c => blinkenlights.c} (61%) create mode 100644 tool/build/emubin/linmap.c create mode 100644 tool/build/emubin/lisp.h rename tool/build/emubin/{spiral.real.c => spiral.c} (100%) create mode 100644 tool/build/lib/argv.h create mode 100644 tool/build/lib/iovs.c create mode 100644 tool/build/lib/iovs.h delete mode 100644 tool/build/lib/pml4t.c rename libc/log/bisectsymbol.c => tool/viz/tailf.c (51%) create mode 100644 tool/viz/unicode.c diff --git a/.gitignore b/.gitignore index 844d010c..dc86715e 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ /TAGS /bx_enh_dbg.ini /tool/emacs/*.elc +/usr/share/dict/words diff --git a/ape/ape.S b/ape/ape.S index e3853100..73c7b3c4 100644 --- a/ape/ape.S +++ b/ape/ape.S @@ -815,8 +815,8 @@ ape.pe: .ascin "PE",4 .short .LDLLEXE # DllCharacteristics .quad 0x0000000000100000 # StackReserve .quad 0x0000000000100000 # StackCommit - .quad 0x0000000000080000 # HeapReserve - .quad 0x0000000000001000 # HeapCommit + .quad 0x0000000000000000 # HeapReserve + .quad 0x0000000000000000 # HeapCommit .long 0x00000000 # LoaderFlags .long 16 # NumberOfDirectoryEntries .long 0,0 # ExportsDirectory diff --git a/dsp/core/getintegercoefficients.c b/dsp/core/getintegercoefficients.c index f52f4555..a47325ef 100644 --- a/dsp/core/getintegercoefficients.c +++ b/dsp/core/getintegercoefficients.c @@ -85,7 +85,7 @@ long GetIntegerCoefficients(long N[static 6], const double C[static 6], long M, N[i] = 0; } } - if (!IsTiny() && least > 1) { + if (!NoDebug() && least > 1) { for (j[0] = 0; j[0] < J[0]; ++j[0]) { for (j[1] = 0; j[1] < J[1]; ++j[1]) { for (j[2] = 0; j[2] < J[2]; ++j[2]) { diff --git a/dsp/scale/gyarados.c b/dsp/scale/gyarados.c index 36a208eb..586d6ebd 100644 --- a/dsp/scale/gyarados.c +++ b/dsp/scale/gyarados.c @@ -75,7 +75,6 @@ static struct SamplingSolution *NewSamplingSolution(long n, long s) { ss->indices = xcalloc(n * s, sizeof(short)); return ss; } - static bool IsNormalized(int n, double A[n]) { int i; double x; diff --git a/dsp/tty/hidecursor.c b/dsp/tty/hidecursor.c index 6e798cfc..7200e55e 100644 --- a/dsp/tty/hidecursor.c +++ b/dsp/tty/hidecursor.c @@ -24,6 +24,9 @@ #include "libc/nt/console.h" #include "libc/nt/runtime.h" #include "libc/nt/struct/consolecursorinfo.h" +#include "libc/runtime/runtime.h" + +/* TODO(jart): DELETE */ static int ttysetcursor(int fd, bool visible) { struct NtConsoleCursorInfo ntcursor; diff --git a/dsp/tty/ident.c b/dsp/tty/ident.c index ef840b40..ae986a69 100644 --- a/dsp/tty/ident.c +++ b/dsp/tty/ident.c @@ -30,6 +30,8 @@ #include "libc/sysv/consts/poll.h" #include "libc/sysv/errfuns.h" +/* TODO(jart): DELETE */ + static int ttyident_probe(struct TtyIdent *ti, int ttyinfd, int ttyoutfd, const char *msg) { ssize_t rc; diff --git a/dsp/tty/send.c b/dsp/tty/send.c index 55366653..ccbf3a6b 100644 --- a/dsp/tty/send.c +++ b/dsp/tty/send.c @@ -20,6 +20,8 @@ #include "dsp/tty/tty.h" #include "libc/str/str.h" +/* TODO(jart): DELETE */ + /** * Sends data to teletypewriter. * @@ -27,4 +29,6 @@ * * @return 0 on success, or -1 w/ errno */ -int ttysend(int fd, const char *str) { return ttywrite(fd, str, strlen(str)); } +int ttysend(int fd, const char *str) { + return ttywrite(fd, str, strlen(str)); +} diff --git a/dsp/tty/sendtitle.c b/dsp/tty/sendtitle.c index 499379f3..8cc51eda 100644 --- a/dsp/tty/sendtitle.c +++ b/dsp/tty/sendtitle.c @@ -22,6 +22,8 @@ #include "libc/runtime/gc.h" #include "libc/x/x.h" +/* TODO(jart): DELETE */ + /** * Changes text in title bar of pseudo-teletypewriter window. * diff --git a/dsp/tty/ttyraw.c b/dsp/tty/ttyraw.c index 025aed48..54451d0e 100644 --- a/dsp/tty/ttyraw.c +++ b/dsp/tty/ttyraw.c @@ -38,6 +38,8 @@ #include "libc/sysv/errfuns.h" #include "libc/x/x.h" +/* TODO(jart): DELETE */ + #define FD STDOUT_FILENO static struct TtyRaw { diff --git a/dsp/tty/write.c b/dsp/tty/write.c index ee5f24ab..c153292c 100644 --- a/dsp/tty/write.c +++ b/dsp/tty/write.c @@ -25,6 +25,8 @@ #include "libc/sysv/consts/poll.h" #include "libc/sysv/consts/termios.h" +/* TODO(jart): DELETE */ + /** * Sends data to teletypewriter the pain-free way. * diff --git a/examples/acid2.c b/examples/acid2.c new file mode 100644 index 00000000..a7300d83 --- /dev/null +++ b/examples/acid2.c @@ -0,0 +1,136 @@ +#if 0 +/*─────────────────────────────────────────────────────────────────╗ +│ To the extent possible under law, Justine Tunney has waived │ +│ all copyright and related or neighboring rights to this file, │ +│ as it is written in the following disclaimers: │ +│ • http://unlicense.org/ │ +│ • http://creativecommons.org/publicdomain/zero/1.0/ │ +╚─────────────────────────────────────────────────────────────────*/ +#endif +#include "libc/calls/calls.h" +#include "libc/calls/ioctl.h" +#include "libc/calls/struct/termios.h" +#include "libc/fmt/fmt.h" +#include "libc/stdio/stdio.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/termios.h" + +int yn2, xn2; +int yn3, xn3; +char b[128], inbuf[128]; +int y, x, yn, xn, my, mx; +struct termios term, oldterm; + +int main(int argc, char *argv[]) { + int i; + setvbuf(stdout, inbuf, _IONBF, 128); /* make things slower */ + + /* raw mode */ + ioctl(1, TCGETS, &oldterm); + memcpy(&term, &oldterm, sizeof(term)); + term.c_cc[VMIN] = 1; + term.c_cc[VTIME] = 1; + term.c_iflag &= ~(INPCK | ISTRIP | PARMRK | INLCR | IGNCR | ICRNL | IXON); + term.c_lflag &= ~(IEXTEN | ICANON | ECHO | ECHONL); + term.c_cflag &= ~(CSIZE | PARENB); + term.c_oflag &= ~OPOST; + term.c_cflag |= CS8; + term.c_iflag |= IUTF8; + ioctl(1, TCSETSF, &term); + + /* get cursor position and display dimensions */ + printf("\e7\e[6n\e[9979;9979H\e[6n\e8"); + read(0, b, sizeof(b)); + sscanf(b, "\e[%d;%dR\e[%d;%dR", &y, &x, &yn, &xn); + + printf("\e[1q"); /* turn on led one */ + printf("\e[2J"); /* clear display */ + printf("\e#8"); /* fill display with E's */ + printf("\e[H"); + + /* clear display again */ + printf("\e[2q"); /* turn on led two */ + for (i = 0; i < yn; ++i) { + if (i) printf("\n"); + printf(" "); + printf("\e[0K"); + } + for (i = 0; i < yn - 1; ++i) { + if (i) printf("\eM"); + printf("\e[1K"); + } + + printf("\e(0"); /* line drawing mode */ + printf("\e[3q"); /* turn on led three */ + printf("\e[H"); + + /* move to center */ + my = yn / 2; + mx = xn / 2 - 7; + if (y > my) { + printf("\e[%dA", y - my); + } else if (y < my) { + printf("\e[%dB", my - y); + } + if (x > mx) { + printf("\e[%dD", x - mx); + } else if (x < mx) { + printf("\e[%dC", mx - x); + } + + printf("\e[90;103m"); /* black on yellow */ + printf("\e[90;103ma ` a"); /* draw nose */ + + printf("\e[0m"); /* reset style */ + printf("\e(B"); /* ascii mode */ + + /* draw corners */ + printf("\e[H"); /* top left */ + printf("A"); + printf("\e[9979C"); /* rightmost */ + printf("B"); + printf("\e[9979;9979H"); /* bottom right corner */ + printf("\e[C"); /* move right gets clamped */ + printf("D"); /* write, set redzone flag */ + printf("\e[2A"); /* move up, unsets redzone */ + + /* gnu screen now reports out of bounds position */ + /* kitty hasnt got a redzone reporting next line */ + printf("\e[6n"); + read(0, b, sizeof(b)); + sscanf(b, "\e[%d;%dR", &yn2, &xn2); + + /* writes to (yn-3,xn-1) normally and (yn-2,0) in gnu screen */ + printf("!"); + + /* draw ruler on top */ + printf("\e[H"); + for (i = 8; i + 1 < xn; i += 8) { + printf("\e[%dG%d", i + 1, i); /* set column */ + } + + printf("\e[9979;9979H"); /* bottom right */ + printf("\e[9979D"); /* leftmost */ + printf("C"); + + /* let's break gnu screen again with multimonospace redzone */ + printf("\e[%d;9979H", yn / 2); /* right middle */ + printf("\e[D"); /* left */ + printf("A"); + printf("\e[6n"); + read(0, b, sizeof(b)); + sscanf(b, "\e[%d;%dR", &yn2, &xn2); + + printf("\e[%dH", yn / 2); + printf("%d %d vs. %d %d\r\n", yn, xn, yn2, xn2); + printf("%d %d vs. %d %d\r\n", yn / 2, xn, yn2, xn2); + printf("\e#6double width\e#5\r\n"); + printf("\e[3mthis text is so \e[1mitalic\e[0m\r\n"); + printf("\e[1;20mpress any fraktur exit\e[0m"); + printf("\a"); + + read(0, b, sizeof(b)); + printf("\r\n"); + ioctl(1, TCSETS, &oldterm); + return 0; +} diff --git a/examples/backtrace.c b/examples/backtrace.c index 66aae016..270c0259 100644 --- a/examples/backtrace.c +++ b/examples/backtrace.c @@ -8,12 +8,14 @@ ╚─────────────────────────────────────────────────────────────────*/ #endif #include "libc/calls/calls.h" +#include "libc/log/backtrace.h" #include "libc/log/log.h" #include "libc/mem/mem.h" #include "libc/runtime/gc.h" #include "libc/runtime/runtime.h" #include "libc/runtime/symbols.h" #include "libc/stdio/stdio.h" +#include "libc/sysv/consts/fileno.h" /* @@ -45,9 +47,9 @@ void hello(void) { gc(malloc(1)); - backtrace(stdout); + ShowBacktrace(STDOUT_FILENO, NULL); setenv("ADDR2LINE", "", true); - backtrace(stdout); + ShowBacktrace(STDOUT_FILENO, NULL); } void world(void) { diff --git a/examples/examples.mk b/examples/examples.mk index 765e2566..f3417d58 100644 --- a/examples/examples.mk +++ b/examples/examples.mk @@ -141,9 +141,9 @@ o/$(MODE)/examples/nesemu1.com.dbg: \ $(APE) @$(APELINK) -o/$(MODE)/usr/share/dict/words: usr/share/dict/words.gz +usr/share/dict/words: usr/share/dict/words.gz @$(MKDIR) $(dir $@) - @$(GZ) $(ZFLAGS) <$< >$@ + @$(GZ) $(ZFLAGS) -d <$< >$@ o/$(MODE)/examples/ugh.ok: o/$(MODE)/examples/wut.com $< diff --git a/examples/hellojs.c b/examples/hellojs.c index 8afb234d..db098b3a 100644 --- a/examples/hellojs.c +++ b/examples/hellojs.c @@ -15,6 +15,8 @@ #include "libc/sysv/consts/o.h" #include "third_party/duktape/duktape.h" +STATIC_YOINK("zip_uri_support"); + static duk_ret_t NativePrint(duk_context *ctx) { duk_push_string(ctx, " "); duk_insert(ctx, 0); diff --git a/examples/ispell.c b/examples/ispell.c index 816b83cf..ed7ab73f 100644 --- a/examples/ispell.c +++ b/examples/ispell.c @@ -22,6 +22,8 @@ #include "libc/sysv/consts/fileno.h" #include "libc/x/x.h" +STATIC_YOINK("zip_uri_support"); + /** * @fileoverview Simple Interactive Spell Checker. * diff --git a/examples/kilo.c b/examples/kilo.c index 43ab0ad5..e8cfd0f3 100644 --- a/examples/kilo.c +++ b/examples/kilo.c @@ -51,6 +51,7 @@ Contact: antirez@gmail.com\"\n\ */ #define KILO_VERSION "0.0.1" +#define SYNTAX 1 #ifndef _BSD_SOURCE #define _BSD_SOURCE @@ -169,14 +170,16 @@ void editorSetStatusMessage(const char *fmt, ...); * There is no support to highlight patterns currently. */ /* C / C++ */ -const char *const C_HL_extensions[] = {".c", ".cpp", NULL}; +const char *const C_HL_extensions[] = {".c", ".h", ".cpp", NULL}; const char *const C_HL_keywords[] = { /* A few C / C++ keywords */ "switch", "if", "while", "for", "break", "continue", "return", "else", "struct", "union", "typedef", "static", "enum", "class", /* C types */ "int|", "long|", "double|", "float|", "char|", "unsigned|", "signed|", - "void|", NULL}; + "void|", "const|", "size_t|", "ssize_t|", "uint8_t|", "int8_t|", + "uint16_t|", "int16_t|", "uint32_t|", "int32_t|", "uint64_t|", "int64_t|", + NULL}; /* Here we define an array of syntax highlights by extensions, keywords, * comments delimiters and flags. */ @@ -201,7 +204,10 @@ void disableRawMode(int64_t fd) { /* Called at exit to avoid remaining in raw mode. */ void editorAtExit(void) { + char buf[64]; disableRawMode(STDIN_FILENO); + write(STDOUT_FILENO, buf, + sprintf(buf, "\e[%d;%dH\r\n\r\n\r\n", E.screenrows, E.screencols)); } /* Raw mode: 1960 magic shit. */ @@ -530,8 +536,10 @@ void editorUpdateSyntax(erow *row) { * state changed. This may recursively affect all the following rows * in the file. */ int oc = editorRowHasOpenComment(row); - /* if (row->hl_oc != oc && row->idx + 1 < E.numrows) */ - /* editorUpdateSyntax(&E.row[row->idx + 1]); */ +#if SYNTAX + if (row->hl_oc != oc && row->idx + 1 < E.numrows) + editorUpdateSyntax(&E.row[row->idx + 1]); +#endif row->hl_oc = oc; } @@ -586,15 +594,17 @@ void editorUpdateRow(erow *row) { * respecting tabs, substituting non printable characters with '?'. */ free(row->render); for (j = 0; j < row->size; j++) { - if (row->chars[j] == CTRL('I')) tabs++; + if (row->chars[j] == '\t') tabs++; } row->render = malloc(row->size + tabs * 8 + nonprint * 9 + 1); idx = 0; for (j = 0; j < row->size; j++) { - if (row->chars[j] == CTRL('I')) { + if (row->chars[j] == '\t') { row->render[idx++] = ' '; - while ((idx + 1) % 8 != 0) row->render[idx++] = ' '; + while (idx % 8 != 0) { + row->render[idx++] = ' '; + } } else { row->render[idx++] = row->chars[j]; } @@ -603,7 +613,9 @@ void editorUpdateRow(erow *row) { row->render[idx] = '\0'; /* Update the syntax highlighting attributes of the row. */ - /* editorUpdateSyntax(row); */ +#if SYNTAX + editorUpdateSyntax(row); +#endif } /* Insert a row at the specified position, shifting the other rows on the bottom @@ -918,38 +930,46 @@ void editorRefreshScreen(void) { r = &E.row[filerow]; int len = r->rsize - E.coloff; - /* int current_color = -1; */ +#if SYNTAX + int current_color = -1; +#endif if (len > 0) { if (len > E.screencols) len = E.screencols; char *c = r->render + E.coloff; - /* unsigned char *hl = r->hl + E.coloff; */ +#if SYNTAX + unsigned char *hl = r->hl + E.coloff; +#endif int j; for (j = 0; j < len; j++) { - /* if (hl[j] == HL_NONPRINT) { */ - /* char sym; */ - /* abAppend(&ab, "\e[7m", 4); */ - /* if (c[j] <= 26) */ - /* sym = '@' + c[j]; */ - /* else */ - /* sym = '?'; */ - /* abAppend(&ab, &sym, 1); */ - /* abAppend(&ab, "\e[0m", 4); */ - /* } else if (hl[j] == HL_NORMAL) { */ - /* if (current_color != -1) { */ - /* abAppend(&ab, "\e[39m", 5); */ - /* current_color = -1; */ - /* } */ - abAppend(&ab, c + j, 1); - /* } else { */ - /* int color = editorSyntaxToColor(hl[j]); */ - /* if (color != current_color) { */ - /* char buf_[16]; */ - /* int clen = snprintf(buf_, sizeof(buf_), "\e[%dm", color); */ - /* current_color = color; */ - /* abAppend(&ab, buf_, clen); */ - /* } */ - /* abAppend(&ab, c + j, 1); */ - /* } */ +#if SYNTAX + if (hl[j] == HL_NONPRINT) { + char sym; + abAppend(&ab, "\e[7m", 4); + if (c[j] <= 26) + sym = '@' + c[j]; + else + sym = '?'; + abAppend(&ab, &sym, 1); + abAppend(&ab, "\e[0m", 4); + } else if (hl[j] == HL_NORMAL) { + if (current_color != -1) { + abAppend(&ab, "\e[39m", 5); + current_color = -1; + } +#endif + abAppend(&ab, c + j, 1); +#if SYNTAX + } else { + int color = editorSyntaxToColor(hl[j]); + if (color != current_color) { + char buf_[16]; + int clen = snprintf(buf_, sizeof(buf_), "\e[%dm", color); + current_color = color; + abAppend(&ab, buf_, clen); + } + abAppend(&ab, c + j, 1); + } +#endif } } abAppend(&ab, "\e[39m", 5); @@ -1200,8 +1220,8 @@ void editorMoveCursor(int key) { void editorProcessKeypress(int64_t fd) { /* When the file is modified, requires Ctrl-q to be pressed N times * before actually quitting. */ - int c, times; static int quit_times; + int c, c2, times, oldcx; c = editorReadKey(fd); switch (c) { @@ -1226,16 +1246,16 @@ void editorProcessKeypress(int64_t fd) { case CTRL('U'): case CTRL('X'): { - int c2 = editorReadKey(fd); + c2 = editorReadKey(fd); switch (c2) { case CTRL('S'): editorSave(); break; - case CTRL('C'): - /* Erase in Display (ED): Clear from cursor to end of screen. */ - write(STDOUT_FILENO, "\r\e[0J", 5); + case CTRL('C'): { + /* write(STDOUT_FILENO, "\r\e[0J", 5); */ exit(0); break; + } default: /* ignore */ break; } @@ -1254,7 +1274,7 @@ void editorProcessKeypress(int64_t fd) { break; case CTRL('K'): { /* Kill Line */ - int oldcx = E.cx; + oldcx = E.cx; do { editorMoveCursor(ARROW_RIGHT); } while (E.cx); @@ -1294,7 +1314,7 @@ void editorProcessKeypress(int64_t fd) { case HOME_KEY: case CTRL('A'): - while (E.cx) editorMoveCursor(ARROW_LEFT); + while (E.cx || E.coloff) editorMoveCursor(ARROW_LEFT); break; case END_KEY: case CTRL('E'): diff --git a/examples/printargs.c b/examples/printargs.c index 70b992bd..eae6b7d3 100644 --- a/examples/printargs.c +++ b/examples/printargs.c @@ -72,5 +72,9 @@ int main(int argc, char *argv[], char **envp) { fmt), kAuxiliaryValues[i].name, key, val, kAuxiliaryValues[i].description); } + printf("\nSpecial Directories:\n"); + printf(" ☼ kTmpPath = %`'s\n", kTmpPath); + printf(" ☼ kNtSystemDirectory = %`'s\n", kNtSystemDirectory); + printf(" ☼ kNtWindowsDirectory = %`'s\n", kNtWindowsDirectory); return 0; } diff --git a/examples/printmysymbols.c b/examples/printmysymbols.c index 759251d7..64cd0cdf 100644 --- a/examples/printmysymbols.c +++ b/examples/printmysymbols.c @@ -9,16 +9,18 @@ #endif #include "libc/calls/calls.h" #include "libc/elf/elf.h" +#include "libc/log/backtrace.h" #include "libc/log/log.h" #include "libc/runtime/symbols.h" #include "libc/stdio/stdio.h" +#include "libc/sysv/consts/fileno.h" int main(int argc, char *argv[]) { int rc = 0; char *filename; struct SymbolTable *tab = NULL; - if ((filename = finddebugbinary()) != NULL && - (tab = opensymboltable(filename))) { + if ((filename = FindDebugBinary()) != NULL && + (tab = OpenSymbolTable(filename))) { for (unsigned i = 0; i < tab->count; ++i) { printf("%p %s\n", tab->addr_base + tab->symbols[i].addr_rva, getelfstring(tab->elf, tab->elfsize, tab->name_base, @@ -26,9 +28,9 @@ int main(int argc, char *argv[]) { } } else { perror("printmysymbols"); - backtrace(stderr); + ShowBacktrace(STDERR_FILENO, NULL); rc = 1; } - closesymboltable(&tab); + CloseSymbolTable(&tab); return rc; } diff --git a/examples/tiny-raw-linux-tutorial.S b/examples/tiny-raw-linux-tutorial.S index d8dc6e9d..81801c40 100644 --- a/examples/tiny-raw-linux-tutorial.S +++ b/examples/tiny-raw-linux-tutorial.S @@ -58,6 +58,3 @@ _start: mov $12,%rdx # arg no. 3 is length jmp 0b .endfn _start,globl .source __FILE__ - - .rodata.cst4 -1: .float -1.5 diff --git a/examples/ttyinfo.c b/examples/ttyinfo.c index bbab0dca..1b9740db 100644 --- a/examples/ttyinfo.c +++ b/examples/ttyinfo.c @@ -16,7 +16,6 @@ #include "libc/fmt/fmt.h" #include "libc/log/check.h" #include "libc/log/log.h" -#include "libc/runtime/gc.h" #include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" @@ -27,82 +26,138 @@ #include "libc/sysv/consts/termios.h" #include "libc/x/x.h" -/* - "\e[c" → "\e[?1;2c" - "\e[x" → "\e[2;1;1;112;112;1;0x" - "\e[>c" → "\e[>83;40500;0c" - "\e[6n" → "\e[52;1R" -*/ +#define CTRL(C) ((C) ^ 0b01000000) +#define ENABLE_MOUSE_TRACKING "\e[?1000;1002;1015;1006h" +#define DISABLE_MOUSE_TRACKING "\e[?1000;1002;1015;1006l" +#define PROBE_DISPLAY_SIZE "\e7\e[9979;9979H\e[6n\e8" -#define CTRL(C) ((C) ^ 0b01000000) -#define PROBE_VT100 "\e[c" /* e.g. "\e[?1;2c", "\e[>0c" */ -#define PROBE_SECONDARY "\e[>c" /* "\e[>83;40500;0c" (Screen v4.05.00) */ -#define PROBE_PARAMETERS "\e[x" /* e.g. "\e[2;1;1;112;112;1;0x" */ -#define PROBE_CURSOR_POSITION "\e[6n" /* e.g. "\e[𝑦;𝑥R" */ -#define PROBE_SUN_DTTERM_SIZE "\e[14t" /* e.g. "\e[𝑦;𝑥R" */ +char code[512]; +struct winsize wsize; +struct termios oldterm; +volatile bool resized, killed; -int fd_; -jmp_buf jb_; -ssize_t got_; -uint8_t buf_[128]; -struct TtyIdent ti_; -struct winsize wsize_; -volatile bool resized_; - -void OnResize(void) { - resized_ = true; -} -void OnKilled(int sig) { - longjmp(jb_, 128 + sig + 1); +void onresize(void) { + resized = true; } -void getsome(void) { - if ((got_ = read(fd_, buf_, sizeof(buf_))) == -1 && errno != EINTR) { - printf("%s%s\r\n", "error: ", strerror(errno)); - longjmp(jb_, EXIT_FAILURE + 1); +void onkilled(int sig) { + killed = true; +} + +void restoretty(void) { + write(1, DISABLE_MOUSE_TRACKING, strlen(DISABLE_MOUSE_TRACKING)); + ioctl(1, TCSETS, &oldterm); +} + +int rawmode(void) { + static bool once; + struct termios t; + if (!once) { + if (ioctl(1, TCGETS, &oldterm) != -1) { + atexit(restoretty); + } else { + return -1; + } + once = true; } - if (got_ >= 0) { - printf("%`'.*s\r\n", got_, buf_); - if (got_ > 0 && buf_[0] == CTRL('C')) { - longjmp(jb_, EXIT_SUCCESS + 1); + memcpy(&t, &oldterm, sizeof(t)); + t.c_cc[VMIN] = 1; + t.c_cc[VTIME] = 1; + t.c_iflag &= ~(INPCK | ISTRIP | PARMRK | INLCR | IGNCR | ICRNL | IXON | + IGNBRK | BRKINT); + t.c_lflag &= ~(IEXTEN | ICANON | ECHO | ECHONL | ISIG); + t.c_cflag &= ~(CSIZE | PARENB); + t.c_oflag &= ~OPOST; + t.c_cflag |= CS8; + t.c_iflag |= IUTF8; + ioctl(1, TCSETS, &t); + write(1, ENABLE_MOUSE_TRACKING, strlen(ENABLE_MOUSE_TRACKING)); + write(1, PROBE_DISPLAY_SIZE, strlen(PROBE_DISPLAY_SIZE)); + return 0; +} + +void getsize(void) { + if (getttysize(1, &wsize) != -1) { + printf("termios says terminal size is %hu×%hu\r\n", wsize.ws_col, + wsize.ws_row); + } else { + printf("%s\n", strerror(errno)); + } +} + +const char *describemouseevent(int e) { + static char buf[64]; + buf[0] = 0; + if (e & 0x10) { + strcat(buf, " ctrl"); + } + if (e & 0x40) { + strcat(buf, " wheel"); + if (e & 0x01) { + strcat(buf, " down"); + } else { + strcat(buf, " up"); + } + } else { + switch (e & 3) { + case 0: + strcat(buf, " left"); + break; + case 1: + strcat(buf, " middle"); + break; + case 2: + strcat(buf, " right"); + break; + default: + unreachable; + } + if (e & 0x20) { + strcat(buf, " drag"); + } else if (e & 0x04) { + strcat(buf, " up"); + } else { + strcat(buf, " down"); } } - if (resized_) { - CHECK_NE(-1, getttysize(fd_, &wsize_)); - printf("SIGWINCH → %hu×%hu\r\n", wsize_.ws_row, wsize_.ws_col); - resized_ = false; - } -} - -void probe(const char *s) { - printf("%`'s → ", s); - write(fd_, s, strlen(s)); - getsome(); + return buf + 1; } int main(int argc, char *argv[]) { - int rc; - char ttyname[64]; - struct termios old; - CHECK_NE(-1, (fd_ = open("/dev/tty", O_RDWR))); - CHECK_NE(-1, ttyconfig(fd_, ttysetrawmode, 0, &old)); - if (!(rc = setjmp(jb_))) { - xsigaction(SIGTERM, OnKilled, 0, 0, NULL); - xsigaction(SIGWINCH, OnResize, 0, 0, NULL); - if (ttyident(&ti_, STDIN_FILENO, STDOUT_FILENO) != -1) { - ttysendtitle(fd_, "justine was here", &ti_); - fputs(ttydescribe(ttyname, sizeof(ttyname), &ti_), stdout); + int e, c, y, x, n, yn, xn; + xsigaction(SIGTERM, onkilled, 0, 0, NULL); + xsigaction(SIGWINCH, onresize, 0, 0, NULL); + xsigaction(SIGCONT, onresize, 0, 0, NULL); + rawmode(); + getsize(); + while (!killed) { + if (resized) { + printf("SIGWINCH "); + getsize(); + resized = false; + } + if ((n = readansi(0, code, sizeof(code))) == -1) { + if (errno == EINTR) continue; + printf("ERROR: READ: %s\r\n", strerror(errno)); + exit(1); + } + printf("%`'.*s ", n, code); + if (iscntrl(code[0]) && !code[1]) { + printf("is CTRL-%c a.k.a. ^%c\r\n", CTRL(code[0]), CTRL(code[0])); + if (code[0] == CTRL('C') || code[0] == CTRL('D')) break; + } else if (startswith(code, "\e[") && endswith(code, "R")) { + yn = 1, xn = 1; + sscanf(code, "\e[%d;%dR", &yn, &xn); + printf("inband signalling says terminal size is %d×%d\r\n", xn, yn); + } else if (startswith(code, "\e[<") && + (endswith(code, "m") || endswith(code, "M"))) { + e = 0, y = 1, x = 1; + sscanf(code, "\e[<%d;%d;%d%c", &e, &y, &x, &c); + printf("mouse %s at %d×%d\r\n", describemouseevent(e | (c == 'm') << 2), + x, y); + } else { + printf("\r\n"); } - fputs("\r\n", stdout); - probe(PROBE_VT100); - probe(PROBE_SECONDARY); - probe(PROBE_PARAMETERS); - probe(PROBE_CURSOR_POSITION); - /* probe(PROBE_SUN_DTTERM_SIZE); */ - getsome(); - for (;;) getsome(); } - ttyrestore(fd_, &old); - ttyidentclear(&ti_); - return rc - 1; + return 0; } diff --git a/libc/calls/calls.h b/libc/calls/calls.h index 3680cc46..213537bc 100644 --- a/libc/calls/calls.h +++ b/libc/calls/calls.h @@ -1,5 +1,6 @@ #ifndef COSMOPOLITAN_LIBC_CALLS_SYSCALLS_H_ #define COSMOPOLITAN_LIBC_CALLS_SYSCALLS_H_ +#include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/timespec.h" #include "libc/calls/typedef/sighandler_t.h" #include "libc/dce.h" @@ -58,12 +59,10 @@ struct iovec; struct rlimit; struct rusage; struct sigaction; -struct siginfo; struct sigset; struct stat; struct sysinfo; struct tms; -struct ucontext; struct utsname; typedef int sig_atomic_t; @@ -210,6 +209,7 @@ size_t getfiledescriptorsize(int); ssize_t copy_file_range(int, long *, int, long *, size_t, uint32_t); ssize_t copyfd(int, int64_t *, int, int64_t *, size_t, uint32_t); ssize_t read(int, void *, size_t); +ssize_t readansi(int, char *, size_t); ssize_t readlinkat(int, const char *, char *, size_t); ssize_t splice(int, int64_t *, int, int64_t *, size_t, uint32_t); ssize_t vmsplice(int, const struct iovec *, int64_t, uint32_t); diff --git a/libc/time/getitimer.c b/libc/calls/getitimer.c similarity index 94% rename from libc/time/getitimer.c rename to libc/calls/getitimer.c index 4e5f4393..ee47d772 100644 --- a/libc/time/getitimer.c +++ b/libc/calls/getitimer.c @@ -17,9 +17,10 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/calls/internal.h" #include "libc/dce.h" #include "libc/sysv/errfuns.h" -#include "libc/time/time.h" /** * Retrieves last setitimer() value, correcting for remaining time. @@ -29,9 +30,8 @@ */ int getitimer(int which, struct itimerval *curvalue) { if (!IsWindows()) { - int getitimer$sysv(int, struct itimerval *) hidden; return getitimer$sysv(which, curvalue); } else { - return enosys(); /* TODO(jart): Implement me! */ + return setitimer$nt(which, NULL, curvalue); } } diff --git a/libc/calls/hefty/mkntcmdline.c b/libc/calls/hefty/mkntcmdline.c index 839284b6..6675056a 100644 --- a/libc/calls/hefty/mkntcmdline.c +++ b/libc/calls/hefty/mkntcmdline.c @@ -45,7 +45,7 @@ static int mkntcmdline_append(char16_t **p, size_t *i, size_t *n, char16_t c) { * Converts System V argv to Windows-style command line. * * Escaping is performed and it's designed to round-trip with - * getdosargv() or getdosargv(). This function does NOT escape + * GetDosArgv() or GetDosArgv(). This function does NOT escape * command interpreter syntax, e.g. $VAR (sh), %VAR% (cmd). * * @param argv is an a NULL-terminated array of UTF-8 strings diff --git a/libc/calls/internal.h b/libc/calls/internal.h index b2f67b0e..48876175 100644 --- a/libc/calls/internal.h +++ b/libc/calls/internal.h @@ -181,6 +181,7 @@ i64 sendfile$sysv(i32, i32, i64 *, u64) hidden; i64 splice$sysv(i32, i64 *, i32, i64 *, u64, u32) hidden; i64 vmsplice$sysv(i32, const struct iovec *, i64, u32) hidden; i64 write$sysv(i32, const void *, u64) hidden; +int getitimer$sysv(i32, struct itimerval *) hidden; int setresgid$sysv(uint32_t, uint32_t, uint32_t) hidden; int setresuid$sysv(uint32_t, uint32_t, uint32_t) hidden; u32 getgid$sysv(void) hidden; @@ -204,6 +205,7 @@ u32 prot2nt(i32, i32) privileged; void __restore_rt() hidden; void __sigenter$xnu(void *, i32, i32, void *, void *) hidden noreturn; int utimensat$xnu(int, const char *, const struct timespec *, int) hidden; +int nanosleep$xnu(const struct timespec *, struct timespec *) hidden; void stat2linux(void *) hidden; void xnutrampoline(void *, i32, i32, const struct __darwin_siginfo *, const struct __darwin_ucontext *) hidden noreturn; @@ -247,6 +249,8 @@ ssize_t read$nt(struct Fd *, const struct iovec *, size_t, ssize_t) hidden; ssize_t write$nt(struct Fd *, const struct iovec *, size_t, ssize_t) hidden; int utimensat$nt(int, const char *, const struct timespec *, int) hidden; int getrusage$nt(int, struct rusage *) hidden; +int setitimer$nt(int, const struct itimerval *, struct itimerval *) hidden; +int nanosleep$nt(const struct timespec *, struct timespec *) hidden; /*───────────────────────────────────────────────────────────────────────────│─╗ │ cosmopolitan § syscalls » windows nt » support ─╬─│┼ @@ -257,6 +261,7 @@ void ntcontext2linux(struct ucontext *, const struct NtContext *) hidden; struct NtOverlapped *offset2overlap(int64_t, struct NtOverlapped *) hidden; bool32 ntsetprivilege(i64, const char16_t *, u32) hidden; bool32 onntconsoleevent$nt(u32) hidden; +void onntalarm(void *, uint32_t, uint32_t) hidden; int ntaccesscheck(const char16_t *, u32) paramsnonnull() hidden; i64 ntreturn(u32); i64 winerr(void) nocallback privileged; diff --git a/libc/calls/ioctl-tcsets-nt.c b/libc/calls/ioctl-tcsets-nt.c index 2a0c9e98..5bcd9e08 100644 --- a/libc/calls/ioctl-tcsets-nt.c +++ b/libc/calls/ioctl-tcsets-nt.c @@ -41,15 +41,23 @@ textwindows int ioctl$tcsets$nt(int ignored, uint64_t request, } inmode &= ~(kNtEnableLineInput | kNtEnableEchoInput | kNtEnableProcessedInput); + if (tio->c_lflag & ICANON) inmode |= kNtEnableLineInput; if (tio->c_lflag & ECHO) inmode |= kNtEnableEchoInput; if (tio->c_lflag & (IEXTEN | ISIG)) inmode |= kNtEnableProcessedInput; + inmode |= kNtEnableWindowInput; + if (NtGetVersion() >= kNtVersionWindows10) { + inmode |= kNtEnableVirtualTerminalInput; + } SetConsoleMode(in, inmode); } if (outok) { - SetConsoleMode(out, outmode | kNtEnableProcessedOutput | - (NtGetVersion() >= kNtVersionWindows10 - ? kNtEnableVirtualTerminalProcessing - : 0)); + outmode |= kNtEnableWrapAtEolOutput; + outmode |= kNtEnableProcessedOutput; + if (!(tio->c_oflag & OPOST)) outmode |= kNtDisableNewlineAutoReturn; + if (NtGetVersion() >= kNtVersionWindows10) { + outmode |= kNtEnableVirtualTerminalProcessing; + } + SetConsoleMode(out, outmode); } return 0; } else { diff --git a/libc/calls/ioctl-tiocgwinsz-nt.c b/libc/calls/ioctl-tiocgwinsz-nt.c index 5aac3c3f..129cfd4b 100644 --- a/libc/calls/ioctl-tiocgwinsz-nt.c +++ b/libc/calls/ioctl-tiocgwinsz-nt.c @@ -28,25 +28,36 @@ #include "libc/sysv/errfuns.h" textwindows int ioctl$tiocgwinsz$nt(int fd, struct winsize *ws) { + int i, fds[3]; uint32_t mode; struct NtConsoleScreenBufferInfoEx sbinfo; - if (!isfdkind(fd, kFdFile)) return ebadf(); - if (!GetConsoleMode(g_fds.p[fd].handle, &mode)) return enotty(); - memset(&sbinfo, 0, sizeof(sbinfo)); - sbinfo.cbSize = sizeof(sbinfo); - if (GetConsoleScreenBufferInfoEx(g_fds.p[fd].handle, &sbinfo)) { - ws->ws_col = sbinfo.srWindow.Right - sbinfo.srWindow.Left; - ws->ws_row = sbinfo.srWindow.Bottom - sbinfo.srWindow.Top; - ws->ws_xpixel = 0; - ws->ws_ypixel = 0; - return 0; - } else if (g_ntstartupinfo.dwFlags & kNtStartfUsecountchars) { - ws->ws_col = g_ntstartupinfo.dwXCountChars; - ws->ws_row = g_ntstartupinfo.dwYCountChars; - ws->ws_xpixel = 0; - ws->ws_ypixel = 0; - return 0; - } else { - return winerr(); + fds[0] = fd, fds[1] = 1, fds[2] = 0; + for (i = 0; i < ARRAYLEN(fds); ++i) { + if (isfdkind(fds[i], kFdFile) || isfdkind(fds[i], kFdConsole)) { + if (GetConsoleMode(g_fds.p[fds[i]].handle, &mode)) { + memset(&sbinfo, 0, sizeof(sbinfo)); + sbinfo.cbSize = sizeof(sbinfo); + if (GetConsoleScreenBufferInfoEx(g_fds.p[fds[i]].handle, &sbinfo)) { + ws->ws_col = sbinfo.srWindow.Right - sbinfo.srWindow.Left + 1; + ws->ws_row = sbinfo.srWindow.Bottom - sbinfo.srWindow.Top + 1; + ws->ws_xpixel = 0; + ws->ws_ypixel = 0; + return 0; + } else if (g_ntstartupinfo.dwFlags & kNtStartfUsecountchars) { + ws->ws_col = g_ntstartupinfo.dwXCountChars; + ws->ws_row = g_ntstartupinfo.dwYCountChars; + ws->ws_xpixel = 0; + ws->ws_ypixel = 0; + return 0; + } else { + winerr(); + } + } else { + enotty(); + } + } else { + ebadf(); + } } + return -1; } diff --git a/libc/calls/nanosleep-nt.c b/libc/calls/nanosleep-nt.c new file mode 100644 index 00000000..c2cc7f30 --- /dev/null +++ b/libc/calls/nanosleep-nt.c @@ -0,0 +1,44 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/internal.h" +#include "libc/nexgen32e/nexgen32e.h" +#include "libc/nt/enum/status.h" +#include "libc/nt/errors.h" +#include "libc/nt/nt/time.h" +#include "libc/nt/synchronization.h" +#include "libc/str/str.h" +#include "libc/sysv/errfuns.h" + +textwindows int nanosleep$nt(const struct timespec *req, struct timespec *rem) { + int64_t millis, hectonanos, relasleep; + if (rem) memcpy(rem, req, sizeof(*rem)); + hectonanos = req->tv_sec * 10000000ull + div100int64(req->tv_nsec); + hectonanos = MAX(1, hectonanos); + relasleep = -hectonanos; + if (NtError(NtDelayExecution(true, &relasleep))) { + millis = div10000int64(hectonanos); + millis = MAX(1, millis); + if (SleepEx(millis, true) == kNtWaitIoCompletion) { + return eintr(); + } + } + if (rem) memset(rem, 0, sizeof(*rem)); + return 0; +} diff --git a/libc/log/getsymbolname.c b/libc/calls/nanosleep-xnu.c similarity index 82% rename from libc/log/getsymbolname.c rename to libc/calls/nanosleep-xnu.c index 9d8a038e..587d4eb0 100644 --- a/libc/log/getsymbolname.c +++ b/libc/calls/nanosleep-xnu.c @@ -17,16 +17,15 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/elf/elf.h" -#include "libc/runtime/symbols.h" +#include "libc/calls/calls.h" +#include "libc/calls/struct/timeval.h" +#include "libc/macros.h" +#include "libc/nexgen32e/nexgen32e.h" +#include "libc/sock/internal.h" -/** - * Finds starting address of symbol. - * @param symbol table pointer (null propagating) - * @return name string or null if not found - */ -const char *getsymbolname(struct SymbolTable *t, const struct Symbol *symbol) { - return (t && symbol) - ? getelfstring(t->elf, t->elfsize, t->name_base, symbol->name_rva) - : NULL; +int nanosleep$xnu(const struct timespec *req, struct timespec *rem) { + long millis; + millis = div1000int64(req->tv_nsec); + millis = MAX(1, millis); + return select$sysv(0, 0, 0, 0, &(struct timeval){req->tv_sec, millis}); } diff --git a/libc/calls/nanosleep.c b/libc/calls/nanosleep.c index e31ecb50..b6f514ff 100644 --- a/libc/calls/nanosleep.c +++ b/libc/calls/nanosleep.c @@ -17,48 +17,23 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" #include "libc/calls/internal.h" -#include "libc/calls/struct/timespec.h" -#include "libc/calls/struct/timeval.h" -#include "libc/conv/conv.h" #include "libc/dce.h" -#include "libc/macros.h" -#include "libc/nexgen32e/nexgen32e.h" -#include "libc/nt/enum/status.h" -#include "libc/nt/errors.h" -#include "libc/nt/nt/time.h" -#include "libc/nt/synchronization.h" -#include "libc/sock/internal.h" -#include "libc/str/str.h" #include "libc/sysv/errfuns.h" /** * Sleeps for a particular amount of time. */ int nanosleep(const struct timespec *req, struct timespec *rem) { - long res, millis, hectonanos; if (!req) return efault(); if (!IsWindows()) { if (!IsXnu()) { return nanosleep$sysv(req, rem); } else { - return select$sysv( - 0, 0, 0, 0, /* lool */ - &(struct timeval){req->tv_sec, div1000int64(req->tv_nsec)}); + return nanosleep$xnu(req, rem); } } else { - if (rem) memcpy(rem, req, sizeof(*rem)); - if (req->tv_sec && req->tv_nsec) { - hectonanos = MAX(1, req->tv_sec * 10000000L + div100int64(req->tv_nsec)); - } else { - hectonanos = 1; - } - if (NtError(NtDelayExecution(true, &hectonanos))) { - millis = div10000int64(hectonanos); - res = SleepEx(millis, true); - if (res == kNtWaitIoCompletion) return eintr(); - } - if (rem) memset(rem, 0, sizeof(*rem)); - return 0; + return nanosleep$nt(req, rem); } } diff --git a/libc/calls/now.c b/libc/calls/now.c index 7ea7adcf..623bf7fa 100644 --- a/libc/calls/now.c +++ b/libc/calls/now.c @@ -31,13 +31,7 @@ static struct Now { bool once; uint64_t k0; long double r0, cpn; -} now_; - -/** - * Returns timestamp without needing system calls. - * @note uses microsecond scale fallback on k8 or vm - */ -long double (*nowl)(void); +} g_now; static long double GetTimeSample(void) { uint64_t tick1, tick2; @@ -48,7 +42,7 @@ static long double GetTimeSample(void) { nanosleep(&(struct timespec){0, 100000}, NULL); time2 = dtime(CLOCK_MONOTONIC); tick2 = rdtsc(); - return (time2 - time1) * 1e9 / (tick2 - tick1); + return (time2 - time1) * 1e9 / MAX(1, tick2 - tick1); } static long double MeasureNanosPerCycle(void) { @@ -62,15 +56,15 @@ static long double MeasureNanosPerCycle(void) { } static void InitTime(void) { - now_.cpn = MeasureNanosPerCycle(); - now_.r0 = dtime(CLOCK_REALTIME); - now_.k0 = rdtsc(); - now_.once = true; + g_now.cpn = MeasureNanosPerCycle(); + g_now.r0 = dtime(CLOCK_REALTIME); + g_now.k0 = rdtsc(); + g_now.once = true; } long double converttickstonanos(uint64_t ticks) { - if (!now_.once) InitTime(); - return ticks * now_.cpn; /* pico scale */ + if (!g_now.once) InitTime(); + return ticks * g_now.cpn; /* pico scale */ } long double converttickstoseconds(uint64_t ticks) { @@ -83,15 +77,7 @@ long double nowl$sys(void) { long double nowl$art(void) { uint64_t ticks; - if (!now_.once) InitTime(); - ticks = unsignedsubtract(rdtsc(), now_.k0); - return now_.r0 + converttickstoseconds(ticks); + if (!g_now.once) InitTime(); + ticks = unsignedsubtract(rdtsc(), g_now.k0); + return g_now.r0 + converttickstoseconds(ticks); } - -INITIALIZER(301, _init_nowl, { - if (X86_HAVE(INVTSC)) { - nowl = nowl$art; - } else { - nowl = nowl$sys; - } -}) diff --git a/libc/nexgen32e/ispunct.S b/libc/calls/nowl.S similarity index 82% rename from libc/nexgen32e/ispunct.S rename to libc/calls/nowl.S index fe0d6e9a..05f1bcf7 100644 --- a/libc/nexgen32e/ispunct.S +++ b/libc/calls/nowl.S @@ -17,25 +17,23 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/nexgen32e/x86feature.h" #include "libc/macros.h" -/ Checks for printable characters that aren't space / alnum. +/ Returns timestamp without needing system calls. / -/ @param edi is character to test (treated as uint8_t) -/ @return nonzero if character matches, otherwise 0 -ispunct:.leafprologue - .profilable - xor %eax,%eax - cmp $255,%edi - ja 1f - movslq %edi,%rdi - ezlea kCtype,cx - mov (%rcx,%rdi),%dl - test $16,%dl - je 1f - xor %eax,%eax - and $7,%dl - sete %al -1: .leafepilogue - .endfn ispunct,globl +/ @return seconds since unix epoch in %st0 +/ @note uses microsecond scale fallback on k8 or vm + .initbss 202,_init_nowl +nowl: .quad 0 + .endobj nowl,globl + .previous + + .init.start 202,_init_nowl + ezlea nowl$sys,ax + ezlea nowl$art,cx + testb X86_HAVE(INVTSC)+kCpuids(%rip) + cmovnz %rcx,%rax + stosq + .init.end 202,_init_nowl .source __FILE__ diff --git a/libc/calls/onntalarm.c b/libc/calls/onntalarm.c new file mode 100644 index 00000000..a8b19efc --- /dev/null +++ b/libc/calls/onntalarm.c @@ -0,0 +1,31 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/internal.h" +#include "libc/calls/struct/siginfo.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/sig.h" + +void onntalarm(void *lpArgToCompletionRoutine, uint32_t dwTimerLowValue, + uint32_t dwTimerHighValue) { + siginfo_t info; + memset(&info, 0, sizeof(info)); + info.si_signo = SIGALRM; + __sigenter(info.si_signo, &info, NULL); +} diff --git a/libc/calls/onntconsoleevent.c b/libc/calls/onntconsoleevent.c index d64cdcc7..d34d5973 100644 --- a/libc/calls/onntconsoleevent.c +++ b/libc/calls/onntconsoleevent.c @@ -37,9 +37,9 @@ textwindows bool32 onntconsoleevent(uint32_t CtrlType) { case kNtCtrlCloseEvent: sig = pushpop(SIGHUP); break; - case kNtCtrlLogoffEvent: - case kNtCtrlShutdownEvent: - sig = pushpop(SIGTERM); + case kNtCtrlLogoffEvent: // only received by services so hack hack hack + case kNtCtrlShutdownEvent: // only received by services so hack hack hack + sig = pushpop(SIGALRM); break; default: return false; diff --git a/libc/calls/pread.c b/libc/calls/pread.c index b4b10f7f..a9101e07 100644 --- a/libc/calls/pread.c +++ b/libc/calls/pread.c @@ -21,6 +21,7 @@ #include "libc/bits/weaken.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/struct/iovec.h" #include "libc/dce.h" #include "libc/macros.h" #include "libc/runtime/runtime.h" @@ -47,7 +48,7 @@ ssize_t pread(int fd, void *buf, size_t size, int64_t offset) { } else if (!IsWindows()) { rc = pread$sysv(fd, buf, size, offset); } else if (isfdkind(fd, kFdFile)) { - rc = read$nt(&g_fds.p[fd], buf, size, offset); + rc = read$nt(&g_fds.p[fd], (struct iovec[]){{buf, size}}, 1, offset); } else { rc = ebadf(); } diff --git a/libc/calls/preadv.c b/libc/calls/preadv.c index a6366078..eb0f1b7d 100644 --- a/libc/calls/preadv.c +++ b/libc/calls/preadv.c @@ -48,7 +48,7 @@ ssize_t preadv(int fd, struct iovec *iovec, int count, int64_t off) { */ if (!once) { once = true; - if (IsLinux() && iovec->iov_len >= __NR_preadv_linux) { + if (IsModeDbg() || (IsLinux() && iovec->iov_len >= __NR_preadv_linux)) { /* * Read size is too large to detect older kernels safely without * introducing nontrivial mechanics. We'll try again later. diff --git a/libc/calls/pwritev.c b/libc/calls/pwritev.c index 8617a553..93efb84a 100644 --- a/libc/calls/pwritev.c +++ b/libc/calls/pwritev.c @@ -29,7 +29,12 @@ #define __NR_pwritev_linux 0x0128 /** - * Writes data from multiple buffers to file descriptor at offset. + * Writes data from multiple buffers to offset. + * + * Please note that it's not an error for a short write to happen. This + * can happen in the kernel if EINTR happens after some of the write has + * been committed. It can also happen if we need to polyfill this system + * call using pwrite(). * * @param count is recommended to be 16 or fewer; if it exceeds IOV_MAX * then the extra buffers are simply ignored @@ -48,7 +53,7 @@ ssize_t pwritev(int fd, const struct iovec *iovec, int count, int64_t off) { */ if (!once) { once = true; - if (IsLinux() && iovec->iov_len >= __NR_pwritev_linux) { + if (IsModeDbg() || (IsLinux() && iovec->iov_len >= __NR_pwritev_linux)) { /* * Write size is too large to detect older kernels safely without * introducing nontrivial mechanics. We'll try again later. diff --git a/libc/calls/readansi.c b/libc/calls/readansi.c new file mode 100644 index 00000000..2f16a63d --- /dev/null +++ b/libc/calls/readansi.c @@ -0,0 +1,124 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/str/thompike.h" +#include "libc/sysv/errfuns.h" + +/** + * Reads single keystroke or control sequence from character device. + * + * When reading ANSI UTF-8 text streams, characters and control codes + * are oftentimes encoded as multi-byte sequences. This function knows + * how long each sequence is, so that each read consumes a single thing + * from the underlying file descriptor, e.g. + * + * "a" ALFA + * "\316\261" ALPHA + * "\033[A" CURSOR UP + * "\033[38;5;202m" ORANGERED + * "\eOP" PF1 + * + * This routine generalizes to ascii, utf-8, chorded modifier keys, + * function keys, color codes, c0/c1 control codes, cursor movement, + * mouse movement, etc. + * + * Userspace buffering isn't required, since ANSI escape sequences and + * UTF-8 are decoded without peeking. Noncanonical overlong encodings + * 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 + * @return number of bytes read (helps differentiate "\0" vs. "") + * @see examples/ttyinfo.c + * @see ANSI X3.64-1979 + * @see ISO/IEC 6429 + * @see FIPS-86 + * @see ECMA-48 + */ +ssize_t readansi(int fd, char *buf, size_t size) { + int i, j; + uint8_t c; + 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 (read(fd, &c, 1) != 1) return -1; + buf[i++] = c; + buf[i] = 0; + switch (t) { + case kAscii: + if (c < 0200) { + if (c == '\e') { + t = kEsc; + } else { + return i; + } + } else if (c >= 0300) { + t = kUtf8; + j = ThomPikeLen(c) - 1; + } + break; + case kUtf8: + if (!--j) return i; + break; + case kEsc: + switch (c) { + case '[': + t = kCsi; + break; + case 'N': + case 'O': + t = kSs; + break; + case 0x20 ... 0x2F: + break; + default: + return i; + } + break; + case kCsi: + switch (c) { + case ':': + case ';': + case '<': + case '=': + case '>': + case '?': + case '0' ... '9': + break; + default: + return i; + } + break; + case kSs: + return i; + default: + unreachable; + } + } +} diff --git a/libc/calls/setitimer-nt.c b/libc/calls/setitimer-nt.c new file mode 100644 index 00000000..9ddbd269 --- /dev/null +++ b/libc/calls/setitimer-nt.c @@ -0,0 +1,104 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/calls/struct/itimerval.h" +#include "libc/conv/conv.h" +#include "libc/errno.h" +#include "libc/log/check.h" +#include "libc/nexgen32e/nexgen32e.h" +#include "libc/nt/files.h" +#include "libc/nt/runtime.h" +#include "libc/nt/synchronization.h" +#include "libc/nt/thread.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/itimer.h" +#include "libc/sysv/errfuns.h" + +/** + * @fileoverview Heartbreaking polyfill for SIGALRM on NT. + * + * Threads are used to trigger the SIGALRM handler, which should + * hopefully be an unfancy function like this: + * + * void OnAlarm(int sig, struct siginfo *si, struct ucontext *uc) { + * g_alarmed = true; + * } + * + * This is needed because WIN32 provides no obvious solutions for + * interrupting i/o operations on the standard input handle. + */ + +static struct ItimerNt { + int64_t ith; + uint32_t tid; + struct itimerval itv; +} g_itimernt; + +static uint32_t ItimerWorker(void *arg) { + do { + if (!WaitForSingleObject(g_itimernt.ith, -1)) { + onntalarm(NULL, 0, 0); + } + } while (g_itimernt.ith && g_itimernt.tid == GetCurrentThreadId()); + return 0; +} + +textwindows int setitimer$nt(int which, const struct itimerval *newvalue, + struct itimerval *out_opt_oldvalue) { + int32_t period; + int64_t ith, duetime; + if (which != ITIMER_REAL) return einval(); + if (newvalue) { + if (newvalue->it_value.tv_sec && newvalue->it_value.tv_usec) { + if (!(ith = CreateWaitableTimer(NULL, false, NULL))) { + return winerr(); + } + duetime = -(newvalue->it_value.tv_sec * HECTONANOSECONDS + + newvalue->it_value.tv_usec * 10); + period = newvalue->it_value.tv_sec * 1000 + + div1000int64(newvalue->it_value.tv_usec); + if (!period && newvalue->it_value.tv_usec) period = 1; + if (!SetWaitableTimer(ith, &duetime, period, NULL, NULL, false)) { + errno = GetLastError(); + CloseHandle(ith); + return -1; + } + } else { + ith = 0; + } + if (g_itimernt.ith) { + CloseHandle(g_itimernt.ith); + g_itimernt.ith = 0; + } + } else { + ith = 0; + } + if (out_opt_oldvalue) { + memcpy(out_opt_oldvalue, &g_itimernt.itv, sizeof(struct itimerval)); + } + if (ith) { + g_itimernt.ith = ith; + memcpy(&g_itimernt.itv, newvalue, sizeof(struct itimerval)); + CloseHandle( + CreateThread(NULL, STACKSIZE, ItimerWorker, NULL, 0, &g_itimernt.tid)); + } + return 0; +} diff --git a/libc/time/setitimer.c b/libc/calls/setitimer.c similarity index 75% rename from libc/time/setitimer.c rename to libc/calls/setitimer.c index 395d4460..4cf00541 100644 --- a/libc/time/setitimer.c +++ b/libc/calls/setitimer.c @@ -24,7 +24,18 @@ #include "libc/time/time.h" /** - * Schedules delivery of one-shot or intermittent wakeup signal, e.g. + * Schedules delivery of one-shot or intermittent interrupt signal, e.g. + * + * Raise SIGALRM every 1.5s: + * + * CHECK_NE(-1, sigaction(SIGALRM, + * &(struct sigaction){.sa_sigaction = missingno}, + * NULL)); + * CHECK_NE(-1, setitimer(ITIMER_REAL, + * &(const struct itimerval){{1, 500000}, {1, 500000}}, + * NULL)); + * + * Set single-shot 50ms timer callback to interrupt laggy connect(): * * CHECK_NE(-1, sigaction(SIGALRM, * &(struct sigaction){.sa_sigaction = missingno, @@ -34,21 +45,29 @@ * &(const struct itimerval){{0, 0}, {0, 50000}}, * NULL)); * if (connect(...) == -1 && errno == EINTR) { ... } - * CHECK_NE(-1, setitimer(ITIMER_REAL, - * &(const struct itimerval){{0, 0}, {0, 0}}, - * NULL)); + * + * Disarm timer: + * + * CHECK_NE(-1, setitimer(ITIMER_REAL, &(const struct itimerval){0}, NULL)); + * + * Be sure to check for EINTR on your i/o calls, for best low latency. * * @param which can be ITIMER_REAL, ITIMER_VIRTUAL, etc. * @param newvalue specifies the interval ({0,0} means one-shot) and - * duration ({0,0} means disarm) in microseconds + * duration ({0,0} means disarm) in microseconds ∈ [0,999999] and + * if this parameter is NULL, we'll polyfill getitimer() behavior * @param out_opt_old may receive remainder of previous op (if any) * @return 0 on success or -1 w/ errno */ int setitimer(int which, const struct itimerval *newvalue, struct itimerval *out_opt_oldvalue) { if (!IsWindows()) { - return setitimer$sysv(which, newvalue, out_opt_oldvalue); + if (newvalue) { + return setitimer$sysv(which, newvalue, out_opt_oldvalue); + } else { + return getitimer$sysv(which, out_opt_oldvalue); + } } else { - return enosys(); /* TODO(jart): Implement me! */ + return setitimer$nt(which, newvalue, out_opt_oldvalue); } } diff --git a/libc/calls/struct/stat.h b/libc/calls/struct/stat.h index 98e246f7..7a4bb64c 100644 --- a/libc/calls/struct/stat.h +++ b/libc/calls/struct/stat.h @@ -3,7 +3,7 @@ #include "libc/calls/struct/timespec.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) -struct stat { +struct stat { /* linux abi */ int64_t st_dev; /* 0: id of device with file */ int64_t st_ino; /* 8: inode number in disk b-tree */ int64_t st_nlink; /* 16: hard link count */ diff --git a/libc/stubs/unprovable.S b/libc/calls/thunks/onntalarm.S similarity index 93% rename from libc/stubs/unprovable.S rename to libc/calls/thunks/onntalarm.S index 9ef9507f..2018eddd 100644 --- a/libc/stubs/unprovable.S +++ b/libc/calls/thunks/onntalarm.S @@ -18,14 +18,10 @@ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/macros.h" -.real +.text.windows .source __FILE__ -.code16 # ∩ .code32 ∩ .code64 -__unprovable: - push %bp - mov %sp,%bp - .softicebp - pop %bp - ret - .endfn __unprovable,globl,hidden +onntalarm$nt: + ezlea onntalarm,ax + jmp nt2sysv + .endfn onntalarm$nt,globl,hidden diff --git a/libc/calls/typedef/sigaction_f.h b/libc/calls/typedef/sigaction_f.h index 2e955e9d..34ec6353 100644 --- a/libc/calls/typedef/sigaction_f.h +++ b/libc/calls/typedef/sigaction_f.h @@ -1,10 +1,9 @@ #ifndef COSMOPOLITAN_LIBC_CALLS_TYPEDEF_SIGACTION_F_H_ #define COSMOPOLITAN_LIBC_CALLS_TYPEDEF_SIGACTION_F_H_ +#include "libc/calls/struct/siginfo.h" +#include "libc/calls/ucontext.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) -struct siginfo; -struct ucontext; - typedef void (*sigaction_f)(int, struct siginfo *, struct ucontext *); #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/calls/ucontext.h b/libc/calls/ucontext.h index 2af25a1e..911a6ccc 100644 --- a/libc/calls/ucontext.h +++ b/libc/calls/ucontext.h @@ -1,6 +1,5 @@ #ifndef COSMOPOLITAN_LIBC_CALLS_UCONTEXT_H_ #define COSMOPOLITAN_LIBC_CALLS_UCONTEXT_H_ -#include "libc/calls/calls.h" #include "libc/calls/struct/sigaltstack.h" #include "libc/calls/struct/sigset.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) diff --git a/libc/calls/writev.c b/libc/calls/writev.c index 1a61dd84..3a0a2336 100644 --- a/libc/calls/writev.c +++ b/libc/calls/writev.c @@ -27,6 +27,11 @@ /** * Writes data from multiple buffers. * + * Please note that it's not an error for a short write to happen. This + * can happen in the kernel if EINTR happens after some of the write has + * been committed. It can also happen if we need to polyfill this system + * call using write(). + * * @return number of bytes actually handed off, or -1 w/ errno */ ssize_t writev(int fd, const struct iovec *iov, int iovlen) { diff --git a/libc/conv/conv.h b/libc/conv/conv.h index 4ac49914..4e9306d8 100644 --- a/libc/conv/conv.h +++ b/libc/conv/conv.h @@ -27,9 +27,9 @@ long long llabs(long long) libcesque pureconst; char *ltpcpy(char *, long) paramsnonnull() libcesque nocallback; int llog10(unsigned long) libcesque pureconst; int unsleb128(const void *, size_t, int64_t *); -int atoi(const char *) paramsnonnull() libcesque nosideeffect; -long atol(const char *) paramsnonnull() libcesque nosideeffect; -long long atoll(const char *) paramsnonnull() libcesque nosideeffect; +int atoi(const char *) paramsnonnull() libcesque; +long atol(const char *) paramsnonnull() libcesque; +long long atoll(const char *) paramsnonnull() libcesque; unsigned long strtoul(const char *, char **, int) paramsnonnull((1)); long long strtoll(const char *, char **, int) paramsnonnull((1)); unsigned long long strtoull(const char *, char **, int) paramsnonnull((1)); @@ -39,8 +39,7 @@ intmax_t strtoimax(const char *, char **, int) paramsnonnull((1)); uintmax_t strtoumax(const char *, char **, int) paramsnonnull((1)); intmax_t wcstoimax(const wchar_t *, wchar_t **, int); long wcstol(const wchar_t *, wchar_t **, int); -long strtol(const char *, char **, int) - paramsnonnull((1)) libcesque nosideeffect; +long strtol(const char *, char **, int) paramsnonnull((1)) libcesque; /*───────────────────────────────────────────────────────────────────────────│─╗ │ cosmopolitan § conversion » time ─╬─│┼ diff --git a/libc/conv/conv.mk b/libc/conv/conv.mk index 062cf03c..6d7f9f60 100644 --- a/libc/conv/conv.mk +++ b/libc/conv/conv.mk @@ -35,8 +35,9 @@ LIBC_CONV_A_CHECKS = \ $(LIBC_CONV_A_HDRS:%=o/$(MODE)/%.ok) LIBC_CONV_A_DIRECTDEPS = \ - LIBC_STUBS \ LIBC_NEXGEN32E \ + LIBC_STUBS \ + LIBC_STR \ LIBC_TINYMATH \ LIBC_SYSV \ THIRD_PARTY_COMPILER_RT diff --git a/libc/fmt/palandprintf.c b/libc/fmt/palandprintf.c index 47e09e1a..dd0012aa 100644 --- a/libc/fmt/palandprintf.c +++ b/libc/fmt/palandprintf.c @@ -92,14 +92,21 @@ static int ppatoi(const char **str) { * * Precision Modifiers * - * - `%.8s` supplied character length (ignore nul terminator) - * - `%.*s` supplied character length argument (ignore nul terminator) + * - `%.8s` supplied byte length (obeys nul terminator) + * - `%.*s` supplied byte length argument (obeys nul terminator) + * - `%`.*s` supplied byte length argument c escaped (ignores nul terminator) + * - `%#.*s` supplied byte length argument visualized (ignores nul terminator) + * - `%.*hs` supplied char16_t length argument (obeys nul terminator) + * - `%.*ls` supplied wchar_t length argument (obeys nul terminator) * * Formatting Modifiers * * - `%,d` thousands separators * - `%'s` escaped c string literal - * - `%'c` escaped c character literal + * - `%`c` c escaped character + * - `%`'c` c escaped character quoted + * - `%`s` c escaped string + * - `%`'s` c escaped string quoted * - `%`s` escaped double quoted c string literal * - `%`c` escaped double quoted c character literal * - `%+d` plus leftpad if positive (aligns w/ negatives) @@ -114,7 +121,7 @@ hidden int palandprintf(void *fn, void *arg, const char *format, va_list va) { void *p; char qchar; long double ldbl; - wchar_t charbuf[3]; + wchar_t charbuf[1]; const char *alphabet; int (*out)(long, void *); unsigned char signbit, log2base; @@ -281,14 +288,15 @@ hidden int palandprintf(void *fn, void *arg, const char *format, va_list va) { break; case 'c': + precision = 1; + flags |= FLAGS_PRECISION; qchar = '\''; p = charbuf; - charbuf[0] = va_arg(va, int); - charbuf[1] = L'\0'; + charbuf[0] = va_arg(va, int); /* assume little endian */ goto showstr; case 'm': - p = weaken(strerror)(lasterr); + p = weaken(strerror) ? weaken(strerror)(lasterr) : "?"; signbit = 0; goto showstr; diff --git a/libc/fmt/stoa.c b/libc/fmt/stoa.c index 3de67697..1cd3b627 100644 --- a/libc/fmt/stoa.c +++ b/libc/fmt/stoa.c @@ -44,7 +44,7 @@ static int StoaEmitWordEncodedString(int f(long, void *), void *a, uint64_t w) { } static int StoaEmitUnicode(int f(long, void *), void *a, wint_t c) { - if (0 <= c && c <= 127) { + if (isascii(c)) { return f(c, a); } else { return StoaEmitWordEncodedString(f, a, tpenc(c)); @@ -52,7 +52,7 @@ static int StoaEmitUnicode(int f(long, void *), void *a, wint_t c) { } static int StoaEmitQuoted(int f(long, void *), void *a, wint_t c) { - if (0 <= c && c <= 127) { + if (isascii(c)) { return StoaEmitWordEncodedString(f, a, cescapec(c)); } else { return StoaEmitWordEncodedString(f, a, tpenc(c)); @@ -92,8 +92,8 @@ int stoa(int out(long, void *), void *arg, void *data, unsigned long flags, wint_t wc; unsigned n; emit_f emit; - bool justdobytes; unsigned w, c, pad; + bool justdobytes, ignorenul; p = data; if (!p) { @@ -105,13 +105,35 @@ int stoa(int out(long, void *), void *arg, void *data, unsigned long flags, if (StoaEmitQuote(out, arg, flags, qchar, signbit) == -1) return -1; } - if (!(flags & FLAGS_PRECISION)) { - if (signbit == 63) { - precision = tinywcsnlen((const wchar_t *)p, -1); - } else if (signbit == 15) { - precision = tinystrnlen16((const char16_t *)p, -1); + ignorenul = false; + justdobytes = false; + if (signbit == 15 || signbit == 63) { + if (flags & FLAGS_QUOTE) { + emit = StoaEmitQuoted; + ignorenul = flags & FLAGS_PRECISION; } else { - precision = strlen(p); + emit = StoaEmitUnicode; + } + } else if ((flags & FLAGS_HASH) && weaken(kCp437)) { + justdobytes = true; + emit = StoaEmitVisualized; + ignorenul = flags & FLAGS_PRECISION; + } else if (flags & FLAGS_QUOTE) { + emit = StoaEmitQuoted; + ignorenul = flags & FLAGS_PRECISION; + } else { + justdobytes = true; + emit = StoaEmitByte; + } + + if (!(flags & FLAGS_PRECISION)) precision = -1; + if (!(flags & FLAGS_PRECISION) || !ignorenul) { + if (signbit == 63) { + precision = tinywcsnlen((const wchar_t *)p, precision); + } else if (signbit == 15) { + precision = tinystrnlen16((const char16_t *)p, precision); + } else { + precision = strnlen(p, precision); } } @@ -138,32 +160,17 @@ int stoa(int out(long, void *), void *arg, void *data, unsigned long flags, if (spacepad(out, arg, pad) == -1) return -1; } - justdobytes = false; - if (signbit == 15 || signbit == 63) { - if (flags & FLAGS_QUOTE) { - emit = StoaEmitQuoted; - } else { - emit = StoaEmitUnicode; - } - } else if ((flags & FLAGS_HASH) && weaken(kCp437)) { - justdobytes = true; - emit = StoaEmitVisualized; - } else if (flags & FLAGS_QUOTE) { - emit = StoaEmitQuoted; - } else { - justdobytes = true; - emit = StoaEmitByte; - } - if (justdobytes) { while (precision--) { wc = *p++ & 0xff; + if (!wc && !ignorenul) break; if (emit(out, arg, wc) == -1) return -1; } } else { while (precision--) { if (signbit == 15) { wc = *(const char16_t *)p; + if (!wc && !ignorenul) break; if (IsUcs2(wc)) { p += sizeof(char16_t); } else if (IsUtf16Cont(wc)) { @@ -177,10 +184,12 @@ int stoa(int out(long, void *), void *arg, void *data, unsigned long flags, } } else if (signbit == 63) { wc = *(const wint_t *)p; + if (!wc && !ignorenul) break; p += sizeof(wint_t); if (!wc) break; } else { wc = *p++ & 0xff; + if (!wc && !ignorenul) break; if (!isascii(wc)) { if (ThomPikeCont(wc)) continue; n = ThomPikeLen(wc) - 1; diff --git a/libc/intrin/pcmpgtb.c b/libc/intrin/pcmpgtb.c index 3dd7c3ae..2f37d3ec 100644 --- a/libc/intrin/pcmpgtb.c +++ b/libc/intrin/pcmpgtb.c @@ -23,6 +23,8 @@ /** * Compares signed 8-bit integers w/ greater than predicate. * + * Note that operands can be xor'd with 0x80 for unsigned compares. + * * @param 𝑎 [w/o] receives result * @param 𝑏 [r/o] supplies first input vector * @param 𝑐 [r/o] supplies second input vector diff --git a/libc/intrin/pmovmskb.c b/libc/intrin/pmovmskb.c index 5dcee3f0..4baf4794 100644 --- a/libc/intrin/pmovmskb.c +++ b/libc/intrin/pmovmskb.c @@ -19,6 +19,12 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/intrin/pmovmskb.h" +/** + * Turns result of byte comparison into bitmask. + * + * @param 𝑝 is byte vector to crunch + * @see pcmpeqb(), bsf(), etc. + */ uint32_t(pmovmskb)(const uint8_t p[16]) { uint32_t i, m; for (m = i = 0; i < 16; ++i) { diff --git a/libc/intrin/pshufd.c b/libc/intrin/pshufd.c index f2178195..d2b5fcdb 100644 --- a/libc/intrin/pshufd.c +++ b/libc/intrin/pshufd.c @@ -18,6 +18,7 @@ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/intrin/pshufd.h" +#include "libc/str/str.h" /** * Shuffles int vector. @@ -30,8 +31,5 @@ void(pshufd)(int32_t b[4], const int32_t a[4], uint8_t m) { t[1] = a[(m & 0b00001100) >> 2]; t[2] = a[(m & 0b00110000) >> 4]; t[3] = a[(m & 0b11000000) >> 6]; - b[0] = t[0]; - b[1] = t[1]; - b[2] = t[2]; - b[3] = t[3]; + memcpy(b, t, 16); } diff --git a/libc/intrin/psubb.c b/libc/intrin/psubb.c index 1f02b6f5..4bf95158 100644 --- a/libc/intrin/psubb.c +++ b/libc/intrin/psubb.c @@ -28,9 +28,9 @@ * @param 𝑐 [r/o] supplies second input vector * @mayalias */ -void(psubb)(int8_t a[16], const int8_t b[16], const int8_t c[16]) { +void(psubb)(uint8_t a[16], const uint8_t b[16], const uint8_t c[16]) { unsigned i; - int8_t r[16]; + uint8_t r[16]; for (i = 0; i < 16; ++i) { r[i] = b[i] - c[i]; } diff --git a/libc/intrin/psubb.h b/libc/intrin/psubb.h index fd6e0f46..37b04263 100644 --- a/libc/intrin/psubb.h +++ b/libc/intrin/psubb.h @@ -4,7 +4,7 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -void psubb(int8_t[16], const int8_t[16], const int8_t[16]); +void psubb(uint8_t[16], const uint8_t[16], const uint8_t[16]); #define psubb(A, B, C) \ INTRIN_SSEVEX_X_X_X_(psubb, SSE2, "psubb", INTRIN_NONCOMMUTATIVE, A, B, C) diff --git a/libc/isystem/windows.h b/libc/isystem/windows.h index 9d58b213..2219bd37 100644 --- a/libc/isystem/windows.h +++ b/libc/isystem/windows.h @@ -37,6 +37,7 @@ #define PVOID void* #define PVOID64 void* +#define LPCVOID const void* #define CHAR char #define SHORT short #define CONST const diff --git a/libc/linux/close.h b/libc/linux/close.h new file mode 100644 index 00000000..a517c9ec --- /dev/null +++ b/libc/linux/close.h @@ -0,0 +1,12 @@ +#ifndef COSMOPOLITAN_LIBC_LINUX_CLOSE_H_ +#define COSMOPOLITAN_LIBC_LINUX_CLOSE_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) + +forceinline long LinuxClose(long fd) { + long rc; + asm volatile("syscall" : "=a"(rc) : "0"(3), "D"(fd) : "rcx", "r11", "memory"); + return rc; +} + +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_LINUX_CLOSE_H_ */ diff --git a/libc/linux/exit.h b/libc/linux/exit.h index 655af633..e5479f2e 100644 --- a/libc/linux/exit.h +++ b/libc/linux/exit.h @@ -3,7 +3,7 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -forceinline noreturn long LinuxExit(int rc) { +forceinline noreturn long LinuxExit(long rc) { asm volatile("syscall" : /* no outputs */ : "a"(0xE7), "D"(rc) diff --git a/libc/linux/fstat.h b/libc/linux/fstat.h new file mode 100644 index 00000000..40b4578f --- /dev/null +++ b/libc/linux/fstat.h @@ -0,0 +1,16 @@ +#ifndef COSMOPOLITAN_LIBC_LINUX_FSTAT_H_ +#define COSMOPOLITAN_LIBC_LINUX_FSTAT_H_ +#include "libc/calls/struct/stat.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) + +forceinline long LinuxFstat(long fd, struct stat *st) { + long rc; + asm volatile("syscall" + : "=a"(rc) + : "0"(5), "D"(fd), "S"(st) + : "rcx", "r11", "memory"); + return rc; +} + +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_LINUX_FSTAT_H_ */ diff --git a/libc/linux/mmap.h b/libc/linux/mmap.h new file mode 100644 index 00000000..024a72bd --- /dev/null +++ b/libc/linux/mmap.h @@ -0,0 +1,20 @@ +#ifndef COSMOPOLITAN_LIBC_LINUX_MMAP_H_ +#define COSMOPOLITAN_LIBC_LINUX_MMAP_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) + +forceinline long LinuxMmap(void *addr, size_t size, long prot, long flags, + long fd, long off) { + long rc; + register long r10 asm("r10") = flags; + register long r8 asm("r8") = fd; + register long r9 asm("r9") = off; + asm volatile("syscall" + : "=a"(rc) + : "0"(9), "D"(addr), "S"(size), "d"(prot), "r"(r10), "r"(r8), + "r"(r9) + : "rcx", "r11", "memory"); + return rc; +} + +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_LINUX_MMAP_H_ */ diff --git a/libc/linux/munmap.h b/libc/linux/munmap.h new file mode 100644 index 00000000..e9515ea5 --- /dev/null +++ b/libc/linux/munmap.h @@ -0,0 +1,15 @@ +#ifndef COSMOPOLITAN_LIBC_LINUX_MUNMAP_H_ +#define COSMOPOLITAN_LIBC_LINUX_MUNMAP_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) + +forceinline long LinuxMunmap(void *addr, size_t size) { + long rc; + asm volatile("syscall" + : "=a"(rc) + : "0"(0xb), "D"(addr), "S"(size) + : "rcx", "r11", "memory"); + return rc; +} + +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_LINUX_MUNMAP_H_ */ diff --git a/libc/linux/open.h b/libc/linux/open.h new file mode 100644 index 00000000..919836b9 --- /dev/null +++ b/libc/linux/open.h @@ -0,0 +1,15 @@ +#ifndef COSMOPOLITAN_LIBC_LINUX_OPEN_H_ +#define COSMOPOLITAN_LIBC_LINUX_OPEN_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) + +forceinline long LinuxOpen(const char *path, long flags, long mode) { + long rc; + asm volatile("syscall" + : "=a"(rc) + : "0"(2), "D"(path), "S"(flags), "d"(mode) + : "rcx", "r11", "memory"); + return rc; +} + +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_LINUX_OPEN_H_ */ diff --git a/libc/linux/read.h b/libc/linux/read.h index b3fae18a..67319f48 100644 --- a/libc/linux/read.h +++ b/libc/linux/read.h @@ -2,7 +2,7 @@ #define COSMOPOLITAN_LIBC_LINUX_READ_H_ #if !(__ASSEMBLER__ + __LINKER__ + 0) -forceinline long LinuxRead(int fd, void *data, unsigned long size) { +forceinline long LinuxRead(long fd, void *data, unsigned long size) { long rc; asm volatile("syscall" : "=a"(rc), "=m"(*(char(*)[size])data) diff --git a/libc/linux/write.h b/libc/linux/write.h index 413f1a29..2d904978 100644 --- a/libc/linux/write.h +++ b/libc/linux/write.h @@ -1,9 +1,8 @@ #ifndef COSMOPOLITAN_LIBC_LINUX_WRITE_H_ #define COSMOPOLITAN_LIBC_LINUX_WRITE_H_ #if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ -forceinline long LinuxWrite(int fd, const void *data, unsigned long size) { +forceinline long LinuxWrite(long fd, const void *data, unsigned long size) { long rc; asm volatile("syscall" : "=a"(rc) @@ -13,6 +12,5 @@ forceinline long LinuxWrite(int fd, const void *data, unsigned long size) { return rc; } -COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_LINUX_WRITE_H_ */ diff --git a/libc/log/asan.c b/libc/log/asan.c index 7b923d62..03d665c2 100644 --- a/libc/log/asan.c +++ b/libc/log/asan.c @@ -17,25 +17,14 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/assert.h" -#include "libc/bits/safemacros.h" #include "libc/bits/weaken.h" #include "libc/calls/calls.h" -#include "libc/conv/conv.h" #include "libc/conv/itoa.h" #include "libc/log/asan.h" #include "libc/log/backtrace.h" -#include "libc/log/log.h" -#include "libc/macros.h" #include "libc/mem/hook/hook.h" #include "libc/runtime/directmap.h" -#include "libc/runtime/internal.h" #include "libc/runtime/memtrack.h" -#include "libc/runtime/missioncritical.h" -#include "libc/runtime/runtime.h" -#include "libc/runtime/symbols.h" -#include "libc/stdio/stdio.h" -#include "libc/str/str.h" #include "libc/sysv/consts/fileno.h" #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/prot.h" @@ -154,9 +143,9 @@ static const char *__asan_describe_access_poison(int c) { } static noreturn void __asan_die(const char *msg, size_t size) { - __print(msg, size); - PrintBacktraceUsingSymbols(stderr, __builtin_frame_address(0), - getsymboltable()); + write(STDERR_FILENO, msg, size); + PrintBacktraceUsingSymbols(STDERR_FILENO, __builtin_frame_address(0), + GetSymbolTable()); DebugBreak(); _Exit(66); } @@ -170,7 +159,7 @@ static noreturn void __asan_report_deallocate_fault(void *addr, int c) { p = mempcpy(p, ibuf, int64toarray_radix10(c, ibuf)); p = stpcpy(p, " at 0x"); p = mempcpy(p, ibuf, uint64toarray_fixed16((intptr_t)addr, ibuf, 48)); - p = stpcpy(p, "\n"); + p = stpcpy(p, "\r\n"); __asan_die(buf, p - buf); } @@ -186,7 +175,7 @@ static noreturn void __asan_report_memory_fault(uint8_t *addr, int size, p = stpcpy(p, kind); p = stpcpy(p, " at 0x"); p = mempcpy(p, ibuf, uint64toarray_fixed16((intptr_t)addr, ibuf, 48)); - p = stpcpy(p, "\n"); + p = stpcpy(p, "\r\n"); __asan_die(buf, p - buf); } @@ -209,13 +198,6 @@ static void __asan_morgue_flush(void) { } } -static bool __asan_is_mapped(void *p) { - int x, i; - x = (intptr_t)p >> 16; - i = FindMemoryInterval(&_mmi, x); - return i < _mmi.i && x >= _mmi.p[i].x && x <= _mmi.p[i].y; -} - static void *__asan_allocate(size_t align, size_t size, int underrun, int overrun) { char *p, *s; @@ -394,44 +376,62 @@ void __asan_install_malloc_hooks(void) { HOOK(hook$malloc_usable_size, __asan_malloc_usable_size); } -void __asan_map_shadow(void *addr, size_t size) { - int i, n, x; - char *a, *b; +static bool __asan_is_mapped(int x) { + int i = FindMemoryInterval(&_mmi, x); + return i < _mmi.i && x >= _mmi.p[i].x && x <= _mmi.p[i].y; +} + +void __asan_map_shadow(void *p, size_t n) { + int i, x, a, b; struct DirectMap sm; - a = (char *)ROUNDDOWN(SHADOW((intptr_t)addr), FRAMESIZE); - b = (char *)ROUNDDOWN(SHADOW((intptr_t)addr + size - 1), FRAMESIZE); - for (; a <= b; a += FRAMESIZE) { + a = SHADOW((uintptr_t)p) >> 16; + b = ROUNDUP(SHADOW(ROUNDUP((uintptr_t)p + n, 8)), 1 << 16) >> 16; + for (; a < b; ++a) { if (!__asan_is_mapped(a)) { - sm = DirectMap(a, FRAMESIZE, PROT_READ | PROT_WRITE, + sm = DirectMap((void *)((uintptr_t)a << 16), 1 << 16, + PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); if (sm.addr == MAP_FAILED || - TrackMemoryInterval(&_mmi, (intptr_t)a >> 16, (intptr_t)a >> 16, - sm.maphandle) == -1) { + TrackMemoryInterval(&_mmi, a, a, sm.maphandle) == -1) { abort(); } } } } -textstartup void __asan_init(int argc, char *argv[], char **envp, - intptr_t *auxv) { - int i; - static bool once; - register intptr_t rsp asm("rsp"); - if (!once) { - __asan_map_shadow(_base, _end - _base); - __asan_map_shadow((void *)ROUNDDOWN(rsp, STACKSIZE), STACKSIZE); - for (i = 0; i < argc; ++i) __asan_map_shadow(argv[i], strlen(argv[i])); - for (; *envp; ++envp) __asan_map_shadow(*envp, strlen(*envp)); - __asan_map_shadow(auxv, sizeof(intptr_t) * 2); - __asan_install_malloc_hooks(); - once = true; +static char *__asan_get_stack_base(void) { + register uintptr_t rsp asm("rsp"); + return (char *)ROUNDDOWN(ROUNDDOWN(rsp, STACKSIZE), FRAMESIZE); +} + +static textstartup size_t __asan_get_auxv_size(intptr_t *auxv) { + unsigned i; + for (i = 0;; i += 2) { + if (!auxv[i]) break; + } + return (i + 2) * sizeof(intptr_t); +} + +static textstartup void __asan_shadow_string_list(char **list) { + for (; *list; ++list) { + __asan_map_shadow(*list, strlen(*list) + 1); } } +textstartup void __asan_init(int argc, char **argv, char **envp, + intptr_t *auxv) { + static bool once; + if (once) return; + __asan_map_shadow(_base, _end - _base); + __asan_map_shadow(__asan_get_stack_base(), STACKSIZE); + __asan_shadow_string_list(argv); + __asan_shadow_string_list(envp); + __asan_map_shadow(auxv, __asan_get_auxv_size(auxv)); + __asan_install_malloc_hooks(); +} + static textstartup void __asan_ctor(void) { - /* __cxa_atexit(__asan_morgue_flush, NULL, NULL); */ - getsymboltable(); + __cxa_atexit(__asan_morgue_flush, NULL, NULL); } const void *const g_asan_ctor[] initarray = {__asan_ctor}; diff --git a/libc/log/attachdebugger.c b/libc/log/attachdebugger.c index 72ce8996..6c17ed00 100644 --- a/libc/log/attachdebugger.c +++ b/libc/log/attachdebugger.c @@ -29,10 +29,13 @@ #include "libc/runtime/runtime.h" #include "libc/runtime/symbols.h" #include "libc/stdio/stdio.h" +#include "libc/str/str.h" #include "libc/sysv/consts/fileno.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/w.h" +#define RESTORE_TTY "\e[?1000;1002;1015;1006l\e[?25h" + /** * Launches GDB debugger GUI for current process. * @@ -50,17 +53,18 @@ * @note this is called via eponymous spinlock macro wrapper */ relegated int(attachdebugger)(intptr_t continuetoaddr) { - int ttyin, ttyout; + int ttyfd; struct StackFrame *bp; char pidstr[11], breakcmd[40]; const char *se, *elf, *gdb, *rewind, *layout; if (IsGenuineCosmo() || !(gdb = GetGdbPath()) || - (ttyin = ttyout = open(_PATH_TTY, O_RDWR, 0)) == -1) { + (ttyfd = open(_PATH_TTY, O_RDWR, 0)) == -1) { return -1; } + write(ttyfd, RESTORE_TTY, strlen(RESTORE_TTY)); snprintf(pidstr, sizeof(pidstr), "%u", getpid()); layout = "layout asm"; - if ((elf = finddebugbinary())) { + if ((elf = FindDebugBinary())) { se = "-se"; if (fileexists(__FILE__)) layout = "layout src"; } else { @@ -78,7 +82,7 @@ relegated int(attachdebugger)(intptr_t continuetoaddr) { rewind = NULL; breakcmd[0] = '\0'; } - return spawnve(0, (int[3]){ttyin, ttyout, STDERR_FILENO}, gdb, + return spawnve(0, (int[3]){ttyfd, ttyfd, STDERR_FILENO}, gdb, (char *const[]){ "gdb", "--tui", "-p", pidstr, diff --git a/libc/log/backtrace.h b/libc/log/backtrace.h index ce31ae14..eb16ea3b 100644 --- a/libc/log/backtrace.h +++ b/libc/log/backtrace.h @@ -2,12 +2,11 @@ #define COSMOPOLITAN_LIBC_LOG_BACKTRACE_H_ #include "libc/nexgen32e/stackframe.h" #include "libc/runtime/symbols.h" -#include "libc/stdio/stdio.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -void showbacktrace(FILE *, const struct StackFrame *); -int PrintBacktraceUsingSymbols(FILE *, const struct StackFrame *, +void ShowBacktrace(int, const struct StackFrame *); +int PrintBacktraceUsingSymbols(int, const struct StackFrame *, struct SymbolTable *); COSMOPOLITAN_C_END_ diff --git a/libc/log/backtrace2.c b/libc/log/backtrace2.c index 163cc43c..5eaf1081 100644 --- a/libc/log/backtrace2.c +++ b/libc/log/backtrace2.c @@ -31,14 +31,13 @@ #include "libc/nexgen32e/gc.h" #include "libc/runtime/runtime.h" #include "libc/runtime/symbols.h" -#include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/sysv/consts/fileno.h" #define kBacktraceMaxFrames 128 #define kBacktraceBufSize ((kBacktraceMaxFrames - 1) * (16 + 1)) -static int PrintBacktraceUsingAddr2line(FILE *f, const struct StackFrame *bp) { +static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) { ssize_t got; intptr_t addr; size_t i, j, gi; @@ -47,7 +46,7 @@ static int PrintBacktraceUsingAddr2line(FILE *f, const struct StackFrame *bp) { const struct StackFrame *frame; const char *debugbin, *p1, *p2, *p3, *addr2line; char buf[kBacktraceBufSize], *argv[kBacktraceMaxFrames]; - if (!(debugbin = finddebugbinary()) || !(addr2line = GetAddr2linePath())) { + if (!(debugbin = FindDebugBinary()) || !(addr2line = GetAddr2linePath())) { return -1; } i = 0; @@ -88,11 +87,11 @@ static int PrintBacktraceUsingAddr2line(FILE *f, const struct StackFrame *bp) { strlen(" (discriminator ") - 1)) && (p3 = memchr(p2, '\n', got - (p2 - p1)))) { if (p3 > p2 && p3[-1] == '\r') --p3; - fwrite(p1, 1, p2 - p1, f); + write(fd, p1, p2 - p1); got -= p3 - p1; p1 += p3 - p1; } else { - fwrite(p1, 1, got, f); + write(fd, p1, got); break; } } @@ -103,20 +102,21 @@ static int PrintBacktraceUsingAddr2line(FILE *f, const struct StackFrame *bp) { return 0; } -static int PrintBacktrace(FILE *f, const struct StackFrame *bp) { +static int PrintBacktrace(int fd, const struct StackFrame *bp) { if (!IsTiny()) { - if (PrintBacktraceUsingAddr2line(f, bp) != -1) { + if (PrintBacktraceUsingAddr2line(fd, bp) != -1) { return 0; } } - return PrintBacktraceUsingSymbols(f, bp, getsymboltable()); + return PrintBacktraceUsingSymbols(fd, bp, GetSymbolTable()); } -void showbacktrace(FILE *f, const struct StackFrame *bp) { +void ShowBacktrace(int fd, const struct StackFrame *bp) { static bool noreentry; + if (!bp) bp = __builtin_frame_address(0); if (!noreentry) { noreentry = true; - PrintBacktrace(f, bp); + PrintBacktrace(fd, bp); noreentry = 0; } } diff --git a/libc/log/backtrace3.c b/libc/log/backtrace3.c index 5fc6f1e3..c123085c 100644 --- a/libc/log/backtrace3.c +++ b/libc/log/backtrace3.c @@ -20,6 +20,7 @@ #include "libc/alg/bisectcarleft.h" #include "libc/assert.h" #include "libc/bits/weaken.h" +#include "libc/calls/calls.h" #include "libc/conv/itoa.h" #include "libc/fmt/fmt.h" #include "libc/log/backtrace.h" @@ -28,26 +29,26 @@ #include "libc/nexgen32e/stackframe.h" #include "libc/runtime/missioncritical.h" #include "libc/runtime/symbols.h" -#include "libc/stdio/stdio.h" #include "libc/str/str.h" /** * Prints stack frames with symbols. * - * PrintBacktraceUsingSymbols(stdout, NULL, getsymboltable()); + * PrintBacktraceUsingSymbols(STDOUT_FILENO, NULL, GetSymbolTable()); * * @param f is output stream * @param bp is rbp which can be NULL to detect automatically * @param st is open symbol table for current executable * @return -1 w/ errno if error happened */ -int PrintBacktraceUsingSymbols(FILE *f, const struct StackFrame *bp, +int PrintBacktraceUsingSymbols(int fd, const struct StackFrame *bp, struct SymbolTable *st) { + char *p; size_t gi; intptr_t addr; int64_t addend; struct Garbages *garbage; - char *p, buf[256], ibuf[21]; + char buf[256], ibuf[21]; const struct Symbol *symbol; const struct StackFrame *frame; if (!st) return -1; @@ -78,8 +79,11 @@ int PrintBacktraceUsingSymbols(FILE *f, const struct StackFrame *bp, } else { p = stpcpy(p, "UNKNOWN"); } + *p++ = '\r'; *p++ = '\n'; - __print(buf, p - buf); + if (write(fd, buf, p - buf) == -1) { + return -1; + } } return 0; } diff --git a/libc/log/check.h b/libc/log/check.h index 77129d50..a8eef092 100644 --- a/libc/log/check.h +++ b/libc/log/check.h @@ -10,7 +10,7 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -#define CHECK(X, ...) __CHK(eq, ==, true, "true", !!(X), #X, "" __VA_ARGS__) +#define CHECK(X, ...) __CHK(ne, !=, false, "false", !!(X), #X, "" __VA_ARGS__) #define CHECK_EQ(Y, X, ...) __CHK(eq, ==, Y, #Y, X, #X, "" __VA_ARGS__) #define CHECK_NE(Y, X, ...) __CHK(ne, !=, Y, #Y, X, #X, "" __VA_ARGS__) #define CHECK_LE(Y, X, ...) __CHK(le, <=, Y, #Y, X, #X, "" __VA_ARGS__) @@ -19,7 +19,7 @@ COSMOPOLITAN_C_START_ #define CHECK_GT(Y, X, ...) __CHK(gt, >, Y, #Y, X, #X, "" __VA_ARGS__) #define CHECK_NOTNULL(X, ...) __CHK(ne, !=, NULL, "NULL", X, #X, "" __VA_ARGS__) -#define DCHECK(X, ...) __DCHK(eq, ==, true, "true", !!(X), #X, "" __VA_ARGS__) +#define DCHECK(X, ...) __DCHK(ne, !=, false, "false", !!(X), #X, "" __VA_ARGS__) #define DCHECK_EQ(Y, X, ...) __DCHK(eq, ==, Y, #Y, X, #X, "" __VA_ARGS__) #define DCHECK_NE(Y, X, ...) __DCHK(ne, !=, Y, #Y, X, #X, "" __VA_ARGS__) #define DCHECK_LE(Y, X, ...) __DCHK(le, <=, Y, #Y, X, #X, "" __VA_ARGS__) diff --git a/libc/log/checkaligned.c b/libc/log/checkaligned.c index 6f2fdb77..bf73f438 100644 --- a/libc/log/checkaligned.c +++ b/libc/log/checkaligned.c @@ -29,7 +29,7 @@ STATIC_YOINK("stoa"); void __check_fail_aligned(unsigned bytes, uint64_t ptr) { fflush(stderr); if (!IsTiny()) memsummary(fileno(stderr)); - (dprintf)(fileno(stderr), "%s%d%s%#p\n", "error: pointer not ", bytes, + (dprintf)(fileno(stderr), "%s%d%s%#p\r\n", "error: pointer not ", bytes, "-byte aligned: ", ptr); die(); } diff --git a/libc/log/checkfail.c b/libc/log/checkfail.c index 5ee422c6..d4ff2649 100644 --- a/libc/log/checkfail.c +++ b/libc/log/checkfail.c @@ -52,10 +52,10 @@ relegated void __check_fail(const char *suffix, const char *opstr, strtoupper(sufbuf); (fprintf)(stderr, - "check failed\n" - "\tCHECK_%s(%s, %s);\n" - "\t\t → %#lx (%s)\n" - "\t\t%s %#lx (%s)\n", + "check failed\r\n" + "\tCHECK_%s(%s, %s);\r\n" + "\t\t → %#lx (%s)\r\n" + "\t\t%s %#lx (%s)\r\n", sufbuf, wantstr, gotstr, want, wantstr, opstr, got, gotstr); if (!isempty(fmt)) { @@ -63,18 +63,18 @@ relegated void __check_fail(const char *suffix, const char *opstr, va_start(va, fmt); (vfprintf)(stderr, fmt, va); va_end(va); - fputc('\n', stderr); + fputs("\r\n", stderr); } - (fprintf)(stderr, "\t%s\n\t%s%s%s%s\n", strerror(lasterr), SUBTLE, + (fprintf)(stderr, "\t%s\r\n\t%s%s%s%s\r\n", strerror(lasterr), SUBTLE, getauxval(AT_EXECFN), g_argc > 1 ? " \\" : "", RESET); for (i = 1; i < g_argc; ++i) { - (fprintf)(stderr, "\t\t%s%s\n", g_argv[i], i < g_argc - 1 ? " \\" : ""); + (fprintf)(stderr, "\t\t%s%s\r\n", g_argv[i], i < g_argc - 1 ? " \\" : ""); } if (!IsTiny() && lasterr == ENOMEM) { - (fprintf)(stderr, "\n"); + (fprintf)(stderr, "\r\n"); fflush(stderr); PrintMemoryIntervals(fileno(stderr), &_mmi); } diff --git a/libc/log/checkfail_ndebug.c b/libc/log/checkfail_ndebug.c index 21c88cc4..6e6fc2fc 100644 --- a/libc/log/checkfail_ndebug.c +++ b/libc/log/checkfail_ndebug.c @@ -47,5 +47,5 @@ relegated void ___check_fail_ndebug(uint64_t want, uint64_t got, __print(bx, uint64toarray_radix16(got, bx)); __print_string(" ("); __print(bx, int64toarray_radix10(lasterr, bx)); - __print_string(")\n"); + __print_string(")\r\n"); } diff --git a/libc/log/die.c b/libc/log/die.c index 241bb8d4..1f456f26 100644 --- a/libc/log/die.c +++ b/libc/log/die.c @@ -18,10 +18,12 @@ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/bits.h" +#include "libc/log/backtrace.h" #include "libc/log/log.h" #include "libc/runtime/internal.h" #include "libc/stdio/stdio.h" #include "libc/sysv/consts/exit.h" +#include "libc/sysv/consts/fileno.h" /** * Aborts process after printing details on its current state. @@ -33,7 +35,7 @@ relegated noreturn void die(void) { if (!IsTiny()) { g_runstate |= RUNSTATE_BROKEN; if (IsDebuggerPresent(false)) DebugBreak(); - backtrace(stderr); + ShowBacktrace(STDERR_FILENO, NULL); } exit(EXIT_FAILURE); unreachable; diff --git a/libc/log/gdbexec.c b/libc/log/gdbexec.c index f21da7d8..9eba4558 100644 --- a/libc/log/gdbexec.c +++ b/libc/log/gdbexec.c @@ -38,7 +38,7 @@ int(gdbexec)(const char *cmd) { char pidstr[11], breakcmd[40]; if (!(gdb = GetGdbPath())) return -1; snprintf(pidstr, sizeof(pidstr), "%u", getpid()); - if ((elf = finddebugbinary())) { + if ((elf = FindDebugBinary())) { se = "-se"; } else { se = "-q"; diff --git a/libc/log/getsymboltable.c b/libc/log/getsymboltable.c index 7127594c..654971cd 100644 --- a/libc/log/getsymboltable.c +++ b/libc/log/getsymboltable.c @@ -24,15 +24,15 @@ * Returns debug binary symbol table, as global singleton. * @return symbol table, or NULL w/ errno on first call */ -struct SymbolTable *getsymboltable(void) { +struct SymbolTable *GetSymbolTable(void) { static bool once; static struct SymbolTable *singleton; const char *debugbin; if (!once) { once = true; - if ((debugbin = finddebugbinary()) && - (singleton = opensymboltable(debugbin))) { - __cxa_atexit(closesymboltable, &singleton, NULL); + if ((debugbin = FindDebugBinary()) && + (singleton = OpenSymbolTable(debugbin))) { + __cxa_atexit(CloseSymbolTable, &singleton, NULL); } } return singleton; diff --git a/libc/log/log.h b/libc/log/log.h index d2b14d3a..efadeafb 100644 --- a/libc/log/log.h +++ b/libc/log/log.h @@ -33,7 +33,6 @@ typedef struct FILE FILE; extern FILE *g_logfile; -void backtrace(FILE *) relegated; /* shows fn backtrace and args */ void perror(const char *) relegated; /* print the last system error */ void die(void) relegated noreturn; /* print backtrace and abort() */ void meminfo(int); /* shows malloc statistics &c. */ diff --git a/libc/log/malloc_stats.c b/libc/log/malloc_stats.c index 262ad8f4..a8029901 100644 --- a/libc/log/malloc_stats.c +++ b/libc/log/malloc_stats.c @@ -25,7 +25,7 @@ STATIC_YOINK("ntoa"); void malloc_stats(void) { struct MallocStats res = dlmalloc_stats(g_dlmalloc); - (fprintf)(stderr, "max system bytes = %'10zu\n", res.maxfp); - (fprintf)(stderr, "system bytes = %'10zu\n", res.fp); - (fprintf)(stderr, "in use bytes = %'10zu\n", res.used); + (fprintf)(stderr, "max system bytes = %'10zu\r\n", res.maxfp); + (fprintf)(stderr, "system bytes = %'10zu\r\n", res.fp); + (fprintf)(stderr, "in use bytes = %'10zu\r\n", res.used); } diff --git a/libc/log/meminfo.c b/libc/log/meminfo.c index d3e0a0c9..1de0e43c 100644 --- a/libc/log/meminfo.c +++ b/libc/log/meminfo.c @@ -26,7 +26,7 @@ STATIC_YOINK("ntoa"); STATIC_YOINK("stoa"); static void onmemchunk(void *start, void *end, size_t used_bytes, void *arg) { - (dprintf)(*(int *)arg, "%p - %p : %08zx / %08lx\n", start, end, used_bytes, + (dprintf)(*(int *)arg, "%p - %p : %08zx / %08lx\r\n", start, end, used_bytes, (intptr_t)end - (intptr_t)start); } @@ -35,7 +35,7 @@ static void onmemchunk(void *start, void *end, size_t used_bytes, void *arg) { */ void meminfo(int fd) { memsummary(fd); - (dprintf)(fd, "%*s %*s %*s %*s\n", POINTER_XDIGITS, "start", + (dprintf)(fd, "%*s %*s %*s %*s\r\n", POINTER_XDIGITS, "start", POINTER_XDIGITS, "end", 8, "used", 8, "size"); malloc_inspect_all(onmemchunk, &fd); } diff --git a/libc/log/oncrash.c b/libc/log/oncrash.c index 2cc4cc75..e7216a59 100644 --- a/libc/log/oncrash.c +++ b/libc/log/oncrash.c @@ -32,9 +32,9 @@ #include "libc/runtime/internal.h" #include "libc/runtime/memtrack.h" #include "libc/runtime/runtime.h" -#include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/sysv/consts/auxv.h" +#include "libc/sysv/consts/fileno.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/sig.h" @@ -49,17 +49,17 @@ STATIC_YOINK("stoa"); struct siginfo; -aligned(1) const char kGregOrder[17] = { +const char kGregOrder[17] aligned(1) = { 13, 11, 8, 14, 12, 9, 10, 15, 16, 0, 1, 2, 3, 4, 5, 6, 7, }; -aligned(1) const char kGregNames[17][4] = { +const char kGregNames[17][4] aligned(1) = { "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15", "RDI", "RSI", "RBP", "RBX", "RDX", "RAX", "RCX", "RSP", "RIP", }; -aligned(1) const char kGodHatesFlags[12] = "CVPRAKZSTIDO"; -aligned(1) const char kCrashSigNames[8][5] = {"QUIT", "FPE", "ILL", "SEGV", +const char kGodHatesFlags[12] aligned(1) = "CVPRAKZSTIDO"; +const char kCrashSigNames[8][5] aligned(1) = {"QUIT", "FPE", "ILL", "SEGV", "TRAP", "ABRT", "BUS"}; int kCrashSigs[8]; @@ -75,60 +75,60 @@ relegated static const char *TinyStrSignal(int sig) { return "???"; } -relegated static void ShowFunctionCalls(FILE *f, ucontext_t *ctx) { +relegated static void ShowFunctionCalls(int fd, ucontext_t *ctx) { struct StackFrame *bp; struct StackFrame goodframe; - fputc('\n', f); + write(fd, "\r\n", 2); if (ctx && ctx->uc_mcontext.rip && ctx->uc_mcontext.rbp) { goodframe.next = (struct StackFrame *)ctx->uc_mcontext.rbp; goodframe.addr = ctx->uc_mcontext.rip; bp = &goodframe; - showbacktrace(f, bp); + ShowBacktrace(fd, bp); } } -relegated static void DescribeCpuFlags(FILE *f, unsigned efl) { - size_t i; +relegated static void DescribeCpuFlags(int fd, unsigned flags) { + unsigned i; + char buf[64], *p; + p = buf; for (i = 0; i < ARRAYLEN(kGodHatesFlags); ++i) { - if (efl & 1) { - fputc(' ', f); - fputc(kGodHatesFlags[i], f); - fputc('F', f); + if (flags & 1) { + *p++ = ' '; + *p++ = kGodHatesFlags[i]; + *p++ = 'F'; } - efl >>= 1; + flags >>= 1; } - (fprintf)(f, " %s%d\n", "IOPL", efl & 3); + p = stpcpy(p, " IOPL"); + *p++ = '0' + (flags & 3); + write(fd, buf, p - buf); } -relegated static void ShowGeneralRegisters(FILE *f, ucontext_t *ctx) { +relegated static void ShowGeneralRegisters(int fd, ucontext_t *ctx) { size_t i, j, k; long double st; - fputc('\n', f); + write(fd, "\r\n", 2); for (i = 0, j = 0, k = 0; i < ARRAYLEN(kGregNames); ++i) { - if (j > 0) { - fputc(' ', f); - } - (fprintf)(f, "%-3s %016lx", kGregNames[(unsigned)kGregOrder[i]], + if (j > 0) write(fd, " ", 1); + (dprintf)(fd, "%-3s %016lx", kGregNames[(unsigned)kGregOrder[i]], ctx->uc_mcontext.gregs[(unsigned)kGregOrder[i]]); if (++j == 3) { j = 0; memcpy(&st, (char *)&ctx->fpustate.st[k], sizeof(st)); - (fprintf)(f, " %s(%zu) %Lf", "ST", k, st); + (dprintf)(fd, " %s(%zu) %Lf", "ST", k, st); ++k; - fputc('\r', f); - fputc('\n', f); + write(fd, "\r\n", 2); } } - fflush(stderr); - DescribeCpuFlags(f, ctx->uc_mcontext.gregs[REG_EFL]); + DescribeCpuFlags(fd, ctx->uc_mcontext.gregs[REG_EFL]); } -relegated static void ShowSseRegisters(FILE *f, ucontext_t *ctx) { +relegated static void ShowSseRegisters(int fd, ucontext_t *ctx) { size_t i; - fputc('\n', f); + write(fd, "\r\n", 2); for (i = 0; i < 8; ++i) { - (fprintf)(f, VEIL("r", "%s%-2zu %016lx%016lx %s%-2d %016lx%016lx\n"), "XMM", - i + 0, ctx->fpustate.xmm[i + 0].u64[0], + (dprintf)(fd, VEIL("r", "%s%-2zu %016lx%016lx %s%-2d %016lx%016lx\r\n"), + "XMM", i + 0, ctx->fpustate.xmm[i + 0].u64[0], ctx->fpustate.xmm[i + 0].u64[1], "XMM", i + 8, ctx->fpustate.xmm[i + 8].u64[0], ctx->fpustate.xmm[i + 8].u64[1]); } @@ -149,23 +149,23 @@ relegated static void ShowMemoryMappings(int outfd) { } } -relegated static void ShowCrashReport(int err, FILE *f, int sig, +relegated static void ShowCrashReport(int err, int fd, int sig, ucontext_t *ctx) { struct utsname names; - (fprintf)(f, VEIL("r", "\n%serror%s: Uncaught SIG%s\n %s\n %s\n"), RED2, - RESET, TinyStrSignal(sig), getauxval(AT_EXECFN), strerror(err)); + (dprintf)(fd, VEIL("r", "\r\n%serror%s: Uncaught SIG%s\r\n %s\r\n %s\r\n"), + RED2, RESET, TinyStrSignal(sig), getauxval(AT_EXECFN), + strerror(err)); if (uname(&names) != -1) { - (fprintf)(f, VEIL("r", " %s %s %s %s\n"), names.sysname, names.nodename, + (dprintf)(fd, VEIL("r", " %s %s %s %s\r\n"), names.sysname, names.nodename, names.release, names.version); } - ShowFunctionCalls(f, ctx); + ShowFunctionCalls(fd, ctx); if (ctx) { - ShowGeneralRegisters(f, ctx); - ShowSseRegisters(f, ctx); + ShowGeneralRegisters(fd, ctx); + ShowSseRegisters(fd, ctx); } - fputc('\n', f); - fflush(f); - ShowMemoryMappings(fileno(f)); + write(fd, "\r\n", 2); + ShowMemoryMappings(fd); } relegated static void RestoreDefaultCrashSignalHandlers(void) { @@ -215,7 +215,7 @@ relegated void oncrash(int sig, struct siginfo *si, ucontext_t *ctx) { : 0); } if (gdbpid > 0 && (sig == SIGTRAP || sig == SIGQUIT)) return; - ShowCrashReport(err, stderr, sig, ctx); - quick_exit(128 + sig); + ShowCrashReport(err, STDERR_FILENO, sig, ctx); + exit(128 + sig); unreachable; } diff --git a/libc/log/perror.c b/libc/log/perror.c index 428fca89..1589f5bd 100644 --- a/libc/log/perror.c +++ b/libc/log/perror.c @@ -17,12 +17,14 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/errno.h" +#include "libc/fmt/fmt.h" #include "libc/log/internal.h" #include "libc/log/log.h" #include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" void perror(const char *message) { - fprintf(stderr, "%s%s%s: %s: %m: %s\n", RED2, "error", RESET, - program_invocation_name, message); + fprintf(stderr, "%s%s%s: %s: %s: %s\r\n", RED2, "error", RESET, + program_invocation_name, strerror(errno), message); } diff --git a/libc/log/ubsan.c b/libc/log/ubsan.c index 67773ce9..042d2945 100644 --- a/libc/log/ubsan.c +++ b/libc/log/ubsan.c @@ -56,7 +56,7 @@ void __ubsan_abort(const struct UbsanSourceLocation *loc, g_runstate |= RUNSTATE_BROKEN; if (IsDebuggerPresent(false)) DebugBreak(); startfatal(loc->file, loc->line); - fprintf(stderr, "%s\n", description); + fprintf(stderr, "%s\r\n", description); die(); unreachable; } @@ -86,7 +86,7 @@ void __ubsan_handle_type_mismatch(struct UbsanTypeMismatchInfo *type_mismatch, type_mismatch->alignment); } else { description = __ubsan_buf; - snprintf(__ubsan_buf, sizeof(__ubsan_buf), "%s\n\t%s %s %p %s %s", + snprintf(__ubsan_buf, sizeof(__ubsan_buf), "%s\r\n\t%s %s %p %s %s", "insufficient size", kind, "address", pointer, "with insufficient space for object of type", type_mismatch->type->name); diff --git a/libc/log/vflogf.c b/libc/log/vflogf.c index 5fe4a11c..dc7dbfd3 100644 --- a/libc/log/vflogf.c +++ b/libc/log/vflogf.c @@ -72,7 +72,7 @@ void vflogf_onfail(FILE *f) { fseek(f, SEEK_SET, 0); f->beg = f->end = 0; clearerr(f); - (fprintf)(f, "performed emergency log truncation: %s\n", strerror(err)); + (fprintf)(f, "performed emergency log truncation: %s\r\n", strerror(err)); } } @@ -111,10 +111,10 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f, } (vfprintf)(f, fmt, va); va_end(va); - fputc('\n', f); + fputs("\r\n", f); if (level == kLogFatal) { startfatal(file, line); - (fprintf)(stderr, "fatal error see logfile\n"); + (fprintf)(stderr, "fatal error see logfile\r\n"); die(); unreachable; } diff --git a/libc/macros.h b/libc/macros.h index e3009eb2..be651c7a 100644 --- a/libc/macros.h +++ b/libc/macros.h @@ -15,7 +15,7 @@ #define TRUE 1 #define FALSE 0 -#define ROUNDUP(X, K) (((X) + ((K)-1)) & ~((K)-1)) +#define ROUNDUP(X, K) (((X) + (K)-1) & -(K)) #define ROUNDDOWN(X, K) ((X) & -(K)) #define ABS(X) ((X) >= 0 ? (X) : -(X)) #define MIN(X, Y) ((Y) > (X) ? (X) : (Y)) diff --git a/libc/nexgen32e/bcopy.S b/libc/nexgen32e/bcopy.S index 21430ce9..a7986bb7 100644 --- a/libc/nexgen32e/bcopy.S +++ b/libc/nexgen32e/bcopy.S @@ -29,4 +29,3 @@ bcopy: jmp memmove .endfn bcopy,globl .source __FILE__ - .source __FILE__ diff --git a/libc/nexgen32e/cescapec.S b/libc/nexgen32e/cescapec.S index f3c1c3a4..2ff8ea00 100644 --- a/libc/nexgen32e/cescapec.S +++ b/libc/nexgen32e/cescapec.S @@ -25,8 +25,10 @@ / This turns stuff like (char)0xFF into \0377. The returned / string is word-encoded, e.g. '\\'|'0'<<010|'3'<<020|etc. / +/ @param dil contains byte to escape / @see libc/nexgen32e/cescapec.c cescapec: + movzbl %dil,%edi lea -7(%rdi),%ecx cmp $85,%cl ja 1f @@ -63,12 +65,11 @@ cescapec: ret .LQM: #endif -1: movzbl %dil,%eax - cmp $127,%eax +1: mov %edi,%eax + lea -0x20(%rax),%ecx + cmp $0x5E,%ecx ja 2f - ezlea kCtype,cx - testb $16,(%rcx,%rax) - jne 3f + ret 2: and $-64,%eax mov %edi,%ecx and $56,%ecx @@ -78,7 +79,7 @@ cescapec: or %ecx,%edi lea (%rdi,%rax,4),%eax add $'0<<030|'0<<020|'0<<010|'\\,%eax -3: ret + ret .endfn cescapec,globl .initro 300,_init_cescapec diff --git a/libc/nexgen32e/ctype.S b/libc/nexgen32e/ctype.S deleted file mode 100644 index 10e11ed4..00000000 --- a/libc/nexgen32e/ctype.S +++ /dev/null @@ -1,101 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -/ Returns nonzero if %dil ∈ [ \t\v\r\n]. -/ -/ @param edi is character to test (treated as uint8_t) -/ @return nonzero if character matches, otherwise 0 -isspace:push $1 - jmp ctypep - .endfn isspace,globl - -/ Returns nonzero if %dil ∈ [A-Za-z]. -/ -/ @param edi is character to test (treated as uint8_t) -/ @return nonzero if character matches, otherwise 0 -isalpha:push $2 - jmp ctypep - .endfn isalpha,globl - -/ Returns nonzero if %dil ∈ [0-9]. -/ -/ @param edi is character to test (treated as uint8_t) -/ @return nonzero if character matches, otherwise 0 -isdigit:push $4 - jmp ctypep - .endfn isdigit,globl - -/ Returns nonzero if %dil ∈ [0-9A-Za-z]. -/ -/ @param edi is character to test (treated as uint8_t) -/ @return nonzero if character matches, otherwise 0 -isalnum:push $6 - jmp ctypep - .endfn isalnum,globl - -/ Returns nonzero if %dil ∈ [0-9A-fa-f]. -/ -/ @param edi is character to test (treated as uint8_t) -/ @return nonzero if character matches, otherwise 0 -isxdigit:push $8 - jmp ctypep - .endfn isxdigit,globl - -/ Returns nonzero if %dil ∈ [ -~] a.k.a. [\x20-\x7e]. -/ -/ @param edi is character to test (treated as uint8_t) -/ @return nonzero if character matches, otherwise 0 -isprint:push $16 - jmp ctypep - .endfn isprint,globl - -/ Returns nonzero if %dil ∈ [a-z] -/ -/ @param edi is character to test (treated as uint8_t) -/ @return nonzero if character matches, otherwise 0 -islower:push $32 - jmp ctypep - .endfn islower,globl - -/ Returns nonzero if %dil ∈ [A-Z] -/ -/ @param edi is character to test (treated as uint8_t) -/ @return nonzero if character matches, otherwise 0 -isupper:push $64 - jmp ctypep - .endfn isupper,globl - -/ Returns nonzero if %dil ∈ [ \t] -/ -/ @param edi is character to test (treated as uint8_t) -/ @return nonzero if character matches, otherwise 0 -isblank:push $-128 -/ fallthrough - .endfn isblank,globl - -ctypep: pop %rsi - movzbl %dil,%edi - ezlea kCtype,cx - movzbl (%rcx,%rdi),%eax - and %esi,%eax - ret - .endfn ctypep - .source __FILE__ diff --git a/libc/nexgen32e/hextoint.S b/libc/nexgen32e/hextoint.S deleted file mode 100644 index 68937521..00000000 --- a/libc/nexgen32e/hextoint.S +++ /dev/null @@ -1,33 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -hextoint: - .leafprologue - .profilable - mov %edi,%eax - sar $6,%eax - and $1,%eax - lea (%rax,%rax,8),%eax - add %edi,%eax - and $15,%eax - .leafepilogue - .endfn hextoint,globl - .source __FILE__ diff --git a/libc/nexgen32e/iscntrl.S b/libc/nexgen32e/iscntrl.S deleted file mode 100644 index 3818bd22..00000000 --- a/libc/nexgen32e/iscntrl.S +++ /dev/null @@ -1,35 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -iscntrl:.leafprologue - .profilable - xor %eax,%eax - cmp $255,%edi - ja 1f - movslq %edi,%rdi - ezlea kCtype,cx - mov (%rcx,%rdi),%al - shr $4,%al - xor $1,%eax - and $1,%eax -1: .leafepilogue - .endfn iscntrl,globl - .source __FILE__ diff --git a/libc/nexgen32e/isgraph.S b/libc/nexgen32e/isgraph.S deleted file mode 100644 index 75f508d0..00000000 --- a/libc/nexgen32e/isgraph.S +++ /dev/null @@ -1,38 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -isgraph:.leafprologue - .profilable - xor %eax,%eax - cmp $255,%edi - ja 1f - movslq %edi,%rax - ezlea kCtype,cx - mov (%rcx,%rax),%al - shr $4,%al - mov %eax,%edx - xor %eax,%eax - cmp $32,%edi - setne %al - and %edx,%eax -1: .leafepilogue - .endfn isgraph,globl - .source __FILE__ diff --git a/libc/nexgen32e/iswalnum.S b/libc/nexgen32e/iswalnum.S deleted file mode 100644 index 1807fce1..00000000 --- a/libc/nexgen32e/iswalnum.S +++ /dev/null @@ -1,33 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -iswalnum: - .leafprologue - .profilable - movslq %edi,%rax - cmpl $127,%eax - ja 1f - ezlea kCtype,cx - movb (%rcx,%rax),%al - andl $6,%eax -1: .leafepilogue - .endfn iswalnum,globl - .source __FILE__ diff --git a/libc/nexgen32e/iswalpha.S b/libc/nexgen32e/iswalpha.S deleted file mode 100644 index da6c5e4f..00000000 --- a/libc/nexgen32e/iswalpha.S +++ /dev/null @@ -1,34 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -iswalpha: - .leafprologue - .profilable - movslq %edi,%rax - cmpl $127,%eax - ja 1f - ezlea kCtype,cx - movb (%rcx,%rax),%al - andl $2,%eax - movzbl %al,%eax -1: .leafepilogue - .endfn iswalpha,globl - .source __FILE__ diff --git a/libc/nexgen32e/iswblank.S b/libc/nexgen32e/iswblank.S deleted file mode 100644 index 8355a213..00000000 --- a/libc/nexgen32e/iswblank.S +++ /dev/null @@ -1,34 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -iswblank: - .leafprologue - .profilable - movslq %edi,%rax - cmpl $127,%eax - ja 1f - ezlea kCtype,cx - movb (%rcx,%rax),%al - andl $-128,%eax - movzbl %al,%eax -1: .leafepilogue - .endfn iswblank,globl - .source __FILE__ diff --git a/libc/nexgen32e/iswcntrl.S b/libc/nexgen32e/iswcntrl.S deleted file mode 100644 index 96c7769b..00000000 --- a/libc/nexgen32e/iswcntrl.S +++ /dev/null @@ -1,35 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -iswcntrl: - .leafprologue - .profilable - movslq %edi,%rax - cmp $127,%eax - ja 1f - ezlea kCtype,cx - mov (%rcx,%rax),%al - shr $4,%al - xor $1,%eax - and $1,%eax -1: .leafepilogue - .endfn iswcntrl,globl - .source __FILE__ diff --git a/libc/nexgen32e/iswdigit.S b/libc/nexgen32e/iswdigit.S deleted file mode 100644 index dd00e32b..00000000 --- a/libc/nexgen32e/iswdigit.S +++ /dev/null @@ -1,34 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -iswdigit: - .leafprologue - .profilable - movl %edi,%eax - cmpl $127,%edi - ja 1f - subl $48,%eax - cmpl $9,%eax - setbe %al - movzbl %al,%eax -1: .leafepilogue - .endfn iswdigit,globl - .source __FILE__ diff --git a/libc/nexgen32e/iswgraph.S b/libc/nexgen32e/iswgraph.S deleted file mode 100644 index 051af6a2..00000000 --- a/libc/nexgen32e/iswgraph.S +++ /dev/null @@ -1,38 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -iswgraph: - .leafprologue - .profilable - movl %edi,%eax - cmpl $127,%edi - ja 1f - movslq %edi,%rdx - ezlea kCtype,cx - movb (%rcx,%rdx),%dl - shrb $4,%dl - cmpl $32,%edi - setne %al - movzbl %al,%eax - andl %edx,%eax -1: .leafepilogue - .endfn iswgraph,globl - .source __FILE__ diff --git a/libc/nexgen32e/iswlower.S b/libc/nexgen32e/iswlower.S deleted file mode 100644 index 71a5f2b2..00000000 --- a/libc/nexgen32e/iswlower.S +++ /dev/null @@ -1,34 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -iswlower: - .leafprologue - .profilable - movslq %edi,%rax - cmpl $127,%eax - ja 1f - ezlea kCtype,cx - movb (%rcx,%rax),%al - andl $32,%eax - movzbl %al,%eax -1: .leafepilogue - .endfn iswlower,globl - .source __FILE__ diff --git a/libc/nexgen32e/iswprint.S b/libc/nexgen32e/iswprint.S deleted file mode 100644 index c313f276..00000000 --- a/libc/nexgen32e/iswprint.S +++ /dev/null @@ -1,35 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -iswprint: - .leafprologue - .profilable - movl $1,%eax - cmpl $127,%edi - ja 1f - movslq %edi,%rdi - ezlea kCtype,cx - movb (%rcx,%rdi),%al - shrb $4,%al - andl $1,%eax -1: .leafepilogue - .endfn iswprint,globl - .source __FILE__ diff --git a/libc/nexgen32e/iswpunct.S b/libc/nexgen32e/iswpunct.S deleted file mode 100644 index 78e8cdfd..00000000 --- a/libc/nexgen32e/iswpunct.S +++ /dev/null @@ -1,39 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -iswpunct: - .leafprologue - .profilable - mov $1,%eax - cmp $127,%edi - ja 1f - movslq %edi,%rdi - ezlea kCtype,cx - mov (%rcx,%rdi),%dl - xor %eax,%eax - test $16,%dl - je 1f - xor %eax,%eax - and $7,%dl - sete %al -1: .leafepilogue - .endfn iswpunct,globl - .source __FILE__ diff --git a/libc/nexgen32e/iswspace.S b/libc/nexgen32e/iswspace.S deleted file mode 100644 index 00c6b4d9..00000000 --- a/libc/nexgen32e/iswspace.S +++ /dev/null @@ -1,33 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -iswspace: - .leafprologue - .profilable - movslq %edi,%rax - cmpl $127,%eax - ja 1f - ezlea kCtype,cx - movb (%rcx,%rax),%al - andl $1,%eax -1: .leafepilogue - .endfn iswspace,globl - .source __FILE__ diff --git a/libc/nexgen32e/iswupper.S b/libc/nexgen32e/iswupper.S deleted file mode 100644 index 1ed6ae18..00000000 --- a/libc/nexgen32e/iswupper.S +++ /dev/null @@ -1,34 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -iswupper: - .leafprologue - .profilable - movslq %edi,%rax - cmpl $127,%eax - ja 1f - ezlea kCtype,cx - movb (%rcx,%rax),%al - andl $64,%eax - movzbl %al,%eax -1: .leafepilogue - .endfn iswupper,globl - .source __FILE__ diff --git a/libc/nexgen32e/iswxdigit.S b/libc/nexgen32e/iswxdigit.S deleted file mode 100644 index e2d4ecf1..00000000 --- a/libc/nexgen32e/iswxdigit.S +++ /dev/null @@ -1,34 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -iswxdigit: - .leafprologue - .profilable - movslq %edi,%rax - cmpl $127,%eax - ja 1f - ezlea kCtype,cx - movb (%rcx,%rax),%al - andl $8,%eax - movzbl %al,%eax -1: .leafepilogue - .endfn iswxdigit,globl - .source __FILE__ diff --git a/libc/nexgen32e/kctype.S b/libc/nexgen32e/kctype.S deleted file mode 100644 index c5d4afd3..00000000 --- a/libc/nexgen32e/kctype.S +++ /dev/null @@ -1,63 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 sw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" -#include "libc/notice.inc" - - .initbss 300,_init_kCtype -kCtype: .rept 256 - .byte 0 - .endr - .endobj kCtype,globl,hidden - .previous - - .initro 300,_init_kCtype -.LkCtype.rodata: -/ ┌─isblank -/ │┌─isupper -/ ││┌─islower -/ │││┌─isprint -/ ││││┌─isxdigit -/ │││││┌─isdigit -/ ││││││┌─isalpha -/ │││││││┌─isspace -/ ││││││││ - .byte 9, 0b00000000 # 00─08 ∅─◘ - .byte 1, 0b10000001 # 09─09 ○ - .byte 4, 0b00000001 # 0a─0d ◙─♪ - .byte 18, 0b00000000 # 0e─1f ♫─▼ - .byte 1, 0b10010001 # 20─20 - .byte 15, 0b00010000 # 21─2f !─/ - .byte 10, 0b00011100 # 30─39 0─9 - .byte 7, 0b00010000 # 3a─40 :─@ - .byte 6, 0b01011010 # 41─46 A─F - .byte 20, 0b01010010 # 47─5a G─Z - .byte 6, 0b00010000 # 5b─60 [─` - .byte 6, 0b00111010 # 61─66 a─f - .byte 20, 0b00110010 # 67─7a g─z - .byte 4, 0b00010000 # 7b─7e {─~ - .byte 129, 0b00000000 # 7f─ff ⌂─λ - .endobj .LkCtype.rodata # 32b ─ 13% - .byte 0,0 # terminatr - .previous - - .init.start 300,_init_kCtype - call rldecode - .init.end 300,_init_kCtype - .source __FILE__ diff --git a/libc/nexgen32e/tolower.S b/libc/nexgen32e/tolower.S deleted file mode 100644 index 0b5209bd..00000000 --- a/libc/nexgen32e/tolower.S +++ /dev/null @@ -1,31 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -tolower:.leafprologue - .profilable - movslq %edi,%rax - cmp $255,%eax - ja 1f - ezlea kToLower,cx - movzbl (%rcx,%rax),%eax -1: .leafepilogue - .endfn tolower,globl - .source __FILE__ diff --git a/libc/nexgen32e/toupper.S b/libc/nexgen32e/toupper.S deleted file mode 100644 index 071faef0..00000000 --- a/libc/nexgen32e/toupper.S +++ /dev/null @@ -1,35 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -toupper:.leafprologue - .profilable - xor %eax,%eax - cmp $255,%edi - ja 1f - movslq %edi,%rax - ezlea kCtype,cx - mov (%rcx,%rax),%al - and $32,%eax - movzbl %al,%eax - andn %edi,%eax,%eax # TODO(jart): FIX -1: .leafepilogue - .endfn toupper,globl - .source __FILE__ diff --git a/libc/nexgen32e/towupper.S b/libc/nexgen32e/towupper.S deleted file mode 100644 index 8aa8462f..00000000 --- a/libc/nexgen32e/towupper.S +++ /dev/null @@ -1,36 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -towupper: - .leafprologue - .profilable - movl %edi,%eax - cmpl $127,%edi - ja 1f - movslq %edi,%rdx - ezlea kCtype,cx - movb (%rcx,%rdx),%dl - andl $32,%edx - movzbl %dl,%edx - andn %edi,%edx,%eax -1: .leafepilogue - .endfn towupper,globl - .source __FILE__ diff --git a/libc/nt/KernelBase/CancelIo.s b/libc/nt/KernelBase/CancelIo.s index ca6db4cf..04652094 100644 --- a/libc/nt/KernelBase/CancelIo.s +++ b/libc/nt/KernelBase/CancelIo.s @@ -1,2 +1,15 @@ .include "o/libc/nt/codegen.inc" .imp KernelBase,__imp_CancelIo,CancelIo,101 + + .text.windows +CancelIo: + push %rbp + mov %rsp,%rbp + .profilable + mov %rdi,%rcx + sub $32,%rsp + call *__imp_CancelIo(%rip) + leave + ret + .endfn CancelIo,globl + .previous diff --git a/libc/nt/KernelBase/CancelIoEx.s b/libc/nt/KernelBase/CancelIoEx.s index b1f1dff9..d056d52c 100644 --- a/libc/nt/KernelBase/CancelIoEx.s +++ b/libc/nt/KernelBase/CancelIoEx.s @@ -1,2 +1,12 @@ .include "o/libc/nt/codegen.inc" .imp KernelBase,__imp_CancelIoEx,CancelIoEx,102 + + .text.windows +CancelIoEx: + push %rbp + mov %rsp,%rbp + .profilable + mov __imp_CancelIoEx(%rip),%rax + jmp __sysv2nt + .endfn CancelIoEx,globl + .previous diff --git a/libc/nt/KernelBase/CancelSynchronousIo.s b/libc/nt/KernelBase/CancelSynchronousIo.s index ee949b11..66c45c06 100644 --- a/libc/nt/KernelBase/CancelSynchronousIo.s +++ b/libc/nt/KernelBase/CancelSynchronousIo.s @@ -1,2 +1,15 @@ .include "o/libc/nt/codegen.inc" .imp KernelBase,__imp_CancelSynchronousIo,CancelSynchronousIo,103 + + .text.windows +CancelSynchronousIo: + push %rbp + mov %rsp,%rbp + .profilable + mov %rdi,%rcx + sub $32,%rsp + call *__imp_CancelSynchronousIo(%rip) + leave + ret + .endfn CancelSynchronousIo,globl + .previous diff --git a/libc/nt/KernelBase/CreateIoCompletionPort.s b/libc/nt/KernelBase/CreateIoCompletionPort.s index 0115b474..6f080d0f 100644 --- a/libc/nt/KernelBase/CreateIoCompletionPort.s +++ b/libc/nt/KernelBase/CreateIoCompletionPort.s @@ -1,2 +1,12 @@ .include "o/libc/nt/codegen.inc" .imp KernelBase,__imp_CreateIoCompletionPort,CreateIoCompletionPort,195 + + .text.windows +CreateIoCompletionPort: + push %rbp + mov %rsp,%rbp + .profilable + mov __imp_CreateIoCompletionPort(%rip),%rax + jmp __sysv2nt + .endfn CreateIoCompletionPort,globl + .previous diff --git a/libc/nt/KernelBase/CreateThread.s b/libc/nt/KernelBase/CreateThread.s index 96963560..c5b66df5 100644 --- a/libc/nt/KernelBase/CreateThread.s +++ b/libc/nt/KernelBase/CreateThread.s @@ -1,2 +1,12 @@ .include "o/libc/nt/codegen.inc" .imp KernelBase,__imp_CreateThread,CreateThread,224 + + .text.windows +CreateThread: + push %rbp + mov %rsp,%rbp + .profilable + mov __imp_CreateThread(%rip),%rax + jmp __sysv2nt6 + .endfn CreateThread,globl + .previous diff --git a/libc/nt/KernelBase/CreateWaitableTimerW.s b/libc/nt/KernelBase/CreateWaitableTimerW.s index dae909bb..916d6fa5 100644 --- a/libc/nt/KernelBase/CreateWaitableTimerW.s +++ b/libc/nt/KernelBase/CreateWaitableTimerW.s @@ -1,2 +1,12 @@ .include "o/libc/nt/codegen.inc" .imp KernelBase,__imp_CreateWaitableTimerW,CreateWaitableTimerW,234 + + .text.windows +CreateWaitableTimer: + push %rbp + mov %rsp,%rbp + .profilable + mov __imp_CreateWaitableTimerW(%rip),%rax + jmp __sysv2nt + .endfn CreateWaitableTimer,globl + .previous diff --git a/libc/nt/KernelBase/GetQueuedCompletionStatus.s b/libc/nt/KernelBase/GetQueuedCompletionStatus.s index 026f4ec6..a8acb1bd 100644 --- a/libc/nt/KernelBase/GetQueuedCompletionStatus.s +++ b/libc/nt/KernelBase/GetQueuedCompletionStatus.s @@ -1,2 +1,12 @@ .include "o/libc/nt/codegen.inc" .imp KernelBase,__imp_GetQueuedCompletionStatus,GetQueuedCompletionStatus,695 + + .text.windows +GetQueuedCompletionStatus: + push %rbp + mov %rsp,%rbp + .profilable + mov __imp_GetQueuedCompletionStatus(%rip),%rax + jmp __sysv2nt6 + .endfn GetQueuedCompletionStatus,globl + .previous diff --git a/libc/nt/KernelBase/GetQueuedCompletionStatusEx.s b/libc/nt/KernelBase/GetQueuedCompletionStatusEx.s index c310de42..7fa2e68d 100644 --- a/libc/nt/KernelBase/GetQueuedCompletionStatusEx.s +++ b/libc/nt/KernelBase/GetQueuedCompletionStatusEx.s @@ -1,2 +1,12 @@ .include "o/libc/nt/codegen.inc" .imp KernelBase,__imp_GetQueuedCompletionStatusEx,GetQueuedCompletionStatusEx,696 + + .text.windows +GetQueuedCompletionStatusEx: + push %rbp + mov %rsp,%rbp + .profilable + mov __imp_GetQueuedCompletionStatusEx(%rip),%rax + jmp __sysv2nt6 + .endfn GetQueuedCompletionStatusEx,globl + .previous diff --git a/libc/nt/KernelBase/PostQueuedCompletionStatus.s b/libc/nt/KernelBase/PostQueuedCompletionStatus.s index 6584c832..f4b8e0e4 100644 --- a/libc/nt/KernelBase/PostQueuedCompletionStatus.s +++ b/libc/nt/KernelBase/PostQueuedCompletionStatus.s @@ -1,2 +1,12 @@ .include "o/libc/nt/codegen.inc" .imp KernelBase,__imp_PostQueuedCompletionStatus,PostQueuedCompletionStatus,1221 + + .text.windows +PostQueuedCompletionStatus: + push %rbp + mov %rsp,%rbp + .profilable + mov __imp_PostQueuedCompletionStatus(%rip),%rax + jmp __sysv2nt + .endfn PostQueuedCompletionStatus,globl + .previous diff --git a/libc/nt/KernelBase/ReadFileEx.s b/libc/nt/KernelBase/ReadFileEx.s index ae5ffddc..f3f7db44 100644 --- a/libc/nt/KernelBase/ReadFileEx.s +++ b/libc/nt/KernelBase/ReadFileEx.s @@ -1,2 +1,12 @@ .include "o/libc/nt/codegen.inc" .imp KernelBase,__imp_ReadFileEx,ReadFileEx,1312 + + .text.windows +ReadFileEx: + push %rbp + mov %rsp,%rbp + .profilable + mov __imp_ReadFileEx(%rip),%rax + jmp __sysv2nt6 + .endfn ReadFileEx,globl + .previous diff --git a/libc/nt/KernelBase/SetWaitableTimer.s b/libc/nt/KernelBase/SetWaitableTimer.s index b0677465..b1e75275 100644 --- a/libc/nt/KernelBase/SetWaitableTimer.s +++ b/libc/nt/KernelBase/SetWaitableTimer.s @@ -1,2 +1,12 @@ .include "o/libc/nt/codegen.inc" .imp KernelBase,__imp_SetWaitableTimer,SetWaitableTimer,1583 + + .text.windows +SetWaitableTimer: + push %rbp + mov %rsp,%rbp + .profilable + mov __imp_SetWaitableTimer(%rip),%rax + jmp __sysv2nt6 + .endfn SetWaitableTimer,globl + .previous diff --git a/libc/nt/KernelBase/WriteFileEx.s b/libc/nt/KernelBase/WriteFileEx.s index 448d1b81..065387c9 100644 --- a/libc/nt/KernelBase/WriteFileEx.s +++ b/libc/nt/KernelBase/WriteFileEx.s @@ -1,2 +1,12 @@ .include "o/libc/nt/codegen.inc" .imp KernelBase,__imp_WriteFileEx,WriteFileEx,1824 + + .text.windows +WriteFileEx: + push %rbp + mov %rsp,%rbp + .profilable + mov __imp_WriteFileEx(%rip),%rax + jmp __sysv2nt6 + .endfn WriteFileEx,globl + .previous diff --git a/libc/nt/files.h b/libc/nt/files.h index 7b473963..b096ec9a 100644 --- a/libc/nt/files.h +++ b/libc/nt/files.h @@ -121,10 +121,10 @@ bool32 CreateDirectory(const char16_t *lpPathName, struct NtSecurityAttributes *lpSecurityAttributes); bool32 RemoveDirectory(const char16_t *lpPathName); -int32_t DuplicateHandle(int64_t hSourceProcessHandle, int64_t hSourceHandle, - int64_t hTargetProcessHandle, int64_t *lpTargetHandle, - uint32_t dwDesiredAccess, bool32 bInheritHandle, - uint32_t dwOptions); +bool32 DuplicateHandle(int64_t hSourceProcessHandle, int64_t hSourceHandle, + int64_t hTargetProcessHandle, int64_t *lpTargetHandle, + uint32_t dwDesiredAccess, bool32 bInheritHandle, + uint32_t dwOptions); bool32 GetHandleInformation(int64_t hObject, uint32_t *out_lpdwFlags); bool32 SetHandleInformation(int64_t hObject, uint32_t dwMask, uint32_t dwFlags); @@ -244,6 +244,7 @@ bool32 ReadFileScatter( uint32_t nNumberOfBytesToReadThatsMultipleOfFileVolumeSectorSize, uint32_t *lpReserved, struct NtOverlapped *inout_lpOverlapped) paramsnonnull(); + bool32 WriteFileGather(int64_t hFileOpenedWithOverlappedAndNoBuffering, const union NtFileSegmentElement aSegmentArray[], uint32_t nNumberOfBytesToWrite, uint32_t *lpReserved, diff --git a/libc/nt/iocp.h b/libc/nt/iocp.h new file mode 100644 index 00000000..18bb0b03 --- /dev/null +++ b/libc/nt/iocp.h @@ -0,0 +1,79 @@ +#ifndef COSMOPOLITAN_LIBC_NT_IOCP_H_ +#define COSMOPOLITAN_LIBC_NT_IOCP_H_ +#if 0 +/* ░░░░ + ▒▒▒░░░▒▒▒▒▒▒▒▓▓▓░ + ▒▒▒▒░░░▒▒▒▒▒▒▓▓▓▓▓▓░ + ▒▒▒▒░░░▒▒▒▒▒▒▒▓▓▓▓▓▓ ▒▓░ + ▒▒▒░░░░▒▒▒▒▒▒▓▓▓▓▓▓ ▓▓▓▓▓▓▒ ▒▒▒▓▓█ + ▒▒▒▒░░░▒▒▒▒▒▒▒▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓▓ + ░▒▒▒░░░░▒▒▒▒▒▒▓▓▓▓▓▓ █▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓█ + ▒▒▒▒░░░▒▒▒▒▒▒▒▓▓▓▓▓░ ▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓▓ + ▒▒▒▒░░░▒▒▒▒▒▒▒▓▓▓▓▓▓ ▒▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓▒ + ▒▒▒▒▓▓ ▓▒▒▓▓▓▓ ▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓█ + ▒▓ ▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓ + ░░░░░░░░░░░▒▒▒▒ ▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓█ + ▒▒░░░░░░░░░░▒▒▒▒▒▓▓▓ ▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓▓ + ░▒░░░░░░░░░░░▒▒▒▒▒▓▓ ▓░ ░▓███▓ + ▒▒░░░░░░░░░░▒▒▒▒▒▓▓░ ▒▓▓▓▒▒▒ ░▒▒▒▓ ████████████ + ▒▒░░░░░░░░░░░▒▒▒▒▒▓▓ ▒▓▓▓▓▒▒▒▒▒▒▒▒░░░▒▒▒▒▒░ ░███ + ▒░░░░░░░░░░░▒▒▒▒▒▓▓ ▓▓▓▓▒▒▒▒▒▒▒▒░░░░▒▒▒▒▓ ███ + ▒▒░░░░░░░░░░▒▒▒▒▒▒▓▓ ▒▓▓▓▒▒▒▒▒▒▒▒░░░░▒▒▒▒▒ ▓██ + ▒░░░░░░░░░░░▒▒▒▒▒▓▓ ▓▓▓▓▒▒▒▒▒▒▒▒░░░▒▒▒▒▒▓ ▓██ + ▒▒░░░▒▒▒░░░▒▒░▒▒▒▓▓▒ ▒▓▓▓▒▒▒▒▒▒▒▒░░░░▒▒▒▒▒ ███ + ░▒▓ ░▓▓▓▓▒▒▒▒▒▒▒▒░░░░▒▒▒▒▓ ▓██ +╔────────────────────────────────────────────────────────────────▀▀▀─────────│─╗ +│ cosmopolitan § new technology » i/o completion ports ─╬─│┼ +╚────────────────────────────────────────────────────────────────────────────│*/ +#endif +#include "libc/nt/struct/overlapped.h" +#include "libc/nt/struct/overlappedentry.h" + +#define kNtFileSkipCompletionPortOnSuccess 1 +#define kNtFileSkipSetEventOnHandle 2 + +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +typedef void (*NtOverlappedCompletionRoutine)( + uint32_t dwErrorCode, uint32_t dwNumberOfBytesTransfered, + struct NtOverlapped *inout_lpOverlapped); + +int64_t CreateIoCompletionPort(int64_t FileHandleOrNeg1, + int64_t opt_ExistingCompletionPortOrZero, + void *StatePointer, + uint32_t NumberOfConcurrentThreads); + +bool32 GetQueuedCompletionStatus(int64_t CompletionPort, + uint32_t *lpNumberOfBytesTransferred, + void *StatePointerPointer, + struct NtOverlapped **lpOverlapped, + uint32_t dwMilliseconds); + +bool32 GetQueuedCompletionStatusEx( + int64_t CompletionPort, + struct NtOverlappedEntry *out_lpCompletionPortEntries, uint32_t ulCount, + uint32_t *out_ulNumEntriesRemoved, uint32_t dwMilliseconds, + bool32 fAlertable); + +bool32 PostQueuedCompletionStatus(int64_t CompletionPort, + uint32_t dwNumberOfBytesTransferred, + uint32_t *dwCompletionKey, + struct NtOverlapped *opt_lpOverlapped); + +bool32 SetFileCompletionNotificationModes(int64_t FileHandle, + unsigned char Flags); + +bool32 ReadFileEx(int64_t hFile, void *lpBuffer, uint32_t nNumberOfBytesToRead, + uint32_t *lpNumberOfBytesRead, + struct NtOverlapped *opt_lpOverlapped, + NtOverlappedCompletionRoutine lpCompletionRoutine); + +bool32 WriteFileEx(int64_t hFile, const void *lpBuffer, + uint32_t nNumberOfBytesToWrite, + struct NtOverlapped *lpOverlapped, + NtOverlappedCompletionRoutine lpCompletionRoutine); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_NT_IOCP_H_ */ diff --git a/libc/nt/ipc.h b/libc/nt/ipc.h index 52158e2d..ad015e1f 100644 --- a/libc/nt/ipc.h +++ b/libc/nt/ipc.h @@ -28,17 +28,17 @@ #endif /* CreateNamedPipe:dwOpenMode */ -#define kNtPipeAccessInbound 0x00000001 +#define kNtPipeAccessInbound 0x00000001 #define kNtPipeAccessOutbound 0x00000002 -#define kNtPipeAccessDuplex 0x00000003 +#define kNtPipeAccessDuplex 0x00000003 /* CreateNamedPipe::dwPipeMode */ -#define kNtPipeWait 0x00000000 -#define kNtPipeNowait 0x00000001 -#define kNtPipeReadmodeByte 0x00000000 -#define kNtPipeReadmodeMessage 0x00000002 -#define kNtPipeTypeByte 0x00000000 -#define kNtPipeTypeMessage 0x00000004 +#define kNtPipeWait 0x00000000 +#define kNtPipeNowait 0x00000001 +#define kNtPipeReadmodeByte 0x00000000 +#define kNtPipeReadmodeMessage 0x00000002 +#define kNtPipeTypeByte 0x00000000 +#define kNtPipeTypeMessage 0x00000004 #define kNtPipeAcceptRemoteClients 0x00000000 #define kNtPipeRejectRemoteClients 0x00000008 @@ -55,28 +55,34 @@ COSMOPOLITAN_C_START_ struct NtOverlapped; struct NtSecurityAttributes; -int CreatePipe(int64_t *out_hReadPipe, int64_t *out_hWritePipe, - const struct NtSecurityAttributes *opt_lpPipeAttributes, - uint32_t nSize) paramsnonnull((1, 2)); -void *CreateNamedPipe( +bool32 CreatePipe(int64_t *out_hReadPipe, int64_t *out_hWritePipe, + const struct NtSecurityAttributes *opt_lpPipeAttributes, + uint32_t nSize) paramsnonnull((1, 2)); + +int64_t CreateNamedPipe( const char16_t *lpName, uint32_t dwOpenMode, uint32_t dwPipeMode, uint32_t nMaxInstances, uint32_t nOutBufferSize, uint32_t nInBufferSize, uint32_t nDefaultTimeOut, const struct NtSecurityAttributes *opt_lpSecurityAttributes) paramsnonnull((1)); -bool32 ConnectNamedPipe(int64_t *hNamedPipe, struct NtOverlapped *lpOverlapped); + bool32 CallNamedPipe(const char16_t *lpNamedPipeName, void *lpInBuffer, uint32_t nInBufferSize, void *lpOutBuffer, uint32_t nOutBufferSize, uint32_t *lpBytesRead, uint32_t nTimeOut); + +bool32 ConnectNamedPipe(int64_t *hNamedPipe, struct NtOverlapped *lpOverlapped); bool32 WaitNamedPipe(const char16_t *lpNamedPipeName, uint32_t nTimeOut); bool32 DisconnectNamedPipe(int64_t *hNamedPipe); + bool32 SetNamedPipeHandleState(int64_t *hNamedPipe, uint32_t *lpMode, uint32_t *lpMaxCollectionCount, uint32_t *lpCollectDataTimeout); + bool32 PeekNamedPipe(int64_t *hNamedPipe, void *lpBuffer, uint32_t nBufferSize, uint32_t *lpBytesRead, uint32_t *lpTotalBytesAvail, uint32_t *lpBytesLeftThisMessage); + bool32 TransactNamedPipe(int64_t *hNamedPipe, void *lpInBuffer, uint32_t nInBufferSize, void *lpOutBuffer, uint32_t nOutBufferSize, uint32_t *lpBytesRead, diff --git a/libc/nt/kernel32/SetFileCompletionNotificationModes.s b/libc/nt/kernel32/SetFileCompletionNotificationModes.s index ed3a3bb4..aa890d72 100644 --- a/libc/nt/kernel32/SetFileCompletionNotificationModes.s +++ b/libc/nt/kernel32/SetFileCompletionNotificationModes.s @@ -1,2 +1,12 @@ .include "o/libc/nt/codegen.inc" .imp kernel32,__imp_SetFileCompletionNotificationModes,SetFileCompletionNotificationModes,1318 + + .text.windows +SetFileCompletionNotificationModes: + push %rbp + mov %rsp,%rbp + .profilable + mov __imp_SetFileCompletionNotificationModes(%rip),%rax + jmp __sysv2nt + .endfn SetFileCompletionNotificationModes,globl + .previous diff --git a/libc/nt/master.sh b/libc/nt/master.sh index 8040d723..f1dd08e4 100755 --- a/libc/nt/master.sh +++ b/libc/nt/master.sh @@ -341,11 +341,11 @@ imp 'CallWindowProc' CallWindowProcW user32 1537 imp 'CallbackMayRunLong' CallbackMayRunLong KernelBase 100 imp 'CancelDC' CancelDC gdi32 1038 imp 'CancelDeviceWakeupRequest' CancelDeviceWakeupRequest kernel32 113 -imp 'CancelIo' CancelIo KernelBase 101 -imp 'CancelIoEx' CancelIoEx KernelBase 102 +imp 'CancelIo' CancelIo KernelBase 101 1 +imp 'CancelIoEx' CancelIoEx KernelBase 102 2 imp 'CancelOverlappedAccess' CancelOverlappedAccess advapi32 1091 imp 'CancelShutdown' CancelShutdown user32 1538 -imp 'CancelSynchronousIo' CancelSynchronousIo KernelBase 103 +imp 'CancelSynchronousIo' CancelSynchronousIo KernelBase 103 1 imp 'CancelTimerQueueTimer' CancelTimerQueueTimer kernel32 118 imp 'CancelWaitableTimer' CancelWaitableTimer KernelBase 105 imp 'CascadeChildWindows' CascadeChildWindows user32 1539 @@ -625,7 +625,7 @@ imp 'CreateIcon' CreateIcon user32 1615 imp 'CreateIconFromResource' CreateIconFromResource user32 1616 imp 'CreateIconFromResourceEx' CreateIconFromResourceEx user32 1617 imp 'CreateIconIndirect' CreateIconIndirect user32 1618 -imp 'CreateIoCompletionPort' CreateIoCompletionPort KernelBase 195 +imp 'CreateIoCompletionPort' CreateIoCompletionPort KernelBase 195 4 imp 'CreateJobObjectA' CreateJobObjectA kernel32 209 imp 'CreateJobObject' CreateJobObjectW kernel32 210 imp 'CreateJobSet' CreateJobSet kernel32 211 @@ -702,7 +702,7 @@ imp 'CreateSymbolicLinkTransactedA' CreateSymbolicLinkTransactedA kernel32 imp 'CreateSymbolicLinkTransacted' CreateSymbolicLinkTransactedW kernel32 238 imp 'CreateSystemThreads' CreateSystemThreads user32 1623 imp 'CreateTapePartition' CreateTapePartition kernel32 240 -imp 'CreateThread' CreateThread KernelBase 224 +imp 'CreateThread' CreateThread KernelBase 224 6 imp 'CreateThreadpool' CreateThreadpool KernelBase 225 imp 'CreateThreadpoolCleanupGroup' CreateThreadpoolCleanupGroup KernelBase 226 imp 'CreateThreadpoolIo' CreateThreadpoolIo KernelBase 227 @@ -717,7 +717,7 @@ imp 'CreateUmsThreadContext' CreateUmsThreadContext kernel32 252 imp 'CreateWaitableTimerA' CreateWaitableTimerA kernel32 253 imp 'CreateWaitableTimerExA' CreateWaitableTimerExA kernel32 254 imp 'CreateWaitableTimerEx' CreateWaitableTimerExW KernelBase 233 -imp 'CreateWaitableTimer' CreateWaitableTimerW KernelBase 234 +imp 'CreateWaitableTimer' CreateWaitableTimerW KernelBase 234 3 imp 'CreateWellKnownSid' CreateWellKnownSid KernelBase 235 imp 'CreateWindowEx' CreateWindowExW user32 1625 12 imp 'CreateWindowExA' CreateWindowExA user32 1624 12 @@ -2516,8 +2516,8 @@ imp 'GetPtrCalDataArray' GetPtrCalDataArray KernelBase 692 imp 'GetPublisherCacheFolder' GetPublisherCacheFolder KernelBase 693 imp 'GetPublisherRootFolder' GetPublisherRootFolder KernelBase 694 imp 'GetQueueStatus' GetQueueStatus user32 1936 -imp 'GetQueuedCompletionStatus' GetQueuedCompletionStatus KernelBase 695 -imp 'GetQueuedCompletionStatusEx' GetQueuedCompletionStatusEx KernelBase 696 +imp 'GetQueuedCompletionStatus' GetQueuedCompletionStatus KernelBase 695 5 +imp 'GetQueuedCompletionStatusEx' GetQueuedCompletionStatusEx KernelBase 696 6 imp 'GetROP2' GetROP2 gdi32 1695 imp 'GetRandomRgn' GetRandomRgn gdi32 1696 imp 'GetRasterizerCaps' GetRasterizerCaps gdi32 1697 @@ -4320,7 +4320,7 @@ imp 'PolylineTo' PolylineTo gdi32 1791 imp 'PoolPerAppKeyStateInternal' PoolPerAppKeyStateInternal KernelBase 1220 imp 'PostMessageA' PostMessageA user32 2204 imp 'PostMessage' PostMessageW user32 2205 -imp 'PostQueuedCompletionStatus' PostQueuedCompletionStatus KernelBase 1221 +imp 'PostQueuedCompletionStatus' PostQueuedCompletionStatus KernelBase 1221 4 imp 'PostQuitMessage' PostQuitMessage user32 2206 1 imp 'PostThreadMessageA' PostThreadMessageA user32 2207 imp 'PostThreadMessage' PostThreadMessageW user32 2208 @@ -4517,7 +4517,7 @@ imp 'ReadEncryptedFileRaw' ReadEncryptedFileRaw advapi32 1601 imp 'ReadEventLogA' ReadEventLogA advapi32 1602 imp 'ReadEventLog' ReadEventLogW advapi32 1603 imp 'ReadFile' ReadFile KernelBase 1311 5 -imp 'ReadFileEx' ReadFileEx KernelBase 1312 +imp 'ReadFileEx' ReadFileEx KernelBase 1312 5 imp 'ReadFileScatter' ReadFileScatter KernelBase 1313 5 imp 'ReadProcessMemory' ReadProcessMemory KernelBase 1314 imp 'ReadStateAtomValue' ReadStateAtomValue KernelBase 1315 @@ -6225,7 +6225,7 @@ imp 'SetFileAttributesA' SetFileAttributesA KernelBase 1507 2 imp 'SetFileAttributesTransactedA' SetFileAttributesTransactedA kernel32 1314 imp 'SetFileAttributesTransacted' SetFileAttributesTransactedW kernel32 1315 imp 'SetFileBandwidthReservation' SetFileBandwidthReservation kernel32 1317 -imp 'SetFileCompletionNotificationModes' SetFileCompletionNotificationModes kernel32 1318 +imp 'SetFileCompletionNotificationModes' SetFileCompletionNotificationModes kernel32 1318 2 imp 'SetFileInformationByHandle' SetFileInformationByHandle KernelBase 1509 imp 'SetFileIoOverlappedRange' SetFileIoOverlappedRange KernelBase 1510 imp 'SetFilePointer' SetFilePointer KernelBase 1511 4 @@ -6425,7 +6425,7 @@ imp 'SetVolumeLabel' SetVolumeLabelW kernel32 1401 imp 'SetVolumeMountPointA' SetVolumeMountPointA kernel32 1402 imp 'SetVolumeMountPoint' SetVolumeMountPointW kernel32 1403 imp 'SetVolumeMountPointWStub' SetVolumeMountPointWStub kernel32 1404 -imp 'SetWaitableTimer' SetWaitableTimer KernelBase 1583 +imp 'SetWaitableTimer' SetWaitableTimer KernelBase 1583 6 imp 'SetWaitableTimerEx' SetWaitableTimerEx KernelBase 1584 imp 'SetWinEventHook' SetWinEventHook user32 2388 imp 'SetWinMetaFileBits' SetWinMetaFileBits gdi32 1917 @@ -7188,7 +7188,7 @@ imp 'WriteConsoleOutputCharacter' WriteConsoleOutputCharacterW KernelBase imp 'WriteConsoleOutputCharacterA' WriteConsoleOutputCharacterA KernelBase 1819 5 imp 'WriteEncryptedFileRaw' WriteEncryptedFileRaw advapi32 1857 imp 'WriteFile' WriteFile KernelBase 1823 5 -imp 'WriteFileEx' WriteFileEx KernelBase 1824 +imp 'WriteFileEx' WriteFileEx KernelBase 1824 5 imp 'WriteFileGather' WriteFileGather KernelBase 1825 5 imp 'WritePrivateProfileSectionA' WritePrivateProfileSectionA kernel32 1565 imp 'WritePrivateProfileSection' WritePrivateProfileSectionW kernel32 1566 diff --git a/libc/nt/nt/time.h b/libc/nt/nt/time.h index 2d5d2e42..6274d5c3 100644 --- a/libc/nt/nt/time.h +++ b/libc/nt/nt/time.h @@ -35,7 +35,7 @@ COSMOPOLITAN_C_START_ each release.” ──Quoth MSDN */ #endif -NtStatus NtDelayExecution(bool32 alertable, int64_t *hectonanoseconds); +NtStatus NtDelayExecution(bool32 alertable, int64_t *AbsCobolOrNegRelHectoNano); #if ShouldUseMsabiAttribute() #include "libc/nt/nt/thunk/time.inc" diff --git a/libc/nt/struct/guid.h b/libc/nt/struct/guid.h new file mode 100644 index 00000000..ce4cbe10 --- /dev/null +++ b/libc/nt/struct/guid.h @@ -0,0 +1,15 @@ +#ifndef COSMOPOLITAN_LIBC_NT_STRUCT_GUID_H_ +#define COSMOPOLITAN_LIBC_NT_STRUCT_GUID_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +struct NtGuid { + uint32_t Data1; + uint16_t Data2; + uint16_t Data3; + uint8_t Data4[8]; +}; + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_NT_STRUCT_GUID_H_ */ diff --git a/libc/nt/struct/overlappedentry.h b/libc/nt/struct/overlappedentry.h new file mode 100644 index 00000000..5fd5385e --- /dev/null +++ b/libc/nt/struct/overlappedentry.h @@ -0,0 +1,16 @@ +#ifndef COSMOPOLITAN_LIBC_NT_STRUCT_OVERLAPPEDENTRY_H_ +#define COSMOPOLITAN_LIBC_NT_STRUCT_OVERLAPPEDENTRY_H_ +#include "libc/nt/struct/overlapped.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +struct NtOverlappedEntry { + uint32_t *lpCompletionKey; + struct NtOverlapped *lpOverlapped; + uint32_t *Internal; + uint32_t dwNumberOfBytesTransferred; +}; + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_NT_STRUCT_OVERLAPPEDENTRY_H_ */ diff --git a/libc/nt/synchronization.h b/libc/nt/synchronization.h index 75584463..ceb32bfe 100644 --- a/libc/nt/synchronization.h +++ b/libc/nt/synchronization.h @@ -1,6 +1,10 @@ #ifndef COSMOPOLITAN_LIBC_NT_SYNCHRONIZATION_H_ #define COSMOPOLITAN_LIBC_NT_SYNCHRONIZATION_H_ +#include "libc/nt/struct/criticalsection.h" +#include "libc/nt/struct/filetime.h" #include "libc/nt/struct/linkedlist.h" +#include "libc/nt/struct/securityattributes.h" +#include "libc/nt/struct/systemtime.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ #if 0 @@ -30,34 +34,19 @@ COSMOPOLITAN_C_START_ ╚────────────────────────────────────────────────────────────────────────────│*/ #endif -struct NtCriticalSection; -struct NtFileTime; -struct NtSystemTime; +typedef void (*NtTimerapcroutine)(void *lpArgToCompletionRoutine, + uint32_t dwTimerLowValue, + uint32_t dwTimerHighValue); void Sleep(uint32_t dwMilliseconds); uint32_t SleepEx(uint32_t dwMilliseconds, bool32 bAlertable); + void GetSystemTime(struct NtSystemTime *lpSystemTime); bool32 SystemTimeToFileTime(const struct NtSystemTime *lpSystemTime, struct NtFileTime *lpFileTime); -void GetSystemTimeAsFileTime( - struct NtFileTime *out_lpSystemTimeAsFileTime); /* win8+ */ -void GetSystemTimePreciseAsFileTime( - struct NtFileTime *out_lpSystemTimeAsFileTime); /* win8+ */ -void InitializeCriticalSection(struct NtCriticalSection *lpCriticalSection); -void EnterCriticalSection(struct NtCriticalSection *lpCriticalSection); -void LeaveCriticalSection(struct NtCriticalSection *lpCriticalSection); -int32_t InitializeCriticalSectionAndSpinCount( - struct NtCriticalSection *lpCriticalSection, uint32_t dwSpinCount); -uint32_t SetCriticalSectionSpinCount( - struct NtCriticalSection *lpCriticalSection, uint32_t dwSpinCount); -int32_t TryEnterCriticalSection(struct NtCriticalSection *lpCriticalSection); -void DeleteCriticalSection(struct NtCriticalSection *lpCriticalSection); -int32_t SetEvent(int64_t hEvent); -int32_t ResetEvent(int64_t hEvent); -int32_t PulseEvent(int64_t hEvent); -int32_t ReleaseSemaphore(int64_t hSemaphore, int32_t lReleaseCount, - int *lpPreviousCount); -int32_t ReleaseMutex(int64_t hMutex); +void GetSystemTimeAsFileTime(struct NtFileTime *); // win8+ +void GetSystemTimePreciseAsFileTime(struct NtFileTime *); // win8+ + uint32_t WaitForSingleObject(int64_t hHandle, uint32_t dwMilliseconds); uint32_t WaitForMultipleObjects(uint32_t nCount, const void **lpHandles, bool32 bWaitAll, uint32_t dwMilliseconds); @@ -67,6 +56,30 @@ uint32_t WaitForMultipleObjectsEx(unsigned int nCount, const void **lpHandles, bool32 bWaitAll, uint32_t dwMilliseconds, bool32 bAlertable); +int64_t CreateWaitableTimer(struct NtSecurityAttributes *lpTimerAttributes, + bool32 bManualReset, const char16_t *lpTimerName); +bool32 SetWaitableTimer(int64_t hTimer, const int64_t *lpDueTimeAsFtOrNegRela, + int32_t opt_lPeriodMs, NtTimerapcroutine opt_callback, + void *lpArgToCallback, bool32 fUnsleepSystem); + +int32_t SetEvent(int64_t hEvent); +int32_t ResetEvent(int64_t hEvent); +int32_t PulseEvent(int64_t hEvent); + +int32_t ReleaseMutex(int64_t hMutex); +int32_t ReleaseSemaphore(int64_t hSemaphore, int32_t lReleaseCount, + int *lpPreviousCount); + +void InitializeCriticalSection(struct NtCriticalSection *lpCriticalSection); +void EnterCriticalSection(struct NtCriticalSection *lpCriticalSection); +void LeaveCriticalSection(struct NtCriticalSection *lpCriticalSection); +int32_t TryEnterCriticalSection(struct NtCriticalSection *lpCriticalSection); +void DeleteCriticalSection(struct NtCriticalSection *lpCriticalSection); +int32_t InitializeCriticalSectionAndSpinCount( + struct NtCriticalSection *lpCriticalSection, uint32_t dwSpinCount); +uint32_t SetCriticalSectionSpinCount( + struct NtCriticalSection *lpCriticalSection, uint32_t dwSpinCount); + COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_NT_SYNCHRONIZATION_H_ */ diff --git a/libc/nt/thread.h b/libc/nt/thread.h index 560a90b7..fb4378ed 100644 --- a/libc/nt/thread.h +++ b/libc/nt/thread.h @@ -1,6 +1,8 @@ #ifndef COSMOPOLITAN_LIBC_NT_THREADS_H_ #define COSMOPOLITAN_LIBC_NT_THREADS_H_ #include "libc/nt/enum/threadaccess.h" +#include "libc/nt/struct/overlapped.h" +#include "libc/nt/struct/securityattributes.h" #include "libc/nt/thunk/msabi.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ @@ -31,6 +33,13 @@ COSMOPOLITAN_C_START_ ╚────────────────────────────────────────────────────────────────────────────│*/ #endif +typedef uint32_t (*NtThreadStartRoutine)(void *lpParameter); + +int64_t CreateThread(struct NtSecurityAttributes *lpThreadAttributes, + size_t dwStackSize, NtThreadStartRoutine lpStartAddress, + void *lpParameter, uint32_t dwCreationFlags, + uint32_t *opt_lpThreadId); + void ExitThread(uint32_t dwExitCode) noreturn; int64_t GetCurrentThread(void); uint32_t GetCurrentThreadId(void); @@ -47,6 +56,10 @@ bool32 SetThreadPriorityBoost(int64_t hThread, bool32 bDisablePriorityBoost); bool32 GetThreadPriorityBoost(int64_t hThread, bool32 *pDisablePriorityBoost); bool32 GetThreadIOPendingFlag(int64_t hThread, bool32 *lpIOIsPending); +bool32 CancelSynchronousIo(int64_t hThread); +bool32 CancelIo(int64_t hFile); +bool32 CancelIoEx(int64_t hFile, struct NtOverlapped opt_lpOverlapped); + #if ShouldUseMsabiAttribute() #include "libc/nt/thunk/thread.inc" #endif /* ShouldUseMsabiAttribute() */ diff --git a/libc/nt/thunk/msabi.h b/libc/nt/thunk/msabi.h index 69edc9ee..89793d50 100644 --- a/libc/nt/thunk/msabi.h +++ b/libc/nt/thunk/msabi.h @@ -9,8 +9,9 @@ * generate code that calls MS ABI functions directly, without needing * to jump through the assembly thunks. */ -#if __GNUC__ * 100 + __GNUC_MINOR__ >= 408 || \ - (__has_attribute(__ms_abi__) || defined(__llvm__)) +#if !defined(__STRICT_ANSI__) && \ + (__GNUC__ * 100 + __GNUC_MINOR__ >= 408 || \ + (__has_attribute(__ms_abi__) || defined(__llvm__))) #define __msabi __attribute__((__ms_abi__)) #endif @@ -18,7 +19,7 @@ * Returns true if header should provide MS-ABI overrides. */ #ifndef ShouldUseMsabiAttribute -#if defined(__msabi) && defined(NDEBUG) && !defined(__STRICT_ANSI__) +#if defined(__msabi) && defined(NDEBUG) && !defined(__PG__) #define ShouldUseMsabiAttribute() 1 #else #define ShouldUseMsabiAttribute() 0 diff --git a/libc/nt/winsock.h b/libc/nt/winsock.h index 23385b7e..1c749ff4 100644 --- a/libc/nt/winsock.h +++ b/libc/nt/winsock.h @@ -1,10 +1,9 @@ #ifndef COSMOPOLITAN_LIBC_NT_WINSOCK_H_ #define COSMOPOLITAN_LIBC_NT_WINSOCK_H_ +#include "libc/nt/struct/guid.h" #include "libc/nt/struct/overlapped.h" #include "libc/nt/struct/pollfd.h" #include "libc/sock/sock.h" -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ #if 0 /* ░▓█████████████████████████████████████████████▓▒ ░█▓░░░░░░░░░▓██▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓██▓▒░ @@ -46,9 +45,8 @@ COSMOPOLITAN_C_START_ ╚────────────────────────────────────────────────────────────────────────────│*/ #endif -#define kNtSioSocketCloseNotify 0x9800000Du -#define kNtSioUdpConnreset 0x9800000Cu -#define kNtSioUdpNetreset 0x9800000F +#define kNtWsaFlagOverlapped 0x01 +#define kNtWsaFlagNoHandleInherit 0x80 #define kNtTfDisconnect 0x01 #define kNtTfReuseSocket 0x02 @@ -57,6 +55,67 @@ COSMOPOLITAN_C_START_ #define kNtTfUseSystemThread 0x10 #define kNtTfUseKernelApc 0x20 +#define kNtSoConnectTime 0x700C +#define kNtSoUpdateAcceptContext 0x700B +#define kNtSoUpdateConnectContext 0x7010 + +#define kNtSioAddressListChange 0x28000017u +#define kNtSioAddressListQuery 0x48000016u +#define kNtSioAddressListSort 0xC8000019u +#define kNtSioAssociateHandle 0x88000001u +#define kNtSioEnableCircularQueueing 0x28000002u +#define kNtSioFindRoute 0x48000003u +#define kNtSioFlush 0x28000004u +#define kNtSioGetBroadcastAddress 0x48000005u +#define kNtSioGetExtensionFunctionPointer 0xC8000006u +#define kNtSioGetGroupQos 0xC8000008u +#define kNtSioGetQos 0xC8000007u +#define kNtSioMulticastScope 0x8800000Au +#define kNtSioMultipointLoopback 0x88000009u +#define kNtSioQueryRssProcessorInfo 0x48000025u +#define kNtSioQueryTargetPnpHandle 0x48000018u +#define kNtSioReserved1 0x8800001Au +#define kNtSioReserved2 0x88000021u +#define kNtSioRoutingInterfaceChange 0x88000015u +#define kNtSioRoutingInterfaceQuery 0xC8000014u +#define kNtSioSetGroupQos 0x8800000Cu +#define kNtSioSetQos 0x8800000Bu +#define kNtSioSocketCloseNotify 0x9800000Du +#define kNtSioTranslateHandle 0xC800000Du +#define kNtSioUdpConnreset 0x9800000Cu +#define kNtSioUdpNetreset 0x9800000Fu + +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +#define kNtWsaidAcceptex \ + { \ + 0xB5367DF1, 0xCBAC, 0x11CF, { \ + 0x95, 0xCA, 0x00, 0x80, 0x5F, 0x48, 0xA1, 0x92 \ + } \ + } + +#define kNtWsaidConnectex \ + { \ + 0x25A207B9, 0xDDF3, 0x4660, { \ + 0x8E, 0xE9, 0x76, 0xE5, 0x8C, 0x74, 0x06, 0x3E \ + } \ + } + +#define kNtWsaidDisconnectex \ + { \ + 0x7FDA2E11, 0x8630, 0x436F, { \ + 0xA0, 0x31, 0xF5, 0x36, 0xA6, 0xEE, 0xC1, 0x57 \ + } \ + } + +#define kNtWsaidTransmitfile \ + { \ + 0xB5367DF0, 0xCBAC, 0x11CF, { \ + 0x95, 0xCA, 0x00, 0x80, 0x5F, 0x48, 0xA1, 0x92 \ + } \ + } + enum NtWsaEComparator { COMP_EQUAL, COMP_NOTLESS }; enum NtWsaCompletionType { @@ -96,13 +155,6 @@ struct NtWsaData { char szSystemStatus[129]; }; -struct NtGuid { - uint32_t Data1; - uint16_t Data2; - uint16_t Data3; - uint8_t Data4[8]; -}; - struct NtSocketAddress { struct sockaddr *lpSockaddr; int32_t iSockaddrLength; @@ -512,6 +564,14 @@ void GetAcceptExSockaddrs( struct sockaddr **out_RemoteSockaddr /*[*RemoteSockaddrLength]*/, int *out_RemoteSockaddrLength); +bool32 ConnectEx(int64_t s, const struct sockaddr *name, int namelen, + const void *opt_lpSendBuffer, uint32_t dwSendDataLength, + uint32_t *out_lpdwBytesSent, + struct NtOverlapped *inout_lpOverlapped); + +bool32 DisconnectEx(int64_t s, struct NtOverlapped *inout_opt_lpOverlapped, + uint32_t dwFlags, uint32_t dwReserved); + COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_NT_WINSOCK_H_ */ diff --git a/libc/runtime/_exit.S b/libc/runtime/_exit.S index 590ba5f4..28192d74 100644 --- a/libc/runtime/_exit.S +++ b/libc/runtime/_exit.S @@ -25,9 +25,6 @@ / Terminates process, ignoring destructors and atexit() handlers. / -/ Normally exit() or quick_exit() is better. This won't even flush -/ stdio streams. Sometimes that makes sense, like after fork(). -/ / @param edi is exit code ∈ [0,256) / @note _exit() is same thing / @asyncsignalsafe diff --git a/libc/runtime/closesymboltable.c b/libc/runtime/closesymboltable.c index 7981892a..56552edc 100644 --- a/libc/runtime/closesymboltable.c +++ b/libc/runtime/closesymboltable.c @@ -25,7 +25,7 @@ * Frees symbol table. * @return 0 on success or -1 on system error */ -int closesymboltable(struct SymbolTable **table) { +int CloseSymbolTable(struct SymbolTable **table) { int rc; struct SymbolTable *t; rc = 0; diff --git a/libc/runtime/directmap.c b/libc/runtime/directmap.c index de1f6850..22e7f972 100644 --- a/libc/runtime/directmap.c +++ b/libc/runtime/directmap.c @@ -17,6 +17,7 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/macros.h" #include "libc/nt/memory.h" diff --git a/libc/runtime/executive.S b/libc/runtime/executive.S index 1a6e0467..92ae7d43 100644 --- a/libc/runtime/executive.S +++ b/libc/runtime/executive.S @@ -57,42 +57,14 @@ _executive: ud2 #ifdef __PG__ -/ Enables plaintext function tracing if --ftrace flag passed. -/ -/ The --ftrace CLI arg is removed before main() is called. This -/ code is intended for diagnostic purposes and assumes binaries -/ are trustworthy and stack isn't corrupted. Logging plain text -/ allows program structure to easily be visualized and hotspots -/ identified w/ sed | sort | uniq -c | sort. A compressed trace -/ can be made by appending --ftrace 2>&1 | gzip -4 >trace.gz to -/ the CLI arguments. Have fun. -/ -/ @see libc/runtime/ftrace.greg.c -/ @see libc/crt/crt.S .init.start 800,_init_ftrace push %rdi push %rsi - xor %edx,%edx - loadstr "--ftrace",di - xor %ecx,%ecx -0: inc %ecx - mov (%r13,%rcx,8),%rsi - test %edx,%edx - jz 1f - mov %rsi,-8(%r13,%rcx,8) -1: test %rsi,%rsi - jz 2f - test %edx,%edx - jnz 0b - call tinystrcmp - test %eax,%eax - setz %dl - jmp 0b -2: sub %rdx,%r12 - test %edx,%edx - jz 2f + mov %r12d,%edi + mov %r13,%rsi call ftrace_init -2: pop %rsi + mov %eax,%r12d + pop %rsi pop %rdi .init.end 800,_init_ftrace -#endif /* -pg */ +#endif diff --git a/libc/runtime/findcombinary.c b/libc/runtime/findcombinary.c index e62f56b0..ef6bf33d 100644 --- a/libc/runtime/findcombinary.c +++ b/libc/runtime/findcombinary.c @@ -28,25 +28,25 @@ struct FindComBinary { char buf[PATH_MAX]; }; -static struct FindComBinary findcombinary_; +static struct FindComBinary g_findcombinary; /** * Returns path of binary without debug information, or null. * * @return path to non-debug binary, or -1 w/ errno */ -const char *findcombinary(void) { +const char *FindComBinary(void) { size_t len; const char *p; - if (!findcombinary_.once) { - findcombinary_.once = true; + if (!g_findcombinary.once) { + g_findcombinary.once = true; if ((p = (const char *)getauxval(AT_EXECFN)) && - (len = strlen(p)) < ARRAYLEN(findcombinary_.buf)) { - findcombinary_.res = memcpy(findcombinary_.buf, p, len + 1); - if (len > 4 && memcmp(&findcombinary_.buf[len - 4], ".dbg", 4) == 0) { - findcombinary_.buf[len - 4] = '\0'; + (len = strlen(p)) < ARRAYLEN(g_findcombinary.buf)) { + g_findcombinary.res = memcpy(g_findcombinary.buf, p, len + 1); + if (len > 4 && memcmp(&g_findcombinary.buf[len - 4], ".dbg", 4) == 0) { + g_findcombinary.buf[len - 4] = '\0'; } } } - return findcombinary_.res; + return g_findcombinary.res; } diff --git a/libc/runtime/finddebugbinary.c b/libc/runtime/finddebugbinary.c index f3a9a909..d672c0f6 100644 --- a/libc/runtime/finddebugbinary.c +++ b/libc/runtime/finddebugbinary.c @@ -31,7 +31,7 @@ * * @return path to debug binary, or -1 w/ errno */ -const char *finddebugbinary(void) { +const char *FindDebugBinary(void) { static char buf[PATH_MAX]; if (buf[0]) return &buf[0]; const char *const trybins[] = {program_invocation_name, diff --git a/libc/runtime/ftrace.greg.c b/libc/runtime/ftrace.greg.c index 0ce88056..e5ea3bc5 100644 --- a/libc/runtime/ftrace.greg.c +++ b/libc/runtime/ftrace.greg.c @@ -24,6 +24,7 @@ #include "libc/calls/internal.h" #include "libc/calls/struct/sigset.h" #include "libc/dce.h" +#include "libc/intrin/repmovsb.h" #include "libc/macros.h" #include "libc/nexgen32e/stackframe.h" #include "libc/nt/files.h" @@ -50,7 +51,7 @@ static char g_buf[512]; static const char *g_lastsymbol; static struct SymbolTable *g_symbols; -forceinline int getnestinglevel(struct StackFrame *frame) { +forceinline int GetNestingLevel(struct StackFrame *frame) { int nesting = -2; while (frame) { ++nesting; @@ -81,7 +82,7 @@ privileged interruptfn void ftrace_hook(void) { frame->addr - g_symbols->addr_base)] .name_rva]; if (symbol != g_lastsymbol && - (nesting = getnestinglevel(frame)) * 2 < ARRAYLEN(g_buf) - 3) { + (nesting = GetNestingLevel(frame)) * 2 < ARRAYLEN(g_buf) - 4) { i = 2; j = 0; while (nesting--) { @@ -91,6 +92,7 @@ privileged interruptfn void ftrace_hook(void) { while (i < ARRAYLEN(g_buf) - 2 && symbol[j]) { g_buf[i++] = symbol[j++]; } + g_buf[i++] = '\r'; g_buf[i++] = '\n'; __print(g_buf, i); } @@ -100,13 +102,42 @@ privileged interruptfn void ftrace_hook(void) { } /** - * Installs plaintext function tracer. Do not call. + * Enables plaintext function tracing if --ftrace flag passed. + * + * The --ftrace CLI arg is removed before main() is called. This + * code is intended for diagnostic purposes and assumes binaries + * are trustworthy and stack isn't corrupted. Logging plain text + * allows program structure to easily be visualized and hotspots + * identified w/ sed | sort | uniq -c | sort. A compressed trace + * can be made by appending --ftrace 2>&1 | gzip -4 >trace.gz to + * the CLI arguments. Have fun. + * * @see libc/runtime/_init.S for documentation */ -textstartup void ftrace_init(void) { - g_buf[0] = '+'; - g_buf[1] = ' '; - if ((g_symbols = opensymboltable(finddebugbinary()))) { - __hook(ftrace_hook, g_symbols); +textstartup int ftrace_init(int argc, char *argv[]) { + int i; + bool foundflag; + foundflag = false; + for (i = 1; i <= argc; ++i) { + if (!foundflag) { + if (argv[i]) { + if (strcmp(argv[i], "--ftrace") == 0) { + foundflag = true; + } else if (strcmp(argv[i], "----ftrace") == 0) { + strcpy(argv[i], "--ftrace"); + } + } + } else { + argv[i - 1] = argv[i]; + } } + if (foundflag) { + --argc; + g_buf[0] = '+'; + g_buf[1] = ' '; + if ((g_symbols = OpenSymbolTable(FindDebugBinary()))) { + __hook(ftrace_hook, g_symbols); + } + } + return argc; } diff --git a/libc/runtime/getdosargv.c b/libc/runtime/getdosargv.c index c160b7f0..c48a0c8e 100644 --- a/libc/runtime/getdosargv.c +++ b/libc/runtime/getdosargv.c @@ -25,6 +25,7 @@ #include "libc/str/appendchar.h" #include "libc/str/str.h" #include "libc/str/tpenc.h" +#include "libc/str/utf16.h" /* TODO(jart): Make early-stage data structures happen. */ #undef isspace @@ -39,11 +40,7 @@ struct DosArgv { wint_t wc; }; -static textwindows void decodedosargv(struct DosArgv *st) { - st->s += getutf16(st->s, &st->wc); -} - -static textwindows void appenddosargv(struct DosArgv *st, wint_t wc) { +static textwindows void AppendDosArgv(struct DosArgv *st, wint_t wc) { AppendChar(&st->p, st->pe, wc); } @@ -66,7 +63,7 @@ static textwindows void appenddosargv(struct DosArgv *st, wint_t wc) { * @see libc/runtime/ntspawn.c * @note kudos to Simon Tatham for figuring out quoting behavior */ -textwindows int getdosargv(const char16_t *cmdline, char *buf, size_t size, +textwindows int GetDosArgv(const char16_t *cmdline, char *buf, size_t size, char **argv, size_t max) { bool inquote; size_t i, argc, slashes, quotes; @@ -75,9 +72,9 @@ textwindows int getdosargv(const char16_t *cmdline, char *buf, size_t size, st.p = buf; st.pe = buf + size; argc = 0; - decodedosargv(&st); + st.wc = DecodeNtsUtf16(&st.s); while (st.wc) { - while (st.wc && iswspace(st.wc)) decodedosargv(&st); + while (st.wc && iswspace(st.wc)) st.wc = DecodeNtsUtf16(&st.s); if (!st.wc) break; if (++argc < max) { argv[argc - 1] = st.p < st.pe ? st.p : NULL; @@ -88,27 +85,27 @@ textwindows int getdosargv(const char16_t *cmdline, char *buf, size_t size, if (st.wc == '"' || st.wc == '\\') { slashes = 0; quotes = 0; - while (st.wc == '\\') decodedosargv(&st), slashes++; - while (st.wc == '"') decodedosargv(&st), quotes++; + while (st.wc == '\\') st.wc = DecodeNtsUtf16(&st.s), slashes++; + while (st.wc == '"') st.wc = DecodeNtsUtf16(&st.s), quotes++; if (!quotes) { - while (slashes--) appenddosargv(&st, '\\'); + while (slashes--) AppendDosArgv(&st, '\\'); } else { - while (slashes >= 2) appenddosargv(&st, '\\'), slashes -= 2; - if (slashes) appenddosargv(&st, '"'), quotes--; + while (slashes >= 2) AppendDosArgv(&st, '\\'), slashes -= 2; + if (slashes) AppendDosArgv(&st, '"'), quotes--; if (quotes > 0) { if (!inquote) quotes--; - for (i = 3; i <= quotes + 1; i += 3) appenddosargv(&st, '"'); + for (i = 3; i <= quotes + 1; i += 3) AppendDosArgv(&st, '"'); inquote = (quotes % 3 == 0); } } } else { - appenddosargv(&st, st.wc); - decodedosargv(&st); + AppendDosArgv(&st, st.wc); + st.wc = DecodeNtsUtf16(&st.s); } } - appenddosargv(&st, '\0'); + AppendDosArgv(&st, '\0'); } - appenddosargv(&st, '\0'); + AppendDosArgv(&st, '\0'); if (size) buf[min(st.p - buf, size - 1)] = '\0'; if (max) argv[min(argc, max - 1)] = NULL; return argc; diff --git a/libc/runtime/getdosenviron.h b/libc/runtime/getdosenviron.h index 8d09aedf..be84af80 100644 --- a/libc/runtime/getdosenviron.h +++ b/libc/runtime/getdosenviron.h @@ -22,6 +22,7 @@ #ifndef __STRICT_ANSI__ #include "libc/bits/safemacros.h" #include "libc/str/appendchar.h" +#include "libc/str/str.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) /** @@ -34,25 +35,28 @@ * @param max is the pointer count capacity of envp * @return number of variables decoded, excluding NULL-terminator */ -static inline int getdosenviron(const char16_t *env, char *buf, size_t size, +static inline int GetDosEnviron(const char16_t *env, char *buf, size_t size, char **envp, size_t max) { - const char16_t *s = env; - size_t envc = 0; + wint_t wc; + size_t envc; + char *p, *pe; + bool endstring; + const char16_t *s; + s = env; + envc = 0; if (size) { - wint_t wc; - char *p = buf; - char *pe = buf + size - 1; + p = buf; + pe = buf + size - 1; if (p < pe) { - s += getutf16(s, &wc); + wc = DecodeNtsUtf16(&s); while (wc) { if (++envc < max) { envp[envc - 1] = p < pe ? p : NULL; } - bool endstring; do { AppendChar(&p, pe, wc); endstring = !wc; - s += getutf16(s, &wc); + wc = DecodeNtsUtf16(&s); } while (!endstring); buf[min(p - buf, size - 2)] = u'\0'; } diff --git a/libc/runtime/gothere.greg.c b/libc/runtime/gothere.greg.c index c591b968..30e1cbff 100644 --- a/libc/runtime/gothere.greg.c +++ b/libc/runtime/gothere.greg.c @@ -48,6 +48,7 @@ privileged interruptfn void GOT_HERE(long num) { msg[len++] = 'e'; msg[len++] = ' '; len += int64toarray_radix10(num, &msg[len]); + msg[len++] = '\r'; msg[len++] = '\n'; msg[len] = '\0'; __print(msg, len); diff --git a/libc/runtime/internal.h b/libc/runtime/internal.h index 22f4699a..dacac5bb 100644 --- a/libc/runtime/internal.h +++ b/libc/runtime/internal.h @@ -25,7 +25,7 @@ void *__cxa_finalize(void *) hidden; void _executive(int, char **, char **, long (*)[2]) hidden noreturn; void __stack_chk_fail(void) noreturn relegated; void __stack_chk_fail_local(void) noreturn relegated hidden; -int getdosargv(const char16_t *, char *, size_t, char **, size_t) hidden; +int GetDosArgv(const char16_t *, char *, size_t, char **, size_t) hidden; forceinline void AssertNeverCalledWhileTerminating(void) { if (!NoDebug() && (g_runstate & RUNSTATE_TERMINATE)) { diff --git a/libc/runtime/isheap.c b/libc/runtime/isheap.c index 40b289e7..ede96732 100644 --- a/libc/runtime/isheap.c +++ b/libc/runtime/isheap.c @@ -17,6 +17,7 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/macros.h" #include "libc/runtime/memtrack.h" #include "libc/runtime/runtime.h" @@ -28,12 +29,13 @@ */ bool isheap(void *p) { int x, i; -#if 1 - register intptr_t rsp asm("rsp"); - if ((intptr_t)p >= rsp) return false; -#endif - if ((intptr_t)p <= (intptr_t)_end) return false; - x = (intptr_t)p >> 16; - i = FindMemoryInterval(&_mmi, x); - return i < _mmi.i && x >= _mmi.p[i].x && x <= _mmi.p[i].y; + register uintptr_t rsp asm("rsp"); + if (ROUNDDOWN(rsp, STACKSIZE) == ROUNDDOWN((intptr_t)p, STACKSIZE)) { + return false; + } else { + if ((intptr_t)p <= (intptr_t)_end) return false; + x = (intptr_t)p >> 16; + i = FindMemoryInterval(&_mmi, x); + return i < _mmi.i && x >= _mmi.p[i].x && x <= _mmi.p[i].y; + } } diff --git a/libc/runtime/mapanon.c b/libc/runtime/mapanon.c index 504080aa..4486f373 100644 --- a/libc/runtime/mapanon.c +++ b/libc/runtime/mapanon.c @@ -24,5 +24,5 @@ void *mapanon(size_t mapsize) { return mmap(NULL, mapsize, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_NONBLOCK | MAP_ANONYMOUS, -1, 0); + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); } diff --git a/libc/runtime/memtrack.h b/libc/runtime/memtrack.h index 64adb453..3f7e4274 100644 --- a/libc/runtime/memtrack.h +++ b/libc/runtime/memtrack.h @@ -1,10 +1,12 @@ #ifndef COSMOPOLITAN_LIBC_RUNTIME_MEMTRACK_H_ #define COSMOPOLITAN_LIBC_RUNTIME_MEMTRACK_H_ +#include "libc/macros.h" +#include "libc/runtime/runtime.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -#define kAutomapStart 0x0000100000000000 -#define kAutomapSize 0x0000100000000000 +#define kAutomapStart 0x0000100080000000 // asan can't spread its poison here +#define kAutomapSize 0x00000fff80000000 #define kFixedmapStart 0x0000200000000000 struct MemoryIntervals { @@ -12,8 +14,8 @@ struct MemoryIntervals { struct MemoryInterval { int x; int y; - } p[64]; - long h[64]; + } p[128]; + long h[128]; }; extern struct MemoryIntervals _mmi; diff --git a/libc/runtime/opensymboltable.c b/libc/runtime/opensymboltable.c index b915fb64..b2e46f21 100644 --- a/libc/runtime/opensymboltable.c +++ b/libc/runtime/opensymboltable.c @@ -30,9 +30,9 @@ /** * Maps debuggable binary into memory and indexes symbol addresses. * - * @return object freeable with closesymboltable(), or NULL w/ errno + * @return object freeable with CloseSymbolTable(), or NULL w/ errno */ -struct SymbolTable *opensymboltable(const char *filename) { +struct SymbolTable *OpenSymbolTable(const char *filename) { unsigned i, j; struct SymbolTable *t; const Elf64_Sym *symtab, *sym; @@ -56,7 +56,7 @@ struct SymbolTable *opensymboltable(const char *filename) { t->count = j; carsort1000(t->count, (void *)t->symbols); } else { - closesymboltable(&t); + CloseSymbolTable(&t); } return t == MAP_FAILED ? NULL : t; } diff --git a/libc/runtime/printmemoryintervals.c b/libc/runtime/printmemoryintervals.c index 9689ffab..573e4782 100644 --- a/libc/runtime/printmemoryintervals.c +++ b/libc/runtime/printmemoryintervals.c @@ -28,18 +28,18 @@ void PrintMemoryIntervals(int fd, const struct MemoryIntervals *mm) { int i, frames, maptally, gaptally; maptally = 0; gaptally = 0; - (dprintf)(fd, "%s%zd%s\n", "mm->i == ", mm->i, ";"); + (dprintf)(fd, "%s%zd%s\r\n", "mm->i == ", mm->i, ";"); for (i = 0; i < mm->i; ++i) { if (i && mm->p[i].x != mm->p[i - 1].y + 1) { frames = mm->p[i].x - mm->p[i - 1].y - 1; gaptally += frames; - (dprintf)(fd, "%s%,zd%s\n", "/* ", frames, " */"); + (dprintf)(fd, "%s%,zd%s\r\n", "/* ", frames, " */"); } frames = mm->p[i].y + 1 - mm->p[i].x; maptally += frames; - (dprintf)(fd, "%s%3u%s0x%08x,0x%08x%s%,zd%s\n", "mm->p[", i, "]=={", + (dprintf)(fd, "%s%3u%s0x%08x,0x%08x%s%,zd%s\r\n", "mm->p[", i, "]=={", mm->p[i].x, mm->p[i].y, "}; /* ", frames, " */"); } - (dprintf)(fd, "%s%,zd%s%,zd%s\n\n", "/* ", maptally, " frames mapped w/ ", + (dprintf)(fd, "%s%,zd%s%,zd%s\r\n\r\n", "/* ", maptally, " frames mapped w/ ", gaptally, " frames gapped */"); } diff --git a/libc/runtime/runtime.h b/libc/runtime/runtime.h index e0e0c3ac..fcb11eb8 100644 --- a/libc/runtime/runtime.h +++ b/libc/runtime/runtime.h @@ -41,7 +41,6 @@ void *mapanon(size_t) vallocesque attributeallocsize((1)); int setjmp(jmp_buf) libcesque returnstwice paramsnonnull(); void longjmp(jmp_buf, int) libcesque noreturn paramsnonnull(); void exit(int) noreturn; -void quick_exit(int) noreturn; void _exit(int) libcesque noreturn; void _Exit(int) libcesque noreturn; long _setstack(void *, void *, ...); diff --git a/libc/runtime/symbols.h b/libc/runtime/symbols.h index 9e9211e9..9608336e 100644 --- a/libc/runtime/symbols.h +++ b/libc/runtime/symbols.h @@ -1,27 +1,8 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│vi: set net ft=c ts=8 sts=2 sw=2 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ #ifndef COSMOPOLITAN_LIBC_SYMBOLS_H_ #define COSMOPOLITAN_LIBC_SYMBOLS_H_ -#if !(__ASSEMBLER__ + __LINKER__ + 0) #include "libc/elf/elf.h" #include "libc/runtime/ezmap.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ struct Symbol { @@ -46,13 +27,11 @@ struct SymbolTable { struct Symbol symbols[]; }; -struct SymbolTable *getsymboltable(void); -const char *findcombinary(void); -const char *finddebugbinary(void); -struct SymbolTable *opensymboltable(const char *) nodiscard; -int closesymboltable(struct SymbolTable **); -const struct Symbol *bisectsymbol(struct SymbolTable *, intptr_t, int64_t *); -const char *getsymbolname(struct SymbolTable *, const struct Symbol *); +struct SymbolTable *GetSymbolTable(void); +const char *FindComBinary(void); +const char *FindDebugBinary(void); +struct SymbolTable *OpenSymbolTable(const char *) nodiscard; +int CloseSymbolTable(struct SymbolTable **); void __hook(void (*)(void), struct SymbolTable *); COSMOPOLITAN_C_END_ diff --git a/libc/runtime/winmain.greg.c b/libc/runtime/winmain.greg.c index 7e6f6073..5a26b133 100644 --- a/libc/runtime/winmain.greg.c +++ b/libc/runtime/winmain.greg.c @@ -38,8 +38,8 @@ static struct CmdExe { bool result; struct OldNtConsole { - unsigned codepage; - unsigned mode; + uint32_t codepage; + uint32_t mode; int64_t handle; } oldin, oldout; } g_cmdexe; @@ -82,6 +82,8 @@ static textwindows void NormalizeCmdExe(void) { SetConsoleCP(kNtCpUtf8); GetConsoleMode(handle, &g_cmdexe.oldin.mode); SetConsoleMode(handle, g_cmdexe.oldin.mode | kNtEnableProcessedInput | + kNtEnableEchoInput | kNtEnableLineInput | + kNtEnableWindowInput | kNtEnableVirtualTerminalInput); } if (GetFileType((handle = hstdout)) == kNtFileTypeChar || @@ -92,7 +94,10 @@ static textwindows void NormalizeCmdExe(void) { SetConsoleOutputCP(kNtCpUtf8); GetConsoleMode(handle, &g_cmdexe.oldout.mode); SetConsoleMode(handle, g_cmdexe.oldout.mode | kNtEnableProcessedOutput | - kNtEnableVirtualTerminalProcessing); + kNtEnableWrapAtEolOutput | + (NtGetVersion() >= kNtVersionWindows10 + ? kNtEnableVirtualTerminalProcessing + : 0)); } } } @@ -136,11 +141,11 @@ textwindows int WinMain(void *hInstance, void *hPrevInstance, *(/*unconst*/ int *)&hostos = WINDOWS; cmd16 = GetCommandLine(); env16 = GetEnvironmentStrings(); - count = getdosargv(cmd16, argblock, ARG_MAX, argarray, 512); + count = GetDosArgv(cmd16, argblock, ARG_MAX, argarray, 512); for (i = 0; argarray[0][i]; ++i) { if (argarray[0][i] == '\\') argarray[0][i] = '/'; } - getdosenviron(env16, envblock, ENV_MAX, envarray, 512); + GetDosEnviron(env16, envblock, ENV_MAX, envarray, 512); FreeEnvironmentStrings(env16); _executive(count, argarray, envarray, auxarray); } diff --git a/libc/str/decodentsutf16.c b/libc/str/decodentsutf16.c new file mode 100644 index 00000000..8e2db23c --- /dev/null +++ b/libc/str/decodentsutf16.c @@ -0,0 +1,33 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" +#include "libc/str/utf16.h" + +wint_t DecodeNtsUtf16(const char16_t **s) { + wint_t x, y; + if (!IsUcs2((x = *(*s)++))) { + if ((y = *(*s)++)) { + x = MergeUtf16(x, y); + } else { + x = 0; + } + } + return x; +} diff --git a/libc/str/hextoint.c b/libc/str/hextoint.c new file mode 100644 index 00000000..03a9e101 --- /dev/null +++ b/libc/str/hextoint.c @@ -0,0 +1,32 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int hextoint(int c) { + if ('0' <= c && c <= '9') { + return c - '0'; + } else if ('a' <= c && c <= 'f') { + return c - 'a' + 10; + } else if ('A' <= c && c <= 'F') { + return c - 'A' + 10; + } else { + return 0; + } +} diff --git a/libc/str/isalnum.c b/libc/str/isalnum.c new file mode 100644 index 00000000..1b94dc17 --- /dev/null +++ b/libc/str/isalnum.c @@ -0,0 +1,25 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int isalnum(int c) { + return ('0' <= c && c <= '9') || ('A' <= c && c <= 'Z') || + ('a' <= c && c <= 'z'); +} diff --git a/libc/log/backtrace.c b/libc/str/isalpha.c similarity index 94% rename from libc/log/backtrace.c rename to libc/str/isalpha.c index 459c8813..64a6d94a 100644 --- a/libc/log/backtrace.c +++ b/libc/str/isalpha.c @@ -17,8 +17,8 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/log/backtrace.h" +#include "libc/str/str.h" -void backtrace(FILE *f) { - showbacktrace(f, __builtin_frame_address(0)); +int isalpha(int c) { + return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'); } diff --git a/tool/build/lib/init.c b/libc/str/isascii.c similarity index 96% rename from tool/build/lib/init.c rename to libc/str/isascii.c index 3170e726..10b0994e 100644 --- a/tool/build/lib/init.c +++ b/libc/str/isascii.c @@ -17,7 +17,7 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "tool/build/lib/machine.h" -void InitMachine(struct Machine *m) { +int isascii(int c) { + return 0x00 <= c && c <= 0x7F; } diff --git a/libc/str/isblank.c b/libc/str/isblank.c new file mode 100644 index 00000000..f1f43ec8 --- /dev/null +++ b/libc/str/isblank.c @@ -0,0 +1,24 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int isblank(int c) { + return c == ' ' || c == '\t'; +} diff --git a/libc/str/iscntrl.c b/libc/str/iscntrl.c new file mode 100644 index 00000000..423965e9 --- /dev/null +++ b/libc/str/iscntrl.c @@ -0,0 +1,24 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int iscntrl(int c) { + return (0x00 <= c && c <= 0x1F) || c == 0x7F; +} diff --git a/libc/str/isdigit.c b/libc/str/isdigit.c new file mode 100644 index 00000000..208528c0 --- /dev/null +++ b/libc/str/isdigit.c @@ -0,0 +1,24 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int isdigit(int c) { + return '0' <= c && c <= '9'; +} diff --git a/libc/str/isgraph.c b/libc/str/isgraph.c new file mode 100644 index 00000000..94d0b0c4 --- /dev/null +++ b/libc/str/isgraph.c @@ -0,0 +1,24 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int isgraph(int c) { + return 0x21 <= c && c <= 0x7E; +} diff --git a/libc/str/islower.c b/libc/str/islower.c new file mode 100644 index 00000000..2578519b --- /dev/null +++ b/libc/str/islower.c @@ -0,0 +1,24 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int islower(int c) { + return 'a' <= c && c <= 'z'; +} diff --git a/libc/str/isprint.c b/libc/str/isprint.c new file mode 100644 index 00000000..5e091465 --- /dev/null +++ b/libc/str/isprint.c @@ -0,0 +1,24 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int isprint(int c) { + return 0x20 <= c && c <= 0x7E; +} diff --git a/libc/str/ispunct.c b/libc/str/ispunct.c new file mode 100644 index 00000000..7ae42d2c --- /dev/null +++ b/libc/str/ispunct.c @@ -0,0 +1,25 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int ispunct(int c) { + return (0x21 <= c && c <= 0x7E) && !('0' <= c && c <= '9') && + !('A' <= c && c <= 'Z') && !('a' <= c && c <= 'z'); +} diff --git a/libc/str/isspace.c b/libc/str/isspace.c new file mode 100644 index 00000000..e5873bad --- /dev/null +++ b/libc/str/isspace.c @@ -0,0 +1,25 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int isspace(int c) { + return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\f' || + c == '\v'; +} diff --git a/libc/str/isupper.c b/libc/str/isupper.c new file mode 100644 index 00000000..a374f695 --- /dev/null +++ b/libc/str/isupper.c @@ -0,0 +1,24 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int isupper(int c) { + return 'A' <= c && c <= 'Z'; +} diff --git a/libc/str/iswalnum.c b/libc/str/iswalnum.c new file mode 100644 index 00000000..ea098a73 --- /dev/null +++ b/libc/str/iswalnum.c @@ -0,0 +1,24 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int iswalnum(wint_t wc) { + return isalnum(wc); +} diff --git a/libc/str/iswalpha.c b/libc/str/iswalpha.c new file mode 100644 index 00000000..225f6f66 --- /dev/null +++ b/libc/str/iswalpha.c @@ -0,0 +1,24 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int iswalpha(wint_t wc) { + return isalpha(wc); +} diff --git a/libc/str/iswblank.c b/libc/str/iswblank.c new file mode 100644 index 00000000..91a32d70 --- /dev/null +++ b/libc/str/iswblank.c @@ -0,0 +1,24 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int iswblank(wint_t wc) { + return isblank(wc); +} diff --git a/libc/str/iswcntrl.c b/libc/str/iswcntrl.c new file mode 100644 index 00000000..4b67fae7 --- /dev/null +++ b/libc/str/iswcntrl.c @@ -0,0 +1,24 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int iswcntrl(wint_t wc) { + return (0x00 <= wc && wc <= 0x1F) || (0x7F <= wc && wc <= 0x9F); +} diff --git a/libc/str/iswctype.c b/libc/str/iswctype.c index a4a48ab0..d41a2a55 100644 --- a/libc/str/iswctype.c +++ b/libc/str/iswctype.c @@ -20,33 +20,71 @@ #include "libc/macros.h" #include "libc/str/str.h" -static const struct WcTypes { - char name[8]; - wctype_t type; -} wctypes[] = {{"alnum", 0x006}, {"alpha", 0x002}, {"blank", 0x080}, - {"cntrl", 0x100}, {"digit", 0x004}, {"graph", 0x200}, - {"lower", 0x020}, {"print", 0x010}, {"punct", 0x300}, - {"space", 0x001}, {"upper", 0x040}, {"xdigit", 0x008}}; +#define ALNUM 1 +#define ALPHA 2 +#define BLANK 3 +#define CNTRL 4 +#define DIGIT 5 +#define GRAPH 6 +#define LOWER 7 +#define PRINT 8 +#define PUNCT 9 +#define SPACE 10 +#define UPPER 11 +#define XDIGIT 12 -wctype_t wctype(const char *s) { - char name[8]; - strncpy(name, s, sizeof(name)); - for (unsigned i = 0; i < ARRAYLEN(wctypes); ++i) { - if (memcmp(name, wctypes[i].name, sizeof(name)) == 0) { - return wctypes[i].type; +static const struct { + char name[7]; + char type; +} kWcTypes[] = { + {"alnum", ALNUM}, {"alpha", ALPHA}, {"blank", BLANK}, {"cntrl", CNTRL}, + {"digit", DIGIT}, {"graph", GRAPH}, {"lower", LOWER}, {"print", PRINT}, + {"punct", PUNCT}, {"space", SPACE}, {"upper", UPPER}, {"xdigit", XDIGIT}, +}; + +static int CompareStrings(const char *l, const char *r) { + size_t i = 0; + while (l[i] == r[i] && r[i]) ++i; + return (l[i] & 0xff) - (r[i] & 0xff); +} + +wctype_t wctype(const char *name) { + unsigned i; + for (i = 0; i < ARRAYLEN(kWcTypes); ++i) { + if (CompareStrings(name, kWcTypes[i].name) == 0) { + return kWcTypes[i].type; } } return 0; } -int iswctype(wint_t c, wctype_t type) { - if (c < 128) { - if (!(type & ~0xff)) { - return kCtype[(unsigned char)type]; - } - if (type == 0x100) return iscntrl((unsigned char)c); - if (type == 0x200) return isgraph((unsigned char)c); - if (type == 0x300) return ispunct((unsigned char)c); +int iswctype(wint_t wc, wctype_t type) { + switch (type) { + case ALNUM: + return iswalnum(wc); + case ALPHA: + return iswalpha(wc); + case BLANK: + return iswblank(wc); + case CNTRL: + return iswcntrl(wc); + case DIGIT: + return iswdigit(wc); + case GRAPH: + return iswgraph(wc); + case LOWER: + return iswlower(wc); + case PRINT: + return iswprint(wc); + case PUNCT: + return iswpunct(wc); + case SPACE: + return iswspace(wc); + case UPPER: + return iswupper(wc); + case XDIGIT: + return iswxdigit(wc); + default: + return 0; } - return 0; } diff --git a/libc/str/iswdigit.c b/libc/str/iswdigit.c new file mode 100644 index 00000000..c8b9f247 --- /dev/null +++ b/libc/str/iswdigit.c @@ -0,0 +1,24 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int iswdigit(wint_t wc) { + return isdigit(wc); +} diff --git a/libc/str/iswgraph.c b/libc/str/iswgraph.c new file mode 100644 index 00000000..d900b0f2 --- /dev/null +++ b/libc/str/iswgraph.c @@ -0,0 +1,24 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int iswgraph(wint_t wc) { + return isgraph(wc); +} diff --git a/libc/str/iswlower.c b/libc/str/iswlower.c new file mode 100644 index 00000000..2b360779 --- /dev/null +++ b/libc/str/iswlower.c @@ -0,0 +1,24 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int iswlower(wint_t wc) { + return islower(wc); +} diff --git a/libc/str/iswprint.c b/libc/str/iswprint.c new file mode 100644 index 00000000..4475214d --- /dev/null +++ b/libc/str/iswprint.c @@ -0,0 +1,24 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int iswprint(wint_t wc) { + return !iswcntrl(wc); +} diff --git a/libc/str/iswpunct.c b/libc/str/iswpunct.c new file mode 100644 index 00000000..c8a8ab05 --- /dev/null +++ b/libc/str/iswpunct.c @@ -0,0 +1,24 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int iswpunct(wint_t wc) { + return ispunct(wc); +} diff --git a/libc/str/iswspace.c b/libc/str/iswspace.c new file mode 100644 index 00000000..71f0a255 --- /dev/null +++ b/libc/str/iswspace.c @@ -0,0 +1,24 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int iswspace(wint_t wc) { + return isspace(wc); +} diff --git a/libc/str/iswupper.c b/libc/str/iswupper.c new file mode 100644 index 00000000..faea172c --- /dev/null +++ b/libc/str/iswupper.c @@ -0,0 +1,24 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int iswupper(wint_t wc) { + return isupper(wc); +} diff --git a/libc/str/iswxdigit.c b/libc/str/iswxdigit.c new file mode 100644 index 00000000..2a70c05f --- /dev/null +++ b/libc/str/iswxdigit.c @@ -0,0 +1,24 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int iswxdigit(wint_t wc) { + return isxdigit(wc); +} diff --git a/libc/str/isxdigit.c b/libc/str/isxdigit.c new file mode 100644 index 00000000..7ef928d7 --- /dev/null +++ b/libc/str/isxdigit.c @@ -0,0 +1,25 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int isxdigit(int c) { + return ('0' <= c && c <= '9') || ('A' <= c && c <= 'F') || + ('a' <= c && c <= 'f'); +} diff --git a/libc/str/str.h b/libc/str/str.h index 7472292c..4090fc73 100644 --- a/libc/str/str.h +++ b/libc/str/str.h @@ -8,7 +8,6 @@ COSMOPOLITAN_C_START_ fourth age telecommunications */ extern const uint8_t gperf_downcase[256]; -extern const uint8_t kCtype[256]; extern const uint8_t kToLower[256]; extern const uint8_t kToUpper[256]; extern const uint16_t kToLower16[256]; @@ -64,6 +63,7 @@ void *isnotplaintext(const void *, size_t) nothrow nocallback nosideeffect; #define INVALID_CODEPOINT 0xfffd +wint_t DecodeNtsUtf16(const char16_t **); unsigned getutf16(const char16_t *, wint_t *); int pututf16(char16_t *, size_t, wint_t, bool); int iswalnum(wint_t); @@ -78,8 +78,8 @@ int iswupper(wint_t); int iswxdigit(wint_t); int iswpunct(wint_t); int iswprint(wint_t); -unsigned towlower(unsigned); -unsigned towupper(unsigned); +wint_t towlower(wint_t); +wint_t towupper(wint_t); /*───────────────────────────────────────────────────────────────────────────│─╗ │ cosmopolitan § strings ─╬─│┼ diff --git a/libc/str/thompike.h b/libc/str/thompike.h index 982e77f9..b8763b10 100644 --- a/libc/str/thompike.h +++ b/libc/str/thompike.h @@ -5,7 +5,7 @@ #define ThomPikeCont(x) (((x)&0b11000000) == 0b10000000) #define ThomPikeByte(x) ((x) & (((1 << ThomPikeMsb(x)) - 1) | 3)) #define ThomPikeLen(x) (7 - ThomPikeMsb(x)) -#define ThomPikeMsb(x) ((x) < 252 ? bsr(~(x)&0xff) : 1) +#define ThomPikeMsb(x) (((x)&0xff) < 252 ? bsr(~(x)&0xff) : 1) #define ThomPikeMerge(x, y) ((x) << 6 | (y)&0b00111111) #endif /* COSMOPOLITAN_LIBC_STR_THOMPIKE_H_ */ diff --git a/libc/str/tolower.c b/libc/str/tolower.c new file mode 100644 index 00000000..8e71c6bd --- /dev/null +++ b/libc/str/tolower.c @@ -0,0 +1,24 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int tolower(int c) { + return 'A' <= c && c <= 'Z' ? c + ('a' - 'A') : c; +} diff --git a/libc/str/toupper.c b/libc/str/toupper.c new file mode 100644 index 00000000..3217f0bd --- /dev/null +++ b/libc/str/toupper.c @@ -0,0 +1,24 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int toupper(int c) { + return 'a' <= c && c <= 'z' ? c - ('a' - 'A') : c; +} diff --git a/libc/str/towlower.c b/libc/str/towlower.c new file mode 100644 index 00000000..48bb7db7 --- /dev/null +++ b/libc/str/towlower.c @@ -0,0 +1,24 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +wint_t towlower(wint_t wc) { + return tolower(wc); +} diff --git a/libc/str/towupper.c b/libc/str/towupper.c new file mode 100644 index 00000000..0d91e7c5 --- /dev/null +++ b/libc/str/towupper.c @@ -0,0 +1,24 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +wint_t towupper(wint_t wc) { + return toupper(wc); +} diff --git a/libc/str/tpenc.S b/libc/str/tpenc.S index 328f1048..a1c07e34 100644 --- a/libc/str/tpenc.S +++ b/libc/str/tpenc.S @@ -24,19 +24,15 @@ / / @param edi is int to encode / @return rax is word-encoded byte buffer -/ @clob edi,rax,flags / @note invented on a napkin in a new jersey diner tpenc: .leafprologue .profilable - push %rcx - push %rdx mov %edi,%edi xor %eax,%eax cmp $127,%edi jbe 3f bsr %edi,%ecx - ezlea kTpenc,dx - mov -7*(1+1)(%rdx,%rcx,2),%ecx + mov kTpenc-7*(1+1)(,%rcx,2),%ecx 1: mov %edi,%edx shr $6,%edi and $0b00111111,%dl @@ -47,12 +43,11 @@ tpenc: .leafprologue jnz 1b 2: or %ch,%al 3: or %rdi,%rax - pop %rdx - pop %rcx .leafepilogue .endfn tpenc,globl .rodata + .align 4 kTpenc: .rept 4 # MSB≤10 (0x7FF) .byte 1,0b11000000 # len,mark .endr @@ -68,4 +63,5 @@ kTpenc: .rept 4 # MSB≤10 (0x7FF) .rept 6 # MSB≤31 (0xffffffff) .byte 5,0b11111100 # len,mark .endr + .zero 2 .endobj kTpenc diff --git a/libc/str/tpenc.h b/libc/str/tpenc.h index a6cb8ded..0400a1bd 100644 --- a/libc/str/tpenc.h +++ b/libc/str/tpenc.h @@ -16,7 +16,7 @@ uint64_t tpenc(int32_t) pureconst; asm("call\ttpenc" \ : "=a"(Buf), "+D"(Code) \ : /* inputs */ \ - : "cc"); \ + : "rcx", "rdx", "cc"); \ } \ Buf; \ }) diff --git a/libc/str/tprecode16to8.c b/libc/str/tprecode16to8.c index 30070a56..4d4e63db 100644 --- a/libc/str/tprecode16to8.c +++ b/libc/str/tprecode16to8.c @@ -19,7 +19,8 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/conv/conv.h" #include "libc/str/str.h" -#include "libc/str/tpencode.h" +#include "libc/str/tpenc.h" +#include "libc/str/utf16.h" /** * Transcodes UTF-16 to UTF-8. @@ -30,19 +31,25 @@ * @return number of bytes written excluding NUL */ size_t tprecode16to8(char *dst, size_t dstsize, const char16_t *src) { - size_t i = 0; + size_t i; + uint64_t w; + wint_t x, y; + i = 0; if (dstsize) { for (;;) { - wint_t wc; - src += abs(getutf16(src, &wc)); - if (!wc || dstsize == 1) { - dst[i] = '\0'; - break; + if (!(x = *src++)) break; + if (IsUtf16Cont(x)) continue; + if (!IsUcs2(x)) { + if (!(y = *src++)) break; + x = MergeUtf16(x, y); + } + w = tpenc(x); + while (w && i + 1 < dstsize) { + dst[i++] = w & 0xFF; + w >>= 8; } - size_t got = abs(tpencode(&dst[i], dstsize, wc, false)); - dstsize -= got; - i += got; } + dst[i] = 0; } return i; } diff --git a/libc/str/tprecode8to16.c b/libc/str/tprecode8to16.c index 3418898f..3ed2436f 100644 --- a/libc/str/tprecode8to16.c +++ b/libc/str/tprecode8to16.c @@ -17,9 +17,9 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/conv/conv.h" #include "libc/str/str.h" -#include "libc/str/tpdecode.h" +#include "libc/str/thompike.h" +#include "libc/str/utf16.h" /** * Transcodes UTF-8 to UTF-16. @@ -30,19 +30,31 @@ * @return number of shorts written excluding NUL */ size_t tprecode8to16(char16_t *dst, size_t dstsize, const char *src) { - size_t i = 0; + size_t i; + unsigned n; + uint64_t w; + wint_t x, y; + i = 0; if (dstsize) { for (;;) { - wint_t wc; - src += abs(tpdecode(src, &wc)); - if (!wc || dstsize == 1) { - dst[i] = u'\0'; - break; + if (!(x = *src++ & 0xff)) break; + if (ThomPikeCont(x)) continue; + if (!isascii(x)) { + n = ThomPikeLen(x); + x = ThomPikeByte(x); + while (--n) { + if (!(y = *src++ & 0xff)) goto stop; + x = ThomPikeMerge(x, y); + } + } + w = EncodeUtf16(x); + while (w && i + 1 < dstsize) { + dst[i++] = w & 0xFFFF; + w >>= 16; } - size_t got = abs(pututf16(&dst[i], dstsize, wc, false)); - dstsize -= got; - i += got; } + stop: + dst[i] = 0; } return i; } diff --git a/libc/str/utf16.h b/libc/str/utf16.h index b3ed77c1..ab7beba5 100644 --- a/libc/str/utf16.h +++ b/libc/str/utf16.h @@ -8,5 +8,13 @@ #define IsUcs2(wc) (((wc)&UTF16_MASK) != UTF16_MOAR) #define IsUtf16Cont(wc) (((wc)&UTF16_MASK) == UTF16_CONT) #define MergeUtf16(lo, hi) ((((lo)-0xD800) << 10) + ((hi)-0xDC00) + 0x10000) +#define EncodeUtf16(wc) \ + (likely((0x0000 <= (wc) && (wc) <= 0xFFFF) || \ + (0xE000 <= (wc) && (wc) <= 0xFFFF)) \ + ? (wc) \ + : 0x10000 <= (wc) && (wc) <= 0x10FFFF \ + ? (((((wc)-0x10000) >> 10) + 0xD800) | \ + ((((wc)-0x10000) & 1023) + 0xDC00) << 16) \ + : 0xFFFD) #endif /* COSMOPOLITAN_LIBC_STR_UTF16_H_ */ diff --git a/libc/stubs/xnu.S b/libc/stubs/xnu.S index 2df54fc0..f6827244 100644 --- a/libc/stubs/xnu.S +++ b/libc/stubs/xnu.S @@ -21,5 +21,5 @@ .source __FILE__ _start_xnu: - jmp abort + ud2 .endfn _start_xnu,weak diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index c0183ac5..adc2ee92 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -214,16 +214,16 @@ syscon open O_APPEND 0x0400 8 8 8 0x00000004 # bsd consensus & kNtFi syscon open O_CREAT 0x40 0x0200 0x0200 0x0200 0x00000040 # bsd consensus & NT faked as Linux syscon open O_EXCL 0x80 0x0800 0x0800 0x0800 0x00000080 # bsd consensus & NT faked as Linux syscon open O_TRUNC 0x0200 0x0400 0x0400 0x0400 0x00000200 # bsd consensus & NT faked as Linux +syscon open O_DIRECTORY 0x010000 0x100000 0x020000 0x020000 0x02000000 # kNtFileFlagBackupSemantics syscon open O_DIRECT 0x4000 0 0x010000 0 0x00200000 # kNtFileFlagNoBuffering>>8 +syscon open O_CLOEXEC 0x080000 0x01000000 0x100000 0x010000 0x00080000 # NT faked as Linux +syscon open O_TMPFILE 0x410000 0 0 0 0x04000100 # Linux 3.11+ (c. 2013) & kNtFileAttributeTemporary|kNtFileFlagDeleteOnClose +syscon open O_SPARSE 0 0 0 0 0x00040000 # we invented it syscon open O_NDELAY 0x0800 4 4 4 0 # bsd consensus & kNtFileFlagWriteThrough>>8 → 0x00800000 (???) syscon open O_NONBLOCK 0x0800 4 4 4 0 # bsd consensus -syscon open O_CLOEXEC 0x080000 0x01000000 0x100000 0x010000 0x00080000 # NT faked as Linux -syscon open O_SPARSE 0 0 0 0 0x00040000 # we invented it -syscon open O_TMPFILE 0x410000 0 0 0 0x04000100 # Linux 3.11+ (c. 2013) & kNtFileAttributeTemporary|kNtFileFlagDeleteOnClose syscon open O_ASYNC 0x2000 0x40 0x40 0x40 0 # bsd consensus syscon open O_NOFOLLOW 0x020000 0x0100 0x0100 0x0100 0 # bsd consensus syscon open O_SYNC 0x101000 0x80 0x80 0x80 0 # bsd consensus -syscon open O_DIRECTORY 0x010000 0x100000 0x020000 0x020000 0 syscon open O_NOCTTY 0x0100 0x020000 0x8000 0x8000 0 # used for remote viewing (default behavior on freebsd) syscon open O_DSYNC 0x1000 0x400000 0 0x80 0 syscon open O_RSYNC 0x101000 0 0 0x80 0 @@ -236,19 +236,19 @@ syscon compat O_LARGEFILE 0 0 0 0 0 syscon compat MAP_FILE 0 0 0 0 0 # consensus syscon mmap MAP_SHARED 1 1 1 1 1 # forced consensus & faked nt syscon mmap MAP_PRIVATE 2 2 2 2 2 # forced consensus & faked nt -syscon mmap MAP_FIXED 16 16 16 16 16 # unix consensus; openbsd appears to forbid; faked nt +syscon mmap MAP_FIXED 0x10 0x10 0x10 0x10 0x10 # unix consensus; openbsd appears to forbid; faked nt syscon mmap MAP_ANONYMOUS 0x20 0x1000 0x1000 0x1000 0x20 # bsd consensus; faked nt -syscon compat MAP_ANON 0x20 0x1000 0x1000 0x1000 0x20 # bsd consensus; faked nt syscon mmap MAP_NORESERVE 0x4000 0x40 0 0 0 # Linux calls it "reserve"; NT calls it "commit"? which is default? syscon mmap MAP_GROWSDOWN 0x0100 0 0x0400 0x0400 0x100000 # MAP_STACK on BSD; MEM_TOP_DOWN on NT -syscon compat MAP_STACK 0x020000 0 0x0400 0x4000 0x100000 syscon mmap MAP_HUGETLB 0x040000 0 0 0 0x80000000 # kNtSecLargePages syscon mmap MAP_HUGE_MASK 63 0 0 0 0 syscon mmap MAP_HUGE_SHIFT 26 0 0 0 0 syscon mmap MAP_LOCKED 0x2000 0 0 0 0 -syscon mmap MAP_NONBLOCK 0x010000 0 0 0 0 +syscon mmap MAP_NONBLOCK 0x10000 0 0 0 0 syscon mmap MAP_POPULATE 0x8000 0 0 0 0 # can avoid madvise(MADV_WILLNEED) on private file mapping syscon mmap MAP_TYPE 15 0 0 0 0 # what is it +syscon compat MAP_ANON 0x20 0x1000 0x1000 0x1000 0x20 # bsd consensus; faked nt +syscon compat MAP_STACK 0x020000 0 0x0400 0x4000 0x100000 syscon compat MAP_EXECUTABLE 0x1000 0 0 0 0 # ignored syscon compat MAP_DENYWRITE 0x0800 0 0 0 0 syscon compat MAP_32BIT 0x40 0 0x080000 0 0 # iffy @@ -581,6 +581,35 @@ syscon so SO_TIMESTAMPING 37 0 0 0 0 syscon so SO_TIMESTAMPNS 35 0 0 0 0 syscon so SO_WIFI_STATUS 41 0 0 0 0 +syscon sol SOL_IP 0 0 0 0 0 # consensus +syscon sol SOL_SOCKET 1 0xffff 0xffff 0xffff 0xffff # bsd+nt consensus (todo: what's up with ipproto_icmp overlap) +syscon sol SOL_TCP 6 6 6 6 6 # consensus +syscon sol SOL_UDP 17 17 17 17 17 # consensus +syscon sol SOL_IPV6 41 41 41 41 41 +syscon sol SOL_ICMPV6 58 58 58 58 0 +syscon sol SOL_AAL 265 0 0 0 0 +syscon sol SOL_ALG 279 0 0 0 0 +syscon sol SOL_ATM 264 0 0 0 0 +syscon sol SOL_BLUETOOTH 274 0 0 0 0 +syscon sol SOL_CAIF 278 0 0 0 0 +syscon sol SOL_DCCP 269 0 0 0 0 +syscon sol SOL_DECNET 261 0 0 0 0 +syscon sol SOL_IRDA 266 0 0 0 0 +syscon sol SOL_IUCV 277 0 0 0 0 +syscon sol SOL_KCM 281 0 0 0 0 +syscon sol SOL_LLC 268 0 0 0 0 +syscon sol SOL_NETBEUI 267 0 0 0 0 +syscon sol SOL_NETLINK 270 0 0 0 0 +syscon sol SOL_NFC 280 0 0 0 0 +syscon sol SOL_PACKET 263 0 0 0 0 +syscon sol SOL_PNPIPE 275 0 0 0 0 +syscon sol SOL_PPPOL2TP 273 0 0 0 0 +syscon sol SOL_RAW 255 0 0 0 0 +syscon sol SOL_RDS 276 0 0 0 0 +syscon sol SOL_RXRPC 272 0 0 0 0 +syscon sol SOL_TIPC 271 0 0 0 0 +syscon sol SOL_X25 262 0 0 0 0 + # {set,get}sockopt(fd, level=IPPROTO_TCP, X, ...) # » most elite of all tuning groups # @@ -591,7 +620,7 @@ syscon so SO_WIFI_STATUS 41 0 0 0 0 # @see https://www.iana.org/assignments/tcp-parameters/tcp-parameters.txt # # group name GNU/Systemd XNU's Not UNIX FreeBSD OpenBSD XENIX Commentary -syscon tcp TCP_NODELAY 1 1 1 1 1 # consensus +syscon tcp TCP_NODELAY 1 1 1 1 1 # strong consensus for disabling nagle's algorithm syscon tcp TCP_MAXSEG 2 2 2 2 0 # reduces tcp segment size; see also tcp offloading syscon tcp TCP_FASTOPEN 23 261 0x0401 0 15 # reduces roundtrips; for listener; Linux 3.7+ (c. 2012) / or is windows it 0x22? /proc/sys/net/ipv4/tcp_fastopen TODO(jart): MSG_FASTOPEN syscon tcp TCP_KEEPIDLE 4 0 0x100 0 0 # keepalives @@ -621,6 +650,39 @@ syscon tcp TCP_REPAIR_OPTIONS 22 0 0 0 0 # what is it syscon tcp TCP_REPAIR_QUEUE 20 0 0 0 0 # what is it syscon tcp TCP_THIN_LINEAR_TIMEOUTS 16 0 0 0 0 # what is it +syscon iproto IPPROTO_IP 0 0 0 0 0 # consensus +syscon iproto IPPROTO_ICMP 1 1 1 1 1 # consensus +syscon iproto IPPROTO_TCP 6 6 6 6 6 # consensus +syscon iproto IPPROTO_UDP 17 17 17 17 17 # consensus +syscon iproto IPPROTO_RAW 255 255 255 255 255 # consensus +syscon iproto IPPROTO_HOPOPTS 0 0 0 0 0 # consensus +syscon iproto IPPROTO_IDP 22 22 22 22 22 # consensus +syscon iproto IPPROTO_IGMP 2 2 2 2 2 # consensus +syscon iproto IPPROTO_PUP 12 12 12 12 12 # consensus +syscon iproto IPPROTO_AH 51 51 51 51 0 # unix consensus +syscon iproto IPPROTO_DSTOPTS 60 60 60 60 0 # unix consensus +syscon iproto IPPROTO_EGP 8 8 8 8 0 # unix consensus +syscon iproto IPPROTO_ENCAP 98 98 98 98 0 # unix consensus +syscon iproto IPPROTO_ESP 50 50 50 50 0 # unix consensus +syscon iproto IPPROTO_FRAGMENT 44 44 44 44 0 # unix consensus +syscon iproto IPPROTO_GRE 47 47 47 47 0 # unix consensus +syscon iproto IPPROTO_ICMPV6 58 58 58 58 0 # unix consensus +syscon iproto IPPROTO_IPIP 4 4 4 4 0 # unix consensus +syscon iproto IPPROTO_IPV6 41 41 41 41 0 # unix consensus +syscon iproto IPPROTO_NONE 59 59 59 59 0 # unix consensus +syscon iproto IPPROTO_PIM 103 103 103 103 0 # unix consensus +syscon iproto IPPROTO_ROUTING 43 43 43 43 0 # unix consensus +syscon iproto IPPROTO_RSVP 46 46 46 46 0 # unix consensus +syscon iproto IPPROTO_TP 29 29 29 29 0 # unix consensus +syscon iproto IPPROTO_MPLS 137 0 137 137 0 +syscon iproto IPPROTO_MTP 92 92 92 0 0 +syscon iproto IPPROTO_SCTP 132 132 132 0 0 +syscon iproto IPPROTO_MH 135 0 135 0 0 +syscon iproto IPPROTO_UDPLITE 136 0 136 0 0 +syscon iproto IPPROTO_BEETPH 94 0 0 0 0 +syscon iproto IPPROTO_COMP 108 0 0 0 0 +syscon iproto IPPROTO_DCCP 33 0 0 0 0 + syscon misc EXTA 14 0x4b00 0x4b00 0x4b00 0 # bsd consensus syscon misc EXTB 15 0x9600 0x9600 0x9600 0 # bsd consensus syscon misc ERA 0x02002c 45 45 0 0 @@ -932,39 +994,6 @@ syscon icmp6 ICMP6_ROUTER_RENUMBERING 138 138 138 138 0 # unix consen syscon icmp6 ICMP6_RR_RESULT_FLAGS_FORBIDDEN 0x0100 0x0100 0x0100 0x0100 0 # unix consensus syscon icmp6 ICMP6_RR_RESULT_FLAGS_OOB 0x0200 0x0200 0x0200 0x0200 0 # unix consensus -syscon iproto IPPROTO_HOPOPTS 0 0 0 0 0 # consensus -syscon iproto IPPROTO_ICMP 1 1 1 1 1 # consensus -syscon iproto IPPROTO_IDP 22 22 22 22 22 # consensus -syscon iproto IPPROTO_IGMP 2 2 2 2 2 # consensus -syscon iproto IPPROTO_IP 0 0 0 0 0 # consensus -syscon iproto IPPROTO_PUP 12 12 12 12 12 # consensus -syscon iproto IPPROTO_RAW 255 255 255 255 255 # consensus -syscon iproto IPPROTO_TCP 6 6 6 6 6 # consensus -syscon iproto IPPROTO_UDP 17 17 17 17 17 # consensus -syscon iproto IPPROTO_AH 51 51 51 51 0 # unix consensus -syscon iproto IPPROTO_DSTOPTS 60 60 60 60 0 # unix consensus -syscon iproto IPPROTO_EGP 8 8 8 8 0 # unix consensus -syscon iproto IPPROTO_ENCAP 98 98 98 98 0 # unix consensus -syscon iproto IPPROTO_ESP 50 50 50 50 0 # unix consensus -syscon iproto IPPROTO_FRAGMENT 44 44 44 44 0 # unix consensus -syscon iproto IPPROTO_GRE 47 47 47 47 0 # unix consensus -syscon iproto IPPROTO_ICMPV6 58 58 58 58 0 # unix consensus -syscon iproto IPPROTO_IPIP 4 4 4 4 0 # unix consensus -syscon iproto IPPROTO_IPV6 41 41 41 41 0 # unix consensus -syscon iproto IPPROTO_NONE 59 59 59 59 0 # unix consensus -syscon iproto IPPROTO_PIM 103 103 103 103 0 # unix consensus -syscon iproto IPPROTO_ROUTING 43 43 43 43 0 # unix consensus -syscon iproto IPPROTO_RSVP 46 46 46 46 0 # unix consensus -syscon iproto IPPROTO_TP 29 29 29 29 0 # unix consensus -syscon iproto IPPROTO_MPLS 137 0 137 137 0 -syscon iproto IPPROTO_MTP 92 92 92 0 0 -syscon iproto IPPROTO_SCTP 132 132 132 0 0 -syscon iproto IPPROTO_MH 135 0 135 0 0 -syscon iproto IPPROTO_UDPLITE 136 0 136 0 0 -syscon iproto IPPROTO_BEETPH 94 0 0 0 0 -syscon iproto IPPROTO_COMP 108 0 0 0 0 -syscon iproto IPPROTO_DCCP 33 0 0 0 0 - syscon sio SIOCADDMULTI 0x8931 0x80206931 0x80206931 0x80206931 0 # bsd consensus syscon sio SIOCATMARK 0x8905 0x40047307 0x40047307 0x40047307 0 # bsd consensus syscon sio SIOCDELMULTI 0x8932 0x80206932 0x80206932 0x80206932 0 # bsd consensus @@ -1413,35 +1442,6 @@ syscon msg MSG_RST 0x1000 0 0 0 0 syscon msg MSG_STAT 11 0 0 0 0 syscon msg MSG_SYN 0x0400 0 0 0 0 -syscon sol SOL_IP 0 0 0 0 0 # consensus -syscon sol SOL_SOCKET 1 0xffff 0xffff 0xffff 0xffff # bsd+nt consensus -syscon sol SOL_TCP 6 6 6 6 6 -syscon sol SOL_UDP 17 17 17 17 17 -syscon sol SOL_IPV6 41 41 41 41 41 -syscon sol SOL_ICMPV6 58 58 58 58 0 -syscon sol SOL_AAL 265 0 0 0 0 -syscon sol SOL_ALG 279 0 0 0 0 -syscon sol SOL_ATM 264 0 0 0 0 -syscon sol SOL_BLUETOOTH 274 0 0 0 0 -syscon sol SOL_CAIF 278 0 0 0 0 -syscon sol SOL_DCCP 269 0 0 0 0 -syscon sol SOL_DECNET 261 0 0 0 0 -syscon sol SOL_IRDA 266 0 0 0 0 -syscon sol SOL_IUCV 277 0 0 0 0 -syscon sol SOL_KCM 281 0 0 0 0 -syscon sol SOL_LLC 268 0 0 0 0 -syscon sol SOL_NETBEUI 267 0 0 0 0 -syscon sol SOL_NETLINK 270 0 0 0 0 -syscon sol SOL_NFC 280 0 0 0 0 -syscon sol SOL_PACKET 263 0 0 0 0 -syscon sol SOL_PNPIPE 275 0 0 0 0 -syscon sol SOL_PPPOL2TP 273 0 0 0 0 -syscon sol SOL_RAW 255 0 0 0 0 -syscon sol SOL_RDS 276 0 0 0 0 -syscon sol SOL_RXRPC 272 0 0 0 0 -syscon sol SOL_TIPC 271 0 0 0 0 -syscon sol SOL_X25 262 0 0 0 0 - syscon in IN_LOOPBACKNET 127 127 127 127 0 # unix consensus syscon in IN_ACCESS 1 0 0 0 0 syscon in IN_ALL_EVENTS 0x0fff 0 0 0 0 @@ -2477,9 +2477,9 @@ syscon termios NETGRAPHDISC 0 0 0x6 0 -1 # boop syscon termios H4DISC 0 0 0x7 0 -1 # boop syscon termios ISIG 0b0000000000000001 0b0000000010000000 0b0000000010000000 0b0000000010000000 0b0000000000000001 # termios.c_lflag|=ISIG makes Ctrl-C, Ctrl-\, etc. generate signals -syscon termios ICANON 0b0000000000000010 0b0000000100000000 0b0000000100000000 0b0000000100000000 0b0000000000000010 # termios.c_lflag&=~ICANON disables 1960's version of gnu readline +syscon termios ICANON 0b0000000000000010 0b0000000100000000 0b0000000100000000 0b0000000100000000 0b0000000000000010 # termios.c_lflag&=~ICANON disables 1960's version of gnu readline (see also VMIN) syscon termios XCASE 0b0000000000000100 0 0 16777216 0b0000000000000100 # termios.c_lflag -syscon termios ECHO 0b0000000000001000 0b0000000000001000 0b0000000000001000 0b0000000000001000 0b0000000000001000 # termios.c_lflag&=~ECHO is for passwords +syscon termios ECHO 0b0000000000001000 0b0000000000001000 0b0000000000001000 0b0000000000001000 0b0000000000001000 # termios.c_lflag&=~ECHO is for passwords and raw mode syscon termios ECHOE 0b0000000000010000 0b0000000000000010 0b0000000000000010 0b0000000000000010 0b0000000000010000 # termios.c_lflag syscon termios ECHOK 0b0000000000100000 0b0000000000000100 0b0000000000000100 0b0000000000000100 0b0000000000100000 # termios.c_lflag syscon termios ECHONL 0b0000000001000000 0b0000000000010000 0b0000000000010000 0b0000000000010000 0b0000000001000000 # termios.c_lflag @@ -2493,8 +2493,8 @@ syscon termios PENDIN 0b0100000000000000 536870912 536870912 536870912 0b syscon termios IEXTEN 0b1000000000000000 0b0000010000000000 0b0000010000000000 0b0000010000000000 0b1000000000000000 # termios.c_lflag&=~IEXTEN disables platform input processing magic syscon termios EXTPROC 65536 0b0000100000000000 0b0000100000000000 0b0000100000000000 65536 # termios.c_lflag -syscon termios IGNBRK 0b0000000000000001 0b0000000000000001 0b0000000000000001 0b0000000000000001 0b0000000000000001 # termios.c_iflag it's complicated UNIXCONSENSUS -syscon termios BRKINT 0b0000000000000010 0b0000000000000010 0b0000000000000010 0b0000000000000010 0b0000000000000010 # termios.c_iflag it's complicated UNIXCONSENSUS +syscon termios IGNBRK 0b0000000000000001 0b0000000000000001 0b0000000000000001 0b0000000000000001 0b0000000000000001 # termios.c_iflag it's complicated, uart only? UNIXCONSENSUS +syscon termios BRKINT 0b0000000000000010 0b0000000000000010 0b0000000000000010 0b0000000000000010 0b0000000000000010 # termios.c_iflag it's complicated, uart only? UNIXCONSENSUS syscon termios IGNPAR 0b0000000000000100 0b0000000000000100 0b0000000000000100 0b0000000000000100 0b0000000000000100 # termios.c_iflag|=IGNPAR ignores parity and framing errors; see PARMRK UNIXCONSENSUS syscon termios PARMRK 0b0000000000001000 0b0000000000001000 0b0000000000001000 0b0000000000001000 0b0000000000001000 # termios.c_iflag|=PARMRK passes-through parity bit UNIXCONSENSUS syscon termios INPCK 0b0000000000010000 0b0000000000010000 0b0000000000010000 0b0000000000010000 0b0000000000010000 # termios.c_iflag|=INPCK enables parity checking UNIXCONSENSUS @@ -2554,8 +2554,8 @@ syscon termios VQUIT 1 9 9 9 0 # termios.c_cc[VQUIT]=𝑥 syscon termios VERASE 2 3 3 3 0 # termios.c_cc[VERASE]=𝑥 syscon termios VKILL 3 5 5 5 0 # termios.c_cc[VKILL]=𝑥 syscon termios VEOF 4 0 0 0 0 # termios.c_cc[VEOF]=𝑥 -syscon termios VTIME 5 17 17 17 0 # termios.c_cc[VTIME]=𝑥 yields 𝑥×𝟷𝟶𝟶ms non-canonical read timeout -syscon termios VMIN 6 16 16 16 0 # termios.c_cc[VMIN]=𝑥 +syscon termios VTIME 5 17 17 17 0 # termios.c_cc[VTIME]=𝑥 sets non-canonical read timeout to 𝑥×𝟷𝟶𝟶ms which is needed when entering escape sequences manually with the escape key +syscon termios VMIN 6 16 16 16 0 # termios.c_cc[VMIN]=𝑥 in non-canonical mode can be set to 0 for non-blocking reads, 1 for single character raw mode reads, or higher to buffer syscon termios VSWTC 7 0 0 0 0 # termios.c_cc[VSWTC]=𝑥 syscon termios VSTART 8 12 12 12 0 # termios.c_cc[VSTART]=𝑥 syscon termios VSTOP 9 13 13 13 0 # termios.c_cc[VSTOP]=𝑥 diff --git a/libc/sysv/consts/O_DIRECTORY.s b/libc/sysv/consts/O_DIRECTORY.s index 7d95cf9b..72a3af84 100644 --- a/libc/sysv/consts/O_DIRECTORY.s +++ b/libc/sysv/consts/O_DIRECTORY.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon open O_DIRECTORY 0x010000 0x100000 0x020000 0x020000 0 +.syscon open O_DIRECTORY 0x010000 0x100000 0x020000 0x020000 0x02000000 diff --git a/libc/sysv/consts/ipproto.h b/libc/sysv/consts/ipproto.h index 08527221..f904d30e 100644 --- a/libc/sysv/consts/ipproto.h +++ b/libc/sysv/consts/ipproto.h @@ -41,38 +41,39 @@ hidden extern const long IPPROTO_UDPLITE; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#define IPPROTO_AH SYMBOLIC(IPPROTO_AH) -#define IPPROTO_BEETPH SYMBOLIC(IPPROTO_BEETPH) -#define IPPROTO_COMP SYMBOLIC(IPPROTO_COMP) -#define IPPROTO_DCCP SYMBOLIC(IPPROTO_DCCP) -#define IPPROTO_DSTOPTS SYMBOLIC(IPPROTO_DSTOPTS) -#define IPPROTO_EGP SYMBOLIC(IPPROTO_EGP) -#define IPPROTO_ENCAP SYMBOLIC(IPPROTO_ENCAP) -#define IPPROTO_ESP SYMBOLIC(IPPROTO_ESP) -#define IPPROTO_FRAGMENT SYMBOLIC(IPPROTO_FRAGMENT) -#define IPPROTO_GRE SYMBOLIC(IPPROTO_GRE) -#define IPPROTO_HOPOPTS SYMBOLIC(IPPROTO_HOPOPTS) +#define IPPROTO_IP LITERALLY(0) #define IPPROTO_ICMP LITERALLY(1) -#define IPPROTO_ICMPV6 SYMBOLIC(IPPROTO_ICMPV6) -#define IPPROTO_IDP SYMBOLIC(IPPROTO_IDP) -#define IPPROTO_IGMP SYMBOLIC(IPPROTO_IGMP) -#define IPPROTO_IP LITERALLY(0) -#define IPPROTO_IPIP SYMBOLIC(IPPROTO_IPIP) -#define IPPROTO_IPV6 SYMBOLIC(IPPROTO_IPV6) -#define IPPROTO_MAX SYMBOLIC(IPPROTO_MAX) -#define IPPROTO_MH SYMBOLIC(IPPROTO_MH) -#define IPPROTO_MPLS SYMBOLIC(IPPROTO_MPLS) -#define IPPROTO_MTP SYMBOLIC(IPPROTO_MTP) -#define IPPROTO_NONE SYMBOLIC(IPPROTO_NONE) -#define IPPROTO_PIM SYMBOLIC(IPPROTO_PIM) -#define IPPROTO_PUP SYMBOLIC(IPPROTO_PUP) -#define IPPROTO_RAW LITERALLY(255) -#define IPPROTO_ROUTING SYMBOLIC(IPPROTO_ROUTING) -#define IPPROTO_RSVP SYMBOLIC(IPPROTO_RSVP) -#define IPPROTO_SCTP SYMBOLIC(IPPROTO_SCTP) -#define IPPROTO_TCP LITERALLY(6) -#define IPPROTO_TP SYMBOLIC(IPPROTO_TP) -#define IPPROTO_UDP LITERALLY(17) -#define IPPROTO_UDPLITE SYMBOLIC(IPPROTO_UDPLITE) +#define IPPROTO_TCP LITERALLY(6) +#define IPPROTO_UDP LITERALLY(17) +#define IPPROTO_RAW LITERALLY(255) + +#define IPPROTO_AH SYMBOLIC(IPPROTO_AH) +#define IPPROTO_BEETPH SYMBOLIC(IPPROTO_BEETPH) +#define IPPROTO_COMP SYMBOLIC(IPPROTO_COMP) +#define IPPROTO_DCCP SYMBOLIC(IPPROTO_DCCP) +#define IPPROTO_DSTOPTS SYMBOLIC(IPPROTO_DSTOPTS) +#define IPPROTO_EGP SYMBOLIC(IPPROTO_EGP) +#define IPPROTO_ENCAP SYMBOLIC(IPPROTO_ENCAP) +#define IPPROTO_ESP SYMBOLIC(IPPROTO_ESP) +#define IPPROTO_FRAGMENT SYMBOLIC(IPPROTO_FRAGMENT) +#define IPPROTO_GRE SYMBOLIC(IPPROTO_GRE) +#define IPPROTO_HOPOPTS SYMBOLIC(IPPROTO_HOPOPTS) +#define IPPROTO_ICMPV6 SYMBOLIC(IPPROTO_ICMPV6) +#define IPPROTO_IDP SYMBOLIC(IPPROTO_IDP) +#define IPPROTO_IGMP SYMBOLIC(IPPROTO_IGMP) +#define IPPROTO_IPIP SYMBOLIC(IPPROTO_IPIP) +#define IPPROTO_IPV6 SYMBOLIC(IPPROTO_IPV6) +#define IPPROTO_MAX SYMBOLIC(IPPROTO_MAX) +#define IPPROTO_MH SYMBOLIC(IPPROTO_MH) +#define IPPROTO_MPLS SYMBOLIC(IPPROTO_MPLS) +#define IPPROTO_MTP SYMBOLIC(IPPROTO_MTP) +#define IPPROTO_NONE SYMBOLIC(IPPROTO_NONE) +#define IPPROTO_PIM SYMBOLIC(IPPROTO_PIM) +#define IPPROTO_PUP SYMBOLIC(IPPROTO_PUP) +#define IPPROTO_ROUTING SYMBOLIC(IPPROTO_ROUTING) +#define IPPROTO_RSVP SYMBOLIC(IPPROTO_RSVP) +#define IPPROTO_SCTP SYMBOLIC(IPPROTO_SCTP) +#define IPPROTO_TP SYMBOLIC(IPPROTO_TP) +#define IPPROTO_UDPLITE SYMBOLIC(IPPROTO_UDPLITE) #endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_IPPROTO_H_ */ diff --git a/libc/sysv/systemfive.S b/libc/sysv/systemfive.S index 24b0666a..5108b382 100644 --- a/libc/sysv/systemfive.S +++ b/libc/sysv/systemfive.S @@ -90,7 +90,6 @@ systemfive: .quad 0 .endobj systemfive,globl,hidden .previous - .Lanchorpoint: systemfive.linux: movswl %ax,%eax @@ -104,24 +103,31 @@ systemfive.linux: cmp $-4095,%rax jae systemfive.error ret + .endfn systemfive.linux,globl,hidden systemfive.error: neg %eax / 𝑠𝑙𝑖𝑑𝑒 + .endfn systemfive.error systemfive.errno: mov %eax,errno(%rip) - pushpop -1,%rax + push $-1 + pop %rax stc ret + .endfn systemfive.errno systemfive.enosys: mov ENOSYS(%rip),%eax jmp systemfive.errno + .endfn systemfive.enosys systemfive.openbsd: shr $48,%rax jmp systemfive.bsd + .endfn systemfive.openbsd,globl,hidden systemfive.freebsd: shr $32,%rax movzwl %ax,%eax / 𝑠𝑙𝑖𝑑𝑒 + .endfn systemfive.freebsd,globl,hidden systemfive.bsd: cmp $0xfff,%ax jae systemfive.enosys @@ -132,26 +138,21 @@ systemfive.bsd: pop %rbp jc systemfive.errno ret + .endfn systemfive.bsd systemfive.xnu: -/ Do this -/ 0x35022721530005 -/ │└┴┴─┐ -/ └┐ ├┬┐ -/ 0x00000002000153 +/ do this to rax +/ 0x????????2153???? +/ │└┴┴─┐ +/ └┐ ├┬┐ +/ 0x0000000002000153 mov %eax,%r11d - shr $0x10,%eax - and $0xfff,%eax - shr $0x1c,%r11d - shl $0x18,%r11d + shr $4*7,%r11d + shl $4*6,%r11d + shl $4*1,%eax + shr $4*5,%eax or %r11d,%eax jmp systemfive.bsd - .endfn systemfive.errno,globl,hidden - .endfn systemfive.error,globl,hidden - .endfn systemfive.enosys,globl,hidden .endfn systemfive.xnu,globl,hidden - .endfn systemfive.freebsd,globl,hidden - .endfn systemfive.openbsd,globl,hidden - .endfn systemfive.linux,globl,hidden .previous / Initializes System Five system call support. diff --git a/libc/testlib/testmain.c b/libc/testlib/testmain.c index a10d526b..5dda0cc9 100644 --- a/libc/testlib/testmain.c +++ b/libc/testlib/testmain.c @@ -51,11 +51,14 @@ static testonly void PrintUsage(int rc, FILE *f) { static testonly void GetOpts(int argc, char *argv[]) { int opt; - while ((opt = getopt(argc, argv, "?hb")) != -1) { + while ((opt = getopt(argc, argv, "?hbv")) != -1) { switch (opt) { case 'b': runbenchmarks_ = true; break; + case 'v': + ++g_loglevel; + break; case '?': case 'h': PrintUsage(EXIT_SUCCESS, stdout); diff --git a/libc/testlib/testmem.c b/libc/testlib/testmem.c index b66d4ad5..d7d7de0b 100644 --- a/libc/testlib/testmem.c +++ b/libc/testlib/testmem.c @@ -33,6 +33,8 @@ #include "libc/sysv/consts/prot.h" #include "libc/testlib/testlib.h" +/* TODO(jart): DELETE */ + struct TestMemoryStack g_testmem; struct TestMemoryStack g_testmem_trash; static struct TestAllocation g_testmem_scratch[2][8]; @@ -86,16 +88,7 @@ static struct TestAllocation talloc(size_t n) { } } -static void testmem_fini(void) { - CHECK_EQ(0, g_testmem.i); - free_s(&g_testmem.p); - while (g_testmem_trash.i) { - testmem_destroy(testmem_pop(&g_testmem_trash)); - } -} - static textstartup void testmem_init(void) { - atexit(testmem_fini); g_testmem.p = g_testmem_scratch[0]; g_testmem.n = ARRAYLEN(g_testmem_scratch[0]); g_testmem_trash.p = g_testmem_scratch[1]; diff --git a/libc/testlib/testrunner.c b/libc/testlib/testrunner.c index 68448e0c..4836ee85 100644 --- a/libc/testlib/testrunner.c +++ b/libc/testlib/testrunner.c @@ -17,10 +17,10 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/safemacros.h" #include "libc/bits/weaken.h" #include "libc/calls/internal.h" #include "libc/errno.h" +#include "libc/macros.h" #include "libc/nt/process.h" #include "libc/stdio/stdio.h" #include "libc/testlib/testlib.h" @@ -36,10 +36,8 @@ void testlib_finish(void) { } noreturn void testlib_abort(void) { - int rc; testlib_finish(); - rc = min(255, g_testlib_failed); - quick_exit(rc); /* so we don't run __testmemory_fini() */ + exit(MIN(255, g_testlib_failed)); unreachable; } diff --git a/libc/time/localtime.c b/libc/time/localtime.c index e1ba128d..a19afd2c 100644 --- a/libc/time/localtime.c +++ b/libc/time/localtime.c @@ -28,7 +28,19 @@ #define TM_ZONE tm_zone #define INITIALIZE(x) x = 0 +STATIC_YOINK("zip_uri_support"); +STATIC_YOINK("usr/share/zoneinfo/Beijing"); +STATIC_YOINK("usr/share/zoneinfo/Berlin"); +STATIC_YOINK("usr/share/zoneinfo/Boulder"); +STATIC_YOINK("usr/share/zoneinfo/Chicago"); STATIC_YOINK("usr/share/zoneinfo/GST"); +STATIC_YOINK("usr/share/zoneinfo/Honolulu"); +STATIC_YOINK("usr/share/zoneinfo/Israel"); +STATIC_YOINK("usr/share/zoneinfo/Japan"); +STATIC_YOINK("usr/share/zoneinfo/London"); +STATIC_YOINK("usr/share/zoneinfo/Melbourne"); +STATIC_YOINK("usr/share/zoneinfo/New_York"); +STATIC_YOINK("usr/share/zoneinfo/Singapore"); /* clang-format off */ /* @@ -807,15 +819,15 @@ getrule(strp, rulep) static time_t transtime(janfirst, year, rulep, offset) -const time_t janfirst; -const int year; -register const struct rule * const rulep; -const int32_t offset; + const time_t janfirst; + const int year; + register const struct rule * const rulep; + const int32_t offset; { - register int leapyear; - register time_t value; - register int i; - int d, m1, yy0, yy1, yy2, dow; + register int leapyear; + register time_t value; + register int i; + int d, m1, yy0, yy1, yy2, dow; INITIALIZE(value); leapyear = isleap(year); @@ -857,9 +869,9 @@ const int32_t offset; */ m1 = (rulep->r_mon + 9) % 12 + 1; yy0 = (rulep->r_mon <= 2) ? (year - 1) : year; - yy1 = div100int64(yy0); - yy2 = rem100int64(yy0); - dow = (div10int64(26 * m1 - 2) + + yy1 = yy0 / 100; + yy2 = yy0 % 100; + dow = ((26 * m1 - 2) / 10 + 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7; if (dow < 0) dow += DAYSPERWEEK; diff --git a/libc/time/strftime.c b/libc/time/strftime.c index 21d43ad1..5c056070 100644 --- a/libc/time/strftime.c +++ b/libc/time/strftime.c @@ -16,6 +16,7 @@ │ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED │ │ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" #include "libc/calls/calls.h" #include "libc/fmt/fmt.h" #include "libc/macros.h" @@ -381,6 +382,7 @@ static char *strftime_timefmt(char *p, const char *pe, const char *format, */ size_t strftime(char *s, size_t size, const char *f, const struct tm *t) { char *p; + assert(t); p = strftime_timefmt(s, s + size, f, t); if (p < s + size) { *p = '\0'; diff --git a/libc/time/time.h b/libc/time/time.h index 772ea20c..8e5203c5 100644 --- a/libc/time/time.h +++ b/libc/time/time.h @@ -27,9 +27,8 @@ int sleep(uint32_t); int usleep(uint32_t); int nanosleep(const struct timespec *, struct timespec *); unsigned alarm(unsigned); -int getitimer(int, struct itimerval *) paramsnonnull(); -int setitimer(int, const struct itimerval *, struct itimerval *) - paramsnonnull((2)); +int getitimer(int, struct itimerval *); +int setitimer(int, const struct itimerval *, struct itimerval *); void tzset(void); struct tm *gmtime(const int64_t *); diff --git a/libc/time/time.mk b/libc/time/time.mk index ba1d8daf..cd5b0067 100644 --- a/libc/time/time.mk +++ b/libc/time/time.mk @@ -3,28 +3,11 @@ PKGS += LIBC_TIME -LIBC_TIME_ZONEINFOS = \ - Beijing \ - Berlin \ - Boulder \ - Chicago \ - GST \ - Honolulu \ - Israel \ - Japan \ - London \ - Melbourne \ - New_York \ - Singapore \ - Sydney \ - UTC - LIBC_TIME_ARTIFACTS += LIBC_TIME_A +LIBC_TIME_ZONEINFOS = $(wildcard usr/share/zoneinfo/*) LIBC_TIME = $(LIBC_TIME_A_DEPS) $(LIBC_TIME_A) LIBC_TIME_A = o/$(MODE)/libc/time/time.a -LIBC_TIME_A_FILES := \ - $(wildcard libc/time/struct/*) \ - $(wildcard libc/time/*) +LIBC_TIME_A_FILES := $(wildcard libc/time/struct/*) $(wildcard libc/time/*) LIBC_TIME_A_HDRS := $(filter %.h,$(LIBC_TIME_A_FILES)) LIBC_TIME_A_SRCS_S = $(filter %.S,$(LIBC_TIME_A_FILES)) LIBC_TIME_A_SRCS_C = $(filter %.c,$(LIBC_TIME_A_FILES)) @@ -37,7 +20,8 @@ LIBC_TIME_A_OBJS = \ $(LIBC_TIME_A_SRCS:%=o/$(MODE)/%.zip.o) \ $(LIBC_TIME_A_SRCS_S:%.S=o/$(MODE)/%.o) \ $(LIBC_TIME_A_SRCS_C:%.c=o/$(MODE)/%.o) \ - o//libc/time/zoneinfo.o + $(LIBC_TIME_A_SRCS_C:%.c=o/$(MODE)/%.o) \ + $(LIBC_TIME_ZONEINFOS:%=o/$(MODE)/%.zip.o) LIBC_TIME_A_CHECKS = \ $(LIBC_TIME_A).pkg \ @@ -87,10 +71,6 @@ o/$(MODE)/libc/time/now.o: \ OVERRIDE_CFLAGS += \ -O3 -o//libc/time/zoneinfo.o: \ - $(LIBC_TIME_ZONEINFOS:%=usr/share/zoneinfo/%) - @build/zipobj $(OUTPUT_OPTION) $(LIBC_TIME_ZONEINFOS:%=usr/share/zoneinfo/%) - LIBC_TIME_LIBS = $(foreach x,$(LIBC_TIME_ARTIFACTS),$($(x))) LIBC_TIME_SRCS = $(foreach x,$(LIBC_TIME_ARTIFACTS),$($(x)_SRCS)) LIBC_TIME_HDRS = $(foreach x,$(LIBC_TIME_ARTIFACTS),$($(x)_HDRS)) diff --git a/libc/tinymath/ceil.S b/libc/tinymath/ceil.S index 54f6b77a..d9ff7777 100644 --- a/libc/tinymath/ceil.S +++ b/libc/tinymath/ceil.S @@ -23,14 +23,14 @@ tinymath_ceil: .leafprologue .profilable - movsd .Llol(%rip),%xmm1 - movsd .Lcat(%rip),%xmm2 + movsd nan(%rip),%xmm1 + movsd sig(%rip),%xmm2 andpd %xmm0,%xmm1 comisd %xmm1,%xmm2 jbe 1f cvttsd2siq %xmm0,%rax pxor %xmm1,%xmm1 - movsd .Lmog(%rip),%xmm2 + movsd one(%rip),%xmm2 cvtsi2sdq %rax,%xmm1 cmpnlesd %xmm1,%xmm0 andpd %xmm2,%xmm0 @@ -39,16 +39,9 @@ tinymath_ceil: .endfn tinymath_ceil,globl .alias tinymath_ceil,ceil - .rodata.cst16 -.Llol: .long 4294967295 - .long 2147483647 - .long 0 - .long 0 - .rodata.cst8 -.Lcat: .long 0 - .long 1127219200 -.Lmog: .long 0 - .long 1072693248 +nan: .double nan +sig: .double 0x0010000000000000 +one: .double 1 / vroundsd $_MM_FROUND_TO_POS_INF|_MM_FROUND_NO_EXC,%xmm0,%xmm0,%xmm0 diff --git a/libc/tzfile.h b/libc/tzfile.h index 0f054156..6db15509 100644 --- a/libc/tzfile.h +++ b/libc/tzfile.h @@ -91,7 +91,7 @@ struct tzhead { */ #ifndef TZ_MAX_TIMES -#define TZ_MAX_TIMES 2000 +#define TZ_MAX_TIMES 1200 #endif /* !defined TZ_MAX_TIMES */ #ifndef TZ_MAX_TYPES diff --git a/libc/unicode/blocks.txt b/libc/unicode/blocks.txt new file mode 100644 index 00000000..56877db1 --- /dev/null +++ b/libc/unicode/blocks.txt @@ -0,0 +1,344 @@ +# Blocks-13.0.0.txt +# Date: 2019-07-10, 19:06:00 GMT [KW] +# © 2019 Unicode®, Inc. +# For terms of use, see http://www.unicode.org/terms_of_use.html +# +# Unicode Character Database +# For documentation, see http://www.unicode.org/reports/tr44/ +# +# Format: +# Start Code..End Code; Block Name + +# ================================================ + +# Note: When comparing block names, casing, whitespace, hyphens, +# and underbars are ignored. +# For example, "Latin Extended-A" and "latin extended a" are equivalent. +# For more information on the comparison of property values, +# see UAX #44: http://www.unicode.org/reports/tr44/ +# +# All block ranges start with a value where (cp MOD 16) = 0, +# and end with a value where (cp MOD 16) = 15. In other words, +# the last hexadecimal digit of the start of range is ...0 +# and the last hexadecimal digit of the end of range is ...F. +# This constraint on block ranges guarantees that allocations +# are done in terms of whole columns, and that code chart display +# never involves splitting columns in the charts. +# +# All code points not explicitly listed for Block +# have the value No_Block. + +# Property: Block +# +# @missing: 0000..10FFFF; No_Block + +0000..007F; Basic Latin +0080..00FF; Latin-1 Supplement +0100..017F; Latin Extended-A +0180..024F; Latin Extended-B +0250..02AF; IPA Extensions +02B0..02FF; Spacing Modifier Letters +0300..036F; Combining Diacritical Marks +0370..03FF; Greek and Coptic +0400..04FF; Cyrillic +0500..052F; Cyrillic Supplement +0530..058F; Armenian +0590..05FF; Hebrew +0600..06FF; Arabic +0700..074F; Syriac +0750..077F; Arabic Supplement +0780..07BF; Thaana +07C0..07FF; NKo +0800..083F; Samaritan +0840..085F; Mandaic +0860..086F; Syriac Supplement +08A0..08FF; Arabic Extended-A +0900..097F; Devanagari +0980..09FF; Bengali +0A00..0A7F; Gurmukhi +0A80..0AFF; Gujarati +0B00..0B7F; Oriya +0B80..0BFF; Tamil +0C00..0C7F; Telugu +0C80..0CFF; Kannada +0D00..0D7F; Malayalam +0D80..0DFF; Sinhala +0E00..0E7F; Thai +0E80..0EFF; Lao +0F00..0FFF; Tibetan +1000..109F; Myanmar +10A0..10FF; Georgian +1100..11FF; Hangul Jamo +1200..137F; Ethiopic +1380..139F; Ethiopic Supplement +13A0..13FF; Cherokee +1400..167F; Unified Canadian Aboriginal Syllabics +1680..169F; Ogham +16A0..16FF; Runic +1700..171F; Tagalog +1720..173F; Hanunoo +1740..175F; Buhid +1760..177F; Tagbanwa +1780..17FF; Khmer +1800..18AF; Mongolian +18B0..18FF; Unified Canadian Aboriginal Syllabics Extended +1900..194F; Limbu +1950..197F; Tai Le +1980..19DF; New Tai Lue +19E0..19FF; Khmer Symbols +1A00..1A1F; Buginese +1A20..1AAF; Tai Tham +1AB0..1AFF; Combining Diacritical Marks Extended +1B00..1B7F; Balinese +1B80..1BBF; Sundanese +1BC0..1BFF; Batak +1C00..1C4F; Lepcha +1C50..1C7F; Ol Chiki +1C80..1C8F; Cyrillic Extended-C +1C90..1CBF; Georgian Extended +1CC0..1CCF; Sundanese Supplement +1CD0..1CFF; Vedic Extensions +1D00..1D7F; Phonetic Extensions +1D80..1DBF; Phonetic Extensions Supplement +1DC0..1DFF; Combining Diacritical Marks Supplement +1E00..1EFF; Latin Extended Additional +1F00..1FFF; Greek Extended +2000..206F; General Punctuation +2070..209F; Superscripts and Subscripts +20A0..20CF; Currency Symbols +20D0..20FF; Combining Diacritical Marks for Symbols +2100..214F; Letterlike Symbols +2150..218F; Number Forms +2190..21FF; Arrows +2200..22FF; Mathematical Operators +2300..23FF; Miscellaneous Technical +2400..243F; Control Pictures +2440..245F; Optical Character Recognition +2460..24FF; Enclosed Alphanumerics +2500..257F; Box Drawing +2580..259F; Block Elements +25A0..25FF; Geometric Shapes +2600..26FF; Miscellaneous Symbols +2700..27BF; Dingbats +27C0..27EF; Miscellaneous Mathematical Symbols-A +27F0..27FF; Supplemental Arrows-A +2800..28FF; Braille Patterns +2900..297F; Supplemental Arrows-B +2980..29FF; Miscellaneous Mathematical Symbols-B +2A00..2AFF; Supplemental Mathematical Operators +2B00..2BFF; Miscellaneous Symbols and Arrows +2C00..2C5F; Glagolitic +2C60..2C7F; Latin Extended-C +2C80..2CFF; Coptic +2D00..2D2F; Georgian Supplement +2D30..2D7F; Tifinagh +2D80..2DDF; Ethiopic Extended +2DE0..2DFF; Cyrillic Extended-A +2E00..2E7F; Supplemental Punctuation +2E80..2EFF; CJK Radicals Supplement +2F00..2FDF; Kangxi Radicals +2FF0..2FFF; Ideographic Description Characters +3000..303F; CJK Symbols and Punctuation +3040..309F; Hiragana +30A0..30FF; Katakana +3100..312F; Bopomofo +3130..318F; Hangul Compatibility Jamo +3190..319F; Kanbun +31A0..31BF; Bopomofo Extended +31C0..31EF; CJK Strokes +31F0..31FF; Katakana Phonetic Extensions +3200..32FF; Enclosed CJK Letters and Months +3300..33FF; CJK Compatibility +3400..4DBF; CJK Unified Ideographs Extension A +4DC0..4DFF; Yijing Hexagram Symbols +4E00..9FFF; CJK Unified Ideographs +A000..A48F; Yi Syllables +A490..A4CF; Yi Radicals +A4D0..A4FF; Lisu +A500..A63F; Vai +A640..A69F; Cyrillic Extended-B +A6A0..A6FF; Bamum +A700..A71F; Modifier Tone Letters +A720..A7FF; Latin Extended-D +A800..A82F; Syloti Nagri +A830..A83F; Common Indic Number Forms +A840..A87F; Phags-pa +A880..A8DF; Saurashtra +A8E0..A8FF; Devanagari Extended +A900..A92F; Kayah Li +A930..A95F; Rejang +A960..A97F; Hangul Jamo Extended-A +A980..A9DF; Javanese +A9E0..A9FF; Myanmar Extended-B +AA00..AA5F; Cham +AA60..AA7F; Myanmar Extended-A +AA80..AADF; Tai Viet +AAE0..AAFF; Meetei Mayek Extensions +AB00..AB2F; Ethiopic Extended-A +AB30..AB6F; Latin Extended-E +AB70..ABBF; Cherokee Supplement +ABC0..ABFF; Meetei Mayek +AC00..D7AF; Hangul Syllables +D7B0..D7FF; Hangul Jamo Extended-B +D800..DB7F; High Surrogates +DB80..DBFF; High Private Use Surrogates +DC00..DFFF; Low Surrogates +E000..F8FF; Private Use Area +F900..FAFF; CJK Compatibility Ideographs +FB00..FB4F; Alphabetic Presentation Forms +FB50..FDFF; Arabic Presentation Forms-A +FE00..FE0F; Variation Selectors +FE10..FE1F; Vertical Forms +FE20..FE2F; Combining Half Marks +FE30..FE4F; CJK Compatibility Forms +FE50..FE6F; Small Form Variants +FE70..FEFF; Arabic Presentation Forms-B +FF00..FFEF; Halfwidth and Fullwidth Forms +FFF0..FFFF; Specials +10000..1007F; Linear B Syllabary +10080..100FF; Linear B Ideograms +10100..1013F; Aegean Numbers +10140..1018F; Ancient Greek Numbers +10190..101CF; Ancient Symbols +101D0..101FF; Phaistos Disc +10280..1029F; Lycian +102A0..102DF; Carian +102E0..102FF; Coptic Epact Numbers +10300..1032F; Old Italic +10330..1034F; Gothic +10350..1037F; Old Permic +10380..1039F; Ugaritic +103A0..103DF; Old Persian +10400..1044F; Deseret +10450..1047F; Shavian +10480..104AF; Osmanya +104B0..104FF; Osage +10500..1052F; Elbasan +10530..1056F; Caucasian Albanian +10600..1077F; Linear A +10800..1083F; Cypriot Syllabary +10840..1085F; Imperial Aramaic +10860..1087F; Palmyrene +10880..108AF; Nabataean +108E0..108FF; Hatran +10900..1091F; Phoenician +10920..1093F; Lydian +10980..1099F; Meroitic Hieroglyphs +109A0..109FF; Meroitic Cursive +10A00..10A5F; Kharoshthi +10A60..10A7F; Old South Arabian +10A80..10A9F; Old North Arabian +10AC0..10AFF; Manichaean +10B00..10B3F; Avestan +10B40..10B5F; Inscriptional Parthian +10B60..10B7F; Inscriptional Pahlavi +10B80..10BAF; Psalter Pahlavi +10C00..10C4F; Old Turkic +10C80..10CFF; Old Hungarian +10D00..10D3F; Hanifi Rohingya +10E60..10E7F; Rumi Numeral Symbols +10E80..10EBF; Yezidi +10F00..10F2F; Old Sogdian +10F30..10F6F; Sogdian +10FB0..10FDF; Chorasmian +10FE0..10FFF; Elymaic +11000..1107F; Brahmi +11080..110CF; Kaithi +110D0..110FF; Sora Sompeng +11100..1114F; Chakma +11150..1117F; Mahajani +11180..111DF; Sharada +111E0..111FF; Sinhala Archaic Numbers +11200..1124F; Khojki +11280..112AF; Multani +112B0..112FF; Khudawadi +11300..1137F; Grantha +11400..1147F; Newa +11480..114DF; Tirhuta +11580..115FF; Siddham +11600..1165F; Modi +11660..1167F; Mongolian Supplement +11680..116CF; Takri +11700..1173F; Ahom +11800..1184F; Dogra +118A0..118FF; Warang Citi +11900..1195F; Dives Akuru +119A0..119FF; Nandinagari +11A00..11A4F; Zanabazar Square +11A50..11AAF; Soyombo +11AC0..11AFF; Pau Cin Hau +11C00..11C6F; Bhaiksuki +11C70..11CBF; Marchen +11D00..11D5F; Masaram Gondi +11D60..11DAF; Gunjala Gondi +11EE0..11EFF; Makasar +11FB0..11FBF; Lisu Supplement +11FC0..11FFF; Tamil Supplement +12000..123FF; Cuneiform +12400..1247F; Cuneiform Numbers and Punctuation +12480..1254F; Early Dynastic Cuneiform +13000..1342F; Egyptian Hieroglyphs +13430..1343F; Egyptian Hieroglyph Format Controls +14400..1467F; Anatolian Hieroglyphs +16800..16A3F; Bamum Supplement +16A40..16A6F; Mro +16AD0..16AFF; Bassa Vah +16B00..16B8F; Pahawh Hmong +16E40..16E9F; Medefaidrin +16F00..16F9F; Miao +16FE0..16FFF; Ideographic Symbols and Punctuation +17000..187FF; Tangut +18800..18AFF; Tangut Components +18B00..18CFF; Khitan Small Script +18D00..18D8F; Tangut Supplement +1B000..1B0FF; Kana Supplement +1B100..1B12F; Kana Extended-A +1B130..1B16F; Small Kana Extension +1B170..1B2FF; Nushu +1BC00..1BC9F; Duployan +1BCA0..1BCAF; Shorthand Format Controls +1D000..1D0FF; Byzantine Musical Symbols +1D100..1D1FF; Musical Symbols +1D200..1D24F; Ancient Greek Musical Notation +1D2E0..1D2FF; Mayan Numerals +1D300..1D35F; Tai Xuan Jing Symbols +1D360..1D37F; Counting Rod Numerals +1D400..1D7FF; Mathematical Alphanumeric Symbols +1D800..1DAAF; Sutton SignWriting +1E000..1E02F; Glagolitic Supplement +1E100..1E14F; Nyiakeng Puachue Hmong +1E2C0..1E2FF; Wancho +1E800..1E8DF; Mende Kikakui +1E900..1E95F; Adlam +1EC70..1ECBF; Indic Siyaq Numbers +1ED00..1ED4F; Ottoman Siyaq Numbers +1EE00..1EEFF; Arabic Mathematical Alphabetic Symbols +1F000..1F02F; Mahjong Tiles +1F030..1F09F; Domino Tiles +1F0A0..1F0FF; Playing Cards +1F100..1F1FF; Enclosed Alphanumeric Supplement +1F200..1F2FF; Enclosed Ideographic Supplement +1F300..1F5FF; Miscellaneous Symbols and Pictographs +1F600..1F64F; Emoticons +1F650..1F67F; Ornamental Dingbats +1F680..1F6FF; Transport and Map Symbols +1F700..1F77F; Alchemical Symbols +1F780..1F7FF; Geometric Shapes Extended +1F800..1F8FF; Supplemental Arrows-C +1F900..1F9FF; Supplemental Symbols and Pictographs +1FA00..1FA6F; Chess Symbols +1FA70..1FAFF; Symbols and Pictographs Extended-A +1FB00..1FBFF; Symbols for Legacy Computing +20000..2A6DF; CJK Unified Ideographs Extension B +2A700..2B73F; CJK Unified Ideographs Extension C +2B740..2B81F; CJK Unified Ideographs Extension D +2B820..2CEAF; CJK Unified Ideographs Extension E +2CEB0..2EBEF; CJK Unified Ideographs Extension F +2F800..2FA1F; CJK Compatibility Ideographs Supplement +30000..3134F; CJK Unified Ideographs Extension G +E0000..E007F; Tags +E0100..E01EF; Variation Selectors Supplement +F0000..FFFFF; Supplementary Private Use Area-A +100000..10FFFF; Supplementary Private Use Area-B + +# EOF diff --git a/libc/unicode/unicode-properties.txt b/libc/unicode/unicode-properties.txt new file mode 100644 index 00000000..bbd3eaea --- /dev/null +++ b/libc/unicode/unicode-properties.txt @@ -0,0 +1,30 @@ +Lu = Letter, uppercase +Ll = Letter, lowercase +Lt = Letter, titlecase +Lm = Letter, modifier +Lo = Letter, other +Mn = Mark, nonspacing +Mc = Mark, spacing combining +Me = Mark, enclosing +Nd = Number, decimal digit +Nl = Number, letter +No = Number, other +Pc = Punctuation, connector +Pd = Punctuation, dash +Ps = Punctuation, open +Pe = Punctuation, close +Pi = Punctuation, initial quote (may behave like Ps or Pe depending on usage) +Pf = Punctuation, final quote (may behave like Ps or Pe depending on usage) +Po = Punctuation, other +Sm = Symbol, math +Sc = Symbol, currency +Sk = Symbol, modifier +So = Symbol, other +Zs = Separator, space +Zl = Separator, line +Zp = Separator, paragraph +Cc = Other, control +Cf = Other, format +Cs = Other, surrogate +Co = Other, private use +Cn = Other, not assigned (including noncharacters) diff --git a/libc/unicode/wcwidth.c b/libc/unicode/wcwidth.c index f58620da..0cd39d90 100644 --- a/libc/unicode/wcwidth.c +++ b/libc/unicode/wcwidth.c @@ -24,9 +24,9 @@ */ int wcwidth(wchar_t ucs) { if (ucs == 0) return 0; - if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) { + if ((0 <= ucs && ucs < 32) || (0x7f <= ucs && ucs < 0xA0)) { return -1; - } else if (0 <= ucs && ucs < kCombiningCharsBits && + } else if ((0 <= ucs && ucs < kCombiningCharsBits) && !!(kCombiningChars[ucs >> 3] & (1 << (ucs & 7)))) { return 0; } else if (0 <= ucs && ucs < kEastAsianWidthBits) { diff --git a/libc/x/x.h b/libc/x/x.h index 05b6d0cd..d5454319 100644 --- a/libc/x/x.h +++ b/libc/x/x.h @@ -60,6 +60,8 @@ void *xcalloc(size_t, size_t) attributeallocsize((1, 2)) _XMAL; void *xvalloc(size_t) attributeallocsize((1)) _XMALPG; void *xmemalign(size_t, size_t) attributeallocalign((1)) attributeallocsize((2)) _XMAL; +void *xmemalignzero(size_t, size_t) attributeallocalign((1)) + attributeallocsize((2)) _XMAL; char *xstrdup(const char *) _XPNN _XMAL; char *xstrndup(const char *, size_t) _XPNN _XMAL; char *xstrcat(const char *, ...) paramsnonnull((1)) nullterminated() _XMAL; diff --git a/libc/runtime/quick_exit.c b/libc/x/xmemalignzero.c similarity index 84% rename from libc/runtime/quick_exit.c rename to libc/x/xmemalignzero.c index 4d3de77e..656f276a 100644 --- a/libc/runtime/quick_exit.c +++ b/libc/x/xmemalignzero.c @@ -17,18 +17,18 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/weaken.h" -#include "libc/runtime/runtime.h" -#include "libc/stdio/stdio.h" +#include "libc/log/log.h" +#include "libc/mem/mem.h" +#include "libc/str/str.h" +#include "libc/x/x.h" /** - * Terminates process normally, running minimal cleanup. - * @noreturn + * Allocates aligned cleared memory, or dies. */ -noreturn textexit void quick_exit(int rc) { - if (weaken(fflush)) { - if (weaken(stdout)) weaken(fflush)(*weaken(stdout)); - if (weaken(stderr)) weaken(fflush)(*weaken(stderr)); - } - _Exit(rc); +void *xmemalignzero(size_t alignment, size_t bytes) { + void *p; + p = memalign(alignment, bytes); + if (!p) xdie(); + memset(p, 0, bytes); + return p; } diff --git a/libc/zipos/zipos.S b/libc/zipos/zipos.S index 567c2316..91eb56b3 100644 --- a/libc/zipos/zipos.S +++ b/libc/zipos/zipos.S @@ -19,6 +19,10 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/macros.h" +/ static_yoink this symbol for open(zip:...) support. + zip_uri_support = 0 + .globl zip_uri_support + .yoink __zip_start .yoink __zip_end .yoink __zipos_close diff --git a/libc/zipos/zipos.mk b/libc/zipos/zipos.mk index 63e47390..82a95029 100644 --- a/libc/zipos/zipos.mk +++ b/libc/zipos/zipos.mk @@ -12,8 +12,7 @@ LIBC_ZIPOS_A_SRCS_C = $(filter %.c,$(LIBC_ZIPOS_A_FILES)) LIBC_ZIPOS = \ $(LIBC_ZIPOS_A_DEPS) \ - $(LIBC_ZIPOS_A) \ - o/$(MODE)/libc/zipos/zipos.o + $(LIBC_ZIPOS_A) LIBC_ZIPOS_A_SRCS = \ $(LIBC_ZIPOS_A_SRCS_S) \ diff --git a/test/libc/fmt/palandprintf_test.c b/test/libc/fmt/palandprintf_test.c index c797aa78..45eb6600 100644 --- a/test/libc/fmt/palandprintf_test.c +++ b/test/libc/fmt/palandprintf_test.c @@ -676,16 +676,24 @@ TEST(snprintf, formatStringLiteral) { EXPECT_EQ('\\' | 'n' << 8, cescapec('\n')); EXPECT_EQ('\\' | '3' << 8 | '7' << 16 | '7' << 24, cescapec('\377')); EXPECT_STREQ("\"hi\\n\"", Format("%`'s", "hi\n")); + EXPECT_STREQ("\"\\000\"", Format("%`'.*s", 1, "\0")); +} + +TEST(palandprintf, precisionStillRespectsNulTerminatorIfNotEscOrRepr) { + EXPECT_STREQ("Makefile - 25 lines ", + Format("%.20s - %d lines %s", "Makefile", 25, "")); } BENCH(palandprintf, bench) { - EZBENCH2("23 %x", donothing, Format("%x", VEIL("r", 23))); - EZBENCH2("23 %d", donothing, Format("%d", VEIL("r", 23))); - EZBENCH2("INT_MIN %x", donothing, Format("%x", VEIL("r", INT_MIN))); - EZBENCH2("INT_MIN %d", donothing, Format("%d", VEIL("r", INT_MIN))); EZBENCH2("ascii %s", donothing, Format("%s", VEIL("r", "hiuhcreohucreo"))); EZBENCH2("utf8 %s", donothing, Format("%s", VEIL("r", "hi (╯°□°)╯"))); EZBENCH2("snprintf %hs", donothing, Format("%hs", VEIL("r", u"hi (╯°□°)╯"))); EZBENCH2("snprintf %ls", donothing, Format("%ls", VEIL("r", L"hi (╯°□°)╯"))); - EZBENCH2("int64toarray", donothing, int64toarray_radix10(-3, buffer)); + EZBENCH2("23 %x", donothing, Format("%x", VEIL("r", 23))); + EZBENCH2("23 %d", donothing, Format("%d", VEIL("r", 23))); + EZBENCH2("INT_MIN %x", donothing, Format("%x", VEIL("r", INT_MIN))); + EZBENCH2("INT_MIN %d", donothing, Format("%d", VEIL("r", INT_MIN))); + EZBENCH2("23 int64toarray", donothing, int64toarray_radix10(23, buffer)); + EZBENCH2("INT_MIN int64toarray", donothing, + int64toarray_radix10(INT_MIN, buffer)); } diff --git a/test/libc/intrin/intrin_test.c b/test/libc/intrin/intrin_test.c index e15d1cc2..61437fbf 100644 --- a/test/libc/intrin/intrin_test.c +++ b/test/libc/intrin/intrin_test.c @@ -1342,7 +1342,7 @@ TEST(paddusw, fuzz) { TEST(psubb, fuzz) { int i, j; - int8_t x[16], y[16], a[16], b[16]; + uint8_t x[16], y[16], a[16], b[16]; for (i = 0; i < 100; ++i) { RngSet(x, sizeof(x)); RngSet(y, sizeof(y)); diff --git a/test/libc/nexgen32e/cescapec_test.c b/test/libc/nexgen32e/cescapec_test.c new file mode 100644 index 00000000..7469a1bd --- /dev/null +++ b/test/libc/nexgen32e/cescapec_test.c @@ -0,0 +1,33 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/escape/escape.h" +#include "libc/testlib/testlib.h" + +TEST(cescapec, test) { + EXPECT_EQ(' ', cescapec(0x20)); + EXPECT_EQ('~', cescapec(0x7E)); + EXPECT_EQ('\\' | 'r' << 8, cescapec('\r')); + EXPECT_EQ('\\' | 'n' << 8, cescapec('\n')); + EXPECT_EQ('\\' | '0' << 8 | '0' << 16 | '0' << 24, cescapec(0)); + EXPECT_EQ('\\' | '1' << 8 | '7' << 16 | '7' << 24, cescapec(0x7F)); + EXPECT_EQ('\\' | '3' << 8 | '7' << 16 | '7' << 24, cescapec(0xFF)); + EXPECT_EQ('\\' | '3' << 8 | '7' << 16 | '7' << 24, cescapec(0xFFFF)); + EXPECT_EQ('\\' | '3' << 8 | '7' << 16 | '7' << 24, cescapec(-1)); +} diff --git a/test/libc/runtime/getdosargv_test.c b/test/libc/runtime/getdosargv_test.c index a5a1863b..7aaeb016 100644 --- a/test/libc/runtime/getdosargv_test.c +++ b/test/libc/runtime/getdosargv_test.c @@ -20,34 +20,34 @@ #include "libc/runtime/internal.h" #include "libc/testlib/testlib.h" -TEST(getdosargv, empty) { +TEST(GetDosArgv, empty) { size_t max = 4; size_t size = ARG_MAX; char *buf = tmalloc(size * sizeof(char)); char **argv = tmalloc(max * sizeof(char *)); - EXPECT_EQ(0, getdosargv(u"", buf, size, argv, max)); + EXPECT_EQ(0, GetDosArgv(u"", buf, size, argv, max)); EXPECT_EQ(NULL, argv[0]); tfree(argv); tfree(buf); } -TEST(getdosargv, emptyish) { +TEST(GetDosArgv, emptyish) { size_t max = 4; size_t size = ARG_MAX; char *buf = tmalloc(size * sizeof(char)); char **argv = tmalloc(max * sizeof(char *)); - EXPECT_EQ(0, getdosargv(u" ", buf, size, argv, max)); + EXPECT_EQ(0, GetDosArgv(u" ", buf, size, argv, max)); EXPECT_EQ(NULL, argv[0]); tfree(argv); tfree(buf); } -TEST(getdosargv, basicUsage) { +TEST(GetDosArgv, basicUsage) { size_t max = 4; size_t size = ARG_MAX; char *buf = tmalloc(size * sizeof(char)); char **argv = tmalloc(max * sizeof(char *)); - EXPECT_EQ(3, getdosargv(u"a\t \"b c\" d ", buf, size, argv, max)); + EXPECT_EQ(3, GetDosArgv(u"a\t \"b c\" d ", buf, size, argv, max)); EXPECT_STREQ("a", argv[0]); EXPECT_STREQ("b c", argv[1]); EXPECT_STREQ("d", argv[2]); @@ -56,12 +56,12 @@ TEST(getdosargv, basicUsage) { tfree(buf); } -TEST(getdosargv, advancedUsage) { +TEST(GetDosArgv, advancedUsage) { size_t max = 4; size_t size = ARG_MAX; char *buf = tmalloc(size * sizeof(char)); char **argv = tmalloc(max * sizeof(char *)); - EXPECT_EQ(2, getdosargv(u"(╯°□°)╯︵ ┻━┻", buf, size, argv, max)); + EXPECT_EQ(2, GetDosArgv(u"(╯°□°)╯︵ ┻━┻", buf, size, argv, max)); EXPECT_STREQ("(╯°□°)╯︵", argv[0]); EXPECT_STREQ("┻━┻", argv[1]); EXPECT_EQ(NULL, argv[2]); @@ -69,12 +69,12 @@ TEST(getdosargv, advancedUsage) { tfree(buf); } -TEST(getdosargv, testAegeanGothicSupplementaryPlanes) { +TEST(GetDosArgv, testAegeanGothicSupplementaryPlanes) { size_t max = 4; /* these symbols are almost as old as dos */ size_t size = ARG_MAX; char *buf = tmalloc(size * sizeof(char)); char **argv = tmalloc(max * sizeof(char *)); - EXPECT_EQ(2, getdosargv(u"𐄷𐄸𐄹𐄺𐄻𐄼 𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷", buf, size, argv, max)); + EXPECT_EQ(2, GetDosArgv(u"𐄷𐄸𐄹𐄺𐄻𐄼 𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷", buf, size, argv, max)); EXPECT_STREQ("𐄷𐄸𐄹𐄺𐄻𐄼", argv[0]); EXPECT_STREQ("𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷", argv[1]); EXPECT_EQ(NULL, argv[2]); @@ -82,12 +82,12 @@ TEST(getdosargv, testAegeanGothicSupplementaryPlanes) { tfree(buf); } -TEST(getdosargv, realWorldUsage) { +TEST(GetDosArgv, realWorldUsage) { size_t max = 512; size_t size = ARG_MAX; char *buf = tmalloc(size * sizeof(char)); char **argv = tmalloc(max * sizeof(char *)); - EXPECT_EQ(5, getdosargv(u"C:\\Users\\jtunn\\printargs.com oh yes yes yes", + EXPECT_EQ(5, GetDosArgv(u"C:\\Users\\jtunn\\printargs.com oh yes yes yes", buf, size, argv, max)); EXPECT_STREQ("C:\\Users\\jtunn\\printargs.com", argv[0]); EXPECT_STREQ("oh", argv[1]); @@ -99,12 +99,12 @@ TEST(getdosargv, realWorldUsage) { tfree(buf); } -TEST(getdosargv, bufferOverrun_countIsStillAccurate_truncatesMemoryWithGrace) { +TEST(GetDosArgv, bufferOverrun_countIsStillAccurate_truncatesMemoryWithGrace) { size_t max = 3; size_t size = 7; char *buf = tmalloc(size * sizeof(char)); char **argv = tmalloc(max * sizeof(char *)); - EXPECT_EQ(3, getdosargv(u"a\t \"b c\" d ", buf, size, argv, max)); + EXPECT_EQ(3, GetDosArgv(u"a\t \"b c\" d ", buf, size, argv, max)); EXPECT_STREQ("a", argv[0]); EXPECT_STREQ("b c", argv[1]); EXPECT_EQ(NULL, argv[2]); @@ -112,44 +112,44 @@ TEST(getdosargv, bufferOverrun_countIsStillAccurate_truncatesMemoryWithGrace) { tfree(buf); } -TEST(getdosargv, pureScanningMode) { +TEST(GetDosArgv, pureScanningMode) { size_t max = 0; size_t size = 0; char *buf = NULL; char **argv = NULL; - EXPECT_EQ(3, getdosargv(u"a b c", buf, size, argv, max)); + EXPECT_EQ(3, GetDosArgv(u"a b c", buf, size, argv, max)); } -TEST(getdosargv, justSlashQuote) { +TEST(GetDosArgv, justSlashQuote) { size_t max = 4, size = 16; char *buf = tmalloc(size * sizeof(char)); char **argv = tmalloc(max * sizeof(char *)); - EXPECT_EQ(1, getdosargv(u"\"\\\\\\\"\"", buf, size, argv, max)); + EXPECT_EQ(1, GetDosArgv(u"\"\\\\\\\"\"", buf, size, argv, max)); EXPECT_STREQ("\\\"", argv[0]); tfree(argv); tfree(buf); } -TEST(getdosargv, quoteInMiddleOfArg_wontSplitArg) { +TEST(GetDosArgv, quoteInMiddleOfArg_wontSplitArg) { size_t max = 4, size = 16; char *buf = tmalloc(size * sizeof(char)); char **argv = tmalloc(max * sizeof(char *)); - EXPECT_EQ(1, getdosargv(u"hi\"\"there", buf, size, argv, max)); + EXPECT_EQ(1, GetDosArgv(u"hi\"\"there", buf, size, argv, max)); EXPECT_STREQ("hithere", argv[0]); max = 4, size = 16; - EXPECT_EQ(1, getdosargv(u"hi\" \"there", buf, size, argv, max)); + EXPECT_EQ(1, GetDosArgv(u"hi\" \"there", buf, size, argv, max)); EXPECT_STREQ("hi there", argv[0]); tfree(argv); tfree(buf); } -TEST(getdosargv, waqQuoting1) { +TEST(GetDosArgv, waqQuoting1) { size_t max = 4; size_t size = ARG_MAX; char *buf = tmalloc(size * sizeof(char)); char **argv = tmalloc(max * sizeof(char *)); EXPECT_EQ(2, - getdosargv(u"a\\\\\"\"\"\"\"\"\"\"b c\" d", buf, size, argv, max)); + GetDosArgv(u"a\\\\\"\"\"\"\"\"\"\"b c\" d", buf, size, argv, max)); EXPECT_STREQ("a\\\"\"b", argv[0]); EXPECT_STREQ("c d", argv[1]); EXPECT_EQ(NULL, argv[2]); @@ -157,12 +157,12 @@ TEST(getdosargv, waqQuoting1) { tfree(buf); } -TEST(getdosargv, waqQuoting2) { +TEST(GetDosArgv, waqQuoting2) { size_t max = 4; size_t size = ARG_MAX; char *buf = tmalloc(size * sizeof(char)); char **argv = tmalloc(max * sizeof(char *)); - EXPECT_EQ(2, getdosargv(u"\"a\\\"b c\" d", buf, size, argv, max)); + EXPECT_EQ(2, GetDosArgv(u"\"a\\\"b c\" d", buf, size, argv, max)); EXPECT_STREQ("a\"b c", argv[0]); EXPECT_STREQ("d", argv[1]); EXPECT_EQ(NULL, argv[2]); diff --git a/test/libc/runtime/getdosenviron_test.c b/test/libc/runtime/getdosenviron_test.c index ee909c21..7b2425d2 100644 --- a/test/libc/runtime/getdosenviron_test.c +++ b/test/libc/runtime/getdosenviron_test.c @@ -21,14 +21,14 @@ #include "libc/str/str.h" #include "libc/testlib/testlib.h" -TEST(getdosenviron, testOneVariable) { +TEST(GetDosEnviron, testOneVariable) { #define kEnv u"A=Und wird die Welt auch in Flammen stehen\0" size_t max = 2; size_t size = sizeof(kEnv) >> 1; char *block = tmalloc(size); char16_t *env = memcpy(tmalloc(sizeof(kEnv)), kEnv, sizeof(kEnv)); char **envp = tmalloc(max * sizeof(char *)); - EXPECT_EQ(1, getdosenviron(env, block, size, envp, max)); + EXPECT_EQ(1, GetDosEnviron(env, block, size, envp, max)); EXPECT_STREQ("A=Und wird die Welt auch in Flammen stehen", envp[0]); EXPECT_EQ(NULL, envp[1]); ASSERT_BINEQ(u"A=Und wird die Welt auch in Flammen stehen  ", block); @@ -38,7 +38,7 @@ TEST(getdosenviron, testOneVariable) { #undef kEnv } -TEST(getdosenviron, testTwoVariables) { +TEST(GetDosEnviron, testTwoVariables) { #define kEnv \ (u"𐌰𐌱𐌲𐌳=Und wird die Welt auch in Flammen stehen\0" \ u"𐌴𐌵𐌶𐌷=Wir werden wieder auferstehen\0") @@ -47,7 +47,7 @@ TEST(getdosenviron, testTwoVariables) { char *block = tmalloc(size); char16_t *env = memcpy(tmalloc(sizeof(kEnv)), kEnv, sizeof(kEnv)); char **envp = tmalloc(max * sizeof(char *)); - EXPECT_EQ(2, getdosenviron(env, block, size, envp, max)); + EXPECT_EQ(2, GetDosEnviron(env, block, size, envp, max)); EXPECT_STREQ("𐌰𐌱𐌲𐌳=Und wird die Welt auch in Flammen stehen", envp[0]); EXPECT_STREQ("𐌴𐌵𐌶𐌷=Wir werden wieder auferstehen", envp[1]); EXPECT_EQ(NULL, envp[2]); @@ -57,14 +57,14 @@ TEST(getdosenviron, testTwoVariables) { #undef kEnv } -TEST(getdosenviron, testOverrun_truncatesWithGrace) { +TEST(GetDosEnviron, testOverrun_truncatesWithGrace) { #define kEnv u"A=Und wird die Welt auch in Flammen stehen\0" size_t max = 2; size_t size = sizeof(kEnv) >> 2; char *block = tmalloc(size); char16_t *env = memcpy(tmalloc(sizeof(kEnv)), kEnv, sizeof(kEnv)); char **envp = tmalloc(max * sizeof(char *)); - EXPECT_EQ(1, getdosenviron(env, block, size, envp, max)); + EXPECT_EQ(1, GetDosEnviron(env, block, size, envp, max)); EXPECT_STREQ("A=Und wird die Welt ", envp[0]); EXPECT_EQ(NULL, envp[1]); ASSERT_BINEQ(u"A=Und wird die Welt   ", block); @@ -74,30 +74,30 @@ TEST(getdosenviron, testOverrun_truncatesWithGrace) { #undef kEnv } -TEST(getdosenviron, testEmpty_doesntTouchMemory) { - EXPECT_EQ(0, getdosenviron(u"", NULL, 0, NULL, 0)); +TEST(GetDosEnviron, testEmpty_doesntTouchMemory) { + EXPECT_EQ(0, GetDosEnviron(u"", NULL, 0, NULL, 0)); } -TEST(getdosenviron, testEmpty_zeroTerminatesWheneverPossible_1) { +TEST(GetDosEnviron, testEmpty_zeroTerminatesWheneverPossible_1) { size_t max = 1; char **envp = tmalloc(max * sizeof(char *)); - EXPECT_EQ(0, getdosenviron(u"", NULL, 0, envp, max)); + EXPECT_EQ(0, GetDosEnviron(u"", NULL, 0, envp, max)); EXPECT_EQ(NULL, envp[0]); tfree(envp); } -TEST(getdosenviron, testEmpty_zeroTerminatesWheneverPossible_2) { +TEST(GetDosEnviron, testEmpty_zeroTerminatesWheneverPossible_2) { size_t size = 1; char *block = tmalloc(size); - EXPECT_EQ(0, getdosenviron(u"", block, size, NULL, 0)); + EXPECT_EQ(0, GetDosEnviron(u"", block, size, NULL, 0)); EXPECT_BINEQ(u" ", block); tfree(block); } -TEST(getdosenviron, testEmpty_zeroTerminatesWheneverPossible_3) { +TEST(GetDosEnviron, testEmpty_zeroTerminatesWheneverPossible_3) { size_t size = 2; char *block = tmalloc(size); - EXPECT_EQ(0, getdosenviron(u"", block, size, NULL, 0)); + EXPECT_EQ(0, GetDosEnviron(u"", block, size, NULL, 0)); EXPECT_BINEQ(u"  ", block); tfree(block); } diff --git a/test/libc/str/strchr_test.c b/test/libc/str/strchr_test.c index cfce6b82..2e2ae89c 100644 --- a/test/libc/str/strchr_test.c +++ b/test/libc/str/strchr_test.c @@ -31,6 +31,11 @@ TEST(strchr, text) { char buf[] = "hellothere"; EXPECT_STREQ("there", strchr(buf, 't')); } +TEST(strchr, testsse) { + char buf[] = "hellohellohellohellohellohellohellohello" + "theretheretheretheretheretheretherethere"; + EXPECT_STREQ("theretheretheretheretheretheretherethere", strchr(buf, 't')); +} TEST(rawmemchr, text) { char buf[] = "hellothere"; EXPECT_STREQ("there", rawmemchr(buf, 't')); diff --git a/test/libc/str/tpenc_test.c b/test/libc/str/tpenc_test.c index 64f2fff9..32bf0416 100644 --- a/test/libc/str/tpenc_test.c +++ b/test/libc/str/tpenc_test.c @@ -37,8 +37,16 @@ TEST(tpenc, test) { EXPECT_EQ(0x8080808080FEul, tpenc(INT_MIN)); } +TEST(tpenc, theimp) { + ASSERT_EQ(0x88989FF0, tpenc(L'😈')); +} + +TEST(tpenc, testBeyondTheStandard) { + ASSERT_EQ(0xBFBFBFBFBFFF, tpenc(-1)); +} + uint64_t Tpenc(int x) { - return (v = tpenc(x)); + return (v = tpenc(VEIL("r", x))); } BENCH(tpenc, bench) { @@ -47,6 +55,7 @@ BENCH(tpenc, bench) { EZBENCH(donothing, Tpenc(' ')); EZBENCH(donothing, Tpenc(0x7f)); EZBENCH(donothing, Tpenc(L'▄')); + EZBENCH(donothing, Tpenc(-1)); EZBENCH(donothing, Tpenc(INT_MIN)); fprintf(stderr, "\n"); } diff --git a/test/libc/str/tpencode_test.c b/test/libc/str/tpencode_test.c index 697192b6..8c0e7ab4 100644 --- a/test/libc/str/tpencode_test.c +++ b/test/libc/str/tpencode_test.c @@ -61,3 +61,7 @@ TEST(tpencode, testMathematicalNotMuhPolicyDrivenBehavior_nonCanonicalNul) { ASSERT_BINEQ(u"└Ç", PROGN(ASSERT_EQ(2, tpencode(buf, 8, 0, true)), buf)); ASSERT_BINEQ(u"└Ç", PROGN(ASSERT_EQ(2, (tpencode)(buf, 8, 0, true)), buf)); } + +TEST(tpencode, testC1Csi) { + ASSERT_BINEQ(u"┬¢", PROGN(ASSERT_EQ(2, tpencode(buf, 8, 0x9B, false)), buf)); +} diff --git a/test/libc/str/tprecode16to8_test.c b/test/libc/str/tprecode16to8_test.c new file mode 100644 index 00000000..471b7022 --- /dev/null +++ b/test/libc/str/tprecode16to8_test.c @@ -0,0 +1,27 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" +#include "libc/testlib/testlib.h" + +TEST(tprecode16to8, test) { + char b[128]; + EXPECT_EQ(69, tprecode16to8(b, 128, u"(╯°□°)╯︵L┻━┻ 𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷𐌸𐌹")); + EXPECT_STREQ("(╯°□°)╯︵L┻━┻ 𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷𐌸𐌹", b); +} diff --git a/test/libc/str/tprecode8to16_test.c b/test/libc/str/tprecode8to16_test.c index 208dc170..2c449c87 100644 --- a/test/libc/str/tprecode8to16_test.c +++ b/test/libc/str/tprecode8to16_test.c @@ -28,3 +28,9 @@ TEST(tprecode8to16, test) { EXPECT_STREQ(u"hello☻♥", buf); tfree(buf); } + +TEST(tprecode8to16, test2) { + char16_t b[128]; + EXPECT_EQ(34, tprecode8to16(b, 128, "(╯°□°)╯︵L┻━┻ 𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷𐌸𐌹")); + EXPECT_STREQ(u"(╯°□°)╯︵L┻━┻ 𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷𐌸𐌹", b); +} diff --git a/test/libc/str/undeflate_test.c b/test/libc/str/undeflate_test.c index c9dbe793..2e0ccd7d 100644 --- a/test/libc/str/undeflate_test.c +++ b/test/libc/str/undeflate_test.c @@ -42,6 +42,7 @@ #include "libc/zip.h" #include "third_party/zlib/zlib.h" +STATIC_YOINK("zip_uri_support"); STATIC_YOINK("libc/testlib/hyperion.txt"); TEST(undeflate, testEmbeddedPlaintextConstant) { @@ -86,7 +87,7 @@ TEST(undeflate, testEmbeddedCompressedZipFile_theHardWay) { struct DeflateState ds; uint8_t *map, *cd, *cf, *lf, *data; found = false; - ASSERT_NE(-1, (fd = open(findcombinary(), O_RDONLY))); + ASSERT_NE(-1, (fd = open(FindComBinary(), O_RDONLY))); ASSERT_NE(-1, fstat(fd, &st)); ASSERT_NE(MAP_FAILED, (map = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0))); diff --git a/test/libc/time/strftime_test.c b/test/libc/time/strftime_test.c index be0891ef..4f1f2924 100644 --- a/test/libc/time/strftime_test.c +++ b/test/libc/time/strftime_test.c @@ -18,11 +18,14 @@ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/limits.h" #include "libc/runtime/runtime.h" #include "libc/testlib/testlib.h" #include "libc/time/time.h" -textstartup static void strftime_test_init(void) { setenv("TZ", "GST", true); } +textstartup static void strftime_test_init(void) { + setenv("TZ", "GST", true); +} const void *const strftime_test_ctor[] initarray = {strftime_test_init}; testonly char *FormatTime(const char *fmt, struct tm *tm) { @@ -37,6 +40,12 @@ TEST(strftime_100, iso8601_ShakaZuluTime) { FormatTime("%Y-%m-%dT%H:%M:%SZ", gmtime(&t))); } +TEST(xiso8601, testUnixYearZero) { + int64_t t = 0; + ASSERT_STREQ("1970-01-01T00:00:00Z", + FormatTime("%Y-%m-%dT%H:%M:%SZ", gmtime(&t))); +} + TEST(strftime_100, rfc2822_ShakaZuluTime) { int64_t t = 0x5cd04d0e; ASSERT_STREQ("Mon, 06 May 2019 15:04:46 +0000", @@ -66,3 +75,27 @@ TEST(strftime_201, rfc822_GoogleStandardTime) { ASSERT_STREQ("Mon, 06 May 19 08:04:46 -0700", FormatTime("%a, %d %b %y %T %z", localtime(&t))); } + +/* TEST(xiso8601, testModernity_TODO) { */ +/* int64_t t = (1600 - 1970) * 31536000; */ +/* ASSERT_STREQ("1600-01-01T00:00:00Z", */ +/* FormatTime("%Y-%m-%dT%H:%M:%SZ", gmtime(&t))); */ +/* } */ + +TEST(xiso8601, testAtLeastBetterThanTraditionalUnixLimit) { + int64_t t = 10737418235; + ASSERT_STREQ("2310-04-04T16:10:35Z", + FormatTime("%Y-%m-%dT%H:%M:%SZ", gmtime(&t))); +} + +TEST(xiso8601, testSomethingHuge) { + int64_t t = 7707318812667; + ASSERT_STREQ("246205-03-18T20:24:27Z", + FormatTime("%Y-%m-%dT%H:%M:%SZ", gmtime(&t))); +} + +/* TEST(xiso8601, testMostOfStelliferousEra_TODO) { */ +/* int64_t t = INT64_MAX; */ +/* ASSERT_STREQ("somethinghuge-01-01T00:00:00Z", */ +/* FormatTime("%Y-%m-%dT%H:%M:%SZ", gmtime(&t))); */ +/* } */ diff --git a/test/tool/build/lib/divmul_test.c b/test/tool/build/lib/divmul_test.c index 6f85b542..a2a34f13 100644 --- a/test/tool/build/lib/divmul_test.c +++ b/test/tool/build/lib/divmul_test.c @@ -48,7 +48,6 @@ void OnSigFpe(void) { void SetUp(void) { m->xedd = xedd; - InitMachine(m); CHECK_NE(-1, xsigaction(SIGFPE, OnSigFpe, SA_NODEFER, 0, oldsigfpe)); } diff --git a/test/tool/build/lib/iovs_test.c b/test/tool/build/lib/iovs_test.c new file mode 100644 index 00000000..c36b398b --- /dev/null +++ b/test/tool/build/lib/iovs_test.c @@ -0,0 +1,85 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/testlib/testlib.h" +#include "tool/build/lib/iovs.h" + +TEST(iovs, testEmpty) { + struct Iovs iv; + InitIovs(&iv); + EXPECT_EQ(0, iv.i); + EXPECT_GE(iv.n, iv.i); + FreeIovs(&iv); +} + +TEST(iovs, testAppendEmptyString_wontCreateEmptyEntries) { + struct Iovs iv; + InitIovs(&iv); + EXPECT_NE(-1, AppendIovs(&iv, "", 0)); + EXPECT_NE(-1, AppendIovs(&iv, NULL, 0)); + EXPECT_EQ(0, iv.i); + EXPECT_GE(iv.n, iv.i); + FreeIovs(&iv); +} + +TEST(iovs, testAppendContiguousVectors_coalescesAdjacentEntries) { + struct Iovs iv; + char buf[8], *b = buf; + InitIovs(&iv); + EXPECT_NE(-1, AppendIovs(&iv, b + 0, 4)); + EXPECT_NE(-1, AppendIovs(&iv, b + 4, 4)); + EXPECT_EQ(1, iv.i); + EXPECT_GE(iv.n, iv.i); + EXPECT_EQ(8, iv.p[0].iov_len); + EXPECT_EQ(b, iv.p[0].iov_base); + FreeIovs(&iv); +} + +TEST(iovs, testAppendNoncontiguousVectors_growsMemoryAndPreservesOrdering) { + struct Iovs iv; + char buf[8], *b = buf; + InitIovs(&iv); + EXPECT_NE(-1, AppendIovs(&iv, b + 0, 1)); + EXPECT_NE(-1, AppendIovs(&iv, b + 2, 1)); + EXPECT_NE(-1, AppendIovs(&iv, b + 4, 1)); + EXPECT_NE(-1, AppendIovs(&iv, b + 6, 1)); + EXPECT_NE(-1, AppendIovs(&iv, b + 1, 1)); + EXPECT_NE(-1, AppendIovs(&iv, b + 3, 1)); + EXPECT_NE(-1, AppendIovs(&iv, b + 5, 1)); + EXPECT_NE(-1, AppendIovs(&iv, b + 7, 1)); + EXPECT_EQ(8, iv.i); + EXPECT_GE(iv.n, iv.i); + EXPECT_EQ(b + 0, iv.p[0].iov_base); + EXPECT_EQ(1, iv.p[0].iov_len); + EXPECT_EQ(b + 2, iv.p[1].iov_base); + EXPECT_EQ(1, iv.p[1].iov_len); + EXPECT_EQ(b + 4, iv.p[2].iov_base); + EXPECT_EQ(1, iv.p[2].iov_len); + EXPECT_EQ(b + 6, iv.p[3].iov_base); + EXPECT_EQ(1, iv.p[3].iov_len); + EXPECT_EQ(b + 1, iv.p[4].iov_base); + EXPECT_EQ(1, iv.p[4].iov_len); + EXPECT_EQ(b + 3, iv.p[5].iov_base); + EXPECT_EQ(1, iv.p[5].iov_len); + EXPECT_EQ(b + 5, iv.p[6].iov_base); + EXPECT_EQ(1, iv.p[6].iov_len); + EXPECT_EQ(b + 7, iv.p[7].iov_base); + EXPECT_EQ(1, iv.p[7].iov_len); + FreeIovs(&iv); +} diff --git a/test/tool/build/lib/machine_test.c b/test/tool/build/lib/machine_test.c index aac04266..8b264199 100644 --- a/test/tool/build/lib/machine_test.c +++ b/test/tool/build/lib/machine_test.c @@ -98,27 +98,30 @@ const uint8_t kTenthprime2[] = { 0xC3, // }; -int64_t base; -uint8_t *real; -size_t realsize; struct Machine *m; void SetUp(void) { - base = 0; m = NewMachine(); - m->cr3 = MallocPage(); - realsize = 0x10000; - real = tmemalign(PAGESIZE, ROUNDUP(realsize, PAGESIZE)); - RegisterMemory(m, base, real, realsize); - m->ip = base; - Write64(m->sp, m->ip + realsize); + m->ip = 0; + ReserveVirtual(m, 0, 4096); + ASSERT_EQ(0x5000, m->real.i); + ASSERT_EQ(0x1007, Read64(m->real.p + 0x0000)); // PML4T + ASSERT_EQ(0x2007, Read64(m->real.p + 0x1000)); // PDPT + ASSERT_EQ(0x3007, Read64(m->real.p + 0x2000)); // PDE + ASSERT_EQ(0x4007, Read64(m->real.p + 0x3000)); // PT + Write64(m->sp, 4096); } void TearDown(void) { - ResetRam(m); - free(m->cr3); - tfree(real); - free(m); + FreeVirtual(m, 0, 4096); + ASSERT_EQ(0x5000, m->real.i); + ASSERT_EQ(0x1007, Read64(m->real.p + 0x0000)); // PML4T + ASSERT_EQ(0x2007, Read64(m->real.p + 0x1000)); // PDPT + ASSERT_EQ(0x3007, Read64(m->real.p + 0x2000)); // PDE + ASSERT_EQ(0x0000, Read64(m->real.p + 0x3000)); // PT + ASSERT_EQ(0x4000, m->realfree->i); + ASSERT_EQ(0x1000, m->realfree->n); + FreeMachine(m); } int ExecuteUntilHalt(struct Machine *m) { @@ -134,121 +137,121 @@ int ExecuteUntilHalt(struct Machine *m) { } TEST(machine, test) { - memcpy(real, kTenthprime, sizeof(kTenthprime)); + VirtualRecv(m, 0, kTenthprime, sizeof(kTenthprime)); ASSERT_EQ(kMachineHalt, ExecuteUntilHalt(m)); ASSERT_EQ(15, Read32(m->ax)); } TEST(machine, testFpu) { - memcpy(real, kPi80, sizeof(kPi80)); + VirtualRecv(m, 0, kPi80, sizeof(kPi80)); ASSERT_EQ(kMachineHalt, ExecuteUntilHalt(m)); ASSERT_TRUE(fabs(3.14159 - FpuPop(m)) < 0.0001); - m->ip = base; + m->ip = 0; ASSERT_EQ(kMachineHalt, ExecuteUntilHalt(m)); ASSERT_TRUE(fabs(3.14159 - FpuPop(m)) < 0.0001); } BENCH(machine, benchPrimeNumberPrograms) { - memcpy(real, kTenthprime2, sizeof(kTenthprime2)); - EZBENCH2("tenthprime2", m->ip = base, ExecuteUntilHalt(m)); + VirtualRecv(m, 0, kTenthprime2, sizeof(kTenthprime2)); + EZBENCH2("tenthprime2", m->ip = 0, ExecuteUntilHalt(m)); ASSERT_EQ(15, Read32(m->ax)); - memcpy(real, kTenthprime, sizeof(kTenthprime)); - EZBENCH2("tenthprime", m->ip = base, ExecuteUntilHalt(m)); + VirtualRecv(m, 0, kTenthprime, sizeof(kTenthprime)); + EZBENCH2("tenthprime", m->ip = 0, ExecuteUntilHalt(m)); ASSERT_EQ(15, Read32(m->ax)); } BENCH(machine, benchFpu) { - memcpy(real, kPi80, sizeof(kPi80)); - EZBENCH2("pi80", m->ip = base, PROGN(ExecuteUntilHalt(m), FpuPop(m))); + VirtualRecv(m, 0, kPi80, sizeof(kPi80)); + EZBENCH2("pi80", m->ip = 0, PROGN(ExecuteUntilHalt(m), FpuPop(m))); } BENCH(machine, benchLoadExec2) { uint8_t kMovCode[] = {0xbe, 0x03, 0x00, 0x00, 0x00}; - memcpy(real, kMovCode, sizeof(kMovCode)); + VirtualRecv(m, 0, kMovCode, sizeof(kMovCode)); LoadInstruction(m); - EZBENCH2("mov", m->ip = base, ExecuteInstruction(m)); + EZBENCH2("mov", m->ip = 0, ExecuteInstruction(m)); } BENCH(machine, benchLoadExec3) { uint8_t kMovdCode[] = {0x66, 0x0f, 0x6e, 0xc0}; Write64(m->ax, 0); - memcpy(real, kMovdCode, sizeof(kMovdCode)); + VirtualRecv(m, 0, kMovdCode, sizeof(kMovdCode)); LoadInstruction(m); - EZBENCH2("movd", m->ip = base, ExecuteInstruction(m)); + EZBENCH2("movd", m->ip = 0, ExecuteInstruction(m)); } BENCH(machine, benchLoadExec4) { uint8_t kAddpsRegregCode[] = {0x0f, 0x58, 0xC0}; uint8_t kAddpsMemregCode[] = {0x0f, 0x58, 0x00}; Write64(m->ax, 0); - memcpy(real, kAddpsRegregCode, sizeof(kAddpsRegregCode)); + VirtualRecv(m, 0, kAddpsRegregCode, sizeof(kAddpsRegregCode)); LoadInstruction(m); - EZBENCH2("addps reg reg", m->ip = base, ExecuteInstruction(m)); - memcpy(real, kAddpsMemregCode, sizeof(kAddpsMemregCode)); + EZBENCH2("addps reg reg", m->ip = 0, ExecuteInstruction(m)); + VirtualRecv(m, 0, kAddpsMemregCode, sizeof(kAddpsMemregCode)); LoadInstruction(m); - EZBENCH2("addps mem reg", m->ip = base, ExecuteInstruction(m)); + EZBENCH2("addps mem reg", m->ip = 0, ExecuteInstruction(m)); } BENCH(machine, benchLoadExec5) { uint8_t kPaddwRegregCode[] = {0x66, 0x0F, 0xFD, 0xC0}; uint8_t kPaddwMemregCode[] = {0x66, 0x0F, 0xFD, 0x00}; Write64(m->ax, 0); - memcpy(real, kPaddwRegregCode, sizeof(kPaddwRegregCode)); + VirtualRecv(m, 0, kPaddwRegregCode, sizeof(kPaddwRegregCode)); LoadInstruction(m); - EZBENCH2("paddw", m->ip = base, ExecuteInstruction(m)); - memcpy(real, kPaddwMemregCode, sizeof(kPaddwMemregCode)); + EZBENCH2("paddw", m->ip = 0, ExecuteInstruction(m)); + VirtualRecv(m, 0, kPaddwMemregCode, sizeof(kPaddwMemregCode)); LoadInstruction(m); - EZBENCH2("paddw mem", m->ip = base, ExecuteInstruction(m)); + EZBENCH2("paddw mem", m->ip = 0, ExecuteInstruction(m)); } BENCH(machine, benchLoadExec6) { uint8_t kPsubqRegregCode[] = {0x66, 0x0F, 0xFB, 0xC0}; uint8_t kPsubqMemregCode[] = {0x66, 0x0F, 0xFB, 0x00}; Write64(m->ax, 0); - memcpy(real, kPsubqRegregCode, sizeof(kPsubqRegregCode)); + VirtualRecv(m, 0, kPsubqRegregCode, sizeof(kPsubqRegregCode)); LoadInstruction(m); - EZBENCH2("psubq", m->ip = base, ExecuteInstruction(m)); - memcpy(real, kPsubqMemregCode, sizeof(kPsubqMemregCode)); + EZBENCH2("psubq", m->ip = 0, ExecuteInstruction(m)); + VirtualRecv(m, 0, kPsubqMemregCode, sizeof(kPsubqMemregCode)); LoadInstruction(m); - EZBENCH2("psubq mem", m->ip = base, ExecuteInstruction(m)); + EZBENCH2("psubq mem", m->ip = 0, ExecuteInstruction(m)); } BENCH(machine, benchAddqMem) { uint8_t kAddMemregCode[] = {0x48, 0x03, 0x08}; Write64(m->ax, 0); - memcpy(real, kAddMemregCode, sizeof(kAddMemregCode)); + VirtualRecv(m, 0, kAddMemregCode, sizeof(kAddMemregCode)); LoadInstruction(m); - EZBENCH2("addq mem", m->ip = base, ExecuteInstruction(m)); + EZBENCH2("addq mem", m->ip = 0, ExecuteInstruction(m)); } BENCH(machine, benchAddlMem) { uint8_t kAddMemregCode[] = {0x03, 0x08}; Write64(m->ax, 0); - memcpy(real, kAddMemregCode, sizeof(kAddMemregCode)); + VirtualRecv(m, 0, kAddMemregCode, sizeof(kAddMemregCode)); LoadInstruction(m); - EZBENCH2("addl mem", m->ip = base, ExecuteInstruction(m)); + EZBENCH2("addl mem", m->ip = 0, ExecuteInstruction(m)); } BENCH(machine, benchAddq) { uint8_t kAddqCode[] = {0x48, 0x01, 0xd8}; Write64(m->ax, 0); - memcpy(real, kAddqCode, sizeof(kAddqCode)); + VirtualRecv(m, 0, kAddqCode, sizeof(kAddqCode)); LoadInstruction(m); - EZBENCH2("addq", m->ip = base, ExecuteInstruction(m)); + EZBENCH2("addq", m->ip = 0, ExecuteInstruction(m)); } BENCH(machine, benchAddb) { uint8_t kAddbCode[] = {0x00, 0xd8}; Write64(m->ax, 0); - memcpy(real, kAddbCode, sizeof(kAddbCode)); + VirtualRecv(m, 0, kAddbCode, sizeof(kAddbCode)); LoadInstruction(m); - EZBENCH2("addb", m->ip = base, ExecuteInstruction(m)); + EZBENCH2("addb", m->ip = 0, ExecuteInstruction(m)); } BENCH(machine, benchXorReg) { - memcpy(real, kTenthprime, sizeof(kTenthprime)); + VirtualRecv(m, 0, kTenthprime, sizeof(kTenthprime)); LoadInstruction(m); - EZBENCH2("xor", m->ip = base, ExecuteInstruction(m)); + EZBENCH2("xor", m->ip = 0, ExecuteInstruction(m)); } BENCH(machine, benchLoadExec8) { @@ -257,16 +260,16 @@ BENCH(machine, benchLoadExec8) { OpFinit(m); *FpuSt(m, 0) = M_PI; FpuSetTag(m, 0, kFpuTagValid); - memcpy(real, kFchsCode, sizeof(kFchsCode)); + VirtualRecv(m, 0, kFchsCode, sizeof(kFchsCode)); LoadInstruction(m); - EZBENCH2("fchs", m->ip = base, ExecuteInstruction(m)); + EZBENCH2("fchs", m->ip = 0, ExecuteInstruction(m)); } BENCH(machine, benchPushpop) { uint8_t kPushpop[] = {0x50, 0x58}; Write64(m->ax, 0); - memcpy(real, kPushpop, sizeof(kPushpop)); - EZBENCH2("pushpop", m->ip = base, + VirtualRecv(m, 0, kPushpop, sizeof(kPushpop)); + EZBENCH2("pushpop", m->ip = 0, PROGN(LoadInstruction(m), ExecuteInstruction(m), LoadInstruction(m), ExecuteInstruction(m))); } @@ -274,29 +277,27 @@ BENCH(machine, benchPushpop) { BENCH(machine, benchPause) { uint8_t kPause[] = {0xf3, 0x90}; Write64(m->ax, 0); - memcpy(real, kPause, sizeof(kPause)); + VirtualRecv(m, 0, kPause, sizeof(kPause)); LoadInstruction(m); - EZBENCH2("pause", m->ip = base, ExecuteInstruction(m)); + EZBENCH2("pause", m->ip = 0, ExecuteInstruction(m)); } BENCH(machine, benchClc) { uint8_t kClc[] = {0xf8}; Write64(m->ax, 0); - memcpy(real, kClc, sizeof(kClc)); + VirtualRecv(m, 0, kClc, sizeof(kClc)); LoadInstruction(m); - EZBENCH2("clc", m->ip = base, ExecuteInstruction(m)); + EZBENCH2("clc", m->ip = 0, ExecuteInstruction(m)); } BENCH(machine, benchNop) { uint8_t kNop[] = {0x90}; Write64(m->ax, 0); - memcpy(real, kNop, sizeof(kNop)); + VirtualRecv(m, 0, kNop, sizeof(kNop)); LoadInstruction(m); - EZBENCH2("nop", m->ip = base, ExecuteInstruction(m)); -} - -TEST(machine, sizeIsReasonable) { - ASSERT_LE(sizeof(struct Machine), 65536 * 3); + EZBENCH2("nop", m->ip = 0, ExecuteInstruction(m)); + EZBENCH2("nop w/ load", m->ip = 0, + PROGN(LoadInstruction(m), ExecuteInstruction(m))); } TEST(x87, fprem1) { @@ -308,7 +309,7 @@ TEST(x87, fprem1) { 0xf4, // hlt 0x00, 0x00, 0xc0, 0xbf, // .float -1.5 }; - memcpy(real, prog, sizeof(prog)); + VirtualRecv(m, 0, prog, sizeof(prog)); ASSERT_EQ(kMachineHalt, ExecuteUntilHalt(m)); ASSERT_LDBL_EQ(1, FpuPop(m)); } @@ -323,7 +324,11 @@ TEST(x87, fprem2) { 0x00, 0x60, 0x5e, 0x75, 0x64, 0xd9, 0x45, 0x43, // 0x5b, 0x14, 0xea, 0x9d, 0x77, 0xb2, 0x0b, 0x3d, // }; - memcpy(real, prog, sizeof(prog)); + VirtualRecv(m, 0, prog, sizeof(prog)); ASSERT_EQ(kMachineHalt, ExecuteUntilHalt(m)); ASSERT_LDBL_EQ(1.1766221079117338e-14, FpuPop(m)); } + +TEST(machine, sizeIsReasonable) { + ASSERT_LE(sizeof(struct Machine), 65536 * 3); +} diff --git a/test/tool/build/lib/pml4t_test.c b/test/tool/build/lib/pml4t_test.c deleted file mode 100644 index 8c7c0e2d..00000000 --- a/test/tool/build/lib/pml4t_test.c +++ /dev/null @@ -1,158 +0,0 @@ -/*-*- 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 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/log/check.h" -#include "libc/macros.h" -#include "libc/mem/mem.h" -#include "libc/stdio/stdio.h" -#include "libc/str/str.h" -#include "libc/testlib/ezbench.h" -#include "libc/testlib/testlib.h" -#include "tool/build/lib/memory.h" -#include "tool/build/lib/pml4t.h" - -struct Unmapped { - size_t i; - struct UnmappedEntry { - void *addr; - size_t size; - } p[8]; -}; - -struct Unmapped unmapped; -uint64_t *pt2, *pt3, *pt4; -pml4t_t cr3; - -void *NewPage(void) { - void *p; - p = tmemalign(4096, 4096); - CHECK_ALIGNED(4096, p); - memset(p, 0, 4096); - return p; -} - -void FreePage(void *p) { - tfree(p); -} - -int FakeMunmap(void *addr, size_t size) { - return 0; -} - -int MockMunmap(void *addr, size_t size) { - CHECK_LT(unmapped.i, ARRAYLEN(unmapped.p)); - unmapped.p[unmapped.i].addr = addr; - unmapped.p[unmapped.i].size = size; - unmapped.i++; - return 0; -} - -void SetUp(void) { - memset(cr3, 0, sizeof(cr3)); -} - -void TearDown(void) { - unmapped.i = 0; - FreePml4t(cr3, -0x800000000000, 0x800000000000, FreePage, FakeMunmap); -} - -TEST(pml4t, testHighestAddress) { - ASSERT_NE(-1, - RegisterPml4t(cr3, 0x7fffffffe000, 0x3133700000, 0x2000, NewPage)); - ASSERT_TRUE(IsValidPage(cr3[255])); - pt2 = UnmaskPageAddr(cr3[255]); - ASSERT_TRUE(IsValidPage(pt2[511])); - pt3 = UnmaskPageAddr(pt2[511]); - ASSERT_TRUE(IsValidPage(pt3[511])); - pt4 = UnmaskPageAddr(pt3[511]); - ASSERT_TRUE(IsValidPage(pt4[510])); - ASSERT_TRUE(IsValidPage(pt4[511])); - EXPECT_EQ(0x3133700000, UnmaskPageAddr(pt4[510])); - EXPECT_EQ(0x3133701000, UnmaskPageAddr(pt4[511])); -} - -TEST(pml4t, testLowestAddress) { - ASSERT_NE(-1, - RegisterPml4t(cr3, -0x800000000000, 0x31337000, 0x2000, NewPage)); - ASSERT_TRUE(IsValidPage(cr3[256])); - pt2 = UnmaskPageAddr(cr3[256]); - ASSERT_TRUE(IsValidPage(pt2[0])); - pt3 = UnmaskPageAddr(pt2[0]); - ASSERT_TRUE(IsValidPage(pt3[0])); - pt4 = UnmaskPageAddr(pt3[0]); - ASSERT_TRUE(IsValidPage(pt4[0])); - ASSERT_TRUE(IsValidPage(pt4[1])); -} - -TEST(pml4t, testOverlapsExistingRegistration_overwritesRegistration) { - ASSERT_NE(-1, - RegisterPml4t(cr3, 0x7fffffffe000, 0x31337000, 0x1000, NewPage)); - ASSERT_TRUE(IsValidPage(cr3[255])); - pt2 = UnmaskPageAddr(cr3[255]); - ASSERT_TRUE(IsValidPage(pt2[511])); - pt3 = UnmaskPageAddr(pt2[511]); - ASSERT_TRUE(IsValidPage(pt3[511])); - pt4 = UnmaskPageAddr(pt3[511]); - EXPECT_TRUE(IsValidPage(pt4[510])); - EXPECT_EQ(0x31337000, UnmaskPageAddr(pt4[510])); - ASSERT_NE(-1, RegisterPml4t(cr3, 0x7fffffffe000, 0x31337000 + 0x1000, 0x1000, - NewPage)); - EXPECT_TRUE(IsValidPage(pt4[510])); - EXPECT_EQ(0x31337000 + 0x1000, UnmaskPageAddr(pt4[510])); -} - -TEST(pml4t, testFindPml4t_holeTooSmall_skipsOver) { - ASSERT_NE(-1, RegisterPml4t(cr3, 0x700000000, 0, 0x1000, NewPage)); - ASSERT_NE(-1, RegisterPml4t(cr3, 0x700005000, 0, 0x1000, NewPage)); - ASSERT_EQ(0x700001000, FindPml4t(cr3, 0x700000000, 0x01000)); - ASSERT_EQ(0x700006000, FindPml4t(cr3, 0x700000000, 0x10000)); -} - -TEST(pml4t, testFindPml4t_bigAllocation) { - ASSERT_EQ(0x00200000, FindPml4t(cr3, 0x00200000, 0x00400000)); - ASSERT_EQ(0x00201000, FindPml4t(cr3, 0x00201000, 0x00400000)); - ASSERT_EQ(0xff200000, FindPml4t(cr3, 0xff200000, 0x00400000)); - ASSERT_EQ(0xff201000, FindPml4t(cr3, 0xff201000, 0x00400000)); -} - -TEST(pml4t, testFreePmlt) { - ASSERT_NE(-1, RegisterPml4t(cr3, 0x000005000, 0x123000, 0x2000, NewPage)); - ASSERT_NE(-1, RegisterPml4t(cr3, 0x000000000, 0x321000, 0x1000, NewPage)); - ASSERT_NE(-1, FreePml4t(cr3, -0x800000000000, 0x800000000000, FreePage, - MockMunmap)); - ASSERT_EQ(2, unmapped.i); - EXPECT_EQ(0x321000, unmapped.p[0].addr); - EXPECT_EQ(0x1000, unmapped.p[0].size); - EXPECT_EQ(0x123000, unmapped.p[1].addr); - EXPECT_EQ(0x2000, unmapped.p[1].size); -} - -BENCH(pml4t, bench) { - EZBENCH2("RegisterPml4t 1mb fresh", - FreePml4t(cr3, -0x800000000000, 0x800000000000, free, FakeMunmap), - RegisterPml4t(cr3, 0x00100000, 0x31337000, 0x00100000, MallocPage)); - EZBENCH2("RegisterPml4t 1gb fresh", - FreePml4t(cr3, -0x800000000000, 0x800000000000, free, FakeMunmap), - RegisterPml4t(cr3, 0x40000000, 0x31337000, 0x40000000, MallocPage)); - EZBENCH2("RegisterPml4t 1mb overwrite", donothing, - RegisterPml4t(cr3, 0x00100000, 0x31337000, 0x00100000, MallocPage)); - EZBENCH2("RegisterPml4t 1gb overwrite", donothing, - RegisterPml4t(cr3, 0x40000000, 0x31337000, 0x40000000, MallocPage)); - FreePml4t(cr3, -0x800000000000, 0x800000000000, free, FakeMunmap); -} diff --git a/test/tool/build/lib/pty_test.c b/test/tool/build/lib/pty_test.c new file mode 100644 index 00000000..12dc860f --- /dev/null +++ b/test/tool/build/lib/pty_test.c @@ -0,0 +1,235 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/log/log.h" +#include "libc/mem/mem.h" +#include "libc/stdio/stdio.h" +#include "libc/str/str.h" +#include "libc/testlib/ezbench.h" +#include "libc/testlib/testlib.h" +#include "libc/unicode/unicode.h" +#include "tool/build/lib/pty.h" + +char *render(struct MachinePty *pty) { + static struct Buffer b; + int y; + b.i = 0; + for (y = 0; y < pty->yn; ++y) { + MachinePtyAppendLine(pty, &b, y); + } + b.p[b.i] = 0; + return b.p; +} + +const char widelatin[] aligned(16) = "\ +A-BCDEFGHIJKLMNOPQRSTUVWXYZ\r\n\ +ab-cdefghijklmnopqrstuvwxyz\r\n\ +012-3456789\r\n\ +ABCD-EFGHIJKLMNOPQRSTUVWXYZ\r\n\ +abcde-fghijklmnopqrstuvwxyz\r\n\ +012345-6789\r\n\ +ABCDEFG-HIJKLMNOPQRSTUVWXYZ\r\n\ +abcdefgh-ijklmnopqrstuvwxyz\r\n\ +012345678-9"; + +static const char widelatin_golden[] = "\ +A-BCDEFGHIJKLMNOPQRSTUVWXYZ \ +ab-cdefghijklmnopqrstuvwxyz \ +012-3456789 \ +ABCD-EFGHIJKLMNOPQRSTUVWXYZ \ +abcde-fghijklmnopqrstuvwxyz \ +012345-6789 \ +ABCDEFG-HIJKLMNOPQRSTUVWXYZ \ +abcdefgh-ijklmnopqrstuvwxyz \ +012345678-9▂ \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + "; + +TEST(pty, testFunWidth) { + struct MachinePty *pty = MachinePtyNew(); + MachinePtyWrite(pty, widelatin, ARRAYLEN(widelatin) - 1); + EXPECT_STREQ(widelatin_golden, render(pty)); + MachinePtyFree(pty); +} + +const char hyperion[] aligned(16) = "\ +Fanatics have their dreams, wherewith they weave \ +A paradise for a sect; the savage too \ +From forth the loftiest fashion of his sleep \ +Guesses at Heaven; pity these have not \ +Trac'd upon vellum or wild Indian leaf \ +The shadows of melodious utterance. \ +But bare of laurel they live, dream, and die; \ +For Poesy alone can tell her dreams, \ +With the fine spell of words alone can save \ +Imagination from the sable charm \ +And dumb enchantment. Who alive can say, \ +'Thou art no Poet may'st not tell thy dreams?' \ +Since every man whose soul is not a clod \ +Hath visions, and would speak, if he had loved \ +And been well nurtured in his mother tongue. \ +Whether the dream now purpos'd to rehearse \ +Be poet's or fanatic's will be known \ +When this warm scribe my hand is in the grave."; + +static const wchar_t hyperion_golden[] = L"\ +Fanatics have their dreams, wherewith they weave A paradise for a sect; the sava\ +ge too From forth the loftiest fashion of his sleep Guesses at Heaven; pity thes\ +e have not Trac'd upon vellum or wild Indian leaf The shadows of melodious utter\ +ance. But bare of laurel they live, dream, and die; For Poesy alone can tell her\ + dreams, With the fine spell of words alone can save Imagination from the sable \ +charm And dumb enchantment. Who alive can say, 'Thou art no Poet may'st not tell\ + thy dreams?' Since every man whose soul is not a clod Hath visions, and would s\ +peak, if he had loved And been well nurtured in his mother tongue. Whether the d\ +ream now purpos'd to rehearse Be poet's or fanatic's will be known When this war\ +m scribe my hand is in the grave. \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + "; + +TEST(pty, testPureAscii_benefitsFromVectorization) { + struct MachinePty *pty = MachinePtyNew(); + MachinePtyWrite(pty, hyperion, ARRAYLEN(hyperion) - 1); + EXPECT_STREQN(pty->wcs, hyperion_golden, ARRAYLEN(hyperion_golden) - 1); + MachinePtyFree(pty); +} + +static const char kKiloAnsi[] = "\ +\e[?25l\e[H\ +#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐\e[39m\e[0K\r\n\ +#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi─────────\e[39m\e[0K\r\n\ +#\e[39m\e[0K\r\n\ +# SYNOPSIS\e[39m\e[0K\r\n\ +#\e[39m\e[0K\r\n\ +# Freestanding Hermetically-Sealed Monolithic Repository\e[39m\e[0K\r\n\ +#\e[39m\e[0K\r\n\ +# REQUIREMENTS\e[39m\e[0K\r\n\ +#\e[39m\e[0K\r\n\ +# You can run your programs on any operating system, but you have\e[39m\e[0K\r\n\ +# to build them on Linux 2.6+ (or WSL) using GNU Make. A modern C\e[39m\e[0K\r\n\ +# compiler that\'s statically-linked comes included as a courtesy.\e[39m\e[0K\r\n\ +#\e[39m\e[0K\r\n\ +# EXAMPLES\e[39m\e[0K\r\n\ +#\e[39m\e[0K\r\n\ +# # build and run everything\e[39m\e[0K\r\n\ +# make -j8 -O\e[39m\e[0K\r\n\ +# make -j8 -O MODE=dbg\e[39m\e[0K\r\n\ +# make -j8 -O MODE=opt\e[39m\e[0K\r\n\ +# make -j8 -O MODE=rel\e[39m\e[0K\r\n\ +# make -j8 -O MODE=tiny\e[39m\e[0K\r\n\ +#\e[39m\e[0K\r\n\ +# # build individual target\e[39m\e[0K\r\n\ +\e[0K\e[7mMakefile - 340 lines 1/340\e[0m\r\n\ +\e[0KHELP: Ctrl-S = save | Ctrl-Q = quit | Ctrl-F = find\e[1;1H\e[?25h"; + +static const wchar_t kKiloGolden[] = L"\ +#-*-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 \ +# \ +# Freestanding Hermetically-Sealed Monolithic Repository \ +# \ +# REQUIREMENTS \ +# \ +# You can run your programs on any operating system, but you have \ +# to build them on Linux 2.6+ (or WSL) using GNU Make. A modern C \ +# compiler that's statically-linked comes included as a courtesy. \ +# \ +# EXAMPLES \ +# \ +# # build and run everything \ +# make -j8 -O \ +# make -j8 -O MODE=dbg \ +# make -j8 -O MODE=opt \ +# make -j8 -O MODE=rel \ +# make -j8 -O MODE=tiny \ +# \ +# # build individual target \ +Makefile - 340 lines 1/340\ +HELP: Ctrl-S = save | Ctrl-Q = quit | Ctrl-F = find \ +"; + +TEST(pty, testLongestPossibleCharacter) { + EXPECT_EQ(60, strlen("\e[21;22;27;24;25;29;38;2;255;255;255;48;2;255;255;" + "255m\377\277\277\277\277\277")); + struct Buffer b = {0}; + struct MachinePty *pty = MachinePtyNew(); + const char *s = "\e[1;2;3;4;5;6;7;9m" + "h" + "\e[0;" + "38;2;255;255;255;" + "48;2;255;255;255m" + "\377\277\277\277\277\277" + "\e[0m"; + MachinePtyWrite(pty, s, strlen(s)); + MachinePtyAppendLine(pty, &b, 0); + AppendChar(&b, '\0'); + EXPECT_STREQ("\e[1;2;4;7;5;9m" + "𝒉" + "\e[22;24;27;25;29;38;2;255;255;255;48;2;255;255;255m" + "\377\277\277\277\277\277" + "\e[0m▂ " + " ", + b.p); + MachinePtyFree(pty); + free(b.p); +} + +TEST(pty, test) { + struct MachinePty *pty = MachinePtyNew(); + MachinePtyWrite(pty, kKiloAnsi, strlen(kKiloAnsi)); + EXPECT_STREQN(kKiloGolden, pty->wcs, wcslen(kKiloGolden)); + MachinePtyFree(pty); +} + +BENCH(pty, bench) { + struct MachinePty *pty = MachinePtyNew(); + EZBENCH2("pty write ascii", donothing, + MachinePtyWrite(pty, hyperion, sizeof(hyperion) - 1)); + EZBENCH2("pty write kilo", donothing, + MachinePtyWrite(pty, kKiloAnsi, sizeof(kKiloAnsi) - 1)); + EZBENCH2("pty render", donothing, render(pty)); +} diff --git a/test/tool/build/lib/test.mk b/test/tool/build/lib/test.mk index 88a00835..410689ab 100644 --- a/test/tool/build/lib/test.mk +++ b/test/tool/build/lib/test.mk @@ -36,6 +36,7 @@ TEST_TOOL_BUILD_LIB_DIRECTDEPS = \ LIBC_NEXGEN32E \ LIBC_RUNTIME \ LIBC_FMT \ + LIBC_STR \ LIBC_STDIO \ LIBC_LOG \ LIBC_SYSV \ diff --git a/libc/nexgen32e/clamp1.S b/third_party/dtoa/divmax.S similarity index 89% rename from libc/nexgen32e/clamp1.S rename to third_party/dtoa/divmax.S index 4247acf6..e5733ad6 100644 --- a/libc/nexgen32e/clamp1.S +++ b/third_party/dtoa/divmax.S @@ -18,18 +18,16 @@ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/macros.h" +.source __FILE__ -clamp1: .leafprologue - minps .Lmax(%rip),%xmm0 - maxps .Lmin(%rip),%xmm0 - .leafepilogue - .endfn clamp1,globl,hidden +/ Avoid dtoa needing .data section. + .bss + .align 4 +dtoa_divmax: + .zero 4 + .endobj dtoa_divmax,globl + .previous - .rodata.cst16 -.Lmin: .rept 4 - .float 0 - .endr -.Lmax: .rept 4 - .float 0.99609375 - .endr - .source __FILE__ + .init.start 202,_init_dtoa_divmax + movb $2,dtoa_divmax(%rip) + .init.end 202,_init_dtoa_divmax diff --git a/third_party/dtoa/dtoa.c b/third_party/dtoa/dtoa.c index 16a1470a..8d72df56 100644 --- a/third_party/dtoa/dtoa.c +++ b/third_party/dtoa/dtoa.c @@ -400,10 +400,11 @@ Exactly one of IEEE_8087, IEEE_MC68k, VAX, or IBM should be defined. #ifdef SET_INEXACT #define dtoa_divmax 27 #else -int dtoa_divmax = 2; /* Permit experimenting: on some systems, 64-bit integer */ - /* division is slow enough that we may sometimes want to */ - /* avoid using it. We assume (but do not check) that */ - /* dtoa_divmax <= 27.*/ +/* Permit experimenting: on some systems, 64-bit integer */ +/* division is slow enough that we may sometimes want to */ +/* avoid using it. We assume (but do not check) that */ +/* dtoa_divmax <= 27.*/ +extern int dtoa_divmax; #endif typedef struct BF96 { /* Normalized 96-bit software floating point numbers */ diff --git a/third_party/duktape/duktape.mk b/third_party/duktape/duktape.mk index d0b5ad6c..70487a70 100644 --- a/third_party/duktape/duktape.mk +++ b/third_party/duktape/duktape.mk @@ -32,7 +32,7 @@ THIRD_PARTY_DUKTAPE_A_DIRECTDEPS = \ LIBC_FMT \ LIBC_TIME \ LIBC_MEM \ - LIBC_MATH \ + LIBC_TINYMATH \ LIBC_UNICODE \ LIBC_NEXGEN32E diff --git a/third_party/stb/ycbcr.c b/third_party/stb/ycbcr.c index d2e9d6bd..30ae98b0 100644 --- a/third_party/stb/ycbcr.c +++ b/third_party/stb/ycbcr.c @@ -26,7 +26,7 @@ /* this is a reduced-precision calculation of YCbCr-to-RGB introduced to make sure the code produces the same results in both SIMD and scalar */ -#define FLOAT2FIXED(x) (((int)((x)*4096.0f + 0.5f)) << 8) +#define FLOAT2FIXED(x) (((int)((x)*4096.0f + .5f)) << 8) void stbi__YCbCr_to_RGB_row(unsigned char *out, const unsigned char *y, const unsigned char *pcb, const unsigned char *pcr, @@ -45,9 +45,9 @@ void stbi__YCbCr_to_RGB_row(unsigned char *out, const unsigned char *y, g = y_fixed + (cr * -FLOAT2FIXED(0.71414f)) + ((cb * -FLOAT2FIXED(0.34414f)) & 0xffff0000); b = y_fixed + cb * FLOAT2FIXED(1.77200f); - r /= 1048576; - g /= 1048576; - b /= 1048576; + r >>= 20; + g >>= 20; + b >>= 20; b4[0] = MIN(255, MAX(0, r)); b4[1] = MIN(255, MAX(0, g)); b4[2] = MIN(255, MAX(0, b)); diff --git a/tool/build/emulator.c b/tool/build/blinkenlights.c similarity index 61% rename from tool/build/emulator.c rename to tool/build/blinkenlights.c index b9ae03b1..02932944 100644 --- a/tool/build/emulator.c +++ b/tool/build/blinkenlights.c @@ -19,9 +19,11 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "dsp/tty/tty.h" #include "libc/alg/arraylist2.h" +#include "libc/assert.h" #include "libc/bits/safemacros.h" #include "libc/calls/calls.h" #include "libc/calls/ioctl.h" +#include "libc/calls/struct/iovec.h" #include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/stat.h" #include "libc/calls/struct/termios.h" @@ -42,12 +44,14 @@ #include "libc/sock/sock.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" +#include "libc/str/thompike.h" #include "libc/str/tpdecode.h" #include "libc/str/tpencode.h" #include "libc/sysv/consts/auxv.h" #include "libc/sysv/consts/ex.h" #include "libc/sysv/consts/exit.h" #include "libc/sysv/consts/fileno.h" +#include "libc/sysv/consts/itimer.h" #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/poll.h" @@ -81,6 +85,7 @@ #include "tool/build/lib/pml4t.h" #include "tool/build/lib/pty.h" #include "tool/build/lib/stats.h" +#include "tool/build/lib/syscall.h" #include "tool/build/lib/throw.h" #define USAGE \ @@ -88,7 +93,8 @@ \n\ DESCRIPTION\n\ \n\ - x86 Visualizing Emulator\n\ + Emulates x86 Linux Programs w/ Dense Machine State Visualization\n\ + Please keep still and only watchen astaunished das blinkenlights\n\ \n\ FLAGS\n\ \n\ @@ -108,18 +114,9 @@ ARGUMENTS\n\ It should use x86_64 in accordance with the System Five ABI.\n\ The SYSCALL ABI is defined as it is written in Linux Kernel.\n\ \n\ -PERFORMANCE\n\ +FEATURES\n\ \n\ - 1000 MIPS w/ NOP loop\n\ - Over 9000 MIPS w/ SIMD & Algorithms\n\ -\n\ -COMPLETENESS\n\ -\n\ - Long user mode is supported in addition to SSE3 and SSSE3.\n\ - Real mode and legacy mode are supported with limited APIs.\n\ - Integer ops are implemented rigorously with lots of tests.\n\ - Floating point instructions are yolo, and tunable more so.\n\ - Loading, virtual memory management, and SYSCALL need work.\n\ + 8086, 8087, i386, x86_64, SSE3, SSSE3, POPCNT, MDA, CGA, TTY\n\ \n" #define DUMPWIDTH 64 @@ -136,10 +133,45 @@ COMPLETENESS\n\ #define INT 0x100 #define QUIT 0x200 #define EXIT 0x400 +#define ALARM 0x800 -#define CTRL(C) ((C) ^ 0100) -#define ALT(C) (('\e' << 010) | (C)) -#define SEX(x, b) ((x) | ((x) & (1ull << (b)) ? -(1ull << (b)) : 0)) +#define kXmmIntegral 0 +#define kXmmDouble 1 +#define kXmmFloat 2 + +#define kXmmDecimal 0 +#define kXmmHex 1 +#define kXmmChar 2 + +#define CTRL(C) ((C) ^ 0100) + +enum Mouse { + kMouseLeftDown = 0, + kMouseMiddleDown = 1, + kMouseRightDown = 2, + kMouseLeftUp = 4, + kMouseMiddleUp = 5, + kMouseRightUp = 6, + kMouseLeftDrag = 32, + kMouseMiddleDrag = 33, + kMouseRightDrag = 34, + kMouseWheelUp = 64, + kMouseWheelDown = 65, +}; + +struct MachineState { + uint64_t ip; + uint8_t cs[8]; + uint8_t ss[8]; + uint8_t es[8]; + uint8_t ds[8]; + uint8_t fs[8]; + uint8_t gs[8]; + uint8_t reg[16][8]; + uint8_t xmm[16][16]; + struct MachineFpu fpu; + struct MachineSse sse; +}; struct Panels { union { @@ -149,8 +181,8 @@ struct Panels { struct Panel breakpoints; struct Panel mapshr; struct Panel maps; - struct Panel tracehr; - struct Panel trace; + struct Panel frameshr; + struct Panel frames; struct Panel displayhr; struct Panel display; struct Panel registers; @@ -174,44 +206,59 @@ static const char kRegisterNames[16][4] = { "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15", }; -static const char kSegmentNames[16][4] = { - "ES", "CS", "SS", "DS", "FS", "GS", -}; +static bool react; +static bool tuimode; +static bool alarmed; +static bool colorize; +static bool mousemode; +static bool printstats; static int tyn; static int txn; static int tick; static int speed; static int vidya; -static int ttyfd; -static bool react; -static bool ssehex; +static int ttyin; +static int focus; +static int ttyout; +static int opline; +static int action; +static int xmmdisp; static int exitcode; -static bool tuimode; +static int codezoom; +static int readzoom; +static int writezoom; +static int stackzoom; + static long rombase; -static bool colorize; static long codesize; -static long ssewidth; -static char *codepath; -static void *onbusted; -static unsigned focus; -static unsigned opline; -static bool printstats; -static unsigned action; static int64_t opstart; -static int64_t memstart; -static struct Elf elf[1]; -static struct Dis dis[1]; -static struct Panels pan; +static int64_t codestart; static int64_t readstart; +static int64_t mapsstart; static int64_t writestart; static int64_t stackstart; -static struct Machine m[2]; +static int64_t framesstart; +static int64_t breakpointsstart; +static char *codepath; +static void *onbusted; +static char *statusmessage; +static struct Machine *m; static struct MachinePty *pty; + +static struct Panels pan; +static struct MachineState laststate; +static struct Breakpoints breakpoints; +static struct Elf elf[1]; +static struct Dis dis[1]; +static uint8_t xmmtype[16]; +static uint8_t xmmsize[16]; + +static long double statusexpires; +static struct termios oldterm; static char logpath[PATH_MAX]; static char systemfailure[128]; static struct sigaction oldsig[4]; -static struct Breakpoints breakpoints; static void SetupDraw(void); static void Redraw(void); @@ -220,6 +267,13 @@ static char *FormatDouble(char *b, double x) { return g_fmt(b, x); } +static int64_t SignExtend(uint64_t x, char b) { + char k; + assert(1 <= b && b <= 64); + k = 64 - b; + return (int64_t)(x << k) >> k; +} + static void SetCarry(bool cf) { m->flags = SetFlag(m->flags, FLAGS_CF, cf); } @@ -241,37 +295,58 @@ static bool IsDebugBreak(void) { } static bool IsRet(void) { - if (m->xedd->op.map == XED_ILD_MAP0) { - switch (m->xedd->op.opcode) { - case 0xC2: - case 0xC3: - case 0xCA: - case 0xCB: - case 0xCF: - return true; - default: - return false; - } - } else { - return false; + switch (m->xedd->op.dispatch) { + case 0x0C2: + case 0x0C3: + case 0x0CA: + case 0x0CB: + case 0x0CF: + return true; + default: + return false; } } -static uint8_t CycleSseType(uint8_t t) { +static int GetXmmTypeCellCount(int r) { + switch (xmmtype[r]) { + case kXmmIntegral: + return 16 / xmmsize[r]; + case kXmmFloat: + return 4; + case kXmmDouble: + return 2; + default: + unreachable; + } +} + +static uint8_t CycleXmmType(uint8_t t) { switch (t) { + default: case kXmmIntegral: return kXmmFloat; case kXmmFloat: return kXmmDouble; case kXmmDouble: return kXmmIntegral; - default: - unreachable; } } -static uint8_t CycleSseWidth(uint8_t w) { +static uint8_t CycleXmmDisp(uint8_t t) { + switch (t) { + default: + case kXmmDecimal: + return kXmmHex; + case kXmmHex: + return kXmmChar; + case kXmmChar: + return kXmmDecimal; + } +} + +static uint8_t CycleXmmSize(uint8_t w) { switch (w) { + default: case 1: return 2; case 2: @@ -280,8 +355,6 @@ static uint8_t CycleSseWidth(uint8_t w) { return 8; case 8: return 1; - default: - unreachable; } } @@ -291,31 +364,187 @@ static int GetPointerWidth(void) { static int64_t GetIp(void) { switch (GetPointerWidth()) { + default: case 8: return m->ip; case 4: return Read64(m->cs) + (m->ip & 0xffff); case 2: return Read64(m->cs) + (m->ip & 0xffff); - default: - abort(); } } static int64_t GetSp(void) { switch (GetPointerWidth()) { + default: case 8: return Read64(m->sp); case 4: return Read64(m->ss) + Read32(m->sp); case 2: return Read64(m->ss) + Read16(m->sp); - default: - abort(); } } -static void OnBusted(void) { +static void UpdateXmmTypes(int regtype, int rmtype) { + xmmtype[RexrReg(m->xedd->op.rde)] = regtype; + if (IsModrmRegister(m->xedd->op.rde)) { + xmmtype[RexbRm(m->xedd->op.rde)] = rmtype; + } +} + +static void UpdateXmmSizes(int regsize, int rmsize) { + xmmsize[RexrReg(m->xedd->op.rde)] = regsize; + if (IsModrmRegister(m->xedd->op.rde)) { + xmmsize[RexbRm(m->xedd->op.rde)] = rmsize; + } +} + +static void UpdateXmmType(void) { + switch (m->xedd->op.dispatch) { + case 0x12E: // UCOMIS + case 0x12F: // COMIS + case 0x151: // SQRT + case 0x152: // RSQRT + case 0x153: // RCP + case 0x158: // ADD + case 0x159: // MUL + case 0x15C: // SUB + case 0x15D: // MIN + case 0x15E: // DIV + case 0x15F: // MAX + case 0x1C2: // CMP + if (Osz(m->xedd->op.rde) || Rep(m->xedd->op.rde) == 2) { + UpdateXmmTypes(kXmmDouble, kXmmDouble); + } else { + UpdateXmmTypes(kXmmFloat, kXmmFloat); + } + break; + case 0x12A: // CVTPI2PS,CVTSI2SS,CVTPI2PD,CVTSI2SD + if (Osz(m->xedd->op.rde) || Rep(m->xedd->op.rde) == 2) { + UpdateXmmSizes(8, 4); + UpdateXmmTypes(kXmmDouble, kXmmIntegral); + } else { + UpdateXmmSizes(4, 4); + UpdateXmmTypes(kXmmFloat, kXmmIntegral); + } + break; + case 0x15A: // CVT{P,S}{S,D}2{P,S}{S,D} + if (Osz(m->xedd->op.rde) || Rep(m->xedd->op.rde) == 2) { + UpdateXmmTypes(kXmmFloat, kXmmDouble); + } else { + UpdateXmmTypes(kXmmDouble, kXmmFloat); + } + break; + case 0x15B: // CVT{,T}{DQ,PS}2{PS,DQ} + UpdateXmmSizes(4, 4); + if (Osz(m->xedd->op.rde) || Rep(m->xedd->op.rde) == 3) { + UpdateXmmTypes(kXmmIntegral, kXmmFloat); + } else { + UpdateXmmTypes(kXmmFloat, kXmmIntegral); + } + break; + case 0x17C: // HADD + case 0x17D: // HSUB + case 0x1D0: // ADDSUB + if (Osz(m->xedd->op.rde)) { + UpdateXmmTypes(kXmmDouble, kXmmDouble); + } else { + UpdateXmmTypes(kXmmFloat, kXmmFloat); + } + break; + case 0x164: // PCMPGTB + case 0x174: // PCMPEQB + case 0x1D8: // PSUBUSB + case 0x1DA: // PMINUB + case 0x1DC: // PADDUSB + case 0x1DE: // PMAXUB + case 0x1E0: // PAVGB + case 0x1E8: // PSUBSB + case 0x1EC: // PADDSB + case 0x1F8: // PSUBB + case 0x1FC: // PADDB + UpdateXmmSizes(1, 1); + UpdateXmmTypes(kXmmIntegral, kXmmIntegral); + break; + case 0x165: // PCMPGTW + case 0x175: // PCMPEQW + case 0x171: // PSRLW,PSRAW,PSLLW + case 0x1D1: // PSRLW + case 0x1D5: // PMULLW + case 0x1D9: // PSUBUSW + case 0x1DD: // PADDUSW + case 0x1E1: // PSRAW + case 0x1E3: // PAVGW + case 0x1E4: // PMULHUW + case 0x1E5: // PMULHW + case 0x1E9: // PSUBSW + case 0x1EA: // PMINSW + case 0x1ED: // PADDSW + case 0x1EE: // PMAXSW + case 0x1F1: // PSLLW + case 0x1F6: // PSADBW + case 0x1F9: // PSUBW + case 0x1FD: // PADDW + UpdateXmmSizes(2, 2); + UpdateXmmTypes(kXmmIntegral, kXmmIntegral); + break; + case 0x166: // PCMPGTD + case 0x176: // PCMPEQD + case 0x172: // PSRLD,PSRAD,PSLLD + case 0x1D2: // PSRLD + case 0x1E2: // PSRAD + case 0x1F2: // PSLLD + case 0x1FA: // PSUBD + case 0x1FE: // PADDD + UpdateXmmSizes(4, 4); + UpdateXmmTypes(kXmmIntegral, kXmmIntegral); + break; + case 0x173: // PSRLQ,PSRLQ,PSRLDQ,PSLLQ,PSLLDQ + case 0x1D3: // PSRLQ + case 0x1D4: // PADDQ + case 0x1F3: // PSLLQ + case 0x1F4: // PMULUDQ + case 0x1FB: // PSUBQ + UpdateXmmSizes(8, 8); + UpdateXmmTypes(kXmmIntegral, kXmmIntegral); + break; + case 0x16B: // PACKSSDW + case 0x1F5: // PMADDWD + UpdateXmmSizes(4, 2); + UpdateXmmTypes(kXmmIntegral, kXmmIntegral); + break; + case 0x163: // PACKSSWB + case 0x167: // PACKUSWB + UpdateXmmSizes(1, 2); + UpdateXmmTypes(kXmmIntegral, kXmmIntegral); + break; + case 0x16F: // MOVDQA Vdq Wdq + if (Osz(m->xedd->op.rde) && IsModrmRegister(m->xedd->op.rde)) { + xmmtype[RexrReg(m->xedd->op.rde)] = xmmtype[RexbRm(m->xedd->op.rde)]; + xmmsize[RexrReg(m->xedd->op.rde)] = xmmsize[RexbRm(m->xedd->op.rde)]; + } + break; + default: + return; + } +} + +static void CopyMachineState(struct MachineState *ms) { + ms->ip = m->ip; + memcpy(ms->cs, m->cs, sizeof(m->cs)); + memcpy(ms->ss, m->ss, sizeof(m->ss)); + memcpy(ms->es, m->es, sizeof(m->es)); + memcpy(ms->ds, m->ds, sizeof(m->ds)); + memcpy(ms->fs, m->fs, sizeof(m->fs)); + memcpy(ms->gs, m->gs, sizeof(m->gs)); + memcpy(ms->reg, m->reg, sizeof(m->reg)); + memcpy(ms->xmm, m->xmm, sizeof(m->xmm)); + memcpy(&ms->fpu, &m->fpu, sizeof(m->fpu)); + memcpy(&ms->sse, &m->sse, sizeof(m->sse)); +} + +static void OnSigBusted(void) { CHECK(onbusted); longjmp(onbusted, 1); } @@ -348,32 +577,74 @@ static void ScrollOp(struct Panel *p, long op) { } } -static void GetTtySize(void) { +static int TtyWriteString(const char *s) { + return write(ttyout, s, strlen(s)); +} + +static void OnFeed(void) { + TtyWriteString("\e[K\e[2J"); +} + +static void HideCursor(void) { + TtyWriteString("\e[?25l"); +} + +static void ShowCursor(void) { + TtyWriteString("\e[?25h"); +} + +static void EnableMouseTracking(void) { + mousemode = true; + TtyWriteString("\e[?1000;1002;1015;1006h"); +} + +static void DisableMouseTracking(void) { + mousemode = false; + TtyWriteString("\e[?1000;1002;1015;1006l"); +} + +static void ToggleMouseTracking(void) { + if (mousemode) { + DisableMouseTracking(); + } else { + EnableMouseTracking(); + } +} + +static void LeaveScreen(void) { + TtyWriteString(gc(xasprintf("\e[%d;%dH\e[S\r\n", tyn, txn))); +} + +static void GetTtySize(int fd) { struct winsize wsize; - wsize.ws_row = 25; - wsize.ws_col = 80; - getttysize(ttyfd, &wsize); + wsize.ws_row = tyn; + wsize.ws_col = txn; + getttysize(fd, &wsize); tyn = wsize.ws_row; txn = wsize.ws_col; } static void TuiRejuvinate(void) { - GetTtySize(); - ttyhidecursor(STDOUT_FILENO); - ttyraw(kTtySigs); - xsigaction(SIGBUS, OnBusted, SA_NODEFER, 0, NULL); -} - -static void OnCtrlC(void) { - if (tuimode) { - action |= INT; - } else { - HaltMachine(m, kMachineExit); - } + struct termios term; + DEBUGF("TuiRejuvinate"); + GetTtySize(ttyout); + HideCursor(); + memcpy(&term, &oldterm, sizeof(term)); + term.c_cc[VMIN] = 1; + term.c_cc[VTIME] = 1; + term.c_iflag &= ~(INPCK | ISTRIP | PARMRK | INLCR | IGNCR | ICRNL | IXON); + term.c_lflag &= ~(IEXTEN | ICANON | ECHO | ECHONL); + term.c_cflag &= ~(CSIZE | PARENB); + term.c_cflag |= CS8; + term.c_iflag |= IUTF8; + CHECK_NE(-1, ioctl(ttyout, TCSETS, &term)); + xsigaction(SIGBUS, OnSigBusted, SA_NODEFER, 0, NULL); + EnableMouseTracking(); } static void OnQ(void) { LOGF("OnQ"); + if (action & FAILURE) exit(1); action |= INT; breakpoints.i = 0; } @@ -382,24 +653,44 @@ static void OnV(void) { vidya = !vidya; } -static void OnWinch(void) { - LOGF("OnWinch"); +static void OnSigWinch(void) { action |= WINCHED; } -static void OnCont(void) { - LOGF("OnCont"); +static void OnSigInt(void) { + if (tuimode) { + action |= INT; + } else { + action |= EXIT; + } +} + +static void OnSigAlarm(void) { + action |= ALARM; +} + +static void OnSigCont(void) { TuiRejuvinate(); SetupDraw(); Redraw(); } +static void TtyRestore1(void) { + DEBUGF("TtyRestore1"); + ShowCursor(); + TtyWriteString("\e[0m"); +} + +static void TtyRestore2(void) { + DEBUGF("TtyRestore2"); + ioctl(ttyout, TCSETS, &oldterm); + DisableMouseTracking(); +} + static void TuiCleanup(void) { - sigaction(SIGWINCH, oldsig + 0, NULL); sigaction(SIGCONT, oldsig + 2, NULL); - ttyraw(-1); - ttyshowcursor(STDOUT_FILENO); - CHECK_NE(-1, close(ttyfd)); + TtyRestore1(); + DisableMouseTracking(); tuimode = false; } @@ -419,7 +710,10 @@ static void ResolveBreakpoints(void) { } static void BreakAtNextInstruction(void) { - struct Breakpoint b = {.addr = m->ip + m->xedd->length, .oneshot = true}; + struct Breakpoint b; + memset(&b, 0, sizeof(b)); + b.addr = GetIp() + m->xedd->length; + b.oneshot = true; PushBreakpoint(&breakpoints, &b); } @@ -428,21 +722,71 @@ static void LoadSyms(void) { DisLoadElf(dis, elf); } +static int DrainInput(int fd) { + char buf[32]; + struct pollfd fds[1]; + for (;;) { + fds[0].fd = fd; + fds[0].events = POLLIN; + if (poll(fds, ARRAYLEN(fds), 0) == -1) return -1; + if (!(fds[0].revents & POLLIN)) break; + if (read(fd, buf, sizeof(buf)) == -1) return -1; + } + return 0; +} + +static int ReadCursorPosition(int *out_y, int *out_x) { + int y, x; + char *p, buf[32]; + if (readansi(ttyin, buf, sizeof(buf)) == 1) return -1; + p = buf; + if (*p == '\e') ++p; + if (*p == '[') ++p; + y = strtol(p, &p, 10); + if (*p == ';') ++p; + x = strtol(p, &p, 10); + if (*p != 'R') return ebadmsg(); + if (out_y) *out_y = MAX(1, y) - 1; + if (out_x) *out_x = MAX(1, x) - 1; + return 0; +} + +static int GetCursorPosition(int *out_y, int *out_x) { + TtyWriteString("\e[6n"); + return ReadCursorPosition(out_y, out_x); +} + +static int GetTerminalDimensions(int *out_y, int *out_x) { + TtyWriteString("\e7\e[9979;9979H\e[6n\e8"); + return ReadCursorPosition(out_y, out_x); +} + void TuiSetup(void) { + int y, x; + bool report; static bool once; + report = false; if (!once) { - LOGF("loaded program %s\n%s", codepath, gc(FormatPml4t(m->cr3))); + LOGF("loaded program %s\n%s", codepath, gc(FormatPml4t(m))); LoadSyms(); ResolveBreakpoints(); - Dis(dis, m, elf->base, elf->base, 100); + ioctl(ttyout, TCGETS, &oldterm); + xsigaction(SIGALRM, OnSigAlarm, 0, 0, 0); + xsigaction(SIGINT, OnSigInt, 0, 0, oldsig + 3); + atexit(TtyRestore2); once = true; + report = true; } - CHECK_NE(-1, (ttyfd = open("/dev/tty", O_RDWR))); - xsigaction(SIGWINCH, OnWinch, 0, 0, oldsig + 0); - xsigaction(SIGCONT, OnCont, SA_RESTART, 0, oldsig + 2); - xsigaction(SIGINT, OnCtrlC, 0 /* SA_NODEFER */, 0, oldsig + 3); - memcpy(&m[1], &m[0], sizeof(m[0])); + xsigaction(SIGCONT, OnSigCont, SA_RESTART | SA_NODEFER, 0, oldsig + 2); + CopyMachineState(&laststate); TuiRejuvinate(); + if (report) { + DrainInput(ttyin); + y = 0; + if (GetCursorPosition(&y, NULL) != -1) { + TtyWriteString(gc(xasprintf("\e[%dS", y))); + } + } } static void ExecSetup(void) { @@ -513,7 +857,7 @@ static void SetupDraw(void) { if (ssey) ++ssey; a = 12 + 1 + DUMPWIDTH; - b = DISPWIDTH; + b = DISPWIDTH + 1; dx[1] = txn >= a + b ? txn - a : txn; dx[0] = txn >= a + b + b ? txn - a - b : dx[1]; @@ -566,15 +910,15 @@ static void SetupDraw(void) { pan.maps.bottom = c2y[1]; pan.maps.right = dx[1] - 1; - pan.tracehr.top = c2y[1]; - pan.tracehr.left = dx[0]; - pan.tracehr.bottom = c2y[1] + 1; - pan.tracehr.right = dx[1] - 1; + pan.frameshr.top = c2y[1]; + pan.frameshr.left = dx[0]; + pan.frameshr.bottom = c2y[1] + 1; + pan.frameshr.right = dx[1] - 1; - pan.trace.top = c2y[1] + 1; - pan.trace.left = dx[0]; - pan.trace.bottom = c2y[2]; - pan.trace.right = dx[1] - 1; + pan.frames.top = c2y[1] + 1; + pan.frames.left = dx[0]; + pan.frames.bottom = c2y[2]; + pan.frames.right = dx[1] - 1; pan.displayhr.top = c2y[2]; pan.displayhr.left = dx[0]; @@ -688,18 +1032,37 @@ static void DrawHr(struct Panel *p, const char *s) { long i, wp, ws, wl, wr; if (p->bottom - p->top < 1) return; wp = p->right - p->left; - ws = strlen(s); + ws = strwidth(s); wl = wp / 4 - ws / 2; wr = wp - (wl + ws); for (i = 0; i < wl; ++i) AppendWide(&p->lines[0], u'─'); AppendStr(&p->lines[0], s); for (i = 0; i < wr; ++i) AppendWide(&p->lines[0], u'─'); + AppendStr(&p->lines[0], "\e[0m"); } -static void DrawTerminal(struct Panel *p) { - long y, yn; +void DrawTerminal(struct Panel *p) { + long i, y, yn; + if (pty->conf & kMachinePtyBell) { + if (!alarmed) { + alarmed = true; + setitimer(ITIMER_REAL, &((struct itimerval){{0, 0}, {0, 800000}}), NULL); + } + AppendStr(&pan.displayhr.lines[0], "\e[1m"); + } + AppendStr( + &pan.displayhr.lines[0], + gc(xasprintf("──────────TELETYPEWRITER──%s──%s──%s──%s", + (pty->conf & kMachinePtyLed1) ? "\e[1;31m◎\e[0m" : "○", + (pty->conf & kMachinePtyLed2) ? "\e[1;32m◎\e[0m" : "○", + (pty->conf & kMachinePtyLed3) ? "\e[1;33m◎\e[0m" : "○", + (pty->conf & kMachinePtyLed4) ? "\e[1;34m◎\e[0m" : "○"))); + for (i = 36; i < pan.displayhr.right - pan.displayhr.left; ++i) { + AppendWide(&pan.displayhr.lines[0], u'─'); + } for (yn = MIN(pty->yn, p->bottom - p->top), y = 0; y < yn; ++y) { MachinePtyAppendLine(pty, p->lines + y, y); + AppendStr(p->lines + y, "\e[0m"); } } @@ -716,7 +1079,6 @@ void DrawDisplay(struct Panel *p) { VirtualSend(m, gc(xmalloc(25 * 80 * 2)), 0xb8000, 25 * 80 * 2)); break; default: - DrawHr(&pan.displayhr, "TELETYPEWRITER"); DrawTerminal(p); break; } @@ -731,8 +1093,8 @@ static void DrawFlag(struct Panel *p, long i, char name, bool value) { static void DrawRegister(struct Panel *p, long i, long r) { char buf[32]; uint64_t value, previous; - value = Read64(m[0].reg[r]); - previous = Read64(m[1].reg[r]); + value = Read64(m->reg[r]); + previous = Read64(laststate.reg[r]); if (value != previous) AppendPanel(p, i, "\e[7m"); snprintf(buf, sizeof(buf), "%-3s", kRegisterNames[r]); AppendPanel(p, i, buf); @@ -743,13 +1105,14 @@ static void DrawRegister(struct Panel *p, long i, long r) { AppendPanel(p, i, " "); } -static void DrawSegment(struct Panel *p, long i, long r) { +static void DrawSegment(struct Panel *p, long i, const uint8_t seg[8], + const uint8_t last[8], const char *name) { char buf[32]; uint64_t value, previous; - value = Read64(GetSegment(m + 0, 0, r)); - previous = Read64(GetSegment(m + 1, 0, r)); + value = Read64(seg); + previous = Read64(last); if (value != previous) AppendPanel(p, i, "\e[7m"); - snprintf(buf, sizeof(buf), "%-3s", kSegmentNames[r]); + snprintf(buf, sizeof(buf), "%-3s", name); AppendPanel(p, i, buf); AppendPanel(p, i, " "); snprintf(buf, sizeof(buf), "0x%016lx", value); @@ -763,16 +1126,16 @@ static void DrawSt(struct Panel *p, long i, long r) { long double value; bool isempty, changed; isempty = FpuGetTag(m, r) == kFpuTagEmpty; - if (isempty) AppendPanel(p, i, "\e[2m"); - value = m[0].fpu.st[(r + m[0].fpu.sp) & 0b111]; - changed = value != m[1].fpu.st[(r + m[0].fpu.sp) & 0b111]; + if (isempty) AppendPanel(p, i, "\e[38;5;241m"); + value = m->fpu.st[(r + m->fpu.sp) & 0b111]; + changed = value != laststate.fpu.st[(r + m->fpu.sp) & 0b111]; if (!isempty && changed) AppendPanel(p, i, "\e[7m"); snprintf(buf, sizeof(buf), "ST%d ", r); AppendPanel(p, i, buf); AppendPanel(p, i, FormatDouble(buf, value)); if (changed) AppendPanel(p, i, "\e[27m"); AppendPanel(p, i, " "); - if (isempty) AppendPanel(p, i, "\e[22m"); + if (isempty) AppendPanel(p, i, "\e[39m"); } static void DrawCpu(struct Panel *p) { @@ -785,16 +1148,16 @@ static void DrawCpu(struct Panel *p) { DrawRegister(p, 5, 9), DrawRegister(p, 5, 13), DrawSt(p, 5, 5); DrawRegister(p, 6, 10), DrawRegister(p, 6, 14), DrawSt(p, 6, 6); DrawRegister(p, 7, 11), DrawRegister(p, 7, 15), DrawSt(p, 7, 7); - snprintf(buf, sizeof(buf), "RIP 0x%016x FLG", m[0].ip); + snprintf(buf, sizeof(buf), "RIP 0x%016x FLG", m->ip); AppendPanel(p, 8, buf); - DrawFlag(p, 8, 'C', GetFlag(m[0].flags, FLAGS_CF)); - DrawFlag(p, 8, 'P', GetFlag(m[0].flags, FLAGS_PF)); - DrawFlag(p, 8, 'A', GetFlag(m[0].flags, FLAGS_AF)); - DrawFlag(p, 8, 'Z', GetFlag(m[0].flags, FLAGS_ZF)); - DrawFlag(p, 8, 'S', GetFlag(m[0].flags, FLAGS_SF)); - DrawFlag(p, 8, 'I', GetFlag(m[0].flags, FLAGS_IF)); - DrawFlag(p, 8, 'D', GetFlag(m[0].flags, FLAGS_DF)); - DrawFlag(p, 8, 'O', GetFlag(m[0].flags, FLAGS_OF)); + DrawFlag(p, 8, 'C', GetFlag(m->flags, FLAGS_CF)); + DrawFlag(p, 8, 'P', GetFlag(m->flags, FLAGS_PF)); + DrawFlag(p, 8, 'A', GetFlag(m->flags, FLAGS_AF)); + DrawFlag(p, 8, 'Z', GetFlag(m->flags, FLAGS_ZF)); + DrawFlag(p, 8, 'S', GetFlag(m->flags, FLAGS_SF)); + DrawFlag(p, 8, 'I', GetFlag(m->flags, FLAGS_IF)); + DrawFlag(p, 8, 'D', GetFlag(m->flags, FLAGS_DF)); + DrawFlag(p, 8, 'O', GetFlag(m->flags, FLAGS_OF)); AppendPanel(p, 8, " "); if (m->fpu.ie) AppendPanel(p, 8, " IE"); if (m->fpu.de) AppendPanel(p, 8, " DE"); @@ -808,44 +1171,34 @@ static void DrawCpu(struct Panel *p) { if (m->fpu.c1) AppendPanel(p, 8, " C1"); if (m->fpu.c2) AppendPanel(p, 8, " C2"); if (m->fpu.bf) AppendPanel(p, 8, " BF"); - DrawSegment(p, 9, 4), DrawSegment(p, 9, 3), DrawSegment(p, 9, 1); - DrawSegment(p, 10, 5), DrawSegment(p, 10, 0), DrawSegment(p, 10, 2); + DrawSegment(p, 9, m->fs, laststate.fs, "FS"); + DrawSegment(p, 9, m->ds, laststate.ds, "DS"); + DrawSegment(p, 9, m->cs, laststate.cs, "CS"); + DrawSegment(p, 10, m->gs, laststate.gs, "GS"); + DrawSegment(p, 10, m->es, laststate.es, "ES"); + DrawSegment(p, 10, m->ss, laststate.ss, "SS"); } static void DrawXmm(struct Panel *p, long i, long r) { float f; double d; long j, k, n; - uint8_t type; bool changed; char buf[32]; uint8_t xmm[16]; uint64_t ival, itmp; int cells, left, cellwidth, panwidth; memcpy(xmm, m->xmm[r], sizeof(xmm)); - changed = memcmp(xmm, m[1].xmm[r], sizeof(xmm)) != 0; + changed = memcmp(xmm, laststate.xmm[r], sizeof(xmm)) != 0; if (changed) AppendPanel(p, i, "\e[7m"); left = sprintf(buf, "XMM%-2d", r); AppendPanel(p, i, buf); - type = m->xmmtype[r / 8][r % 8]; - switch (type) { - case kXmmFloat: - cells = 4; - break; - case kXmmDouble: - cells = 2; - break; - case kXmmIntegral: - cells = 16 / ssewidth; - break; - default: - unreachable; - } + cells = GetXmmTypeCellCount(r); panwidth = p->right - p->left; cellwidth = MIN(MAX(0, (panwidth - left) / cells - 1), sizeof(buf) - 1); for (j = 0; j < cells; ++j) { AppendPanel(p, i, " "); - switch (type) { + switch (xmmtype[r]) { case kXmmFloat: memcpy(&f, xmm + j * sizeof(f), sizeof(f)); FormatDouble(buf, f); @@ -856,19 +1209,19 @@ static void DrawXmm(struct Panel *p, long i, long r) { break; case kXmmIntegral: ival = 0; - for (k = 0; k < ssewidth; ++k) { - itmp = xmm[j * ssewidth + k] & 0xff; + for (k = 0; k < xmmsize[r]; ++k) { + itmp = xmm[j * xmmsize[r] + k] & 0xff; itmp <<= k * 8; ival |= itmp; } - if (!ssehex) { - if (0 && ssewidth == 1 && (040 <= ival && ival < 0200 - 1)) { - sprintf(buf, "%`'c", ival); + if (xmmdisp == kXmmHex || xmmdisp == kXmmChar) { + if (xmmdisp == kXmmChar && iswalnum(ival)) { + sprintf(buf, "%lc", ival); } else { - int64toarray_radix10(SEX(ival, ssewidth * 8), buf); + uint64toarray_fixed16(ival, buf, xmmsize[r] * 8); } } else { - uint64toarray_fixed16(ival, buf, ssewidth * 8); + int64toarray_radix10(SignExtend(ival, xmmsize[r] * 8), buf); } break; default: @@ -895,15 +1248,15 @@ static void ScrollCode(struct Panel *p) { long i, n; n = p->bottom - p->top; i = GetIp() / DUMPWIDTH; - if (!(memstart <= i && i < memstart + n)) { - memstart = i; + if (!(codestart <= i && i < codestart + n)) { + codestart = i; } } static void ScrollReadData(struct Panel *p) { long i, n, addr; n = p->bottom - p->top; - i = m->readaddr / DUMPWIDTH; + i = m->readaddr / (DUMPWIDTH * (1 << readzoom)); if (!(readstart <= i && i < readstart + n)) { readstart = i - n / 3; } @@ -927,18 +1280,49 @@ static void ScrollStack(struct Panel *p) { } } -static void DrawMemory(struct Panel *p, long beg, long a, long b) { +static uint8_t Downsample(uint8_t al, uint8_t bl, uint8_t cl, uint8_t dl) { + int16_t ax, bx; + bx = bl; + bx += cl; + bx *= 3; + ax = al; + ax += dl; + ax += bx; + ax += 4; + ax >>= 3; + al = ax; + return al; +} + +static uint8_t Sharpen(uint8_t al, uint8_t bl, uint8_t cl) { + int16_t ax, bx, cx; + ax = al; + bx = bl; + cx = cl; + ax *= -1; + bx *= +6; + cx *= -1; + ax += bx; + ax += cx; + ax += 2; + ax >>= 2; + return MIN(255, MAX(0, ax)); +} + +static void DrawMemory(struct Panel *p, int zoom, long startline, long histart, + long hiend) { char buf[16]; - long i, j, k, c; bool high, changed; + long i, j, k, c, width; high = false; + width = DUMPWIDTH * (1 << zoom); for (i = 0; i < p->bottom - p->top; ++i) { - snprintf(buf, sizeof(buf), "%p ", (beg + i) * DUMPWIDTH); + snprintf(buf, sizeof(buf), "%p ", (startline + i) * width); AppendStr(&p->lines[i], buf); - for (j = 0; j < DUMPWIDTH; ++j) { - k = (beg + i) * DUMPWIDTH + j; + for (j = 0; j < width; ++j) { + k = (startline + i) * DUMPWIDTH + j; c = VirtualBing(k); - changed = a <= k && k < b; + changed = histart <= k && k < hiend; if (changed && !high) { high = true; AppendStr(&p->lines[i], "\e[7m"); @@ -958,10 +1342,12 @@ static void DrawMemory(struct Panel *p, long beg, long a, long b) { static void DrawMaps(struct Panel *p) { int i; char *text, *p1, *p2; - p1 = text = FormatPml4t(m->cr3); + p1 = text = FormatPml4t(m); for (i = 0; p1; ++i, p1 = p2) { if ((p2 = strchr(p1, '\n'))) *p2++ = '\0'; - AppendPanel(p, i, p1); + if (i >= mapsstart) { + AppendPanel(p, i - mapsstart, p1); + } } free(text); } @@ -973,16 +1359,18 @@ static void DrawBreakpoints(struct Panel *p) { long i, line, sym; for (line = 0, i = breakpoints.i; i--;) { if (breakpoints.p[i].disable) continue; - addr = breakpoints.p[i].addr; - sym = DisFindSym(dis, addr); - name = sym != -1 ? dis->syms.stab + dis->syms.p[sym].name : "UNKNOWN"; - s = buf; - s += sprintf(s, "%p ", addr); - CHECK_LT(Demangle(s, name, DIS_MAX_SYMBOL_LENGTH), buf + ARRAYLEN(buf)); - AppendPanel(p, line, buf); - if (sym != -1 && addr != dis->syms.p[sym].addr) { - snprintf(buf, sizeof(buf), "+%#lx", addr - dis->syms.p[sym].addr); - AppendPanel(p, line, buf); + if (line >= breakpointsstart) { + addr = breakpoints.p[i].addr; + sym = DisFindSym(dis, addr); + name = sym != -1 ? dis->syms.stab + dis->syms.p[sym].name : "UNKNOWN"; + s = buf; + s += sprintf(s, "%p ", addr); + CHECK_LT(Demangle(s, name, DIS_MAX_SYMBOL_LENGTH), buf + ARRAYLEN(buf)); + AppendPanel(p, line - breakpointsstart, buf); + if (sym != -1 && addr != dis->syms.p[sym].addr) { + snprintf(buf, sizeof(buf), "+%#lx", addr - dis->syms.p[sym].addr); + AppendPanel(p, line, buf); + } } ++line; } @@ -1001,7 +1389,7 @@ static int GetPreferredStackAlignmentMask(void) { } } -static void DrawTrace(struct Panel *p) { +static void DrawFrames(struct Panel *p) { int i, n; long sym; uint8_t *r; @@ -1017,25 +1405,25 @@ static void DrawTrace(struct Panel *p) { s = line; s += sprintf(s, "%p %p ", Read64(m->ss) + bp, rp); s = Demangle(s, name, DIS_MAX_SYMBOL_LENGTH); - AppendPanel(p, i, line); + AppendPanel(p, i - framesstart, line); if (sym != -1 && rp != dis->syms.p[sym].addr) { snprintf(line, sizeof(line), "+%#lx", rp - dis->syms.p[sym].addr); - AppendPanel(p, i, line); + AppendPanel(p, i - framesstart, line); } if (!bp) break; if (bp < sp) { - AppendPanel(p, i, " [STRAY]"); - } else { + AppendPanel(p, i - framesstart, " [STRAY]"); + } else if (bp - sp <= 0x1000) { snprintf(line, sizeof(line), " %,ld bytes", bp - sp); - AppendPanel(p, i, line); + AppendPanel(p, i - framesstart, line); } if (bp & GetPreferredStackAlignmentMask() && i) { - AppendPanel(p, i, " [MISALIGN]"); + AppendPanel(p, i - framesstart, " [MISALIGN]"); } ++i; if (((Read64(m->ss) + bp) & 0xfff) > 0xff0) break; if (!(r = FindReal(m, Read64(m->ss) + bp))) { - AppendPanel(p, i, "CORRUPT FRAME POINTER"); + AppendPanel(p, i - framesstart, "CORRUPT FRAME POINTER"); break; } sp = bp; @@ -1074,6 +1462,10 @@ forceinline void CheckFramePointer(void) { } } +static bool IsExecuting(void) { + return (action & (CONTINUE | STEP | NEXT | FINISH)) && !(action & FAILURE); +} + static void Redraw(void) { int i, j; for (i = 0; i < ARRAYLEN(pan.p); ++i) { @@ -1085,30 +1477,32 @@ static void Redraw(void) { DrawDisplay(&pan.display); DrawCpu(&pan.registers); DrawSse(&pan.sse); - ScrollCode(&pan.code); - ScrollStack(&pan.stack); - ScrollReadData(&pan.readdata); - ScrollWriteData(&pan.writedata); DrawHr(&pan.breakpointshr, "BREAKPOINTS"); DrawHr(&pan.mapshr, "MAPS"); - DrawHr(&pan.tracehr, m->bofram[0] ? "PROTECTED FRAMES" : "FRAMES"); + DrawHr(&pan.frameshr, m->bofram[0] ? "PROTECTED FRAMES" : "FRAMES"); DrawHr(&pan.ssehr, "SSE"); DrawHr(&pan.codehr, "CODE"); DrawHr(&pan.readhr, "READ"); DrawHr(&pan.writehr, "WRITE"); DrawHr(&pan.stackhr, "STACK"); DrawMaps(&pan.maps); - DrawTrace(&pan.trace); + DrawFrames(&pan.frames); DrawBreakpoints(&pan.breakpoints); - DrawMemory(&pan.code, memstart, GetIp(), GetIp() + m->xedd->length); - DrawMemory(&pan.readdata, readstart, m->readaddr, m->readaddr + m->readsize); - DrawMemory(&pan.writedata, writestart, m->writeaddr, + DrawMemory(&pan.code, codezoom, codestart, GetIp(), + GetIp() + m->xedd->length); + DrawMemory(&pan.readdata, readzoom, readstart, m->readaddr, + m->readaddr + m->readsize); + DrawMemory(&pan.writedata, writezoom, writestart, m->writeaddr, m->writeaddr + m->writesize); - DrawMemory(&pan.stack, stackstart, GetSp(), GetSp() + GetPointerWidth()); - if (PrintPanels(ttyfd, ARRAYLEN(pan.p), pan.p, tyn, txn) == -1) { + DrawMemory(&pan.stack, stackzoom, stackstart, GetSp(), + GetSp() + GetPointerWidth()); + if (PrintPanels(ttyout, ARRAYLEN(pan.p), pan.p, tyn, txn) == -1) { LOGF("PrintPanels Interrupted"); CHECK_EQ(EINTR, errno); } + if (statusmessage && nowl() < statusexpires) { + TtyWriteString(statusmessage); + } } static void ReactiveDraw(void) { @@ -1121,6 +1515,37 @@ static void ReactiveDraw(void) { } } +static void HandleAlarm(void) { + alarmed = false; + action &= ~ALARM; + pty->conf &= ~kMachinePtyBell; + free(statusmessage); + statusmessage = NULL; +} + +static void HandleAppReadInterrupt(void) { + DEBUGF("HandleAppReadInterrupt"); + if (action & ALARM) { + HandleAlarm(); + } + if (action & WINCHED) { + GetTtySize(ttyout); + action &= ~WINCHED; + } + if (action & INT) { + action &= ~INT; + if (action & CONTINUE) { + action &= ~CONTINUE; + } else { + if (tuimode) { + LeaveScreen(); + TuiCleanup(); + } + exit(0); + } + } +} + static int OnPtyFdClose(int fd) { return 0; } @@ -1134,39 +1559,88 @@ static bool HasPendingInput(int fd) { return fds[0].revents & (POLLIN | POLLERR); } -static ssize_t ConsumePtyInput(int fd) { +static ssize_t ReadPtyFdDirect(int fd, void *data, size_t size) { char *buf; ssize_t rc; + DEBUGF("ReadPtyFdDirect"); buf = malloc(PAGESIZE); pty->conf |= kMachinePtyBlinkcursor; - ReactiveDraw(); - rc = read(fd, buf, PAGESIZE); + if (tuimode) DisableMouseTracking(); + for (;;) { + ReactiveDraw(); + if ((rc = read(fd, buf, PAGESIZE)) != -1) break; + CHECK_EQ(EINTR, errno); + HandleAppReadInterrupt(); + } + if (tuimode) EnableMouseTracking(); pty->conf &= ~kMachinePtyBlinkcursor; - ReactiveDraw(); - if (rc > 0) MachinePtyWriteInput(pty, buf, rc); + if (rc > 0) { + MachinePtyWriteInput(pty, buf, rc); + ReactiveDraw(); + rc = MachinePtyRead(pty, data, size); + } free(buf); return rc; } -static ssize_t OnPtyFdRead(int fd, void *data, size_t size) { +static ssize_t OnPtyFdReadv(int fd, const struct iovec *iov, int iovlen) { + int i; ssize_t rc; - if (!size) return 0; - if (HasPendingInput(fd)) { - if ((rc = ConsumePtyInput(fd)) <= 0) return rc; + void *data; + size_t size; + for (size = i = 0; i < iovlen; ++i) { + if (iov[i].iov_len) { + data = iov[i].iov_base; + size = iov[i].iov_len; + break; + } } - while (!(rc = MachinePtyRead(pty, data, size))) { - if ((rc = ConsumePtyInput(fd)) <= 0) return rc; + if (size) { + if (!(rc = MachinePtyRead(pty, data, size))) { + rc = ReadPtyFdDirect(fd, data, size); + } + return rc; + } else { + return 0; } - return rc; } -static ssize_t OnPtyFdWrite(int fd, const void *data, size_t size) { - if (tuimode) { - return MachinePtyWrite(pty, data, size); - } else { - MachinePtyWrite(pty, data, size); - return write(fd, data, size); +static void DrawTerminalOnly(void) { + struct Buffer b; + int i, y, yn, xn, tly, tlx, conf; + conf = pty->conf & kMachinePtyNocursor; + pty->conf |= kMachinePtyNocursor; + memset(&b, 0, sizeof(b)); + yn = MIN(tyn, pty->yn); + xn = MIN(txn, pty->xn); + tly = tyn / 2 - yn / 2; + tlx = txn / 2 - xn / 2; + AppendStr(&b, "\e[0m\e[H"); + for (y = 0; y < tyn; ++y) { + if (y) AppendStr(&b, "\r\n"); + if (tly <= y && y <= tly + yn) { + for (i = 0; i < tlx; ++i) { + AppendChar(&b, ' '); + } + MachinePtyAppendLine(pty, &b, y - tly); + } + AppendStr(&b, "\e[0m\e[K"); } + AppendFmt(&b, "\e[%d;%dH", tly + pty->y + 1, tlx + pty->x + 1); + write(ttyout, b.p, b.i); + free(b.p); + pty->conf |= conf; +} + +static ssize_t OnPtyFdWritev(int fd, const struct iovec *iov, int iovlen) { + int i; + size_t size; + for (size = i = 0; i < iovlen; ++i) { + MachinePtyWrite(pty, iov[i].iov_base, iov[i].iov_len); + size += iov[i].iov_len; + } + if (!tuimode) DrawTerminalOnly(); + return size; } static int OnPtyFdTiocgwinsz(int fd, struct winsize *ws) { @@ -1216,8 +1690,8 @@ static int OnPtyFdIoctl(int fd, uint64_t request, void *memory) { static const struct MachineFdCb kMachineFdCbPty = { .close = OnPtyFdClose, - .read = OnPtyFdRead, - .write = OnPtyFdWrite, + .readv = OnPtyFdReadv, + .writev = OnPtyFdWritev, .ioctl = OnPtyFdIoctl, }; @@ -1366,38 +1840,48 @@ static void OnDiskService(void) { } static void OnVidyaServiceSetMode(void) { - vidya = m->ax[0]; + if (FindReal(m, 0xB0000)) { + vidya = m->ax[0]; + } else { + WARNF("maybe you forgot -r flag"); + } } static void OnVidyaServiceGetMode(void) { m->ax[0] = vidya; - m->ax[1] = 80; /* columns */ - m->bx[1] = 0; /* page */ + m->ax[1] = 80; // columns + m->bx[1] = 0; // page } static void OnVidyaServiceSetCursorPosition(void) { - pty->y = m->dx[1]; - pty->x = m->dx[0]; + MachinePtySetY(pty, m->dx[1]); + MachinePtySetX(pty, m->dx[0]); } static void OnVidyaServiceGetCursorPosition(void) { m->dx[1] = pty->y; m->dx[0] = pty->x; - m->cx[1] = 5; /* cursor ▂ scan lines 5..7 of 0..7 */ + m->cx[1] = 5; // cursor ▂ scan lines 5..7 of 0..7 m->cx[0] = 7 | !!(pty->conf & kMachinePtyNocursor) << 5; } +static int GetVidyaByte(unsigned char b) { + if (0x20 <= b && b <= 0x7F) return b; + if (b == 0xFF) b = 0x00; + return kCp437[b]; +} + static void OnVidyaServiceWriteCharacter(void) { - char buf[12]; int i, n, y, x; - y = pty->y; - x = pty->x; - n = FormatCga(m->bx[0], buf); - n += tpencode(buf + n, 6, kCp437[m->ax[0]], false); - i = Read16(m->cx); - while (i--) MachinePtyWrite(pty, buf, n); - pty->y = y; - pty->x = x; + char *p, buf[32]; + p = buf; + p += FormatCga(m->bx[0], p); + p = stpcpy(p, "\e7"); + p += tpencode(p, 8, GetVidyaByte(m->ax[0]), false); + p = stpcpy(p, "\e8"); + for (i = Read16(m->cx); i--;) { + MachinePtyWrite(pty, buf, p - buf); + } } static char16_t VidyaServiceXlatTeletype(uint8_t c) { @@ -1409,7 +1893,7 @@ static char16_t VidyaServiceXlatTeletype(uint8_t c) { case 0177: return c; default: - return kCp437[c]; + return GetVidyaByte(c); } } @@ -1448,18 +1932,19 @@ static void OnVidyaService(void) { static void OnKeyboardServiceReadKeyPress(void) { uint8_t b; - ReactiveDraw(); - read(0, &b, 1); - switch (b) { - case 0177: - b = '\b'; - break; - case CTRL('C'): - raise(SIGINT); - break; - default: - break; + ssize_t rc; + pty->conf |= kMachinePtyBlinkcursor; + if (tuimode) DisableMouseTracking(); + for (;;) { + ReactiveDraw(); + if ((rc = read(0, &b, 1)) != -1) break; + CHECK_EQ(EINTR, errno); + HandleAppReadInterrupt(); } + if (tuimode) EnableMouseTracking(); + pty->conf &= ~kMachinePtyBlinkcursor; + ReactiveDraw(); + if (b == 0x7F) b = '\b'; m->ax[0] = b; m->ax[1] = 0; } @@ -1520,7 +2005,6 @@ static void OnInt15h(void) { } static bool OnHalt(int interrupt) { - LOGF("OnHalt(%d)", interrupt); ReactiveDraw(); switch (interrupt) { case 1: @@ -1576,29 +2060,47 @@ static void OnPageDown(void) { opstart += tyn / 2; } -static void OnUpArrow(void) { - if (action & CONTINUE) { - if (speed >= -1) { - speed = MIN(0x40000000, MAX(1, speed) << 1); // 1..40mips skip - } else { - speed >>= 1; - } +static void SetStatus(const char *fmt, ...) { + char *s; + va_list va; + int y, x, n; + va_start(va, fmt); + s = gc(xvasprintf(fmt, va)); + va_end(va); + n = strwidth(s); + y = tyn - (n / txn + 1); + x = txn / 2 - n / 2; + free(statusmessage); + statusmessage = xasprintf("\e[0m\e[%d;%dH%s", y + 1, x + 1, s); + TtyWriteString(statusmessage); + setitimer(ITIMER_REAL, &((struct itimerval){{0, 0}, {1, 0}}), NULL); + statusexpires = nowl() + 1; +} + +static void OnTurbo(void) { + if (speed >= -1) { + speed = MIN(0x40000000, MAX(1, speed) << 1); // 1..40mips skip } else { - --opstart; + speed >>= 1; } - LOGF("speed %d", speed); + SetStatus("speed %,d", speed); +} + +static void OnSlowmo(void) { + if (speed > 0) { + speed >>= 1; + } else { + speed = MAX(-(5 * 1000), MIN(-1, speed) << 1); // 1ms..5s delay + } + SetStatus("speed %,d", speed); +} + +static void OnUpArrow(void) { + --opstart; } static void OnDownArrow(void) { - if (action & CONTINUE) { - if (speed > 0) { - speed >>= 1; - } else { - speed = MAX(-(5 * 1000), MIN(-10, speed) << 1); // 10ms..5s delay - } - } else { - ++opstart; - } + ++opstart; } static void OnHome(void) { @@ -1617,10 +2119,6 @@ static void OnTab(void) { focus = (focus + 1) % ARRAYLEN(pan.p); } -static void OnFeed(void) { - write(ttyfd, "\e[K\e[J", 6); -} - static void OnUp(void) { } @@ -1666,116 +2164,172 @@ static void OnRestart(void) { action |= RESTART; } -static void OnSseType(void) { +static void OnXmmType(void) { uint8_t t; - long i, j; - t = CycleSseType(m->xmmtype[0][0]); - for (i = 0; i < 2; ++i) { - for (j = 0; j < 8; ++j) { - m->xmmtype[i][j] = t; + unsigned i; + t = CycleXmmType(xmmtype[0]); + for (i = 0; i < 16; ++i) { + xmmtype[i] = t; + } +} + +static void SetXmmSize(int bytes) { + unsigned i; + for (i = 0; i < 16; ++i) { + xmmsize[i] = bytes; + } +} + +static void SetXmmDisp(int disp) { + xmmdisp = disp; +} + +static void OnXmmSize(void) { + SetXmmSize(CycleXmmSize(xmmsize[0])); +} + +static void OnXmmDisp(void) { + SetXmmDisp(CycleXmmDisp(xmmdisp)); +} + +static bool HasPendingKeyboard(void) { + return HasPendingInput(ttyin); +} + +static void Sleep(int ms) { + poll((struct pollfd[]){{ttyin, POLLIN}}, 1, ms); +} + +static void OnMouse(char *p) { + enum Mouse e; + int i, x, y, dy; + struct Panel *ep; + e = strtol(p, &p, 10); + if (*p == ';') ++p; + x = min(txn, max(1, strtol(p, &p, 10))) - 1; + if (*p == ';') ++p; + y = min(tyn, max(1, strtol(p, &p, 10))) - 1; + e |= (*p == 'm') << 2; + for (ep = 0, i = 0; i < ARRAYLEN(pan.p); ++i) { + if ((pan.p[i].left <= x && x < pan.p[i].right) && + (pan.p[i].top <= y && y < pan.p[i].bottom)) { + ep = &pan.p[i]; + break; + } + } + if (ep) { + dy = 3; + switch (e) { + case kMouseWheelUp: + if (ep == &pan.disassembly) opstart -= dy; + if (ep == &pan.code) codestart -= dy; + if (ep == &pan.readdata) readstart -= dy; + if (ep == &pan.writedata) writestart -= dy; + if (ep == &pan.stack) stackstart -= dy; + if (ep == &pan.maps) mapsstart = MAX(0, mapsstart - 1); + if (ep == &pan.frames) framesstart = MAX(0, framesstart - 1); + if (ep == &pan.breakpoints) { + breakpointsstart = MAX(0, breakpointsstart - 1); + } + break; + case kMouseWheelDown: + if (ep == &pan.disassembly) opstart += dy; + if (ep == &pan.code) codestart += dy; + if (ep == &pan.readdata) readstart += dy; + if (ep == &pan.writedata) writestart += dy; + if (ep == &pan.stack) stackstart += dy; + if (ep == &pan.maps) mapsstart += 1; + if (ep == &pan.frames) framesstart += 1; + if (ep == &pan.breakpoints) breakpointsstart += 1; + break; + default: + break; } } } -static void OnSseWidth(void) { - ssewidth = CycleSseWidth(ssewidth); -} - -static void OnSseHex(void) { - ssehex = !ssehex; -} - -static bool HasPendingKeyboard(void) { - return HasPendingInput(ttyfd); -} - -static bool IsExecuting(void) { - return (action & (CONTINUE | STEP | NEXT | FINISH)) && !(action & FAILURE); -} - static void ReadKeyboard(void) { - int c; - char b[20]; - ssize_t rc; - size_t i, n; - if ((rc = read(ttyfd, b, 16)) != -1) { - for (n = rc, i = 0; i < n; ++i) { - switch (b[i++]) { - CASE('q', OnQ()); - CASE('v', OnV()); - CASE('s', OnStep()); - CASE('n', OnNext()); - CASE('f', OnFinish()); - CASE('x', OnSseHex()); - CASE('c', OnContinue()); - CASE('R', OnRestart()); - CASE('t', OnSseType()); - CASE('w', OnSseWidth()); - CASE('u', OnUp()); - CASE('d', OnDown()); - CASE('B', PopBreakpoint(&breakpoints)); - CASE('\t', OnTab()); - CASE('\r', OnEnter()); - CASE('\n', OnEnter()); - CASE(CTRL('C'), OnCtrlC()); - CASE(CTRL('D'), OnCtrlC()); - CASE(CTRL('\\'), OnQuit()); - CASE(CTRL('Z'), raise(SIGSTOP)); - CASE(CTRL('L'), OnFeed()); - CASE(CTRL('P'), OnUpArrow()); - CASE(CTRL('N'), OnDownArrow()); - CASE(CTRL('V'), OnPageDown()); - case '\e': - switch (b[i++]) { - CASE('v', OnPageUp()); - case '[': - switch (b[i++]) { - CASE('A', OnUpArrow()); - CASE('B', OnDownArrow()); - CASE('F', OnEnd()); - CASE('H', OnHome()); - case '1': - switch (b[i++]) { - CASE('~', OnHome()); - default: - break; - } + char buf[64], *p = buf; + if (readansi(ttyin, buf, sizeof(buf)) == -1) { + if (errno == EINTR) return; + FATALF("readkeyboard failed: %s", strerror(errno)); + } + switch (*p++) { + CASE('q', OnQ()); + CASE('v', OnV()); + CASE('s', OnStep()); + CASE('n', OnNext()); + CASE('f', OnFinish()); + CASE('c', OnContinue()); + CASE('R', OnRestart()); + CASE('x', OnXmmDisp()); + CASE('t', OnXmmType()); + CASE('T', OnXmmSize()); + CASE('u', OnUp()); + CASE('d', OnDown()); + CASE('B', PopBreakpoint(&breakpoints)); + CASE('M', ToggleMouseTracking()); + CASE('\t', OnTab()); + CASE('\r', OnEnter()); + CASE('\n', OnEnter()); + CASE(CTRL('C'), OnSigInt()); + CASE(CTRL('D'), OnSigInt()); + CASE(CTRL('\\'), OnQuit()); + CASE(CTRL('Z'), raise(SIGSTOP)); + CASE(CTRL('L'), OnFeed()); + CASE(CTRL('P'), OnUpArrow()); + CASE(CTRL('N'), OnDownArrow()); + CASE(CTRL('V'), OnPageDown()); + CASE(CTRL('T'), OnTurbo()); + case '\e': + switch (*p++) { + CASE('v', OnPageUp()); + CASE('t', OnSlowmo()); + case '[': + switch (*p++) { + CASE('<', OnMouse(p)); + CASE('A', OnUpArrow()); + CASE('B', OnDownArrow()); + CASE('F', OnEnd()); + CASE('H', OnHome()); + case '1': + switch (*p++) { + CASE('~', OnHome()); + default: break; - case '4': - switch (b[i++]) { - CASE('~', OnEnd()); - default: - break; - } + } + break; + case '4': + switch (*p++) { + CASE('~', OnEnd()); + default: break; - case '5': - switch (b[i++]) { - CASE('~', OnPageUp()); - default: - break; - } + } + break; + case '5': + switch (*p++) { + CASE('~', OnPageUp()); + default: break; - case '6': - switch (b[i++]) { - CASE('~', OnPageDown()); - default: - break; - } + } + break; + case '6': + switch (*p++) { + CASE('~', OnPageDown()); + default: break; - case '7': - switch (b[i++]) { - CASE('~', OnHome()); - default: - break; - } - break; - case '8': - switch (b[i++]) { - CASE('~', OnEnd()); - default: - break; - } + } + break; + case '7': + switch (*p++) { + CASE('~', OnHome()); + default: break; + } + break; + case '8': + switch (*p++) { + CASE('~', OnEnd()); default: break; } @@ -1787,14 +2341,9 @@ static void ReadKeyboard(void) { default: break; } - } - } else if (errno == EINTR) { - LOGF("ReadKeyboard Interrupted"); - } else if (errno == EAGAIN) { - poll((struct pollfd[]){{ttyfd, POLLIN}}, 1, -1); - } else { - perror("ReadKeyboard"); - exit(1); + break; + default: + break; } } @@ -1812,7 +2361,8 @@ static int64_t ParseHexValue(const char *s) { } static void HandleBreakpointFlag(const char *s) { - struct Breakpoint b = {0}; + struct Breakpoint b; + memset(&b, 0, sizeof(b)); if (isdigit(*s)) { b.addr = ParseHexValue(s); } else { @@ -1826,11 +2376,6 @@ static noreturn void PrintUsage(int rc, FILE *f) { exit(rc); } -static void LeaveScreen(void) { - char buf[64]; - write(ttyfd, buf, sprintf(buf, "\e[%d;%dH\e[S\r\n", tyn - 1, txn - 1)); -} - static void Exec(void) { long op; ssize_t bp; @@ -1895,6 +2440,7 @@ static void Tui(void) { for (;;) { if (!(action & FAILURE)) { LoadInstruction(m); + UpdateXmmType(); } else { m->xedd = (struct XedDecodedInst *)m->icache[0]; m->xedd->length = 1; @@ -1902,13 +2448,21 @@ static void Tui(void) { m->xedd->op.opcode = 0xCC; } if (action & WINCHED) { - LOGF("WINCHED"); - GetTtySize(); + GetTtySize(ttyout); action &= ~WINCHED; } interactive = ++tick > speed; if (interactive && speed < 0) { - dsleep(.001L * -speed); + Sleep(-speed); + } + if (action & ALARM) { + HandleAlarm(); + } + if (action & FAILURE) { + ScrollCode(&pan.code); + ScrollStack(&pan.stack); + ScrollReadData(&pan.readdata); + ScrollWriteData(&pan.writedata); } if (!(action & CONTINUE) || interactive) { tick = 0; @@ -1918,10 +2472,10 @@ static void Tui(void) { } if (action & FAILURE) { LOGF("TUI FAILURE"); - PrintMessageBox(ttyfd, systemfailure, tyn, txn); + PrintMessageBox(ttyout, systemfailure, tyn, txn); ReadKeyboard(); - } else if (!IsExecuting() || ((interactive || !(action & CONTINUE)) && - !(action & INT) && HasPendingKeyboard())) { + } else if (!IsExecuting() || (!(action & CONTINUE) && !(action & INT) && + HasPendingKeyboard())) { ReadKeyboard(); } if (action & INT) { @@ -1954,7 +2508,7 @@ static void Tui(void) { op = GetDisIndex(); ScrollOp(&pan.disassembly, op); VERBOSEF("%s", DisGetLine(dis, m, op)); - memcpy(&m[1], &m[0], sizeof(m[0])); + CopyMachineState(&laststate); if (!(action & CONTINUE)) { action &= ~STEP; if (action & NEXT) { @@ -1975,6 +2529,12 @@ static void Tui(void) { } if (!IsDebugBreak()) { ExecuteInstruction(m); + if (!(action & CONTINUE) || interactive) { + ScrollCode(&pan.code); + ScrollStack(&pan.stack); + ScrollReadData(&pan.readdata); + ScrollWriteData(&pan.writedata); + } if (IsLongBranch()) { Disassemble(); } @@ -1995,7 +2555,7 @@ static void Tui(void) { } } if ((action & (FINISH | NEXT | CONTINUE)) && - (bp = IsAtBreakpoint(&breakpoints, m->ip)) != -1) { + (bp = IsAtBreakpoint(&breakpoints, GetIp())) != -1) { action &= ~(FINISH | NEXT | CONTINUE); LOGF("BREAK %p", breakpoints.p[bp].addr); break; @@ -2056,19 +2616,43 @@ int Emulator(int argc, char *argv[]) { int rc, fd; codepath = argv[optind++]; pty = MachinePtyNew(); - InitMachine(m); - m->cr3 = MallocPage(); m->fds.p = xcalloc((m->fds.n = 8), sizeof(struct MachineFd)); Restart: action = 0; LoadProgram(m, codepath, argv + optind, environ, elf); m->fds.i = 3; m->fds.p[0].fd = STDIN_FILENO; - m->fds.p[0].cb = &kMachineFdCbPty; + m->fds.p[0].cb = &kMachineFdCbHost; m->fds.p[1].fd = STDOUT_FILENO; - m->fds.p[1].cb = &kMachineFdCbPty; + m->fds.p[1].cb = &kMachineFdCbHost; m->fds.p[2].fd = STDERR_FILENO; - m->fds.p[2].cb = &kMachineFdCbPty; + m->fds.p[2].cb = &kMachineFdCbHost; + if (tuimode) { + ttyin = isatty(0) ? 0 : open("/dev/tty", O_RDWR | O_NOCTTY); + ttyout = isatty(1) ? 1 : open("/dev/tty", O_RDWR | O_NOCTTY); + } else { + ttyin = -1; + ttyout = -1; + } + if (ttyout != -1) { + atexit(TtyRestore1); + xsigaction(SIGWINCH, OnSigWinch, 0, 0, 0); + tyn = 24; + txn = 80; + GetTtySize(ttyout); + if (isatty(0)) { + m->fds.p[0].fd = 0; + m->fds.p[0].cb = &kMachineFdCbPty; + } + if (isatty(1)) { + m->fds.p[1].fd = 1; + m->fds.p[1].cb = &kMachineFdCbPty; + } + if (isatty(2)) { + m->fds.p[2].fd = 2; + m->fds.p[2].cb = &kMachineFdCbPty; + } + } while (!(action & EXIT)) { if (!tuimode) { Exec(); @@ -2093,15 +2677,15 @@ Restart: } static void OnlyRunOnFirstCpu(void) { - uint64_t bs; - bs = 1; + uint64_t bs = 1; sched_setaffinity(0, sizeof(bs), &bs); } int main(int argc, char *argv[]) { - int rc; - m->mode = XED_MACHINE_MODE_LONG_64; - ssewidth = 2; /* 16-bit is best bit */ + m = NewMachine(); + speed = 16; + SetXmmSize(2); + SetXmmDisp(kXmmHex); if (!NoDebug()) showcrashreports(); /* OnlyRunOnFirstCpu(); */ if ((colorize = cancolor())) { @@ -2114,6 +2698,5 @@ int main(int argc, char *argv[]) { } GetOpts(argc, argv); if (optind == argc) PrintUsage(EX_USAGE, stderr); - rc = Emulator(argc, argv); - return rc; + return Emulator(argc, argv); } diff --git a/tool/build/emubin/emubin.mk b/tool/build/emubin/emubin.mk index 918aeb7a..38f5ddc8 100644 --- a/tool/build/emubin/emubin.mk +++ b/tool/build/emubin/emubin.mk @@ -11,7 +11,8 @@ TOOL_BUILD_EMUBIN_BINS = \ o/$(MODE)/tool/build/emubin/prime.bin \ o/$(MODE)/tool/build/emubin/prime.bin.dbg \ o/$(MODE)/tool/build/emubin/pi.bin \ - o/$(MODE)/tool/build/emubin/pi.bin.dbg + o/$(MODE)/tool/build/emubin/pi.bin.dbg \ + o/$(MODE)/tool/build/emubin/linmap.elf TOOL_BUILD_EMUBIN_A = o/$(MODE)/tool/build/emubin/emubin.a TOOL_BUILD_EMUBIN_FILES := $(wildcard tool/build/emubin/*) @@ -50,6 +51,13 @@ o/$(MODE)/tool/build/emubin/%.bin.dbg: \ $(TOOL_BUILD_EMUBIN_A).pkg @$(ELFLINK) -e emucrt -z max-page-size=0x10 +o/$(MODE)/tool/build/emubin/%.elf: \ + $(TOOL_BUILD_EMUBIN_DEPS) \ + $(TOOL_BUILD_EMUBIN_A) \ + o/$(MODE)/tool/build/emubin/%.o \ + $(ELF) + @$(ELFLINK) + o/dbg/tool/build/emubin/lisp.real.com.dbg: \ $(TOOL_BUILD_EMUBIN_DEPS) \ $(TOOL_BUILD_EMUBIN_A) \ diff --git a/tool/build/emubin/linmap.c b/tool/build/emubin/linmap.c new file mode 100644 index 00000000..654174cd --- /dev/null +++ b/tool/build/emubin/linmap.c @@ -0,0 +1,32 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/linux/exit.h" +#include "libc/linux/fstat.h" +#include "libc/linux/mmap.h" +#include "libc/linux/open.h" + +struct stat st; + +void _start(void) { + long fd = LinuxOpen("/etc/passwd", 0, 0); + LinuxFstat(fd, &st); + LinuxMmap((void *)0x000000000000, st.st_size, 1, 2, fd, 0); + LinuxExit(0); +} diff --git a/tool/build/emubin/lisp.c b/tool/build/emubin/lisp.c index 4814f369..b390b88b 100644 --- a/tool/build/emubin/lisp.c +++ b/tool/build/emubin/lisp.c @@ -16,237 +16,20 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "tool/build/emubin/lisp.h" -#define TRACE 0 -#define ERRORS 1 -#define LONG long -#define WORD short -#define WORDS 8192 +#define TRACE 0 // print eval input output +#define RETRO 1 // auto capitalize input +#define ERRORS 1 // print messages or undefined behavior +#define DELETE 1 // allow backspace to rub out symbol +#define QUOTES 1 // allow 'X shorthand (QUOTE X) +#define MUTABLE 0 // allow setting globals +#define PROMPT 1 // show repl prompt +#define WORD short +#define WORDS 8192 /*───────────────────────────────────────────────────────────────────────────│─╗ -│ The LISP Challenge § Impure x86_64 Linux 8086 PC BIOS System Integration ─╬─│┼ -╚────────────────────────────────────────────────────────────────────────────│*/ - -#define TYPE(x) /* a.k.a. x&1 */ \ - ({ \ - char IsAtom; \ - asm("test%z1\t$1,%1" : "=@ccnz"(IsAtom) : "Qm"((char)x)); \ - IsAtom; \ - }) - -#define OBJECT(t, v) /* a.k.a. v<<1|t */ \ - ({ \ - __typeof(v) Val = (v); \ - asm("shl\t%0" : "+r"(Val)); \ - Val | (t); \ - }) - -#define SUB(x, y) /* a.k.a. x-y */ \ - ({ \ - __typeof(x) Reg = (x); \ - asm("sub\t%1,%0" : "+rm"(Reg) : "g"(y)); \ - Reg; \ - }) - -#define STOS(di, c) asm("stos%z1" : "+D"(di), "=m"(*(di)) : "a"(c)) -#define LODS(si) \ - ({ \ - typeof(*(si)) c; \ - asm("lods%z2" : "+S"(si), "=a"(c) : "m"(*(si))); \ - c; \ - }) - -#define REAL_READ_(REG, BASE, INDEX, DISP) \ - ({ \ - __typeof(*(BASE)) Reg; \ - if (__builtin_constant_p(INDEX) && !(INDEX)) { \ - asm("mov\t%c2(%1),%0" \ - : REG(Reg) \ - : "bDS"(BASE), "i"((DISP) * sizeof(*(BASE))), \ - "m"(BASE[(INDEX) + (DISP)])); \ - } else { \ - asm("mov\t%c3(%1,%2),%0" \ - : REG(Reg) \ - : "b"(BASE), "DS"((long)(INDEX) * sizeof(*(BASE))), \ - "i"((DISP) * sizeof(*(BASE))), "m"(BASE[(INDEX) + (DISP)])); \ - } \ - Reg; \ - }) - -/* #ifdef __REAL_MODE__ */ -#define REAL_READ(BASE, INDEX, DISP) /* a.k.a. b[i] */ \ - (sizeof(*(BASE)) == 1 ? REAL_READ_("=Q", BASE, INDEX, DISP) \ - : REAL_READ_("=r", BASE, INDEX, DISP)) -/* #else */ -/* #define REAL_READ(BASE, INDEX, DISP) BASE[INDEX + DISP] */ -/* #endif */ - -#define REAL_READ_ARRAY_FIELD_(REG, OBJECT, MEMBER, INDEX, DISP) \ - ({ \ - __typeof(*(OBJECT->MEMBER)) Reg; \ - if (!(OBJECT)) { \ - asm("mov\t%c2(%1),%0" \ - : REG(Reg) \ - : "bDS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \ - "i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \ - sizeof(*(OBJECT->MEMBER)) * (DISP)), \ - "m"(OBJECT->MEMBER)); \ - } else { \ - asm("mov\t%c3(%1,%2),%0" \ - : REG(Reg) \ - : "b"(OBJECT), "DS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \ - "i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \ - sizeof(*(OBJECT->MEMBER)) * (DISP)), \ - "m"(OBJECT->MEMBER)); \ - } \ - Reg; \ - }) - -/* #ifdef __REAL_MODE__ */ -#define REAL_READ_ARRAY_FIELD(OBJECT, MEMBER, INDEX, DISP) /* o->m[i] */ \ - (sizeof(*(OBJECT->MEMBER)) == 1 \ - ? REAL_READ_ARRAY_FIELD_("=Q", OBJECT, MEMBER, INDEX, DISP) \ - : REAL_READ_ARRAY_FIELD_("=r", OBJECT, MEMBER, INDEX, DISP)) -/* #else */ -/* #define REAL_READ_ARRAY_FIELD(o, m, i, d) o->m[i + d] */ -/* #endif */ - -#define REAL_WRITE_ARRAY_FIELD_(REG, OBJECT, MEMBER, INDEX, DISP, VALUE) \ - do { \ - if (!(OBJECT)) { \ - asm("mov\t%1,%c3(%2)" \ - : "=m"(OBJECT->MEMBER) \ - : REG((__typeof(*(OBJECT->MEMBER)))(VALUE)), \ - "bDS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \ - "i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \ - sizeof(*(OBJECT->MEMBER)) * (DISP))); \ - } else { \ - asm("mov\t%1,%c4(%2,%3)" \ - : "=m"(OBJECT->MEMBER) \ - : REG((__typeof(*(OBJECT->MEMBER)))(VALUE)), "b"(OBJECT), \ - "DS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \ - "i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \ - sizeof(*(OBJECT->MEMBER)) * (DISP))); \ - } \ - } while (0) - -/* #ifdef __REAL_MODE__ */ -#define REAL_WRITE_ARRAY_FIELD(OBJECT, MEMBER, INDEX, DISP, VALUE) \ - do { \ - __typeof(*(OBJECT->MEMBER)) Reg; \ - switch (sizeof(*(OBJECT->MEMBER))) { \ - case 1: \ - REAL_WRITE_ARRAY_FIELD_("Q", OBJECT, MEMBER, INDEX, DISP, VALUE); \ - break; \ - default: \ - REAL_WRITE_ARRAY_FIELD_("ri", OBJECT, MEMBER, INDEX, DISP, VALUE); \ - break; \ - } \ - } while (0) -/* #else */ -/* #define REAL_WRITE_ARRAY_FIELD(o, m, i, d, v) o->m[i + d] = v */ -/* #endif */ - -long jb[8]; -int setjmp(void *) __attribute__((__returns_twice__)); -int longjmp(void *, int) __attribute__((__noreturn__)); - -static inline void *SetMemory(void *di, int al, unsigned long cx) { - asm("rep stosb" - : "=D"(di), "=c"(cx), "=m"(*(char(*)[cx])di) - : "0"(di), "1"(cx), "a"(al)); - return di; -} - -static inline void *CopyMemory(void *di, void *si, unsigned long cx) { - asm("rep movsb" - : "=D"(di), "=S"(si), "=c"(cx), "=m"(*(char(*)[cx])di) - : "0"(di), "1"(si), "2"(cx)); - return di; -} - -static void RawMode(void) { -#ifndef __REAL_MODE__ - int rc; - int c[14]; - asm volatile("syscall" - : "=a"(rc) - : "0"(0x10), "D"(0), "S"(0x5401), "d"(c) - : "rcx", "r11", "memory"); - c[0] &= ~0b0000010111111000; // INPCK|ISTRIP|PARMRK|INLCR|IGNCR|ICRNL|IXON - c[2] &= ~0b0000000100110000; // CSIZE|PARENB - c[2] |= 0b00000000000110000; // CS8 - c[3] &= ~0b1000000001011010; // ECHONL|ECHO|ECHOE|IEXTEN|ICANON - asm volatile("syscall" - : "=a"(rc) - : "0"(0x10), "D"(0), "S"(0x5402), "d"(c) - : "rcx", "r11", "memory"); -#endif -} - -__attribute__((__noinline__)) static void PrintChar(LONG c) { -#ifdef __REAL_MODE__ - asm volatile("mov\t$0x0E,%%ah\n\t" - "int\t$0x10" - : /* no outputs */ - : "a"(c), "b"(7) - : "memory"); -#else - static short buf; - int rc; - buf = c; - asm volatile("syscall" - : "=a"(rc) - : "0"(1), "D"(1), "S"(&buf), "d"(1) - : "rcx", "r11", "memory"); -#endif -} - -static void PrintString(char *s) { - char c; - for (;;) { - if (!(c = REAL_READ(s, 0, 0))) break; - PrintChar(c); - ++s; - } -} - -static int XlatChar(LONG c) { - if (c == 0x7F) return '\b'; - if (c >= 'a') { - asm volatile("" ::: "memory"); - if (c <= 'z') c -= 'a' - 'A'; - } - return c; -} - -static int EchoChar(LONG c) { - if (c != '\b') { - PrintChar(c); - if (c == '\r') { - PrintChar('\n'); - } - } - return c; -} - -__attribute__((__noinline__)) static noinline int ReadChar(void) { - int c; -#ifdef __REAL_MODE__ - asm volatile("int\t$0x16" : "=a"(c) : "0"(0) : "memory"); -#else - static int buf; - asm volatile("syscall" - : "=a"(c) - : "0"(0), "D"(0), "S"(&buf), "d"(1) - : "rcx", "r11", "memory"); - c = buf; -#endif - return EchoChar(XlatChar(c)); -} - -/*───────────────────────────────────────────────────────────────────────────│─╗ -│ The LISP Challenge § Pure Original LISP Machine ─╬─│┼ +│ The LISP Challenge § LISP Machine ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ #define ATOM 0 @@ -261,10 +44,8 @@ __attribute__((__noinline__)) static noinline int ReadChar(void) { #define ATOM_CAR 50 #define ATOM_CDR 58 #define ATOM_CONS 66 -#define ATOM_LABEL 76 -#define ATOM_LAMBDA 88 -#define ATOM_SET 102 -#define ATOM_DEFUN 110 +#define ATOM_LAMBDA 76 +#define ATOM_SET 90 #define Quote(x) List(ATOM_QUOTE, x) #define List(x, y) Cons(x, Cons(y, NIL)) @@ -279,9 +60,6 @@ __attribute__((__noinline__)) static noinline int ReadChar(void) { #define VALUE(x) ((x) >> 1) #define PTR(i) ((i) << 1 | CONS) -#define ARRAYLEN(A) \ - ((sizeof(A) / sizeof(*(A))) / ((unsigned)!(sizeof(A) % sizeof(*(A))))) - struct Lisp { WORD mem[WORDS]; unsigned char syntax[256]; @@ -289,25 +67,24 @@ struct Lisp { WORD globals; WORD index; char token[128]; + long jb[8]; char str[WORDS]; }; _Static_assert(sizeof(struct Lisp) <= 0x7c00 - 0x600, "LISP Machine too large for real mode"); -_Alignas(char) const char kSymbols[] = "\ -NIL\0T\0QUOTE\0ATOM\0EQ\0COND\0CAR\0CDR\0CONS\0LABEL\0LAMBDA\0SET\0DEFUN\0"; - -_Alignas(WORD) const WORD kGlobals[] = { - [0] = PTR(2), // ((T . T) (NIL . NIL)) - [1] = PTR(4), // - [2] = ATOM_T, // (T . T) - [3] = ATOM_T, // - [4] = PTR(6), // ((NIL . NIL)) - [5] = NIL, // - [6] = NIL, // (NIL . NIL) - [7] = NIL, // -}; +_Alignas(char) const char kSymbols[] = "NIL\0" + "T\0" + "QUOTE\0" + "ATOM\0" + "EQ\0" + "COND\0" + "CAR\0" + "CDR\0" + "CONS\0" + "LAMBDA\0" + "SET\0"; #ifdef __REAL_MODE__ static struct Lisp *const q; @@ -315,56 +92,55 @@ static struct Lisp *const q; static struct Lisp q[1]; #endif -static void Print(LONG); +static void Print(long); static WORD GetList(void); static WORD GetObject(void); -static void PrintObject(LONG); -static WORD Eval(LONG, LONG); +static void PrintObject(long); +static WORD Eval(long, long); static void SetupSyntax(void) { - q->syntax[' '] = ' '; - q->syntax['\t'] = ' '; - q->syntax['\r'] = ' '; - q->syntax['\n'] = ' '; - q->syntax['('] = '('; - q->syntax[')'] = ')'; - q->syntax['.'] = '.'; - q->syntax['\''] = '\''; + unsigned char *syntax = q->syntax; + asm("" : "+bSD"(syntax)); + syntax[' '] = ' '; + syntax['\r'] = ' '; + syntax['\n'] = ' '; + syntax['('] = '('; + syntax[')'] = ')'; + syntax['.'] = '.'; +#if QUOTES + syntax['\''] = '\''; +#endif } -static inline WORD Car(LONG x) { - return REAL_READ_ARRAY_FIELD(q, mem, VALUE(x), 0); +static inline WORD Car(long x) { + return PEEK_ARRAY(q, mem, VALUE(x), 0); } -static inline WORD Cdr(LONG x) { - return REAL_READ_ARRAY_FIELD(q, mem, VALUE(x), 1); +static inline WORD Cdr(long x) { + return PEEK_ARRAY(q, mem, VALUE(x), 1); +} + +static WORD Set(long i, long k, long v) { + POKE_ARRAY(q, mem, VALUE(i), 0, k); + POKE_ARRAY(q, mem, VALUE(i), 1, v); + return i; } static WORD Cons(WORD car, WORD cdr) { -#if TRACE - PrintString("CONS->"); - Print(car); - PrintString(" "); - Print(cdr); -#endif int i, cell; i = q->index; - REAL_WRITE_ARRAY_FIELD(q, mem, i, 0, car); - REAL_WRITE_ARRAY_FIELD(q, mem, i, 1, cdr); + POKE_ARRAY(q, mem, i, 0, car); + POKE_ARRAY(q, mem, i, 1, cdr); q->index = i + 2; cell = OBJECT(CONS, i); -#if TRACE - PrintString("CONS<-"); - Print(cell); -#endif return cell; } static void SetupBuiltins(void) { CopyMemory(q->str, kSymbols, sizeof(kSymbols)); - CopyMemory(q->mem, kGlobals, sizeof(kGlobals)); - q->index = ARRAYLEN(kGlobals); + q->mem[0] = PTR(2); q->globals = PTR(0); + q->index = 4; } static char *StpCpy(char *d, char *s) { @@ -383,7 +159,7 @@ WORD Intern(char *s) { c = LODS(z); while (c) { for (j = 0;; ++j) { - if (c != REAL_READ(s, j, 0)) { + if (c != PEEK(s, j, 0)) { break; } if (!c) { @@ -400,7 +176,33 @@ WORD Intern(char *s) { } forceinline unsigned char XlatSyntax(unsigned char b) { - return REAL_READ_ARRAY_FIELD(q, syntax, b, 0); + return PEEK_ARRAY(q, syntax, b, 0); +} + +static void PrintString(char *s) { + char c; + for (;;) { + if (!(c = PEEK(s, 0, 0))) break; + PrintChar(c); + ++s; + } +} + +static int GetChar(void) { + int c; + c = ReadChar(); +#if RETRO + if (c >= 'a') { + CompilerBarrier(); + if (c <= 'z') c -= 'a' - 'A'; + } +#endif +#if DELETE + if (c == '\b') return c; +#endif + PrintChar(c); + if (c == '\r') PrintChar('\n'); + return c; } static void GetToken(void) { @@ -409,20 +211,20 @@ static void GetToken(void) { b = q->look; t = q->token; while (XlatSyntax(b) == ' ') { - b = ReadChar(); + b = GetChar(); } if (XlatSyntax(b)) { STOS(t, b); - b = ReadChar(); + b = GetChar(); } else { while (b && !XlatSyntax(b)) { - if (b != '\b') { + if (!DELETE || b != '\b') { STOS(t, b); } else if (t > q->token) { PrintString("\b \b"); if (t > q->token) --t; } - b = ReadChar(); + b = GetChar(); } } STOS(t, 0); @@ -447,12 +249,14 @@ static WORD GetList(void) { switch (*q->token & 0xFF) { default: return AddList(GetObject()); - case '\'': - return AddList(GetQuote()); case ')': return NIL; case '.': return ConsumeObject(); +#if QUOTES + case '\'': + return AddList(GetQuote()); +#endif } } @@ -460,15 +264,17 @@ static WORD GetObject(void) { switch (*q->token & 0xFF) { default: return Intern(q->token); - case '\'': - return GetQuote(); case '(': return GetList(); +#if QUOTES + case '\'': + return GetQuote(); +#endif } } static WORD ReadObject(void) { - q->look = ReadChar(); + q->look = GetChar(); GetToken(); return GetObject(); } @@ -477,11 +283,18 @@ static WORD Read(void) { return ReadObject(); } -static void PrintAtom(LONG x) { +static void PrintAtom(long x) { PrintString(q->str + VALUE(x)); } -static void PrintList(LONG x) { +static void PrintList(long x) { +#if QUOTES + if (Car(x) == ATOM_QUOTE) { + PrintChar('\''); + PrintObject(Cadr(x)); + return; + } +#endif PrintChar('('); PrintObject(Car(x)); while ((x = Cdr(x))) { @@ -491,12 +304,13 @@ static void PrintList(LONG x) { } else { PrintString(" . "); PrintObject(x); + break; } } PrintChar(')'); } -static void PrintObject(LONG x) { +static void PrintObject(long x) { if (TYPE(x) == ATOM) { PrintAtom(x); } else { @@ -504,19 +318,13 @@ static void PrintObject(LONG x) { } } -static void Print(LONG i) { +static void Print(long i) { PrintObject(i); PrintString("\r\n"); } __attribute__((__noreturn__)) static void Reset(void) { - longjmp(jb, 1); -} - -__attribute__((__noreturn__)) static void OnUndefined(LONG x) { - PrintString("UNDEF! "); - Print(x); - Reset(); + longjmp(q->jb, 1); } __attribute__((__noreturn__)) static void OnArity(void) { @@ -524,145 +332,69 @@ __attribute__((__noreturn__)) static void OnArity(void) { Reset(); } +__attribute__((__noreturn__)) static void OnUndefined(long x) { + PrintString("UNDEF! "); + Print(x); + Reset(); +} + #if !ERRORS -#define OnUndefined(x) __builtin_unreachable() #define OnArity() __builtin_unreachable() +#define OnUndefined(x) __builtin_unreachable() #endif /*───────────────────────────────────────────────────────────────────────────│─╗ │ The LISP Challenge § Bootstrap John McCarthy's Metacircular Evaluator ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ -static WORD Atom(LONG x) { +static WORD Atom(long x) { return BOOL(TYPE(x) == ATOM); } -static WORD Null(LONG x) { +static WORD Null(long x) { return BOOL(!x); } -static WORD Eq(LONG x, LONG y) { - return BOOL(x == y); /* undefined if !Atom(x)||!Atom(y) */ +static WORD Eq(long x, long y) { + return BOOL(x == y); } -static WORD Assoc(LONG x, LONG y) { - if (Null(y)) OnUndefined(x); - if (Eq(Caar(y), x)) return Cdar(y); - return Assoc(x, Cdr(y)); +static WORD Arg1(long e, long a) { + return Eval(Cadr(e), a); } -static WORD Append(LONG x, LONG y) { -#if TRACE - PrintString("APPEND->"); - Print(x); - PrintString(" "); - Print(y); -#endif - if (!Null(x)) { - x = Cons(Car(x), Append(Cdr(x), y)); - } else { - x = y; - } -#if TRACE - PrintString("APPEND<-"); - Print(x); -#endif - return x; +static WORD Arg2(long e, long a) { + return Eval(Caddr(e), a); } -/** - * Gives list of pairs of corresponding elements of the lists x and y. - * E.g. pair[(A,B,C);(X,(Y,Z),U)] = ((A.X),(B.(Y,Z)),(C.U)) - * @note recoded to make lists in dot notation - * @note it's zip() basically - */ -static WORD Pair_(LONG x, LONG y) { +static WORD Append(long x, long y) { + return Null(x) ? y : Cons(Car(x), Append(Cdr(x), y)); +} + +static WORD Evcon(long c, long a) { + return Eval(Caar(c), a) ? Eval(Cadar(c), a) : Evcon(Cdr(c), a); +} + +static WORD Evlis(long m, long a) { + return m ? Cons(Eval(Car(m), a), Evlis(Cdr(m), a)) : NIL; +} + +static WORD Assoc(long x, long y) { + if (!y) OnUndefined(x); + return Eq(Caar(y), x) ? Cdar(y) : Assoc(x, Cdr(y)); +} + +static WORD Pair(long x, long y) { if (Null(x) && Null(y)) { return NIL; - } else if (TYPE(x) == CONS && TYPE(y) == CONS) { - return Cons(Cons(Car(x), Car(y)), Pair_(Cdr(x), Cdr(y))); + } else if (!Atom(x) && !Atom(y)) { + return Cons(Cons(Car(x), Car(y)), Pair(Cdr(x), Cdr(y))); } else { OnArity(); } } -static WORD Pair(LONG x, LONG y) { -#if TRACE - PrintString("PAIR->"); - Print(x); - PrintString(" "); - Print(y); -#endif - x = Pair_(x, y); -#if TRACE - PrintString("PAIR<-"); - Print(x); -#endif - return x; -} - -static WORD Appq(long m) { - if (m) { - return Cons(List(ATOM_QUOTE, Car(m)), Appq(Cdr(m))); - } else { - return NIL; - } -} - -static WORD Apply(long f, long a) { - return Eval(Cons(f, Appq(a)), NIL); -} - -static WORD Evcon(LONG c, LONG a) { - if (Eval(Caar(c), a)) { - return Eval(Cadar(c), a); - } else { - return Evcon(Cdr(c), a); - } -} - -static WORD Evlis_(LONG m, LONG a) { - if (m) { - return Cons(Eval(Car(m), a), Evlis_(Cdr(m), a)); - } else { - return NIL; - } -} - -static WORD Evlis(LONG m, LONG a) { -#if TRACE - PrintString("EVLIS->"); - Print(m); - PrintString(" "); - Print(a); -#endif - m = Evlis_(m, a); -#if TRACE - PrintString("EVLIS<-"); - Print(m); -#endif - return m; -} - -static WORD Set(LONG e) { - WORD name, value; - name = Car(e); - value = Cadr(e); - q->globals = Cons(Cons(name, value), q->globals); - return value; -} - -static WORD Defun(LONG e) { - WORD name, args, body, lamb; - name = Car(e); - args = Cadr(e); - body = Caddr(e); - lamb = Cons(ATOM_LAMBDA, List(args, body)); - q->globals = Cons(Cons(name, lamb), q->globals); - return name; -} - -static WORD Evaluate(LONG e, LONG a) { +static WORD Evaluate(long e, long a) { if (Atom(e)) { return Assoc(e, a); } else if (Atom(Car(e))) { @@ -670,26 +402,24 @@ static WORD Evaluate(LONG e, LONG a) { case ATOM_QUOTE: return Cadr(e); case ATOM_ATOM: - return Atom(Eval(Cadr(e), a)); + return Atom(Arg1(e, a)); case ATOM_EQ: - return Eq(Eval(Cadr(e), a), Eval(Caddr(e), a)); + return Eq(Arg1(e, a), Arg2(e, a)); case ATOM_COND: return Evcon(Cdr(e), a); case ATOM_CAR: - return Car(Eval(Cadr(e), a)); + return Car(Arg1(e, a)); case ATOM_CDR: - return Cdr(Eval(Cadr(e), a)); + return Cdr(Arg1(e, a)); case ATOM_CONS: - return Cons(Eval(Cadr(e), a), Eval(Caddr(e), a)); - case ATOM_DEFUN: - return Defun(Cdr(e)); + return Cons(Arg1(e, a), Arg2(e, a)); +#if MUTABLE case ATOM_SET: - return Set(Cdr(e)); + return Cdar(Set(a, Cons(Arg1(e, a), Arg2(e, a)), Cons(Car(a), Cdr(a)))); +#endif default: return Eval(Cons(Assoc(Car(e), a), Evlis(Cdr(e), a)), a); } - } else if (Eq(Caar(e), ATOM_LABEL)) { - return Eval(Cons(Caddar(e), Cdr(e)), Cons(Cons(Cadar(e), Car(e)), a)); } else if (Eq(Caar(e), ATOM_LAMBDA)) { return Eval(Caddar(e), Append(Pair(Cadar(e), Evlis(Cdr(e), a)), a)); } else { @@ -697,7 +427,8 @@ static WORD Evaluate(LONG e, LONG a) { } } -static WORD Eval(LONG e, LONG a) { +static WORD Eval(long e, long a) { + WORD r; #if TRACE PrintString("->"); Print(e); @@ -717,9 +448,13 @@ static WORD Eval(LONG e, LONG a) { ╚────────────────────────────────────────────────────────────────────────────│*/ void Repl(void) { - setjmp(jb); +#if ERRORS + setjmp(q->jb); +#endif for (;;) { +#if PROMPT PrintString("* "); +#endif Print(Eval(Read(), q->globals)); } } @@ -728,8 +463,10 @@ int main(int argc, char *argv[]) { /* RawMode(); */ SetupSyntax(); SetupBuiltins(); +#if PROMPT PrintString("THE LISP CHALLENGE V1\r\n" "VISIT GITHUB.COM/JART\r\n"); +#endif Repl(); return 0; } diff --git a/tool/build/emubin/lisp.h b/tool/build/emubin/lisp.h new file mode 100644 index 00000000..de2c13ae --- /dev/null +++ b/tool/build/emubin/lisp.h @@ -0,0 +1,182 @@ +/*───────────────────────────────────────────────────────────────────────────│─╗ +│ The LISP Challenge § Hardware Integration w/ x86_64 Linux & 8086 PC BIOS ─╬─│┼ +╚────────────────────────────────────────────────────────────────────────────│*/ + +#define CompilerBarrier() asm volatile("" ::: "memory"); + +#define TYPE(x) /* a.k.a. x&1 */ \ + ({ \ + char IsAtom; \ + asm("test%z1\t$1,%1" : "=@ccnz"(IsAtom) : "Qm"((char)x)); \ + IsAtom; \ + }) + +#define OBJECT(t, v) /* a.k.a. v<<1|t */ \ + ({ \ + __typeof(v) Val = (v); \ + asm("shl\t%0" : "+r"(Val)); \ + Val | (t); \ + }) + +#define SUB(x, y) /* a.k.a. x-y */ \ + ({ \ + __typeof(x) Reg = (x); \ + asm("sub\t%1,%0" : "+rm"(Reg) : "g"(y)); \ + Reg; \ + }) + +#define STOS(di, c) asm("stos%z1" : "+D"(di), "=m"(*(di)) : "a"(c)) +#define LODS(si) \ + ({ \ + typeof(*(si)) c; \ + asm("lods%z2" : "+S"(si), "=a"(c) : "m"(*(si))); \ + c; \ + }) + +#define PEEK_(REG, BASE, INDEX, DISP) \ + ({ \ + __typeof(*(BASE)) Reg; \ + if (__builtin_constant_p(INDEX) && !(INDEX)) { \ + asm("mov\t%c2(%1),%0" \ + : REG(Reg) \ + : "bDS"(BASE), "i"((DISP) * sizeof(*(BASE))), \ + "m"(BASE[(INDEX) + (DISP)])); \ + } else { \ + asm("mov\t%c3(%1,%2),%0" \ + : REG(Reg) \ + : "b"(BASE), "DS"((long)(INDEX) * sizeof(*(BASE))), \ + "i"((DISP) * sizeof(*(BASE))), "m"(BASE[(INDEX) + (DISP)])); \ + } \ + Reg; \ + }) + +#define PEEK(BASE, INDEX, DISP) /* a.k.a. b[i] */ \ + (sizeof(*(BASE)) == 1 ? PEEK_("=Q", BASE, INDEX, DISP) \ + : PEEK_("=r", BASE, INDEX, DISP)) + +#define PEEK_ARRAY_(REG, OBJECT, MEMBER, INDEX, DISP) \ + ({ \ + __typeof(*(OBJECT->MEMBER)) Reg; \ + if (!(OBJECT)) { \ + asm("mov\t%c2(%1),%0" \ + : REG(Reg) \ + : "bDS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \ + "i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \ + sizeof(*(OBJECT->MEMBER)) * (DISP)), \ + "m"(OBJECT->MEMBER)); \ + } else { \ + asm("mov\t%c3(%1,%2),%0" \ + : REG(Reg) \ + : "b"(OBJECT), "DS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \ + "i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \ + sizeof(*(OBJECT->MEMBER)) * (DISP)), \ + "m"(OBJECT->MEMBER)); \ + } \ + Reg; \ + }) + +#define PEEK_ARRAY(OBJECT, MEMBER, INDEX, DISP) /* o->m[i] */ \ + (sizeof(*(OBJECT->MEMBER)) == 1 \ + ? PEEK_ARRAY_("=Q", OBJECT, MEMBER, INDEX, DISP) \ + : PEEK_ARRAY_("=r", OBJECT, MEMBER, INDEX, DISP)) + +#define POKE_ARRAY_(REG, OBJECT, MEMBER, INDEX, DISP, VALUE) \ + do { \ + if (!(OBJECT)) { \ + asm("mov\t%1,%c3(%2)" \ + : "=m"(OBJECT->MEMBER) \ + : REG((__typeof(*(OBJECT->MEMBER)))(VALUE)), \ + "bDS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \ + "i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \ + sizeof(*(OBJECT->MEMBER)) * (DISP))); \ + } else { \ + asm("mov\t%1,%c4(%2,%3)" \ + : "=m"(OBJECT->MEMBER) \ + : REG((__typeof(*(OBJECT->MEMBER)))(VALUE)), "b"(OBJECT), \ + "DS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \ + "i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \ + sizeof(*(OBJECT->MEMBER)) * (DISP))); \ + } \ + } while (0) + +#define POKE_ARRAY(OBJECT, MEMBER, INDEX, DISP, VALUE) /* o->m[i]=v */ \ + do { \ + __typeof(*(OBJECT->MEMBER)) Reg; \ + switch (sizeof(*(OBJECT->MEMBER))) { \ + case 1: \ + POKE_ARRAY_("Q", OBJECT, MEMBER, INDEX, DISP, VALUE); \ + break; \ + default: \ + POKE_ARRAY_("r", OBJECT, MEMBER, INDEX, DISP, VALUE); \ + break; \ + } \ + } while (0) + +int setjmp(void *) __attribute__((__returns_twice__)); +int longjmp(void *, int) __attribute__((__noreturn__)); + +static inline void *SetMemory(void *di, int al, unsigned long cx) { + asm("rep stosb" + : "=D"(di), "=c"(cx), "=m"(*(char(*)[cx])di) + : "0"(di), "1"(cx), "a"(al)); + return di; +} + +static inline void *CopyMemory(void *di, void *si, unsigned long cx) { + asm("rep movsb" + : "=D"(di), "=S"(si), "=c"(cx), "=m"(*(char(*)[cx])di) + : "0"(di), "1"(si), "2"(cx)); + return di; +} + +static void RawMode(void) { +#ifndef __REAL_MODE__ + int rc; + int c[14]; + asm volatile("syscall" + : "=a"(rc) + : "0"(0x10), "D"(0), "S"(0x5401), "d"(c) + : "rcx", "r11", "memory"); + c[0] &= ~0b0000010111111000; // INPCK|ISTRIP|PARMRK|INLCR|IGNCR|ICRNL|IXON + c[2] &= ~0b0000000100110000; // CSIZE|PARENB + c[2] |= 0b00000000000110000; // CS8 + c[3] &= ~0b1000000001011010; // ECHONL|ECHO|ECHOE|IEXTEN|ICANON + asm volatile("syscall" + : "=a"(rc) + : "0"(0x10), "D"(0), "S"(0x5402), "d"(c) + : "rcx", "r11", "memory"); +#endif +} + +__attribute__((__noinline__)) static void PrintChar(long c) { +#ifdef __REAL_MODE__ + asm volatile("mov\t$0x0E,%%ah\n\t" + "int\t$0x10" + : /* no outputs */ + : "a"(c), "b"(7) + : "memory"); +#else + static short buf; + int rc; + buf = c; + asm volatile("syscall" + : "=a"(rc) + : "0"(1), "D"(1), "S"(&buf), "d"(1) + : "rcx", "r11", "memory"); +#endif +} + +static int ReadChar(void) { + int c; +#ifdef __REAL_MODE__ + asm volatile("int\t$0x16" : "=a"(c) : "0"(0) : "memory"); +#else + static int buf; + asm volatile("syscall" + : "=a"(c) + : "0"(0), "D"(0), "S"(&buf), "d"(1) + : "rcx", "r11", "memory"); + c = buf; +#endif + return c; +} diff --git a/tool/build/emubin/lisp.lds b/tool/build/emubin/lisp.lds index 55ab5881..25dc53bd 100644 --- a/tool/build/emubin/lisp.lds +++ b/tool/build/emubin/lisp.lds @@ -23,12 +23,13 @@ ENTRY(_start) SECTIONS { .text 0x7c00 - 0x600 : { - *(.start) + *(.start .start.*) rodata = .; *(.rodata .rodata.*) . = 0x1fe; SHORT(0xaa55); *(.text .text.*) + BYTE(0x90); _etext = .; . = ALIGN(512); } diff --git a/tool/build/emubin/lisp.lisp b/tool/build/emubin/lisp.lisp index dea2498c..b8bf7312 100644 --- a/tool/build/emubin/lisp.lisp +++ b/tool/build/emubin/lisp.lisp @@ -1,12 +1,30 @@ -(DEFUN FF (X) - (COND ((ATOM X) X) - ((QUOTE T) (FF (CAR X))))) -(FF '(A B C)) +;; (SET 'APPLY '(LAMBDA (E ARGS) +;; ((LAMBDA (APPQ) +;; (CONS )) +;; '(LAMBDA (M) +;; (COND ((EQ M 'NIL) 'NIL) +;; ('T (CONS (QUOTE (CAR M)) +;; (APPQ (CONS 'QUOTE (CDR M)))))))))) +;; (SET 'LIST '(LAMBDA (X Y) (CONS X (CONS Y 'NIL)))) +;; (SET 'AND '(LAMBDA (P Q) (COND ((EQ P 'T) Q) ('T 'F)))) +;; (SET 'OR '(LAMBDA (P Q) (COND ((EQ P 'T) 'T) ('T Q)))) +;; (SET 'NOT '(LAMBDA (P) (COND ((EQ P 'F) 'T) ('T 'T)))) +;; (SET 'IMPLIES '(LAMBDA (P Q) (COND ((EQ P 'T) Q) ('T 'T)))) -((LABEL FF - (LAMBDA (X) - (COND ((ATOM X) - X) - ((QUOTE T) - (FF (CAR X)))))) - (QUOTE ((A B) C))) +((LAMBDA (CALL MKQUOT NULL AND APPEND KEYS VALS E A) + (CALL (CONS (CONS (QUOTE LAMBDA) (CONS (KEYS (QUOTE A)) (CONS E NIL))) (VALS (QUOTE A))))) + (QUOTE (LAMBDA (X) (X))) + (QUOTE (LAMBDA (X) (CONS (QUOTE QUOTE) (CONS X NIL)))) + (QUOTE (LAMBDA (P Q) (COND ((EQ P (QUOTE T)) Q) ((QUOTE T) (QUOTE F))))) + (QUOTE (LAMBDA (X) (AND (QUOTE (ATOM X)) (QUOTE (EQ X NIL))))) + (QUOTE (LAMBDA (X Y) (COND ((EQ X NIL) Y) ((QUOTE T) (CONS (CAR X) (APPEND (QUOTE (CDR X)) (QUOTE Y))))))) + (QUOTE (LAMBDA (A) (COND ((EQ A NIL) NIL) ((QUOTE T) (CONS (CAR (CAR A)) (KEYS (QUOTE (CDR A)))))))) + (QUOTE (LAMBDA (A) (COND ((EQ A NIL) NIL) ((QUOTE T) (CONS (MKQUOT (QUOTE (CDR (CAR A)))) (VALS (QUOTE (CDR A)))))))) + (QUOTE (AND (QUOTE A) (QUOTE C))) + (CONS (CONS (QUOTE A) (QUOTE B)) (CONS (CONS (QUOTE C) (QUOTE D)) NIL))) + +((LAMBDA (FF X) (FF 'X)) + '(LAMBDA (X) + (COND ((ATOM X) X) + ((QUOTE T) (FF '(CAR X))))) + '((A) B C)) diff --git a/tool/build/emubin/lispstart.S b/tool/build/emubin/lispstart.S index 34e33fbf..f34d670f 100644 --- a/tool/build/emubin/lispstart.S +++ b/tool/build/emubin/lispstart.S @@ -17,11 +17,15 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ +.code16 +.section .start,"ax",@progbits - .code16 - .section .start,"ax",@progbits _start: jmp 1f 1: ljmp $0x600>>4,$_begin + .type _start,@function + .size _start,.-_start + .globl _start + _begin: push %cs pop %ds push %cs @@ -42,26 +46,24 @@ _begin: push %cs mov $v_sectors+0x0200,%ax int $0x13 xor %bp,%bp - sub $6,%sp - call main - nop - .type _start,@function - .size _start,.-_start - .globl _start - .globl v_sectors - .globl main + jmp main + .type _begin,@function + .size _begin,.-_begin + .section .start.setjmp,"ax",@progbits setjmp: mov %sp,%ax - stosw # sp + stosw xchg %ax,%si - movsw %ss:(%si),(%di) # ip + movsw %ss:(%si),(%di) mov %bp,%ax - stosw # bp - ret + stosw + ret $6 .type setjmp,@function .size setjmp,.-setjmp .globl setjmp + .previous + .section .start.longjmp,"ax",@progbits longjmp: mov (%di),%sp mov 2(%di),%dx @@ -72,23 +74,4 @@ longjmp: .type longjmp,@function .size longjmp,.-longjmp .globl longjmp - - .globl q.syntax - .type q.syntax,@function - .globl q.look - .type q.look,@function - .globl q.globals - .type q.globals,@function - .globl q.index - .type q.index,@function - .globl q.token - .type q.token,@function - .globl q.str - .type q.str,@function - - .globl boot - .type boot,@function - .globl bss - .type bss,@function - .globl rodata - .type rodata,@function + .previous diff --git a/tool/build/emubin/spiral.real.c b/tool/build/emubin/spiral.c similarity index 100% rename from tool/build/emubin/spiral.real.c rename to tool/build/emubin/spiral.c diff --git a/tool/build/lib/argv.h b/tool/build/lib/argv.h new file mode 100644 index 00000000..11328fcc --- /dev/null +++ b/tool/build/lib/argv.h @@ -0,0 +1,11 @@ +#ifndef COSMOPOLITAN_TOOL_BUILD_LIB_ARGV_H_ +#define COSMOPOLITAN_TOOL_BUILD_LIB_ARGV_H_ +#include "tool/build/lib/machine.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +void LoadArgv(struct Machine *, const char *, char **, char **); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_TOOL_BUILD_LIB_ARGV_H_ */ diff --git a/tool/build/lib/breakpoint.c b/tool/build/lib/breakpoint.c index 434d0869..2c8b43c5 100644 --- a/tool/build/lib/breakpoint.c +++ b/tool/build/lib/breakpoint.c @@ -19,6 +19,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/alg/arraylist2.h" #include "libc/assert.h" +#include "libc/log/log.h" #include "tool/build/lib/breakpoint.h" void PopBreakpoint(struct Breakpoints *bps) { diff --git a/tool/build/lib/buffer.c b/tool/build/lib/buffer.c index 4a894e8f..634a01cc 100644 --- a/tool/build/lib/buffer.c +++ b/tool/build/lib/buffer.c @@ -22,6 +22,8 @@ #include "libc/errno.h" #include "libc/fmt/fmt.h" #include "libc/mem/mem.h" +#include "libc/str/str.h" +#include "libc/str/tpenc.h" #include "libc/str/tpencode.h" #include "tool/build/lib/buffer.h" @@ -38,8 +40,13 @@ void AppendStr(struct Buffer *b, const char *s) { } void AppendWide(struct Buffer *b, wint_t wc) { - char cbuf[8]; - AppendData(b, cbuf, tpencode(cbuf, 8, wc, false)); + uint64_t wb; + wb = wc; + if (!isascii(wb)) wb = tpenc(wb); + do { + AppendChar(b, wb & 0xFF); + wb >>= 8; + } while (wb); } void AppendFmt(struct Buffer *b, const char *fmt, ...) { diff --git a/tool/build/lib/dis.c b/tool/build/lib/dis.c index 02a53213..bff902cc 100644 --- a/tool/build/lib/dis.c +++ b/tool/build/lib/dis.c @@ -178,9 +178,9 @@ static long DisAppendOpLines(struct Dis *d, struct Machine *m, int64_t addr) { void *r; int64_t ip; unsigned k; - uint8_t b[15]; struct DisOp op; long i, n, symbol; + uint8_t *p, b[15]; n = 15; ip = addr - Read64(m->cs); if ((symbol = DisFindSym(d, ip)) != -1) { @@ -202,8 +202,9 @@ static long DisAppendOpLines(struct Dis *d, struct Machine *m, int64_t addr) { if (!(r = FindReal(m, addr))) return -1; k = 0x1000 - (addr & 0xfff); if (n <= k) { - memcpy(b, r, n); + p = r; } else { + p = b; memcpy(b, r, k); if ((r = FindReal(m, addr + k))) { memcpy(b + k, r, n - k); @@ -212,7 +213,7 @@ static long DisAppendOpLines(struct Dis *d, struct Machine *m, int64_t addr) { } } xed_decoded_inst_zero_set_mode(d->xedd, m->mode); - xed_instruction_length_decode(d->xedd, b, n); + xed_instruction_length_decode(d->xedd, p, n); n = d->xedd->op.error ? 1 : d->xedd->length; op.addr = addr; op.size = n; @@ -240,7 +241,6 @@ long Dis(struct Dis *d, struct Machine *m, uint64_t addr, uint64_t ip, } const char *DisGetLine(struct Dis *d, struct Machine *m, size_t i) { - char *p; void *r[2]; uint8_t b[15]; if (i >= d->ops.i) return ""; @@ -250,10 +250,9 @@ const char *DisGetLine(struct Dis *d, struct Machine *m, size_t i) { xed_instruction_length_decode( d->xedd, AccessRam(m, d->ops.p[i].addr, d->ops.p[i].size, r, b, true), d->ops.p[i].size); - d->addr = d->ops.p[i].addr; d->m = m; - p = DisLineCode(d, d->buf); - CHECK_LT(p - d->buf, sizeof(d->buf)); + d->addr = d->ops.p[i].addr; + CHECK_LT(DisLineCode(d, d->buf) - d->buf, sizeof(d->buf)); return d->buf; } diff --git a/tool/build/lib/elfwriter.c b/tool/build/lib/elfwriter.c index 817e79e4..611747c1 100644 --- a/tool/build/lib/elfwriter.c +++ b/tool/build/lib/elfwriter.c @@ -17,10 +17,8 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/alg/arraylist.h" #include "libc/alg/arraylist2.h" #include "libc/assert.h" -#include "libc/bits/safemacros.h" #include "libc/calls/calls.h" #include "libc/log/check.h" #include "libc/macros.h" @@ -44,7 +42,8 @@ static const Elf64_Ehdr kObjHeader = { .e_machine = EM_NEXGEN32E, .e_version = 1, .e_ehsize = sizeof(Elf64_Ehdr), - .e_shentsize = sizeof(Elf64_Shdr)}; + .e_shentsize = sizeof(Elf64_Shdr), +}; static size_t AppendSection(struct ElfWriter *elf, const char *name, int sh_type, int sh_flags) { @@ -193,7 +192,7 @@ void elfwriter_close(struct ElfWriter *elf) { void elfwriter_align(struct ElfWriter *elf, size_t addralign, size_t entsize) { elf->entsize = entsize; elf->addralign = addralign; - elf->wrote = roundup(elf->wrote, addralign); + elf->wrote = ROUNDUP(elf->wrote, addralign); } size_t elfwriter_startsection(struct ElfWriter *elf, const char *name, @@ -214,7 +213,7 @@ void *elfwriter_reserve(struct ElfWriter *elf, size_t size) { do { greed = greed + (greed >> 1); } while (need > greed); - greed = roundup(greed, FRAMESIZE); + greed = ROUNDUP(greed, FRAMESIZE); CHECK_NE(-1, ftruncate(elf->fd, greed)); CHECK_NE(MAP_FAILED, mmap((char *)elf->map + elf->mapsize, greed - elf->mapsize, PROT_READ | PROT_WRITE, diff --git a/tool/build/lib/elfwriter_yoink.c b/tool/build/lib/elfwriter_yoink.c index 9f1ee7f4..f68adc13 100644 --- a/tool/build/lib/elfwriter_yoink.c +++ b/tool/build/lib/elfwriter_yoink.c @@ -23,11 +23,11 @@ void elfwriter_yoink(struct ElfWriter *elf, const char *symbol) { unsigned char *p; struct ElfWriterSymRef sym; - const unsigned char nopl[8] = "\x0f\x1f\x04\x25\x00\x00\x00\x00"; + const unsigned char kNopl[8] = "\017\037\004\045\000\000\000\000"; p = elfwriter_reserve(elf, 8); - memcpy(p, nopl, sizeof(nopl)); + memcpy(p, kNopl, sizeof(kNopl)); sym = elfwriter_linksym(elf, symbol, ELF64_ST_INFO(STB_GLOBAL, STT_OBJECT), STV_HIDDEN); - elfwriter_appendrela(elf, sizeof(nopl) - 4, sym, R_X86_64_32, 0); - elfwriter_commit(elf, sizeof(nopl)); + elfwriter_appendrela(elf, sizeof(kNopl) - 4, sym, R_X86_64_32, 0); + elfwriter_commit(elf, sizeof(kNopl)); } diff --git a/tool/build/lib/endian.h b/tool/build/lib/endian.h index af12655c..da716f70 100644 --- a/tool/build/lib/endian.h +++ b/tool/build/lib/endian.h @@ -1,5 +1,6 @@ #ifndef COSMOPOLITAN_TOOL_BUILD_LIB_ENDIAN_H_ #define COSMOPOLITAN_TOOL_BUILD_LIB_ENDIAN_H_ +#include "libc/dce.h" #include "libc/str/str.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) #if __BYTE_ORDER__ + 0 == 1234 diff --git a/tool/build/lib/fds.h b/tool/build/lib/fds.h index 860b3b8b..27d016e9 100644 --- a/tool/build/lib/fds.h +++ b/tool/build/lib/fds.h @@ -1,5 +1,6 @@ #ifndef COSMOPOLITAN_TOOL_BUILD_LIB_FDS_H_ #define COSMOPOLITAN_TOOL_BUILD_LIB_FDS_H_ +#include "libc/calls/struct/iovec.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ @@ -9,8 +10,8 @@ struct MachineFds { int fd; struct MachineFdCb { int (*close)(int); - ssize_t (*read)(int, void *, size_t); - ssize_t (*write)(int, const void *, size_t); + ssize_t (*readv)(int, const struct iovec *, int); + ssize_t (*writev)(int, const struct iovec *, int); int (*ioctl)(int, uint64_t, void *); } * cb; } * p; diff --git a/tool/build/lib/instruction.c b/tool/build/lib/instruction.c index d1076cd4..a3d283fa 100644 --- a/tool/build/lib/instruction.c +++ b/tool/build/lib/instruction.c @@ -75,12 +75,12 @@ void LoadInstruction(struct Machine *m) { key = ip & (ARRAYLEN(m->icache) - 1); m->xedd = (struct XedDecodedInst *)m->icache[key]; if ((ip & 0xfff) < 0x1000 - 15) { - if (ip - (ip & 0xfff) == m->codevirt && ip) { - addr = m->codereal + (ip & 0xfff); + if (ip - (ip & 0xfff) == m->codevirt && m->codehost) { + addr = m->codehost + (ip & 0xfff); } else { m->codevirt = ip - (ip & 0xfff); - m->codereal = ResolveAddress(m, m->codevirt); - addr = m->codereal + (ip & 0xfff); + m->codehost = ResolveAddress(m, m->codevirt); + addr = m->codehost + (ip & 0xfff); } if (!IsOpcodeEqual(m->xedd, addr)) { DecodeInstruction(m, addr, 15); diff --git a/tool/build/lib/ioports.c b/tool/build/lib/ioports.c index 21591368..02861ac6 100644 --- a/tool/build/lib/ioports.c +++ b/tool/build/lib/ioports.c @@ -17,6 +17,7 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/struct/iovec.h" #include "libc/sysv/consts/fileno.h" #include "tool/build/lib/ioports.h" @@ -26,7 +27,7 @@ static int OpE9Read(struct Machine *m) { fd = STDIN_FILENO; if (fd >= m->fds.i) return -1; if (!m->fds.p[fd].cb) return -1; - if (m->fds.p[fd].cb->read(m->fds.p[fd].fd, &b, 1) == 1) { + if (m->fds.p[fd].cb->readv(m->fds.p[fd].fd, &(struct iovec){&b, 1}, 1) == 1) { return b; } else { return -1; @@ -38,7 +39,7 @@ static void OpE9Write(struct Machine *m, uint8_t b) { fd = STDOUT_FILENO; if (fd >= m->fds.i) return; if (!m->fds.p[fd].cb) return; - m->fds.p[fd].cb->write(m->fds.p[fd].fd, &b, 1); + m->fds.p[fd].cb->writev(m->fds.p[fd].fd, &(struct iovec){&b, 1}, 1); } uint64_t OpIn(struct Machine *m, uint16_t p) { diff --git a/tool/build/lib/iovs.c b/tool/build/lib/iovs.c new file mode 100644 index 00000000..50ec67a9 --- /dev/null +++ b/tool/build/lib/iovs.c @@ -0,0 +1,53 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" +#include "tool/build/lib/iovs.h" + +/** + * Appends memory region to i/o vector builder. + */ +int AppendIovs(struct Iovs *ib, void *base, size_t len) { + unsigned i, n; + struct iovec *p; + if (len) { + p = ib->p; + i = ib->i; + n = ib->n; + if (i && (intptr_t)base == (intptr_t)p[i - 1].iov_base + p[i - 1].iov_len) { + p[i - 1].iov_len += len; + } else { + if (unlikely(i == n)) { + n += n >> 1; + if (p == ib->init) { + if (!(p = malloc(sizeof(struct iovec) * n))) return -1; + memcpy(p, ib->init, sizeof(ib->init)); + } else { + if (!(p = realloc(p, sizeof(struct iovec) * n))) return -1; + } + ib->p = p; + ib->n = n; + } + p[i].iov_base = base; + p[i].iov_len = len; + ++ib->i; + } + } + return 0; +} diff --git a/tool/build/lib/iovs.h b/tool/build/lib/iovs.h new file mode 100644 index 00000000..09089a84 --- /dev/null +++ b/tool/build/lib/iovs.h @@ -0,0 +1,31 @@ +#ifndef COSMOPOLITAN_TOOL_BUILD_LIB_IOVS_H_ +#define COSMOPOLITAN_TOOL_BUILD_LIB_IOVS_H_ +#include "libc/calls/struct/iovec.h" +#include "libc/macros.h" +#include "libc/mem/mem.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +struct Iovs { + struct iovec *p; + unsigned i, n; + struct iovec init[2]; +}; + +int AppendIovs(struct Iovs *, void *, size_t); + +forceinline void InitIovs(struct Iovs *ib) { + ib->p = ib->init; + ib->i = 0; + ib->n = ARRAYLEN(ib->init); +} + +forceinline void FreeIovs(struct Iovs *ib) { + if (ib->p != ib->init) { + free(ib->p); + } +} + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_TOOL_BUILD_LIB_IOVS_H_ */ diff --git a/tool/build/lib/loader.c b/tool/build/lib/loader.c index 035ded92..48c60785 100644 --- a/tool/build/lib/loader.c +++ b/tool/build/lib/loader.c @@ -23,20 +23,21 @@ #include "libc/elf/elf.h" #include "libc/elf/struct/phdr.h" #include "libc/log/check.h" +#include "libc/log/log.h" #include "libc/macros.h" +#include "libc/nexgen32e/vendor.h" #include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" #include "libc/sysv/consts/fileno.h" #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/prot.h" +#include "tool/build/lib/argv.h" #include "tool/build/lib/endian.h" #include "tool/build/lib/loader.h" #include "tool/build/lib/machine.h" #include "tool/build/lib/memory.h" -#define DSOLOL "ERROR: ELF not ET_EXEC try `gcc -static -o foo foo.c`\n" - static void LoadElfLoadSegment(struct Machine *m, void *code, size_t codesize, Elf64_Phdr *phdr) { void *rbss; @@ -45,13 +46,20 @@ static void LoadElfLoadSegment(struct Machine *m, void *code, size_t codesize, align = MAX(phdr->p_align, PAGESIZE); CHECK_EQ(1, popcnt(align)); CHECK_EQ(0, (phdr->p_vaddr - phdr->p_offset) % align); + /*-Type-Offset---VirtAddr-----------PhysAddr-----------FileSiz--MemSiz---Flg-Align----*/ + /*-LOAD-0x000000-0x0000000000400000-0x0000000000400000-0x0008e4-0x0008e4-R-E-0x200000-*/ + /*-LOAD-0x000fe0-0x0000000000600fe0-0x0000000000600fe0-0x000030-0x000310-RW--0x200000-*/ felf = (int64_t)(intptr_t)code; vstart = ROUNDDOWN(phdr->p_vaddr, align); vbss = ROUNDUP(phdr->p_vaddr + phdr->p_filesz, align); vend = ROUNDUP(phdr->p_vaddr + phdr->p_memsz, align); fstart = felf + ROUNDDOWN(phdr->p_offset, align); - fend = felf + ROUNDUP(phdr->p_offset + phdr->p_filesz, align); + fend = felf + phdr->p_offset + phdr->p_filesz; bsssize = vend - vbss; + LOGF("LOADELFLOADSEGMENT" + " VSTART %#lx VBSS %#lx VEND %#lx" + " FSTART %#lx FEND %#lx BSSSIZE %#lx", + vstart, vbss, vend, fstart, fend, bsssize); m->brk = MAX(m->brk, vend); CHECK_GE(vend, vstart); CHECK_GE(fend, fstart); @@ -61,12 +69,9 @@ static void LoadElfLoadSegment(struct Machine *m, void *code, size_t codesize, CHECK_GE(vend - vstart, fstart - fend); CHECK_LE(phdr->p_filesz, phdr->p_memsz); CHECK_EQ(felf + phdr->p_offset - fstart, phdr->p_vaddr - vstart); - CHECK_NE(-1, RegisterMemory(m, vstart, (void *)fstart, fend - fstart)); - if (bsssize) { - CHECK_NE(MAP_FAILED, (rbss = mmap(NULL, bsssize, PROT_READ | PROT_WRITE, - MAP_ANONYMOUS | MAP_PRIVATE, -1, 0))); - CHECK_NE(-1, RegisterMemory(m, vbss, rbss, bsssize)); - } + CHECK_NE(-1, ReserveVirtual(m, vstart, fend - fstart)); + VirtualRecv(m, vstart, (void *)fstart, fend - fstart); + if (bsssize) CHECK_NE(-1, ReserveVirtual(m, vbss, bsssize)); if (phdr->p_memsz - phdr->p_filesz > bsssize) { VirtualSet(m, phdr->p_vaddr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz - bsssize); @@ -76,11 +81,8 @@ static void LoadElfLoadSegment(struct Machine *m, void *code, size_t codesize, static void LoadElf(struct Machine *m, struct Elf *elf) { unsigned i; Elf64_Phdr *phdr; - if (elf->ehdr->e_type != ET_EXEC) { - write(STDERR_FILENO, DSOLOL, strlen(DSOLOL)); - exit(1); - } m->ip = elf->base = elf->ehdr->e_entry; + LOGF("LOADELF ENTRY %p", m->ip); for (i = 0; i < elf->ehdr->e_phnum; ++i) { phdr = getelfsegmentheaderaddress(elf->ehdr, elf->size, i); switch (phdr->p_type) { @@ -114,10 +116,11 @@ void LoadProgram(struct Machine *m, const char *prog, char **args, char **vars, struct Elf *elf) { int fd; ssize_t rc; + int64_t sp; + char *real; void *stack; struct stat st; - char *real, *memory; - size_t i, codesize, mappedsize, extrasize, stacksize; + size_t i, codesize, mappedsize, extrasize; DCHECK_NOTNULL(prog); elf->prog = prog; if ((fd = open(prog, O_RDONLY)) == -1 || @@ -148,14 +151,11 @@ void LoadProgram(struct Machine *m, const char *prog, char **args, char **vars, ResetCpu(m); if (m->mode == XED_MACHINE_MODE_REAL) { elf->base = 0x7c00; - CHECK_NE(MAP_FAILED, - (memory = mmap(NULL, BIGPAGESIZE, PROT_READ | PROT_WRITE, - MAP_ANONYMOUS | MAP_PRIVATE, -1, 0))); - RegisterMemory(m, 0, memory, BIGPAGESIZE); + CHECK_NE(-1, ReserveVirtual(m, 0, BIGPAGESIZE)); m->ip = 0x7c00; Write64(m->cs, 0); Write64(m->dx, 0); - memcpy(memory + 0x7c00, elf->map, 512); + VirtualRecv(m, m->ip, elf->map, 512); if (memcmp(elf->map, "\177ELF", 4) == 0) { elf->ehdr = (void *)elf->map; elf->size = codesize; @@ -166,11 +166,9 @@ void LoadProgram(struct Machine *m, const char *prog, char **args, char **vars, elf->size = 0; } } else { - stacksize = STACKSIZE; - CHECK_NE(MAP_FAILED, (stack = mmap(NULL, stacksize, PROT_READ | PROT_WRITE, - MAP_ANONYMOUS | MAP_PRIVATE, -1, 0))); - Write64(m->sp, 0x0000800000000000); - RegisterMemory(m, 0x0000800000000000 - stacksize, stack, stacksize); + sp = 0x800000000000; + Write64(m->sp, sp); + CHECK_NE(-1, ReserveVirtual(m, sp - STACKSIZE, STACKSIZE)); LoadArgv(m, prog, args, vars); if (memcmp(elf->map, "\177ELF", 4) == 0) { elf->ehdr = (void *)elf->map; diff --git a/tool/build/lib/machine.c b/tool/build/lib/machine.c index 5f3416ab..8ebcc057 100644 --- a/tool/build/lib/machine.c +++ b/tool/build/lib/machine.c @@ -17,11 +17,6 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/assert.h" -#include "libc/conv/conv.h" -#include "libc/dce.h" -#include "libc/log/check.h" -#include "libc/log/log.h" #include "libc/macros.h" #include "libc/runtime/runtime.h" #include "tool/build/lib/abp.h" @@ -558,7 +553,7 @@ static void OpMovZvqpIvqp(struct Machine *m, uint32_t rde) { WriteRegister(rde, RegRexbSrm(m, rde), m->xedd->op.uimm0); } -static void OpIncZv(struct Machine *m, uint32_t rde) { +static relegated void OpIncZv(struct Machine *m, uint32_t rde) { if (!Osz(rde)) { Write32(RegSrm(m, rde), Inc32(Read32(RegSrm(m, rde)), 0, &m->flags)); } else { @@ -566,7 +561,7 @@ static void OpIncZv(struct Machine *m, uint32_t rde) { } } -static void OpDecZv(struct Machine *m, uint32_t rde) { +static relegated void OpDecZv(struct Machine *m, uint32_t rde) { if (!Osz(rde)) { Write32(RegSrm(m, rde), Dec32(Read32(RegSrm(m, rde)), 0, &m->flags)); } else { @@ -888,22 +883,6 @@ static void OpBsubiImm(struct Machine *m, uint32_t rde) { Bsubi(m, rde, m->xedd->op.uimm0); } -static relegated void LoadFarPointer(struct Machine *m, uint32_t rde, - uint8_t seg[8]) { - uint32_t fp; - fp = Read32(ComputeReserveAddressRead4(m, rde)); - Write64(seg, (fp & 0x0000ffff) << 4); - Write16(RegRexrReg(m, rde), fp >> 16); -} - -static relegated void OpLes(struct Machine *m, uint32_t rde) { - LoadFarPointer(m, rde, m->es); -} - -static relegated void OpLds(struct Machine *m, uint32_t rde) { - LoadFarPointer(m, rde, m->ds); -} - static void OpLgdtMs(struct Machine *m, uint32_t rde) { } @@ -1234,6 +1213,22 @@ static void OpNegEb(struct Machine *m, uint32_t rde) { AluEb(m, rde, Neg8); } +static relegated void LoadFarPointer(struct Machine *m, uint32_t rde, + uint8_t seg[8]) { + uint32_t fp; + fp = Read32(ComputeReserveAddressRead4(m, rde)); + Write64(seg, (fp & 0x0000ffff) << 4); + Write16(RegRexrReg(m, rde), fp >> 16); +} + +static relegated void OpLes(struct Machine *m, uint32_t rde) { + LoadFarPointer(m, rde, m->es); +} + +static relegated void OpLds(struct Machine *m, uint32_t rde) { + LoadFarPointer(m, rde, m->ds); +} + static relegated void Loop(struct Machine *m, uint32_t rde, bool cond) { uint64_t cx; cx = Read64(m->cx) - 1; @@ -1386,10 +1381,12 @@ static void OpSalc(struct Machine *m, uint32_t rde) { } static void OpNopEv(struct Machine *m, uint32_t rde) { - if (ModrmMod(rde) == 0b01 && ModrmReg(rde) == 0 && ModrmRm(rde) == 0b101) { - OpBofram(m, rde); - } else { - OpNoop(m, rde); + switch (ModrmMod(rde) << 6 | ModrmReg(rde) << 3 | ModrmRm(rde)) { + case 0x45: + OpBofram(m, rde); + break; + default: + OpNoop(m, rde); } } diff --git a/tool/build/lib/machine.h b/tool/build/lib/machine.h index 130d7e21..41469d2c 100644 --- a/tool/build/lib/machine.h +++ b/tool/build/lib/machine.h @@ -5,10 +5,6 @@ #include "tool/build/lib/fds.h" #include "tool/build/lib/pml4t.h" -#define kXmmIntegral 0 -#define kXmmDouble 1 -#define kXmmFloat 2 - #define kMachineHalt -1 #define kMachineDecodeError -2 #define kMachineUndefinedInstruction -3 @@ -28,7 +24,7 @@ struct Machine { uint8_t cs[8]; uint8_t ss[8]; uint64_t codevirt; - uint8_t *codereal; + uint8_t *codehost; uint32_t mode; uint32_t flags; uint32_t tlbindex; @@ -59,13 +55,15 @@ struct Machine { uint8_t r15[8]; }; }; - uint8_t *real; - uint64_t realsize; - uint64_t *cr3; - struct TlbEntry { - int64_t v; - void *r; + struct MachineTlb { + int64_t virt; + uint8_t *host; } tlb[16]; + struct MachineReal { + size_t i, n; + uint8_t *p; + } real; + uint64_t cr3; uint8_t xmm[16][16] aligned(16); uint8_t es[8]; uint8_t ds[8]; @@ -76,34 +74,34 @@ struct Machine { union { uint32_t cw; struct { - unsigned im : 1; /* invalid operation mask */ - unsigned dm : 1; /* denormal operand mask */ - unsigned zm : 1; /* zero divide mask */ - unsigned om : 1; /* overflow mask */ - unsigned um : 1; /* underflow mask */ - unsigned pm : 1; /* precision mask */ - unsigned _p1 : 2; /* reserved */ - unsigned pc : 2; /* precision: 32,∅,64,80 */ - unsigned rc : 2; /* rounding: even,→-∞,→+∞,→0 */ + unsigned im : 1; // invalid operation mask + unsigned dm : 1; // denormal operand mask + unsigned zm : 1; // zero divide mask + unsigned om : 1; // overflow mask + unsigned um : 1; // underflow mask + unsigned pm : 1; // precision mask + unsigned _p1 : 2; // reserved + unsigned pc : 2; // precision: 32,∅,64,80 + unsigned rc : 2; // rounding: even,→-∞,→+∞,→0 }; }; union { uint32_t sw; struct { - unsigned ie : 1; /* invalid operation */ - unsigned de : 1; /* denormalized operand */ - unsigned ze : 1; /* zero divide */ - unsigned oe : 1; /* overflow */ - unsigned ue : 1; /* underflow */ - unsigned pe : 1; /* precision */ - unsigned sf : 1; /* stack fault */ - unsigned es : 1; /* exception summary status */ - unsigned c0 : 1; /* condition 0 */ - unsigned c1 : 1; /* condition 1 */ - unsigned c2 : 1; /* condition 2 */ - unsigned sp : 3; /* top stack */ - unsigned c3 : 1; /* condition 3 */ - unsigned bf : 1; /* busy flag */ + unsigned ie : 1; // invalid operation + unsigned de : 1; // denormalized operand + unsigned ze : 1; // zero divide + unsigned oe : 1; // overflow + unsigned ue : 1; // underflow + unsigned pe : 1; // precision + unsigned sf : 1; // stack fault + unsigned es : 1; // exception summary status + unsigned c0 : 1; // condition 0 + unsigned c1 : 1; // condition 1 + unsigned c2 : 1; // condition 2 + unsigned sp : 3; // top stack + unsigned c3 : 1; // condition 3 + unsigned bf : 1; // busy flag }; }; int tw; @@ -111,28 +109,33 @@ struct Machine { int64_t ip; int64_t dp; } fpu; - struct { + struct MachineSse { union { uint32_t mxcsr; struct { - unsigned ie : 1; /* invalid operation flag */ - unsigned de : 1; /* denormal flag */ - unsigned ze : 1; /* divide by zero flag */ - unsigned oe : 1; /* overflow flag */ - unsigned ue : 1; /* underflow flag */ - unsigned pe : 1; /* precision flag */ - unsigned daz : 1; /* denormals are zeros */ - unsigned im : 1; /* invalid operation mask */ - unsigned dm : 1; /* denormal mask */ - unsigned zm : 1; /* divide by zero mask */ - unsigned om : 1; /* overflow mask */ - unsigned um : 1; /* underflow mask */ - unsigned pm : 1; /* precision mask */ - unsigned rc : 2; /* rounding control */ - unsigned ftz : 1; /* flush to zero */ + unsigned ie : 1; // invalid operation flag + unsigned de : 1; // denormal flag + unsigned ze : 1; // divide by zero flag + unsigned oe : 1; // overflow flag + unsigned ue : 1; // underflow flag + unsigned pe : 1; // precision flag + unsigned daz : 1; // denormals are zeros + unsigned im : 1; // invalid operation mask + unsigned dm : 1; // denormal mask + unsigned zm : 1; // divide by zero mask + unsigned om : 1; // overflow mask + unsigned um : 1; // underflow mask + unsigned pm : 1; // precision mask + unsigned rc : 2; // rounding control + unsigned ftz : 1; // flush to zero }; }; } sse; + struct MachineRealFree { + uint64_t i; + uint64_t n; + struct MachineRealFree *next; + } * realfree; struct FreeList { uint32_t i; void *p[6]; @@ -141,18 +144,23 @@ struct Machine { int64_t bofram[2]; jmp_buf onhalt; int64_t faultaddr; - uint8_t stash[4096]; - uint8_t xmmtype[2][8]; - uint8_t icache[4096 / 4][40] aligned(16); struct MachineFds fds; + uint8_t stash[4096]; + uint8_t icache[1024][40]; } aligned(64); +struct Machine *NewMachine(void) nodiscard; +void FreeMachine(struct Machine *); +void ResetMem(struct Machine *); void ResetCpu(struct Machine *); +void ResetTlb(struct Machine *); void LoadInstruction(struct Machine *); void ExecuteInstruction(struct Machine *); -struct Machine *NewMachine(void) nodiscard; -void LoadArgv(struct Machine *, const char *, char **, char **); -void InitMachine(struct Machine *); +long AllocateLinearPage(struct Machine *); +int ReserveVirtual(struct Machine *, int64_t, size_t); +char *FormatPml4t(struct Machine *) nodiscard; +int64_t FindVirtual(struct Machine *, int64_t, size_t); +int FreeVirtual(struct Machine *, int64_t, size_t); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/tool/build/lib/memory.c b/tool/build/lib/memory.c index 58d911b8..7d6a7ab6 100644 --- a/tool/build/lib/memory.c +++ b/tool/build/lib/memory.c @@ -31,39 +31,54 @@ #include "tool/build/lib/throw.h" void SetReadAddr(struct Machine *m, int64_t addr, uint32_t size) { - m->readaddr = addr; - m->readsize = size; + if (size) { + m->readaddr = addr; + m->readsize = size; + } } void SetWriteAddr(struct Machine *m, int64_t addr, uint32_t size) { - m->writeaddr = addr; - m->writesize = size; + if (size) { + m->writeaddr = addr; + m->writesize = size; + } } -void *FindReal(struct Machine *m, int64_t v) { - uint64_t *p; - unsigned skew; - unsigned char i; - skew = v & 0xfff; - v &= -0x1000; +void *FindReal(struct Machine *m, int64_t virt) { + uint8_t *host; + uint64_t real, pte, *pt; + unsigned skew, level, i; + if (!(-0x800000000000 <= virt && virt < 0x800000000000)) { + return NULL; + } + skew = virt & 0xfff; + virt &= -0x1000; for (i = 0; i < ARRAYLEN(m->tlb); ++i) { - if (m->tlb[i].v == v && m->tlb[i].r) { - return (char *)m->tlb[i].r + skew; + if (m->tlb[i].virt == virt && m->tlb[i].host) { + return m->tlb[i].host + skew; } } - for (p = m->cr3, i = 39; i >= 12; i -= 9) { - if (IsValidPage(p[(v >> i) & 511])) { - p = UnmaskPageAddr(p[(v >> i) & 511]); - } else { + level = 39; + real = m->cr3; + for (;;) { + if (real + 0x1000 > m->real.n) { return NULL; } + host = m->real.p + real; + if (level < 12) break; + pt = (uint64_t *)host; + pte = pt[(virt >> level) & 511]; + if (!(pte & 1)) { + return NULL; + } + real = pte & 0x00007ffffffff000; + level -= 9; } m->tlbindex = (m->tlbindex + 1) & (ARRAYLEN(m->tlb) - 1); m->tlb[m->tlbindex] = m->tlb[0]; - m->tlb[0].r = p; - m->tlb[0].v = ROUNDDOWN(v, 0x1000); - DCHECK_NOTNULL(p); - return (char *)p + skew; + m->tlb[0].host = host; + m->tlb[0].virt = virt; + return host + skew; } void *ResolveAddress(struct Machine *m, int64_t v) { @@ -196,7 +211,7 @@ void *LoadStr(struct Machine *m, int64_t addr) { if (!addr) return NULL; if (!(page = FindReal(m, addr))) return NULL; if ((p = memchr(page, '\0', have))) { - SetReadAddr(m, addr, p - page); + SetReadAddr(m, addr, p - page + 1); return page; } CHECK_LT(m->freelist.i, ARRAYLEN(m->freelist.p)); @@ -205,7 +220,7 @@ void *LoadStr(struct Machine *m, int64_t addr) { for (;;) { if (!(page = FindReal(m, addr + have))) break; if ((p = memccpy(copy + have, page, '\0', 0x1000))) { - SetReadAddr(m, addr, have + (p - (copy + have))); + SetReadAddr(m, addr, have + (p - (copy + have)) + 1); return (m->freelist.p[m->freelist.i++] = copy); } have += 0x1000; @@ -217,19 +232,19 @@ void *LoadStr(struct Machine *m, int64_t addr) { } void *LoadBuf(struct Machine *m, int64_t addr, size_t size) { - char *buf, *copy; size_t have, need; + char *buf, *copy, *page; have = 0x1000 - (addr & 0xfff); if (!addr) return NULL; if (!(buf = FindReal(m, addr))) return NULL; if (size > have) { CHECK_LT(m->freelist.i, ARRAYLEN(m->freelist.p)); if (!(copy = malloc(size))) return NULL; - memcpy(copy, buf, have); + buf = memcpy(copy, buf, have); do { need = MIN(0x1000, size - have); - if ((buf = FindReal(m, addr + have))) { - memcpy(copy + have, buf, need); + if ((page = FindReal(m, addr + have))) { + memcpy(copy + have, page, need); have += need; } else { free(copy); diff --git a/tool/build/lib/memorymalloc.c b/tool/build/lib/memorymalloc.c index 1d9fdbf6..8fea2bb9 100644 --- a/tool/build/lib/memorymalloc.c +++ b/tool/build/lib/memorymalloc.c @@ -17,35 +17,167 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" #include "libc/calls/calls.h" +#include "libc/log/check.h" +#include "libc/log/log.h" #include "libc/mem/mem.h" +#include "libc/runtime/runtime.h" #include "libc/str/str.h" +#include "libc/sysv/errfuns.h" +#include "libc/x/x.h" +#include "tool/build/lib/buffer.h" +#include "tool/build/lib/endian.h" #include "tool/build/lib/machine.h" #include "tool/build/lib/memory.h" #include "tool/build/lib/pml4t.h" -void *MallocPage(void) { - void *p; - size_t n; - if ((p = memalign(4096, 4096))) { - memset(p, 0, 4096); - } - return p; -} - -int RegisterMemory(struct Machine *m, int64_t v, void *r, size_t n) { - return RegisterPml4t(m->cr3, v, (int64_t)(intptr_t)r, n, MallocPage); -} - -void ResetRam(struct Machine *m) { - FreePml4t(m->cr3, -0x800000000000, 0x800000000000, free, munmap); -} - struct Machine *NewMachine(void) { struct Machine *m; - m = memalign(alignof(struct Machine), sizeof(struct Machine)); - memset(m, 0, sizeof(struct Machine)); + m = xmemalignzero(alignof(struct Machine), sizeof(struct Machine)); m->mode = XED_MACHINE_MODE_LONG_64; - InitMachine(m); + ResetCpu(m); + ResetMem(m); return m; } + +static void FreeMachineRealFree(struct Machine *m) { + struct MachineRealFree *rf; + while ((rf = m->realfree)) { + m->realfree = rf->next; + free(rf); + } +} + +void FreeMachine(struct Machine *m) { + if (m) { + FreeMachineRealFree(m); + free(m->real.p); + free(m); + } +} + +void ResetMem(struct Machine *m) { + FreeMachineRealFree(m); + ResetTlb(m); + m->real.i = 0; + m->cr3 = AllocateLinearPage(m); +} + +long AllocateLinearPage(struct Machine *m) { + uint8_t *p; + size_t i, n; + struct MachineRealFree *rf; + if ((rf = m->realfree)) { + DCHECK(rf->n); + DCHECK_EQ(0, rf->i & 0xfff); + DCHECK_EQ(0, rf->n & 0xfff); + DCHECK_LE(rf->i + rf->n, m->real.i); + i = rf->i; + rf->i += 0x1000; + if (!(rf->n -= 0x1000)) { + m->realfree = rf->next; + free(rf); + } + } else { + i = m->real.i; + n = m->real.n; + p = m->real.p; + if (i == n) { + if (n) { + n += n >> 1; + } else { + n = 0x10000; + } + n = ROUNDUP(n, 0x1000); + if ((p = realloc(p, n))) { + m->real.p = p; + m->real.n = n; + ResetTlb(m); + } else { + return -1; + } + } + DCHECK_EQ(0, i & 0xfff); + DCHECK_EQ(0, n & 0xfff); + DCHECK_LE(i + 0x1000, n); + m->real.i += 0x1000; + } + memset(m->real.p + i, 0, 0x1000); /* TODO: lazy page clearing */ + return i; +} + +static uint64_t MachineRead64(struct Machine *m, unsigned long i) { + CHECK_LE(i + 8, m->real.n); + return Read64(m->real.p + i); +} + +static void MachineWrite64(struct Machine *m, unsigned long i, uint64_t x) { + CHECK_LE(i + 8, m->real.n); + Write64(m->real.p + i, x); +} + +int ReserveVirtual(struct Machine *m, int64_t virt, size_t size) { + int64_t level, pt, ti, mi, end; + for (end = virt + size; virt < end; virt += 0x1000) { + for (pt = m->cr3, level = 39; level >= 12; level -= 9) { + pt = pt & 0x00007ffffffff000; + ti = (virt >> level) & 511; + DEBUGF("reserve %p level %d table %p index %ld", virt, level, pt, ti); + mi = pt + ti * 8; + pt = MachineRead64(m, mi); + if (!(pt & 1)) { + if ((pt = AllocateLinearPage(m)) == -1) return -1; + MachineWrite64(m, mi, pt | 7); + } + } + } + return 0; +} + +int64_t FindVirtual(struct Machine *m, int64_t virt, size_t size) { + uint64_t i, pt, got; + got = 0; + do { + if (virt >= 0x800000000000) return enomem(); + for (pt = m->cr3, i = 39; i >= 12; i -= 9) { + pt = MachineRead64(m, (pt & 0x7ffffffff000) + ((virt >> i) & 511) * 8); + if (!(pt & 1)) break; + } + if (i >= 12) { + got += 1ull << i; + } else { + virt += 0x1000; + got = 0; + } + } while (got < size); + return virt; +} + +int FreeVirtual(struct Machine *m, int64_t base, size_t size) { + struct MachineRealFree *rf; + uint64_t i, mi, pt, la, end, virt; + for (virt = base, end = virt + size; virt < end;) { + for (pt = m->cr3, i = 39;; i -= 9) { + mi = (pt & 0x7ffffffff000) + ((virt >> i) & 511) * 8; + pt = MachineRead64(m, mi); + if (!(pt & 1)) { + break; + } else if (i == 12) { + MachineWrite64(m, mi, 0); + la = pt & 0x7ffffffff000; + if (m->realfree && la == m->realfree->i + m->realfree->n) { + m->realfree->n += 0x1000; + } else if ((rf = malloc(sizeof(struct MachineRealFree)))) { + rf->i = la; + rf->n = 0x1000; + rf->next = m->realfree; + m->realfree = rf; + } + break; + } + } + virt += 1ull << i; + } + return 0; +} diff --git a/tool/build/lib/pml4t.c b/tool/build/lib/pml4t.c deleted file mode 100644 index 6f8800a8..00000000 --- a/tool/build/lib/pml4t.c +++ /dev/null @@ -1,192 +0,0 @@ -/*-*- 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 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/log/check.h" -#include "libc/macros.h" -#include "libc/mem/mem.h" -#include "libc/str/str.h" -#include "libc/sysv/errfuns.h" -#include "tool/build/lib/memory.h" -#include "tool/build/lib/pml4t.h" - -static int64_t MakeAddress(unsigned short a[4]) { - uint64_t x; - x = 0; - x |= a[0]; - x <<= 9; - x |= a[1]; - x <<= 9; - x |= a[2]; - x <<= 9; - x |= a[3]; - x <<= 12; - return SignExtendAddr(x); -} - -static uint64_t *GetPageTable(pml4t_t p, long i, void *NewPhysicalPage(void)) { - uint64_t *res; - DCHECK_ALIGNED(4096, p); - DCHECK(0 <= i && i < 512); - if (IsValidPage(p[i])) { - res = UnmaskPageAddr(p[i]); - } else if ((res = NewPhysicalPage())) { - DCHECK_ALIGNED(4096, res); - p[i] = MaskPageAddr(res) | 0b11; - } - return res; -} - -static void PtFinder(uint64_t *a, uint64_t *b, uint64_t n, pml4t_t pd, int k) { - unsigned i; - uint64_t e, c; - while (*b - *a < n) { - i = (*b >> k) & 511; - e = pd[i]; - c = ROUNDUP(*b + 1, 1 << k); - if (!IsValidPage(e)) { - *b = c; - } else if (k && *b - *a + (c - *b) > n) { - PtFinder(a, b, n, UnmaskPageAddr(e), k - 9); - } else { - *a = *b = c; - } - if (((*b >> k) & 511) < i) { - break; - } - } -} - -/** - * Locates free memory range. - * - * @param h specifies signedness and around where to start searching - * @return virtual page address with size bytes free, or -1 w/ errno - */ -int64_t FindPml4t(pml4t_t pml4t, uint64_t h, uint64_t n) { - uint64_t a, b; - n = ROUNDUP(n, 4096) >> 12; - a = b = (h & 0x0000fffffffff000) >> 12; - if (!n || n > 0x10000000) return einval(); - PtFinder(&a, &b, n, pml4t, 9 * 3); - if (b > 0x0000001000000000) return eoverflow(); - if (h < 0x0000800000000000 && b > 0x0000000800000000) return eoverflow(); - if (b - a < n) return enomem(); - return a << 12; -} - -/** - * Maps virtual page region to system memory region. - * - * @param pml4t is root of 48-bit page tables - * @param v is fixed page-aligned virtual address, rounded down - * @param r is real memory address, rounded down - * @param n is number of bytes needed, rounded up - * @return 0 on success, or -1 w/ errno - * @note existing pages are overwritten - */ -int RegisterPml4t(pml4t_t pml4t, int64_t v, int64_t r, size_t n, - void *NewPhysicalPage(void)) { - unsigned i, j, k, l; - uint64_t *pdpt, *pdt, *pd, u; - if (!n) return 0; - u = ROUNDDOWN(r, 4096); - n = ROUNDUP(n, 4096) >> 12; - i = (v >> 39) & 511; - j = (v >> 30) & 511; - k = (v >> 21) & 511; - l = (v >> 12) & 511; - if (u + n > 0x800000000000) return eoverflow(); - if (r + n > 0x800000000000) return eoverflow(); - for (; i < 512; ++i) { - if (!(pdpt = GetPageTable(pml4t, i, NewPhysicalPage))) return -1; - for (; j < 512; ++j) { - if (!(pdt = GetPageTable(pdpt, j, NewPhysicalPage))) return -1; - for (; k < 512; ++k) { - if (!(pd = GetPageTable(pdt, k, NewPhysicalPage))) return -1; - for (; l < 512; ++l) { - pd[l] = MaskPageAddr(u) | 0b11; - if (!--n) return 0; - u += 4096; - } - l = 0; - } - k = 0; - } - j = 0; - } - return enomem(); -} - -/** - * Unmaps pages and frees page tables. - */ -int FreePml4t(pml4t_t pml4t, int64_t addr, uint64_t size, - void FreePhysicalPageTable(void *), - int FreePhysicalPages(void *, size_t)) { - int rc; - char *pages; - uint64_t i, *pdpt, *pdt, *pd; - unsigned short r, s[4], a[4], R[2][2] = {{256, 512}, {0, 256}}; - a[0] = addr >> 39; - a[1] = addr >> 30; - a[2] = addr >> 21; - a[3] = addr >> 12; - size = ROUNDUP(size, 4096) >> 12; - for (rc = r = 0; r < ARRAYLEN(R); ++r) { - for (a[0] &= 511; size && R[r][0] <= a[0] && a[0] < R[r][1]; ++a[0]) { - if (!IsValidPage(pml4t[a[0]])) continue; - pdpt = UnmaskPageAddr(pml4t[a[0]]); - for (s[1] = (a[1] &= 511); size && a[1] < 512; ++a[1]) { - if (!IsValidPage(pdpt[a[1]])) continue; - pdt = UnmaskPageAddr(pdpt[a[1]]); - for (s[2] = (a[2] &= 511); size && a[2] < 512; ++a[2]) { - if (!IsValidPage(pdt[a[2]])) continue; - pd = UnmaskPageAddr(pdt[a[2]]); - for (s[3] = (a[3] &= 511); size && a[3] < 512; ++a[3]) { - if (IsValidPage(pd[a[3]])) { - pages = UnmaskPageAddr(pd[a[3]]); - pd[a[3]] = 0; - for (i = 1; i + 1 < size && a[3] + i < 512; ++i) { - if (!IsValidPage(pd[a[3] + i])) break; - if (UnmaskPageAddr(pd[a[3] + i]) != pages + i * 4096) break; - pd[a[3] + i] = 0; - } - FreePhysicalPages(pages, i * 4096); - a[3] += i - 1; - size -= i; - } - } - if (s[3] == 0 && a[3] == 512) { - FreePhysicalPageTable(pd); - pdt[a[2]] = 0; - } - } - if (s[2] == 0 && a[2] == 512) { - FreePhysicalPageTable(pdt); - pdpt[a[1]] = 0; - } - } - if (s[1] == 0 && a[1] == 512) { - FreePhysicalPageTable(pdpt); - pml4t[a[0]] = 0; - } - } - } - return 0; -} diff --git a/tool/build/lib/pml4t.h b/tool/build/lib/pml4t.h index 349e98f5..919341d1 100644 --- a/tool/build/lib/pml4t.h +++ b/tool/build/lib/pml4t.h @@ -4,17 +4,9 @@ COSMOPOLITAN_C_START_ #define IsValidPage(x) ((x)&1) -#define UnmaskPageAddr(x) ((void *)SignExtendAddr(MaskPageAddr(x))) -#define MaskPageAddr(x) ((int64_t)(intptr_t)(x)&0x00007ffffffff000) -#define SignExtendAddr(x) (!((x)&0x800000000000) ? (x) : (x) | -0x800000000000) - -typedef uint64_t pml4t_t[512] aligned(4096); - -int FreePml4t(pml4t_t, int64_t, uint64_t, void (*)(void *), - int (*)(void *, size_t)); -int RegisterPml4t(pml4t_t, int64_t, int64_t, size_t, void *(*)(void)); -int64_t FindPml4t(pml4t_t, uint64_t, uint64_t); -char *FormatPml4t(pml4t_t) nodiscard; +#define MaskPageAddr(x) ((x)&0x00007ffffffff000) +#define UnmaskPageAddr(x) SignExtendAddr(MaskPageAddr(x)) +#define SignExtendAddr(x) ((int64_t)((uint64_t)(x) << 16) >> 16) COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/tool/build/lib/pml4tfmt.c b/tool/build/lib/pml4tfmt.c index 18c72a65..434ffb84 100644 --- a/tool/build/lib/pml4tfmt.c +++ b/tool/build/lib/pml4tfmt.c @@ -17,6 +17,7 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/log/check.h" #include "libc/macros.h" #include "libc/mem/mem.h" #include "libc/x/x.h" @@ -59,12 +60,17 @@ static void FormatEndPage(struct Pml4tFormater *pp, int64_t end) { AppendFmt(&pp->b, "%p %p %,ld bytes", end - 1, size, size); } -char *FormatPml4t(uint64_t pml4t[512]) { +static void *GetPt(struct Machine *m, uint64_t r) { + CHECK_LE(r + 0x1000, m->real.n); + return m->real.p + r; +} + +char *FormatPml4t(struct Machine *m) { uint64_t *pd[4]; unsigned short i, a[4]; struct Pml4tFormater pp = {0}; unsigned short range[][2] = {{256, 512}, {0, 256}}; - pd[0] = pml4t; + pd[0] = GetPt(m, m->cr3); for (i = 0; i < ARRAYLEN(range); ++i) { a[0] = range[i][0]; do { @@ -72,19 +78,19 @@ char *FormatPml4t(uint64_t pml4t[512]) { if (!IsValidPage(pd[0][a[0]])) { if (pp.t) FormatEndPage(&pp, MakeAddress(a)); } else { - pd[1] = UnmaskPageAddr(pd[0][a[0]]); + pd[1] = GetPt(m, UnmaskPageAddr(pd[0][a[0]])); do { a[2] = a[3] = 0; if (!IsValidPage(pd[1][a[1]])) { if (pp.t) FormatEndPage(&pp, MakeAddress(a)); } else { - pd[2] = UnmaskPageAddr(pd[1][a[1]]); + pd[2] = GetPt(m, UnmaskPageAddr(pd[1][a[1]])); do { a[3] = 0; if (!IsValidPage(pd[2][a[2]])) { if (pp.t) FormatEndPage(&pp, MakeAddress(a)); } else { - pd[3] = UnmaskPageAddr(pd[2][a[2]]); + pd[3] = GetPt(m, UnmaskPageAddr(pd[2][a[2]])); do { if (!IsValidPage(pd[3][a[3]])) { if (pp.t) FormatEndPage(&pp, MakeAddress(a)); diff --git a/tool/build/lib/pty.c b/tool/build/lib/pty.c index 31e81594..5e9d8d3d 100644 --- a/tool/build/lib/pty.c +++ b/tool/build/lib/pty.c @@ -18,28 +18,356 @@ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/alg/arraylist2.h" +#include "libc/assert.h" #include "libc/bits/bits.h" #include "libc/bits/safemacros.h" #include "libc/conv/conv.h" #include "libc/conv/itoa.h" #include "libc/log/check.h" +#include "libc/log/log.h" #include "libc/macros.h" #include "libc/mem/mem.h" #include "libc/nexgen32e/bsr.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" #include "libc/str/thompike.h" +#include "libc/str/tpenc.h" #include "libc/unicode/unicode.h" #include "libc/x/x.h" #include "tool/build/lib/pty.h" +/** + * @fileoverview Pseudoteletypewriter + * + * \t TAB + * \a BELL + * \r CURSOR START + * \b CURSOR LEFT + * \177 CURSOR LEFT + * \n CURSOR DOWN AND START IF OPOST + * \f CURSOR DOWN AND START IF OPOST + * \v CURSOR DOWN AND START IF OPOST + * \eD CURSOR DOWN AND START + * \eE CURSOR DOWN + * \eM CURSOR UP + * \ec FULL RESET + * \e7 SAVE CURSOR POSITION + * \e8 RESTORE CURSOR POSITION + * \e(0 DEC SPECIAL GRAPHICS + * \e(B USAS X3.4-1967 + * \e#5 SINGLE WIDTH + * \e#6 DOUBLE WIDTH + * \e#8 SO MANY E + * \eZ → \e/Z REPORT IDENTITY + * \e[𝑛A CURSOR UP [CLAMPED] + * \e[𝑛B CURSOR DOWN [CLAMPED] + * \e[𝑛C CURSOR RIGHT [CLAMPED] + * \e[𝑛D CURSOR LEFT [CLAMPED] + * \e[𝑦;𝑥H SET CURSOR POSITION [CLAMPED] + * \e[𝑥G SET CURSOR COLUMN [CLAMPED] + * \e[𝑦d SET CURSOR ROW [CLAMPED] + * \e[𝑛E CURSOR DOWN AND START [CLAMPED] + * \e[𝑛F CURSOR UP AND START [CLAMPED] + * \e[𝑛S SCROLL UP + * \e[𝑛T SCROLL DOWN + * \e[𝑛@ INSERT CELLS + * \e[𝑛P DELETE CELLS + * \e[𝑛L INSERT LINES + * \e[𝑛M DELETE LINES + * \e[J ERASE DISPLAY FORWARDS + * \e[1J ERASE DISPLAY BACKWARDS + * \e[2J ERASE DISPLAY + * \e[K ERASE LINE FORWARD + * \e[1K ERASE LINE BACKWARD + * \e[2K ERASE LINE + * \e[𝑛X ERASE CELLS + * \e[0m RESET + * \e[1m BOLD + * \e[2m FAINT + * \e[3m ITALIC + * \e[4m UNDER + * \e[5m BLINK + * \e[7m INVERT + * \e[8m CONCEAL + * \e[9m STRIKE + * \e[20m FRAKTUR + * \e[21m DUNDER + * \e[22m RESET BOLD & FAINT + * \e[23m RESET ITALIC & FRAKTUR + * \e[24m RESET UNDER & DUNDER + * \e[25m RESET BLINK + * \e[27m RESET INVERT + * \e[28m RESET CONCEAL + * \e[29m RESET STRIKE + * \e[39m RESET FOREGROUND + * \e[49m RESET BACKGROUND + * \e[30m BLACK FOREGROUND + * \e[31m RED FOREGROUND + * \e[32m GREEN FOREGROUND + * \e[33m YELLOW FOREGROUND + * \e[34m BLUE FOREGROUND + * \e[35m MAGENTA FOREGROUND + * \e[36m CYAN FOREGROUND + * \e[37m WHITE FOREGROUND + * \e[40m BLACK BACKGROUND + * \e[41m RED BACKGROUND + * \e[42m GREEN BACKGROUND + * \e[44m YELLOW BACKGROUND + * \e[44m BLUE BACKGROUND + * \e[45m MAGENTA BACKGROUND + * \e[46m CYAN BACKGROUND + * \e[47m WHITE BACKGROUND + * \e[90m BRIGHT BLACK FOREGROUND + * \e[91m BRIGHT RED FOREGROUND + * \e[92m BRIGHT GREEN FOREGROUND + * \e[93m BRIGHT YELLOW FOREGROUND + * \e[94m BRIGHT BLUE FOREGROUND + * \e[95m BRIGHT MAGENTA FOREGROUND + * \e[96m BRIGHT CYAN FOREGROUND + * \e[97m BRIGHT WHITE FOREGROUND + * \e[100m BRIGHT BLACK BACKGROUND + * \e[101m BRIGHT RED BACKGROUND + * \e[102m BRIGHT GREEN BACKGROUND + * \e[103m BRIGHT YELLOW BACKGROUND + * \e[104m BRIGHT BLUE BACKGROUND + * \e[105m BRIGHT MAGENTA BACKGROUND + * \e[106m BRIGHT CYAN BACKGROUND + * \e[107m BRIGHT WHITE BACKGROUND + * \e[38;5;𝑥m XTERM256 FOREGROUND + * \e[48;5;𝑥m XTERM256 BACKGROUND + * \e[38;2;𝑟;𝑔;𝑏m RGB FOREGROUND + * \e[48;2;𝑟;𝑔;𝑏m RGB BACKGROUND + * \e[?25h SHOW CURSOR + * \e[?25l HIDE CURSOR + * \e[s SAVE CURSOR POSITION + * \e[u RESTORE CURSOR POSITION + * \e[0q RESET LEDS + * \e[1q TURN ON FIRST LED + * \e[2q TURN ON SECOND LED + * \e[3q TURN ON THIRD LED + * \e[4q TURN ON FOURTH LED + * \e[c → \e[?1;0c REPORT DEVICE TYPE + * \e[5n → \e[0n REPORT DEVICE STATUS + * \e[6n → \e[𝑦;𝑥R REPORT CURSOR POSITION + * \e7\e[9979;9979H\e[6n\e8 → \e[𝑦;𝑥R REPORT DISPLAY DIMENSIONS + * + * @see https://vt100.net/docs/vt220-rm/chapter4.html + * @see https://invisible-island.net/xterm/ + * @see ANSI X3.64-1979 + * @see ISO/IEC 6429 + * @see FIPS-86 + * @see ECMA-48 + */ + struct MachinePty *MachinePtyNew(void) { struct MachinePty *pty; pty = xcalloc(1, sizeof(struct MachinePty)); MachinePtyResize(pty, 25, 80); + MachinePtyFullReset(pty); + MachinePtyErase(pty, 0, pty->yn * pty->xn); return pty; } +static void MachinePtyFreePlanes(struct MachinePty *pty) { + free(pty->wcs); + free(pty->fgs); + free(pty->bgs); + free(pty->prs); +} + +void MachinePtyFree(struct MachinePty *pty) { + if (pty) { + MachinePtyFreePlanes(pty); + free(pty); + } +} + +static wchar_t *MachinePtyGetXlatAscii(void) { + unsigned i; + static bool once; + static wchar_t xlat[128]; + if (!once) { + for (i = 0; i < 128; ++i) { + xlat[i] = i; + } + once = true; + } + return xlat; +} + +static wchar_t *MachinePtyGetXlatLineDrawing(void) { + unsigned i; + static bool once; + static wchar_t xlat[128]; + if (!once) { + for (i = 0; i < 128; ++i) { + if (0x5F <= i && i <= 0x7E) { + xlat[i] = u" ◆▒␉␌␍␊°±␤␋┘┐┌└┼⎺⎻─⎼⎽├┤┴┬│≤≥π≠£·"[i - 0x5F]; + } else { + xlat[i] = i; + } + } + once = true; + } + return xlat; +} + +static void MachinePtyXlatAlphabet(wchar_t xlat[128], int a, int b) { + unsigned i; + for (i = 0; i < 128; ++i) { + if ('a' <= i && i <= 'z') { + xlat[i] = i - 'a' + a; + } else if ('A' <= i && i <= 'Z') { + xlat[i] = i - 'A' + b; + } else { + xlat[i] = i; + } + } +} + +static wchar_t *MachinePtyGetXlatItalic(void) { + static bool once; + static wchar_t xlat[128]; + if (!once) { + MachinePtyXlatAlphabet(xlat, L'𝑎', L'𝐴'); + once = true; + } + return xlat; +} + +static wchar_t *MachinePtyGetXlatBoldItalic(void) { + static bool once; + static wchar_t xlat[128]; + if (!once) { + MachinePtyXlatAlphabet(xlat, L'𝒂', L'𝑨'); + once = true; + } + return xlat; +} + +static wchar_t *MachinePtyGetXlatBoldFraktur(void) { + static bool once; + static wchar_t xlat[128]; + if (!once) { + MachinePtyXlatAlphabet(xlat, L'𝖆', L'𝕬'); + once = true; + } + return xlat; +} + +static wchar_t *MachinePtyGetXlatFraktur(void) { + unsigned i; + static bool once; + static wchar_t xlat[128]; + if (!once) { + for (i = 0; i < ARRAYLEN(xlat); ++i) { + if ('A' <= i && i <= 'Z') { + xlat[i] = L"𝔄𝔅ℭ𝔇𝔈𝔉𝔊ℌℑ𝔍𝔎𝔏𝔐𝔑𝔒𝔓𝔔ℜ𝔖𝔗𝔘𝔙𝔚𝔛𝔜ℨ"[i - 'A']; + } else if ('a' <= i && i <= 'z') { + xlat[i] = i - 'a' + L'𝔞'; + } else { + xlat[i] = i; + } + } + once = true; + } + return xlat; +} + +static wchar_t *MachinePtyGetXlatDoubleWidth(void) { + unsigned i; + static bool once; + static wchar_t xlat[128]; + if (!once) { + for (i = 0; i < ARRAYLEN(xlat); ++i) { + if ('!' <= i && i <= '~') { + xlat[i] = -(i - '!' + L'!'); + } else { + xlat[i] = i; + } + } + once = true; + } + return xlat; +} + +static wchar_t *MachinePtyGetXlatSgr(struct MachinePty *pty) { + switch (!!(pty->pr & kMachinePtyFraktur) << 2 | + !!(pty->pr & kMachinePtyItalic) << 1 | + !!(pty->pr & kMachinePtyBold) << 0) { + case 0b100: + case 0b110: + return MachinePtyGetXlatFraktur(); + case 0b101: + case 0b111: + return MachinePtyGetXlatBoldFraktur(); + case 0b011: + return MachinePtyGetXlatBoldItalic(); + case 0b010: + return MachinePtyGetXlatItalic(); + default: + return MachinePtyGetXlatAscii(); + } +} + +static void MachinePtySetXlat(struct MachinePty *pty, wchar_t *xlat) { + pty->xlat = xlat; + pty->pr &= ~(kMachinePtyItalic | kMachinePtyFraktur); +} + +static void MachinePtySetCodepage(struct MachinePty *pty, char id) { + unsigned i; + switch (id) { + default: + case 'B': + MachinePtySetXlat(pty, MachinePtyGetXlatAscii()); + break; + case '0': + MachinePtySetXlat(pty, MachinePtyGetXlatLineDrawing()); + break; + } +} + +void MachinePtyErase(struct MachinePty *pty, long dst, long n) { + DCHECK_LE(dst + n, pty->yn * pty->xn); + wmemset((void *)(pty->wcs + dst), ' ', n); + wmemset((void *)(pty->prs + dst), 0, n); +} + +void MachinePtyMemmove(struct MachinePty *pty, long dst, long src, long n) { + DCHECK_LE(src + n, pty->yn * pty->xn); + DCHECK_LE(dst + n, pty->yn * pty->xn); + memmove(pty->wcs + dst, pty->wcs + src, n * 4); + memmove(pty->fgs + dst, pty->fgs + src, n * 4); + memmove(pty->bgs + dst, pty->bgs + src, n * 4); + memmove(pty->prs + dst, pty->prs + src, n * 4); +} + +void MachinePtyFullReset(struct MachinePty *pty) { + pty->y = 0; + pty->x = 0; + pty->pr = 0; + pty->u8 = 0; + pty->n8 = 0; + pty->conf = 0; + pty->save = 0; + pty->state = 0; + pty->esc.i = 0; + pty->input.i = 0; + pty->xlat = MachinePtyGetXlatAscii(); + MachinePtyErase(pty, 0, pty->yn * pty->xn); +} + +void MachinePtySetY(struct MachinePty *pty, int y) { + pty->conf &= ~kMachinePtyRedzone; + pty->y = MAX(0, MIN(pty->yn - 1, y)); +} + +void MachinePtySetX(struct MachinePty *pty, int x) { + pty->conf &= ~kMachinePtyRedzone; + pty->x = MAX(0, MIN(pty->xn - 1, x)); +} + void MachinePtyResize(struct MachinePty *pty, int yn, int xn) { unsigned y, ym, xm, y0; uint32_t *wcs, *fgs, *bgs, *prs; @@ -59,119 +387,204 @@ void MachinePtyResize(struct MachinePty *pty, int yn, int xn) { memcpy(bgs + y * xn, pty->bgs + y * pty->xn, xm * 4); memcpy(prs + y * xn, pty->prs + y * pty->xn, xm * 4); } - free(pty->wcs); - free(pty->fgs); - free(pty->bgs); - free(pty->prs); + MachinePtyFreePlanes(pty); pty->wcs = wcs; pty->fgs = fgs; pty->bgs = bgs; pty->prs = prs; - pty->y = MIN(pty->y, yn - 1); - pty->x = MIN(pty->x, xn - 1); pty->yn = yn; pty->xn = xn; + MachinePtySetY(pty, pty->y); + MachinePtySetX(pty, pty->x); } -void MachinePtyFree(struct MachinePty *pty) { - if (pty) { - free(pty->wcs); - free(pty->fgs); - free(pty->bgs); - free(pty->prs); - free(pty); - } -} - -static void MachinePtyScrollPlane(struct MachinePty *pty, uint32_t *p) { - memcpy(p, p + pty->xn, sizeof(p[0]) * pty->xn * (pty->yn - 1)); - memset(p + pty->xn * (pty->yn - 1), 0, sizeof(p[0]) * pty->xn); +static void MachinePtyConcatInput(struct MachinePty *pty, const char *data, + size_t n) { + CONCAT(&pty->input.p, &pty->input.i, &pty->input.n, data, n); } static void MachinePtyScroll(struct MachinePty *pty) { - MachinePtyScrollPlane(pty, pty->wcs); - MachinePtyScrollPlane(pty, pty->fgs); - MachinePtyScrollPlane(pty, pty->bgs); - MachinePtyScrollPlane(pty, pty->prs); + MachinePtyMemmove(pty, 0, pty->xn, pty->xn * (pty->yn - 1)); + MachinePtyErase(pty, pty->xn * (pty->yn - 1), pty->xn); +} + +static void MachinePtyReverse(struct MachinePty *pty) { + MachinePtyMemmove(pty, pty->xn, 0, pty->xn * (pty->yn - 1)); + MachinePtyErase(pty, 0, pty->xn); +} + +static void MachinePtyIndex(struct MachinePty *pty) { + if (pty->y < pty->yn - 1) { + ++pty->y; + } else { + MachinePtyScroll(pty); + } +} + +static void MachinePtyReverseIndex(struct MachinePty *pty) { + if (pty->y) { + --pty->y; + } else { + MachinePtyReverse(pty); + } +} + +static void MachinePtyCarriageReturn(struct MachinePty *pty) { + MachinePtySetX(pty, 0); } static void MachinePtyNewline(struct MachinePty *pty) { - pty->x = 0; - if (++pty->y == pty->yn) { - --pty->y; - if (!(pty->conf & kMachinePtyNoopost)) { - MachinePtyScroll(pty); - } + MachinePtyIndex(pty); + if (!(pty->conf & kMachinePtyNoopost)) { + MachinePtyCarriageReturn(pty); } } -static void SetMachinePtyCell(struct MachinePty *pty, wint_t wc) { - uint32_t w, i; - if ((w = MAX(0, wcwidth(wc))) > 0) { - i = pty->y * pty->xn + pty->x; - pty->wcs[i] = wc; +static void MachinePtyAdvance(struct MachinePty *pty) { + DCHECK_EQ(pty->xn - 1, pty->x); + DCHECK(pty->conf & kMachinePtyRedzone); + pty->conf &= ~kMachinePtyRedzone; + pty->x = 0; + if (pty->y < pty->yn - 1) { + ++pty->y; + } else { + MachinePtyScroll(pty); + } +} + +static void MachinePtyWriteGlyph(struct MachinePty *pty, wint_t wc, int w) { + uint32_t i; + if (w < 1) wc = ' ', w = 1; + if ((pty->conf & kMachinePtyRedzone) || pty->x + w > pty->xn) { + MachinePtyAdvance(pty); + } + i = pty->y * pty->xn + pty->x; + pty->wcs[i] = wc; + if ((pty->prs[i] = pty->pr) & (kMachinePtyFg | kMachinePtyBg)) { pty->fgs[i] = pty->fg; pty->bgs[i] = pty->bg; - pty->prs[i] = pty->pr; - if ((pty->x += w) >= pty->xn) { - pty->x = 0; - MachinePtyNewline(pty); - } + } + if ((pty->x += w) >= pty->xn) { + pty->x = pty->xn - 1; + pty->conf |= kMachinePtyRedzone; } } -static void MachinePtyTab(struct MachinePty *pty) { +static void MachinePtyWriteTab(struct MachinePty *pty) { unsigned x, x2; - x2 = pty->x + ROUNDUP(pty->x + 1, 8); - if (x2 >= pty->xn) x2 = pty->xn - 1; + if (pty->conf & kMachinePtyRedzone) { + MachinePtyAdvance(pty); + } + x2 = MIN(pty->xn, ROUNDUP(pty->x + 1, 8)); for (x = pty->x; x < x2; ++x) { - pty->wcs[pty->y * pty->xn + x] = 0; + pty->wcs[pty->y * pty->xn + x] = ' '; } - pty->x = x2; -} - -static void MachinePtyCursorSet(struct MachinePty *pty, int y, int x) { - pty->y = MAX(0, MIN(pty->yn - 1, y)); - pty->x = MAX(0, MIN(pty->xn - 1, x)); -} - -static void MachinePtyCursorMove(struct MachinePty *pty, int dy, int dx) { - int n; - if (pty->esc.i > 1) { - n = atoi(pty->esc.s); - dy *= n; - dx *= n; + if (x2 < pty->xn) { + pty->x = x2; + } else { + pty->x = pty->xn - 1; + pty->conf |= kMachinePtyRedzone; } - MachinePtyCursorSet(pty, pty->y + dy, pty->x + dx); } -static void MachinePtyCursorPosition(struct MachinePty *pty) { +static int MachinePtyAtoi(const char *s, const char **e) { + int i; + for (i = 0; isdigit(*s); ++s) i *= 10, i += *s - '0'; + if (e) *e = s; + return i; +} + +static int MachinePtyGetMoveParam(struct MachinePty *pty) { + int x = MachinePtyAtoi(pty->esc.s, NULL); + if (x < 1) x = 1; + return x; +} + +static void MachinePtySetCursorPosition(struct MachinePty *pty) { int row, col; - row = MAX(1, atoi(pty->esc.s)); - col = MAX(1, atoi((char *)firstnonnull(strchr(pty->esc.s, ';'), "x") + 1)); - MachinePtyCursorSet(pty, row - 1, col - 1); + const char *s = pty->esc.s; + row = max(1, MachinePtyAtoi(s, &s)); + if (*s == ';') ++s; + col = max(1, MachinePtyAtoi(s, &s)); + MachinePtySetY(pty, row - 1); + MachinePtySetX(pty, col - 1); } -static void MachinePtyEraseRange(struct MachinePty *pty, size_t i, size_t j) { - size_t n; - n = (j - i) * sizeof(uint32_t); - memset(pty->wcs + i, 0, n); - memset(pty->fgs + i, 0, n); - memset(pty->bgs + i, 0, n); - memset(pty->prs + i, 0, n); +static void MachinePtySetCursorRow(struct MachinePty *pty) { + MachinePtySetY(pty, MachinePtyGetMoveParam(pty) - 1); +} + +static void MachinePtySetCursorColumn(struct MachinePty *pty) { + MachinePtySetX(pty, MachinePtyGetMoveParam(pty) - 1); +} + +static void MachinePtyMoveCursor(struct MachinePty *pty, int dy, int dx) { + int n = MachinePtyGetMoveParam(pty); + MachinePtySetY(pty, pty->y + dy * n); + MachinePtySetX(pty, pty->x + dx * n); +} + +static void MachinePtyScrollUp(struct MachinePty *pty) { + int n = MachinePtyGetMoveParam(pty); + while (n--) MachinePtyScroll(pty); +} + +static void MachinePtyScrollDown(struct MachinePty *pty) { + int n = MachinePtyGetMoveParam(pty); + while (n--) MachinePtyReverse(pty); +} + +static void MachinePtySetCursorStatus(struct MachinePty *pty, bool status) { + if (status) { + pty->conf &= ~kMachinePtyNocursor; + } else { + pty->conf |= kMachinePtyNocursor; + } +} + +static void MachinePtySetMode(struct MachinePty *pty, bool status) { + const char *p = pty->esc.s; + switch (*p++) { + case '?': + while (isdigit(*p)) { + switch (MachinePtyAtoi(p, &p)) { + case 25: + MachinePtySetCursorStatus(pty, status); + break; + default: + break; + } + if (*p == ';') { + ++p; + } + } + break; + default: + break; + } +} + +static void MachinePtySaveCursorPosition(struct MachinePty *pty) { + pty->save = (pty->y & 0x7FFF) | (pty->x & 0x7FFF) << 16; +} + +static void MachinePtyRestoreCursorPosition(struct MachinePty *pty) { + MachinePtySetY(pty, (pty->save & 0x00007FFF) >> 000); + MachinePtySetX(pty, (pty->save & 0x7FFF0000) >> 020); } static void MachinePtyEraseDisplay(struct MachinePty *pty) { - switch (atoi(pty->esc.s)) { - case 3: - case 2: - pty->y = 0; - pty->x = 0; + switch (MachinePtyAtoi(pty->esc.s, NULL)) { case 0: - MachinePtyEraseRange(pty, pty->y * pty->xn + pty->x, pty->yn * pty->xn); + MachinePtyErase(pty, pty->y * pty->xn + pty->x, + pty->yn * pty->xn - (pty->y * pty->xn + pty->x)); break; case 1: - MachinePtyEraseRange(pty, 0, pty->y * pty->xn + pty->x); + MachinePtyErase(pty, 0, pty->y * pty->xn + pty->x); + break; + case 2: + case 3: + MachinePtyErase(pty, 0, pty->yn * pty->xn); break; default: break; @@ -179,17 +592,122 @@ static void MachinePtyEraseDisplay(struct MachinePty *pty) { } static void MachinePtyEraseLine(struct MachinePty *pty) { - switch (atoi(pty->esc.s)) { + switch (MachinePtyAtoi(pty->esc.s, NULL)) { case 0: - MachinePtyEraseRange(pty, pty->y * pty->xn + pty->x, - pty->y * pty->xn + pty->xn); + MachinePtyErase(pty, pty->y * pty->xn + pty->x, pty->xn - pty->x); break; case 1: - MachinePtyEraseRange(pty, pty->y * pty->xn + pty->xn, - pty->y * pty->xn + pty->x); + MachinePtyErase(pty, pty->y * pty->xn, pty->x); break; case 2: - MachinePtyEraseRange(pty, pty->y * pty->xn, pty->y * pty->xn + pty->xn); + MachinePtyErase(pty, pty->y * pty->xn, pty->xn); + break; + default: + break; + } +} + +static void MachinePtyEraseCells(struct MachinePty *pty) { + int i, n, x; + i = pty->y * pty->xn + pty->x; + n = pty->yn * pty->xn; + x = min(max(MachinePtyAtoi(pty->esc.s, NULL), 1), n - i); + MachinePtyErase(pty, i, x); +} + +static int MachinePtyArg1(struct MachinePty *pty) { + return max(1, MachinePtyAtoi(pty->esc.s, NULL)); +} + +static void MachinePtyInsertCells(struct MachinePty *pty) { + int n = min(pty->xn - pty->x, MachinePtyArg1(pty)); + MachinePtyMemmove(pty, pty->y * pty->xn + pty->x + n, + pty->y * pty->xn + pty->x, pty->xn - (pty->x + n)); + MachinePtyErase(pty, pty->y * pty->xn + pty->x, n); +} + +static void MachinePtyInsertLines(struct MachinePty *pty) { + int n = min(pty->yn - pty->y, MachinePtyArg1(pty)); + MachinePtyMemmove(pty, (pty->y + n) * pty->xn, pty->y * pty->xn, + (pty->yn - pty->y - n) * pty->xn); + MachinePtyErase(pty, pty->y * pty->xn, n * pty->xn); +} + +static void MachinePtyDeleteCells(struct MachinePty *pty) { + int n = min(pty->xn - pty->x, MachinePtyArg1(pty)); + MachinePtyMemmove(pty, pty->y * pty->xn + pty->x, + pty->y * pty->xn + pty->x + n, pty->xn - (pty->x + n)); + MachinePtyErase(pty, pty->y * pty->xn + pty->x, n); +} + +static void MachinePtyDeleteLines(struct MachinePty *pty) { + int n = min(pty->yn - pty->y, MachinePtyArg1(pty)); + MachinePtyMemmove(pty, pty->y * pty->xn, (pty->y + n) * pty->xn, + (pty->yn - pty->y - n) * pty->xn); + MachinePtyErase(pty, (pty->y + n) * pty->xn, n * pty->xn); +} + +static void MachinePtyReportDeviceStatus(struct MachinePty *pty) { + MachinePtyWriteInput(pty, "\e[0n", 4); +} + +static void MachinePtyReportPreferredVtType(struct MachinePty *pty) { + MachinePtyWriteInput(pty, "\e[?1;0c", 4); +} + +static void MachinePtyReportPreferredVtIdentity(struct MachinePty *pty) { + MachinePtyWriteInput(pty, "\e/Z", 4); +} + +static void MachinePtyBell(struct MachinePty *pty) { + pty->conf |= kMachinePtyBell; +} + +static void MachinePtyLed(struct MachinePty *pty) { + switch (MachinePtyAtoi(pty->esc.s, NULL)) { + case 0: + pty->conf &= ~kMachinePtyLed1; + pty->conf &= ~kMachinePtyLed2; + pty->conf &= ~kMachinePtyLed3; + pty->conf &= ~kMachinePtyLed4; + break; + case 1: + pty->conf |= kMachinePtyLed1; + break; + case 2: + pty->conf |= kMachinePtyLed2; + break; + case 3: + pty->conf |= kMachinePtyLed3; + break; + case 4: + pty->conf |= kMachinePtyLed4; + break; + default: + break; + } +} + +static void MachinePtyReportCursorPosition(struct MachinePty *pty) { + char *p; + char buf[2 + 10 + 1 + 10 + 1]; + p = buf; + *p++ = '\e'; + *p++ = '['; + p += uint64toarray_radix10((pty->y + 1) & 0x7fff, p); + *p++ = ';'; + p += uint64toarray_radix10((pty->x + 1) & 0x7fff, p); + *p++ = 'R'; + MachinePtyWriteInput(pty, buf, p - buf); +} + +static void MachinePtyCsiN(struct MachinePty *pty) { + switch (MachinePtyAtoi(pty->esc.s, NULL)) { + case 5: + MachinePtyReportDeviceStatus(pty); + break; + case 6: + MachinePtyReportCursorPosition(pty); break; default: break; @@ -229,57 +747,96 @@ static void MachinePtySelectGraphicsRendition(struct MachinePty *pty) { switch (t) { case kSgr: switch (code[0]) { + case 38: + t = kSgrFg; + break; + case 48: + t = kSgrBg; + break; case 0: - pty->fg = 0; - pty->bg = 0; pty->pr = 0; + pty->xlat = MachinePtyGetXlatSgr(pty); break; case 1: pty->pr |= kMachinePtyBold; - break; - case 21: - pty->pr &= ~kMachinePtyBold; + pty->xlat = MachinePtyGetXlatSgr(pty); break; case 2: pty->pr |= kMachinePtyFaint; break; - case 22: - pty->pr &= ~kMachinePtyFaint; + case 3: + pty->pr |= kMachinePtyItalic; + pty->xlat = MachinePtyGetXlatSgr(pty); + break; + case 4: + pty->pr |= kMachinePtyUnder; + break; + case 5: + pty->pr |= kMachinePtyBlink; break; case 7: pty->pr |= kMachinePtyFlip; break; + case 8: + pty->pr |= kMachinePtyConceal; + break; + case 9: + pty->pr |= kMachinePtyStrike; + break; + case 20: + pty->pr |= kMachinePtyFraktur; + pty->xlat = MachinePtyGetXlatSgr(pty); + break; + case 21: + pty->pr |= kMachinePtyUnder | kMachinePtyDunder; + break; + case 22: + pty->pr &= ~(kMachinePtyFaint | kMachinePtyBold); + pty->xlat = MachinePtyGetXlatSgr(pty); + break; + case 23: + pty->pr &= ~kMachinePtyItalic; + pty->xlat = MachinePtyGetXlatSgr(pty); + break; + case 24: + pty->pr &= ~(kMachinePtyUnder | kMachinePtyDunder); + break; + case 25: + pty->pr &= ~kMachinePtyBlink; + break; case 27: pty->pr &= ~kMachinePtyFlip; break; + case 28: + pty->pr &= ~kMachinePtyConceal; + break; + case 29: + pty->pr &= ~kMachinePtyStrike; + break; + case 39: + pty->pr &= ~kMachinePtyFg; + break; + case 49: + pty->pr &= ~kMachinePtyBg; + break; case 90 ... 97: code[0] -= 90 - 30; code[0] += 8; + /* fallthrough */ case 30 ... 37: pty->fg = code[0] - 30; pty->pr |= kMachinePtyFg; pty->pr &= ~kMachinePtyTrue; break; - case 38: - t = kSgrFg; - break; - case 39: - pty->pr &= ~kMachinePtyFg; - break; case 100 ... 107: code[0] -= 100 - 40; code[0] += 8; + /* fallthrough */ case 40 ... 47: pty->bg = code[0] - 40; pty->pr |= kMachinePtyBg; pty->pr &= ~kMachinePtyTrue; break; - case 48: - t = kSgrBg; - break; - case 49: - pty->pr &= ~kMachinePtyBg; - break; default: break; } @@ -336,63 +893,53 @@ static void MachinePtySelectGraphicsRendition(struct MachinePty *pty) { } } -static void MachinePtyHideCursor(struct MachinePty *pty) { - pty->conf |= kMachinePtyNocursor; -} - -static void MachinePtyShowCursor(struct MachinePty *pty) { - pty->conf &= ~kMachinePtyNocursor; -} - -static void MachinePtyReportCursorPosition(struct MachinePty *pty) { - char *p; - char buf[2 + 10 + 1 + 10 + 1]; - p = buf; - *p++ = '\e'; - *p++ = '['; - p += uint64toarray_radix10((pty->y + 1) & 0xffffffff, p); - *p++ = ';'; - p += uint64toarray_radix10((pty->x + 1) & 0xffffffff, p); - *p++ = 'R'; - CONCAT(&pty->input.p, &pty->input.i, &pty->input.n, buf, p - buf); -} - -static void MachinePtyCsiN(struct MachinePty *pty) { - switch (atoi(pty->esc.s)) { - case 6: - MachinePtyReportCursorPosition(pty); - break; - default: - break; - } -} - -static void MachinePtyCsiScrollUp(struct MachinePty *pty) { - int n; - n = atoi(pty->esc.s); - n = MAX(1, n); - while (n--) { - MachinePtyScroll(pty); - } -} - static void MachinePtyCsi(struct MachinePty *pty) { switch (pty->esc.s[pty->esc.i - 1]) { - case 'A': - MachinePtyCursorMove(pty, -1, +0); - break; - case 'B': - MachinePtyCursorMove(pty, +1, +0); - break; - case 'C': - MachinePtyCursorMove(pty, +0, +1); - break; - case 'D': - MachinePtyCursorMove(pty, +0, -1); - break; case 'f': case 'H': - MachinePtyCursorPosition(pty); + MachinePtySetCursorPosition(pty); + break; + case 'G': + MachinePtySetCursorColumn(pty); + break; + case 'd': + MachinePtySetCursorRow(pty); + break; + case 'F': + pty->x = 0; + /* fallthrough */ + case 'A': + MachinePtyMoveCursor(pty, -1, +0); + break; + case 'E': + pty->x = 0; + /* fallthrough */ + case 'B': + MachinePtyMoveCursor(pty, +1, +0); + break; + case 'C': + MachinePtyMoveCursor(pty, +0, +1); + break; + case 'D': + MachinePtyMoveCursor(pty, +0, -1); + break; + case 'S': + MachinePtyScrollUp(pty); + break; + case 'T': + MachinePtyScrollDown(pty); + break; + case '@': + MachinePtyInsertCells(pty); + break; + case 'P': + MachinePtyDeleteCells(pty); + break; + case 'L': + MachinePtyInsertLines(pty); + break; + case 'M': + MachinePtyDeleteLines(pty); break; case 'J': MachinePtyEraseDisplay(pty); @@ -400,24 +947,52 @@ static void MachinePtyCsi(struct MachinePty *pty) { case 'K': MachinePtyEraseLine(pty); break; - case 'm': - MachinePtySelectGraphicsRendition(pty); + case 'X': + MachinePtyEraseCells(pty); + break; + case 's': + MachinePtySaveCursorPosition(pty); + break; + case 'u': + MachinePtyRestoreCursorPosition(pty); break; case 'n': MachinePtyCsiN(pty); break; - case 'S': - MachinePtyCsiScrollUp(pty); - break; - case 'l': - if (strcmp(pty->esc.s, "?25l") == 0) { - MachinePtyHideCursor(pty); - } + case 'm': + MachinePtySelectGraphicsRendition(pty); break; case 'h': - if (strcmp(pty->esc.s, "?25h") == 0) { - MachinePtyShowCursor(pty); - } + MachinePtySetMode(pty, true); + break; + case 'l': + MachinePtySetMode(pty, false); + break; + case 'c': + MachinePtyReportPreferredVtType(pty); + break; + case 'q': + MachinePtyLed(pty); + break; + default: + break; + } +} + +static void MachinePtyScreenAlignmentDisplay(struct MachinePty *pty) { + wmemset((void *)pty->wcs, 'E', pty->yn * pty->xn); +} + +static void MachinePtyEscHash(struct MachinePty *pty) { + switch (pty->esc.s[1]) { + case '5': + MachinePtySetXlat(pty, MachinePtyGetXlatAscii()); + break; + case '6': + MachinePtySetXlat(pty, MachinePtyGetXlatDoubleWidth()); + break; + case '8': + MachinePtyScreenAlignmentDisplay(pty); break; default: break; @@ -425,46 +1000,99 @@ static void MachinePtyCsi(struct MachinePty *pty) { } static void MachinePtyEsc(struct MachinePty *pty) { + switch (pty->esc.s[0]) { + case 'c': + MachinePtyFullReset(pty); + break; + case '7': + MachinePtySaveCursorPosition(pty); + break; + case '8': + MachinePtyRestoreCursorPosition(pty); + break; + case 'E': + pty->x = 0; + case 'D': + MachinePtyIndex(pty); + break; + case 'M': + MachinePtyReverseIndex(pty); + break; + case 'Z': + MachinePtyReportPreferredVtIdentity(pty); + break; + case '(': + MachinePtySetCodepage(pty, pty->esc.s[1]); + break; + case '#': + MachinePtyEscHash(pty); + break; + default: + break; + } +} + +static void MachinePtyCntrl(struct MachinePty *pty, int c01) { + switch (c01) { + case '\a': + MachinePtyBell(pty); + break; + case 0x85: + case '\f': + case '\v': + case '\n': + MachinePtyNewline(pty); + break; + case '\r': + MachinePtyCarriageReturn(pty); + break; + case '\e': + pty->state = kMachinePtyEsc; + pty->esc.i = 0; + break; + case '\t': + MachinePtyWriteTab(pty); + break; + case 0x7F: + case '\b': + pty->x = MAX(0, pty->x - 1); + break; + case 0x84: + MachinePtyIndex(pty); + break; + case 0x8D: + MachinePtyReverseIndex(pty); + break; + case 0x9B: + pty->state = kMachinePtyCsi; + break; + default: + break; + } } static void MachinePtyEscAppend(struct MachinePty *pty, char c) { pty->esc.i = MIN(pty->esc.i + 1, ARRAYLEN(pty->esc.s) - 1); pty->esc.s[pty->esc.i - 1] = c; - pty->esc.s[pty->esc.i - 0] = '\0'; + pty->esc.s[pty->esc.i - 0] = 0; } ssize_t MachinePtyWrite(struct MachinePty *pty, const void *data, size_t n) { int i; + wchar_t wc; const uint8_t *p; for (p = data, i = 0; i < n; ++i) { switch (pty->state) { case kMachinePtyAscii: - if (p[i] < 0b10000000) { - switch (p[i]) { - case '\e': - pty->state = kMachinePtyEsc; - pty->esc.i = 0; - break; - case '\t': - MachinePtyTab(pty); - break; - case '\r': - pty->x = 0; - break; - case '\n': - MachinePtyNewline(pty); - break; - case 0177: - case '\b': - pty->x = MAX(0, pty->x - 1); - break; - case '\f': - break; - case '\a': - break; - default: - SetMachinePtyCell(pty, p[i]); - break; + if (0x00 <= p[i] && p[i] <= 0x7F) { + if (0x20 <= p[i] && p[i] <= 0x7E) { + if ((wc = pty->xlat[p[i]]) >= 0) { + MachinePtyWriteGlyph(pty, wc, 1); + } else { + MachinePtyWriteGlyph(pty, -wc, 2); + } + } else { + MachinePtyCntrl(pty, p[i]); } } else if (!ThomPikeCont(p[i])) { pty->state = kMachinePtyUtf8; @@ -477,7 +1105,12 @@ ssize_t MachinePtyWrite(struct MachinePty *pty, const void *data, size_t n) { pty->u8 = ThomPikeMerge(pty->u8, p[i]); if (--pty->n8) break; } - SetMachinePtyCell(pty, pty->u8); + wc = pty->u8; + if ((0x00 <= wc && wc <= 0x1F) || (0x7F <= wc && wc <= 0x9F)) { + MachinePtyCntrl(pty, wc); + } else { + MachinePtyWriteGlyph(pty, wc, wcwidth(wc)); + } pty->state = kMachinePtyAscii; pty->u8 = 0; --i; @@ -528,7 +1161,7 @@ ssize_t MachinePtyWrite(struct MachinePty *pty, const void *data, size_t n) { } break; default: - abort(); + unreachable; } } return n; @@ -536,8 +1169,7 @@ ssize_t MachinePtyWrite(struct MachinePty *pty, const void *data, size_t n) { ssize_t MachinePtyWriteInput(struct MachinePty *pty, const void *data, size_t n) { - const char *p = data; - CONCAT(&pty->input.p, &pty->input.i, &pty->input.n, data, n); + MachinePtyConcatInput(pty, data, n); if (!(pty->conf & kMachinePtyNoecho)) { MachinePtyWrite(pty, data, n); } @@ -561,65 +1193,187 @@ ssize_t MachinePtyRead(struct MachinePty *pty, void *buf, size_t size) { return n; } -void MachinePtyAppendLine(struct MachinePty *pty, struct Buffer *buf, - unsigned y) { - bool atcursor; - uint32_t x, i, fg, bg, pr, wc, w; - if (y >= pty->yn) return; - for (fg = bg = pr = x = 0; x < pty->xn; x += w) { - i = y * pty->xn + x; - wc = pty->wcs[i]; - w = MAX(0, wcwidth(wc)); - atcursor = y == pty->y && x == pty->x; - if ((w && atcursor) || - (pty->prs[i] != pr || pty->fgs[i] != fg || pty->bgs[i] != bg)) { - fg = pty->fgs[i]; - bg = pty->bgs[i]; - pr = pty->prs[i]; - AppendStr(buf, "\e[0"); - if (w && atcursor) { - pr ^= kMachinePtyFlip; +static char *MachinePtyEncodeRgb(char *p, int rgb) { + *p++ = '2'; + *p++ = ';'; + p += uint64toarray_radix10((rgb & 0x0000ff) >> 000, p); + *p++ = ';'; + p += uint64toarray_radix10((rgb & 0x00ff00) >> 010, p); + *p++ = ';'; + p += uint64toarray_radix10((rgb & 0xff0000) >> 020, p); + return p; +} + +static char *MachinePtyEncodeXterm256(char *p, int xt) { + *p++ = '5'; + *p++ = ';'; + p += uint64toarray_radix10(xt, p); + return p; +} + +char *MachinePtyEncodeStyle(char *p, uint32_t xr, uint32_t pr, uint32_t fg, + uint32_t bg) { + *p++ = '\e'; + *p++ = '['; + if (pr & (kMachinePtyBold | kMachinePtyFaint | kMachinePtyFlip | + kMachinePtyUnder | kMachinePtyDunder | kMachinePtyBlink | + kMachinePtyStrike | kMachinePtyFg | kMachinePtyBg)) { + if (xr & (kMachinePtyBold | kMachinePtyFaint)) { + if ((xr & (kMachinePtyBold | kMachinePtyFaint)) ^ + (pr & (kMachinePtyBold | kMachinePtyFaint))) { + *p++ = '2'; + *p++ = '2'; + *p++ = ';'; } - if (pr & kMachinePtyBold) AppendStr(buf, ";1"); - if (pr & kMachinePtyFaint) AppendStr(buf, ";2"); - if (pr & kMachinePtyBlink) AppendStr(buf, ";5"); - if (pr & kMachinePtyFlip) AppendStr(buf, ";7"); - if (pr & kMachinePtyFg) { - if (pr & kMachinePtyTrue) { - AppendFmt(buf, ";38;2;%d;%d;%d", (fg & 0x0000ff) >> 000, - (fg & 0x00ff00) >> 010, (fg & 0xff0000) >> 020); - } else { - AppendFmt(buf, ";38;5;%d", fg); - } + if (pr & kMachinePtyBold) { + *p++ = '1'; + *p++ = ';'; } - if (pr & kMachinePtyBg) { - if (pr & kMachinePtyTrue) { - AppendFmt(buf, ";48;2;%d;%d;%d", (bg & 0x0000ff) >> 000, - (bg & 0x00ff00) >> 010, (bg & 0xff0000) >> 020); - } else { - AppendFmt(buf, ";48;5;%d", bg); - } + if (pr & kMachinePtyFaint) { + *p++ = '2'; + *p++ = ';'; } - AppendStr(buf, "m"); } - if (w) { - AppendWide(buf, wc); - } else { - w = 1; - if (atcursor) { - if (!(pty->conf & kMachinePtyNocursor)) { - if (pty->conf & kMachinePtyBlinkcursor) { - AppendStr(buf, "\e[5m"); - } - AppendWide(buf, u'▂'); - if (pty->conf & kMachinePtyBlinkcursor) { - AppendStr(buf, "\e[25m"); - } + if (xr & (kMachinePtyUnder | kMachinePtyDunder)) { + if ((xr & (kMachinePtyUnder | kMachinePtyDunder)) ^ + (pr & (kMachinePtyUnder | kMachinePtyDunder))) { + *p++ = '2'; + *p++ = '4'; + *p++ = ';'; + } + if (pr & kMachinePtyUnder) { + *p++ = '4'; + *p++ = ';'; + } + if (pr & kMachinePtyDunder) { + *p++ = '2'; + *p++ = '1'; + *p++ = ';'; + } + } + if (xr & (kMachinePtyFlip | kMachinePtyBlink | kMachinePtyStrike)) { + if (xr & kMachinePtyFlip) { + if (!(pr & kMachinePtyFlip)) *p++ = '2'; + *p++ = '7'; + *p++ = ';'; + } + if (xr & kMachinePtyBlink) { + if (!(pr & kMachinePtyBlink)) *p++ = '2'; + *p++ = '5'; + *p++ = ';'; + } + if (xr & kMachinePtyStrike) { + if (!(pr & kMachinePtyStrike)) *p++ = '2'; + *p++ = '9'; + *p++ = ';'; + } + } + if (xr & (kMachinePtyFg | kMachinePtyTrue)) { + *p++ = '3'; + if (pr & kMachinePtyFg) { + *p++ = '8'; + *p++ = ';'; + if (pr & kMachinePtyTrue) { + p = MachinePtyEncodeRgb(p, fg); + } else { + p = MachinePtyEncodeXterm256(p, fg); } } else { - AppendChar(buf, ' '); + *p++ = '9'; + } + *p++ = ';'; + } + if (xr & (kMachinePtyBg | kMachinePtyTrue)) { + *p++ = '4'; + if (pr & kMachinePtyBg) { + *p++ = '8'; + *p++ = ';'; + if (pr & kMachinePtyTrue) { + p = MachinePtyEncodeRgb(p, bg); + } else { + p = MachinePtyEncodeXterm256(p, bg); + } + } else { + *p++ = '9'; + } + *p++ = ';'; + } + DCHECK_EQ(';', p[-1]); + p[-1] = 'm'; + } else { + *p++ = '0'; + *p++ = 'm'; + } + return p; +} + +void MachinePtyAppendLine(struct MachinePty *pty, struct Buffer *buf, + unsigned y) { + uint64_t u; + size_t need; + char *p, *pb; + uint32_t i, j, w, wc, np, xp, pr, fg, bg, ci; + if (y >= pty->yn) return; + need = buf->i + pty->xn * 60; + if (need > buf->n) { + CHECK_NOTNULL((buf->p = realloc(buf->p, need))); + buf->n = need; + } + i = y * pty->xn; + j = (y + 1) * pty->xn; + pb = buf->p + buf->i; + ci = !(pty->conf & kMachinePtyNocursor) && y == pty->y ? i + pty->x : -1; + for (pr = 0; i < j; i += w) { + np = pty->prs[i]; + if (!(np & kMachinePtyConceal)) { + wc = pty->wcs[i]; + DCHECK(!(0x00 <= wc && wc <= 0x1F)); + DCHECK(!(0x7F <= wc && wc <= 0x9F)); + if (0x20 <= wc && wc <= 0x7E) { + u = wc; + w = 1; + } else { + u = tpenc(wc); + w = max(1, wcwidth(wc)); + } + } else { + u = ' '; + w = 1; + } + if (i == ci) { + if (u != ' ') { + np ^= kMachinePtyFlip; + } else { + u = tpenc(u'▂'); + if (pty->conf & kMachinePtyBlinkcursor) { + np |= kMachinePtyBlink; + } } } + fg = bg = -1; + xp = pr ^ np; + if (np & (kMachinePtyFg | kMachinePtyBg)) { + if (np & kMachinePtyFg) { + if (pty->fgs[i] != fg) xp |= kMachinePtyFg; + fg = pty->fgs[i]; + } + if (np & kMachinePtyBg) { + if (pty->bgs[i] != bg) xp |= kMachinePtyBg; + bg = pty->bgs[i]; + } + } + p = pb; + if (xp) { + pr = np; + p = MachinePtyEncodeStyle(p, xp, pr, fg, bg); + } + do { + *p++ = u & 0xFF; + u >>= 8; + } while (u); + DCHECK_LE(p - pb, 60); + pb = p; } - AppendStr(buf, "\e[0m"); + DCHECK_LE(pb - buf->p, buf->n); + buf->i = pb - buf->p; } diff --git a/tool/build/lib/pty.h b/tool/build/lib/pty.h index 4e91f22a..e4918d55 100644 --- a/tool/build/lib/pty.h +++ b/tool/build/lib/pty.h @@ -2,19 +2,31 @@ #define COSMOPOLITAN_TOOL_BUILD_LIB_PTY_H_ #include "tool/build/lib/buffer.h" -#define kMachinePtyFg 0x01 -#define kMachinePtyBg 0x02 -#define kMachinePtyTrue 0x04 -#define kMachinePtyBold 0x08 -#define kMachinePtyFaint 0x10 -#define kMachinePtyFlip 0x20 -#define kMachinePtyBlink 0x40 +#define kMachinePtyFg 0x0001 +#define kMachinePtyBg 0x0002 +#define kMachinePtyBold 0x0004 +#define kMachinePtyFlip 0x0008 +#define kMachinePtyFaint 0x0010 +#define kMachinePtyUnder 0x0020 +#define kMachinePtyDunder 0x0040 +#define kMachinePtyTrue 0x0080 +#define kMachinePtyBlink 0x0100 +#define kMachinePtyItalic 0x0200 +#define kMachinePtyFraktur 0x0400 +#define kMachinePtyStrike 0x0800 +#define kMachinePtyConceal 0x1000 -#define kMachinePtyNocursor 0x01 -#define kMachinePtyBlinkcursor 0x02 -#define kMachinePtyNocanon 0x04 -#define kMachinePtyNoecho 0x08 -#define kMachinePtyNoopost 0x10 +#define kMachinePtyBell 0x001 +#define kMachinePtyRedzone 0x002 +#define kMachinePtyNocursor 0x004 +#define kMachinePtyBlinkcursor 0x008 +#define kMachinePtyNocanon 0x010 +#define kMachinePtyNoecho 0x020 +#define kMachinePtyNoopost 0x040 +#define kMachinePtyLed1 0x080 +#define kMachinePtyLed2 0x100 +#define kMachinePtyLed3 0x200 +#define kMachinePtyLed4 0x400 #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ @@ -24,16 +36,18 @@ struct MachinePty { int x; int yn; int xn; + uint32_t u8; + uint32_t n8; uint32_t pr; uint32_t fg; uint32_t bg; - uint32_t u8; - uint32_t n8; + uint32_t conf; + uint32_t save; uint32_t *wcs; + uint32_t *prs; uint32_t *fgs; uint32_t *bgs; - uint32_t *prs; - uint32_t conf; + wchar_t *xlat; enum MachinePtyState { kMachinePtyAscii, kMachinePtyUtf8, @@ -57,6 +71,11 @@ ssize_t MachinePtyRead(struct MachinePty *, void *, size_t); ssize_t MachinePtyWrite(struct MachinePty *, const void *, size_t); ssize_t MachinePtyWriteInput(struct MachinePty *, const void *, size_t); void MachinePtyAppendLine(struct MachinePty *, struct Buffer *, unsigned); +void MachinePtyFullReset(struct MachinePty *); +void MachinePtyMemmove(struct MachinePty *, long, long, long); +void MachinePtyErase(struct MachinePty *, long, long); +void MachinePtySetY(struct MachinePty *, int); +void MachinePtySetX(struct MachinePty *, int); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/tool/build/lib/reset.c b/tool/build/lib/reset.c index 3192c4ef..65376a06 100644 --- a/tool/build/lib/reset.c +++ b/tool/build/lib/reset.c @@ -60,17 +60,9 @@ static void ResetSse(struct Machine *m) { m->sse.rc = RINT; m->sse.ftz = false; memset(m->xmm, 0, sizeof(m->xmm)); - memset(m->xmmtype, 0, sizeof(m->xmmtype)); -} - -static void ResetTlb(struct Machine *m) { - m->tlbindex = 0; - memset(m->tlb, 0, sizeof(m->tlb)); } void ResetCpu(struct Machine *m) { - m->codevirt = 0; - m->codereal = NULL; m->faultaddr = 0; m->stashsize = 0; m->stashaddr = 0; @@ -89,7 +81,13 @@ void ResetCpu(struct Machine *m) { memset(m->reg, 0, sizeof(m->reg)); memset(m->bofram, 0, sizeof(m->bofram)); memset(&m->freelist, 0, sizeof(m->freelist)); - ResetTlb(m); ResetSse(m); ResetFpu(m); } + +void ResetTlb(struct Machine *m) { + m->tlbindex = 0; + memset(m->tlb, 0, sizeof(m->tlb)); + m->codevirt = 0; + m->codehost = NULL; +} diff --git a/tool/build/lib/sse.c b/tool/build/lib/sse.c index eb5f6da5..a26c835f 100644 --- a/tool/build/lib/sse.c +++ b/tool/build/lib/sse.c @@ -94,7 +94,6 @@ #include "libc/intrin/punpcklwd.h" #include "libc/intrin/pxor.h" #include "libc/macros.h" -#include "libc/str/str.h" #include "tool/build/lib/case.h" #include "tool/build/lib/machine.h" #include "tool/build/lib/memory.h" diff --git a/tool/build/lib/ssefloat.c b/tool/build/lib/ssefloat.c index d1b23525..44e95135 100644 --- a/tool/build/lib/ssefloat.c +++ b/tool/build/lib/ssefloat.c @@ -24,6 +24,7 @@ #include "libc/intrin/pshufw.h" #include "libc/intrin/shufpd.h" #include "libc/intrin/shufps.h" +#include "libc/log/log.h" #include "libc/macros.h" #include "libc/math.h" #include "libc/str/str.h" @@ -387,33 +388,26 @@ static void VspsdWspsd(struct Machine *m, uint32_t rde, double_v opd(struct Machine *, double_v, double_v)) { float_v xf, yf; double_v xd, yd; - switch (Rep(rde) | Osz(rde)) { - case 0: - memcpy(&yf, GetModrmRegisterXmmPointerRead16(m, rde), 16); - memcpy(&xf, XmmRexrReg(m, rde), 16); - xf = opf(m, xf, yf); - memcpy(XmmRexrReg(m, rde), &xf, 16); - break; - case 1: - memcpy(&yd, GetModrmRegisterXmmPointerRead16(m, rde), 16); - memcpy(&xd, XmmRexrReg(m, rde), 16); - xd = opd(m, xd, yd); - memcpy(XmmRexrReg(m, rde), &xd, 16); - break; - case 2: - memcpy(&yd, GetModrmRegisterXmmPointerRead8(m, rde), 8); - memcpy(&xd, XmmRexrReg(m, rde), 8); - xd = opd(m, xd, yd); - memcpy(XmmRexrReg(m, rde), &xd, 8); - break; - case 3: - memcpy(&yf, GetModrmRegisterXmmPointerRead4(m, rde), 4); - memcpy(&xf, XmmRexrReg(m, rde), 4); - xf = opf(m, xf, yf); - memcpy(XmmRexrReg(m, rde), &xf, 4); - break; - default: - unreachable; + if (Rep(rde) == 2) { + memcpy(&yd, GetModrmRegisterXmmPointerRead8(m, rde), 8); + memcpy(&xd, XmmRexrReg(m, rde), 8); + xd = opd(m, xd, yd); + memcpy(XmmRexrReg(m, rde), &xd, 8); + } else if (Rep(rde) == 3) { + memcpy(&yf, GetModrmRegisterXmmPointerRead4(m, rde), 4); + memcpy(&xf, XmmRexrReg(m, rde), 4); + xf = opf(m, xf, yf); + memcpy(XmmRexrReg(m, rde), &xf, 4); + } else if (Osz(rde)) { + memcpy(&yd, GetModrmRegisterXmmPointerRead16(m, rde), 16); + memcpy(&xd, XmmRexrReg(m, rde), 16); + xd = opd(m, xd, yd); + memcpy(XmmRexrReg(m, rde), &xd, 16); + } else { + memcpy(&yf, GetModrmRegisterXmmPointerRead16(m, rde), 16); + memcpy(&xf, XmmRexrReg(m, rde), 16); + xf = opf(m, xf, yf); + memcpy(XmmRexrReg(m, rde), &xf, 16); } } diff --git a/tool/build/lib/ssemov.c b/tool/build/lib/ssemov.c index a56b39b3..86c69bf8 100644 --- a/tool/build/lib/ssemov.c +++ b/tool/build/lib/ssemov.c @@ -172,7 +172,12 @@ static void MovqVqWq(struct Machine *m, uint32_t rde) { } static void MovssVpsWps(struct Machine *m, uint32_t rde) { - memcpy(XmmRexrReg(m, rde), GetModrmRegisterXmmPointerRead4(m, rde), 4); + if (IsModrmRegister(rde)) { + memcpy(XmmRexrReg(m, rde), XmmRexbRm(m, rde), 4); + } else { + memcpy(XmmRexrReg(m, rde), ComputeReserveAddressRead4(m, rde), 4); + memset(XmmRexrReg(m, rde) + 4, 0, 12); + } } static void MovssWpsVps(struct Machine *m, uint32_t rde) { @@ -180,11 +185,16 @@ static void MovssWpsVps(struct Machine *m, uint32_t rde) { } static void MovsdVpsWps(struct Machine *m, uint32_t rde) { - memcpy(XmmRexrReg(m, rde), GetModrmRegisterXmmPointerRead16(m, rde), 8); + if (IsModrmRegister(rde)) { + memcpy(XmmRexrReg(m, rde), XmmRexbRm(m, rde), 8); + } else { + memcpy(XmmRexrReg(m, rde), ComputeReserveAddressRead8(m, rde), 8); + memset(XmmRexrReg(m, rde) + 8, 0, 8); + } } static void MovsdWpsVps(struct Machine *m, uint32_t rde) { - memcpy(GetModrmRegisterXmmPointerWrite16(m, rde), XmmRexrReg(m, rde), 8); + memcpy(GetModrmRegisterXmmPointerWrite8(m, rde), XmmRexrReg(m, rde), 8); } static void MovhlpsVqUq(struct Machine *m, uint32_t rde) { diff --git a/tool/build/lib/syscall.c b/tool/build/lib/syscall.c index 3cc81b7d..30f9122c 100644 --- a/tool/build/lib/syscall.c +++ b/tool/build/lib/syscall.c @@ -34,6 +34,8 @@ #include "libc/log/log.h" #include "libc/macros.h" #include "libc/mem/mem.h" +#include "libc/nexgen32e/vendor.h" +#include "libc/runtime/gc.h" #include "libc/runtime/runtime.h" #include "libc/sock/sock.h" #include "libc/str/str.h" @@ -65,6 +67,7 @@ #include "libc/x/x.h" #include "tool/build/lib/case.h" #include "tool/build/lib/endian.h" +#include "tool/build/lib/iovs.h" #include "tool/build/lib/machine.h" #include "tool/build/lib/memory.h" #include "tool/build/lib/pml4t.h" @@ -93,8 +96,8 @@ const struct MachineFdCb kMachineFdCbHost = { .close = close, - .read = read, - .write = write, + .readv = readv, + .writev = writev, .ioctl = ioctl, }; @@ -281,10 +284,10 @@ static int XlatTcp(int x) { switch (x) { XLAT(1, TCP_NODELAY); XLAT(2, TCP_MAXSEG); - XLAT(23, TCP_FASTOPEN); XLAT(4, TCP_KEEPIDLE); XLAT(5, TCP_KEEPINTVL); XLAT(6, TCP_KEEPCNT); + XLAT(23, TCP_FASTOPEN); default: return x; } @@ -398,18 +401,58 @@ static int XlatRusage(int x) { } } -static void *VirtualSendRead(struct Machine *m, void *dst, int64_t addr, - uint64_t n) { +static void VirtualSendRead(struct Machine *m, void *dst, int64_t addr, + uint64_t n) { VirtualSend(m, dst, addr, n); SetReadAddr(m, addr, n); - return dst; } -static void *VirtualRecvWrite(struct Machine *m, int64_t addr, void *dst, - uint64_t n) { - VirtualRecv(m, addr, dst, n); +static void VirtualRecvWrite(struct Machine *m, int64_t addr, void *src, + uint64_t n) { + VirtualRecv(m, addr, src, n); SetWriteAddr(m, addr, n); - return dst; +} + +static int AppendIovsReal(struct Machine *m, struct Iovs *ib, int64_t addr, + size_t size) { + void *real; + size_t have; + unsigned got; + while (size) { + if (!(real = FindReal(m, addr))) return efault(); + have = 0x1000 - (addr & 0xfff); + got = MIN(size, have); + if (AppendIovs(ib, real, got) == -1) return -1; + addr += got; + size -= got; + } + return 0; +} + +static int AppendIovsGuest(struct Machine *m, struct Iovs *iv, int64_t iovaddr, + long iovlen) { + int rc; + long i, iovsize; + struct iovec *guestiovs; + if (!__builtin_mul_overflow(iovlen, sizeof(struct iovec), &iovsize) && + (0 <= iovsize && iovsize <= 0x7ffff000)) { + if ((guestiovs = malloc(iovsize))) { + VirtualSendRead(m, guestiovs, iovaddr, iovsize); + for (rc = i = 0; i < iovlen; ++i) { + if (AppendIovsReal(m, iv, (intptr_t)guestiovs[i].iov_base, + guestiovs[i].iov_len) == -1) { + rc = -1; + break; + } + } + free(guestiovs); + } else { + rc = enomem(); + } + } else { + rc = eoverflow(); + } + return rc; } static struct sigaction *CoerceSigactionToCosmo( @@ -463,44 +506,52 @@ static int OpMadvise(struct Machine *m, int64_t addr, size_t length, } static int64_t OpBrk(struct Machine *m, int64_t addr) { - void *real; - if (addr && addr != m->brk) { - if (addr < m->brk) { - addr = ROUNDUP(addr, FRAMESIZE); - FreePml4t(m->cr3, addr, m->brk - addr, free, munmap); + addr = ROUNDUP(addr, PAGESIZE); + if (addr > m->brk) { + if (ReserveVirtual(m, m->brk, addr - m->brk) != -1) { + m->brk = addr; + } + } else if (addr < m->brk) { + if (FreeVirtual(m, addr, m->brk - addr) != -1) { m->brk = addr; - } else { - addr = ROUNDUP(addr, FRAMESIZE); - if ((real = mmap(NULL, addr - m->brk, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)) != MAP_FAILED) { - CHECK_NE(-1, RegisterMemory(m, m->brk, real, addr - m->brk)); - m->brk = addr; - } } } return m->brk; } static int64_t OpMmap(struct Machine *m, int64_t virt, size_t size, int prot, - int flags, int fd, int64_t off) { - void *real; + int flags, int fd, int64_t offset) { + void *tmp; + LOGF("MMAP%s %p %,ld %#x %#x %d %#lx", IsGenuineCosmo() ? " SIMULATED" : "", + virt, size, prot, flags, fd, offset); flags = XlatMapFlags(flags); if (fd != -1 && (fd = XlatFd(m, fd)) == -1) return -1; - real = mmap(NULL, size, prot, flags & ~MAP_FIXED, fd, off); - if (real == MAP_FAILED) return -1; if (!(flags & MAP_FIXED)) { - if (0 <= virt && virt < 0x400000) virt = 0x400000; - if ((virt = FindPml4t(m->cr3, virt, size)) == -1) return -1; + if (!virt) { + if ((virt = FindVirtual(m, m->brk, size)) == -1) return -1; + m->brk = virt + size; + } else { + if ((virt = FindVirtual(m, virt, size)) == -1) return -1; + } + } + if (ReserveVirtual(m, virt, size) == -1) return -1; + if (fd != -1 && !(flags & MAP_ANONYMOUS)) { + /* TODO: lazy page loading */ + CHECK_NOTNULL((tmp = malloc(size))); + CHECK_EQ(size, pread(fd, tmp, size, offset)); + VirtualRecvWrite(m, virt, tmp, size); + free(tmp); } - CHECK_NE(-1, RegisterMemory(m, virt, real, size)); return virt; } static int OpMunmap(struct Machine *m, int64_t addr, uint64_t size) { - return FreePml4t(m->cr3, addr, size, free, munmap); + return FreeVirtual(m, addr, size); } static int OpMsync(struct Machine *m, int64_t virt, size_t size, int flags) { + return enosys(); +#if 0 size_t i; void *page; virt = ROUNDDOWN(virt, 4096); @@ -510,42 +561,7 @@ static int OpMsync(struct Machine *m, int64_t virt, size_t size, int flags) { if (msync(page, 4096, flags) == -1) return -1; } return 0; -} - -static void *GetDirectBuf(struct Machine *m, int64_t addr, size_t *size) { - void *page; - *size = MIN(*size, 0x1000 - (addr & 0xfff)); - if (!(page = FindReal(m, addr))) return MAP_FAILED; - return page; -} - -static struct iovec *GetDirectIov(struct Machine *m, int64_t addr, int *len) { - int i; - size_t n, size; - struct iovec *iov; - if (!__builtin_mul_overflow(sizeof(*iov), *len, &n) && n <= 0x7ffff000) { - if ((iov = malloc(n))) { - VirtualSendRead(m, iov, addr, n); - for (i = 0; i < *len; ++i) { - size = iov[i].iov_len; - if ((iov[i].iov_base = GetDirectBuf( - m, (int64_t)(intptr_t)iov[i].iov_base, &size)) == MAP_FAILED) { - free(iov); - return (struct iovec *)efault(); - } - if (size < iov[i].iov_len) { - iov[i].iov_len = size; - *len = i + 1; - break; - } - } - return iov; - } else { - return (struct iovec *)-1; - } - } else { - return (struct iovec *)eoverflow(); - } +#endif } static int OpClose(struct Machine *m, int fd) { @@ -575,36 +591,39 @@ static int OpOpenat(struct Machine *m, int dirfd, int64_t path, int flags, } static int OpPipe(struct Machine *m, int64_t pipefds_addr) { - void *p[2]; - uint8_t b[8]; - int rc, i, j, *pipefds; - if ((i = MachineFdAdd(&m->fds)) == -1) return -1; - if ((j = MachineFdAdd(&m->fds)) == -1) return -1; - if ((rc = pipe((pipefds = BeginStoreNp(m, pipefds_addr, 8, p, b)))) != -1) { - EndStoreNp(m, pipefds_addr, 8, p, b); - m->fds.p[i].cb = &kMachineFdCbHost; - m->fds.p[i].fd = pipefds[0]; - m->fds.p[j].cb = &kMachineFdCbHost; - m->fds.p[j].fd = pipefds[1]; - } else { + int i, j, pipefds[2]; + if ((i = MachineFdAdd(&m->fds)) != -1) { + if ((j = MachineFdAdd(&m->fds)) != -1) { + if (pipe(pipefds) != -1) { + m->fds.p[i].cb = &kMachineFdCbHost; + m->fds.p[i].fd = pipefds[0]; + m->fds.p[j].cb = &kMachineFdCbHost; + m->fds.p[j].fd = pipefds[1]; + pipefds[0] = i; + pipefds[1] = j; + VirtualRecvWrite(m, pipefds_addr, pipefds, sizeof(pipefds)); + return 0; + } + MachineFdRemove(&m->fds, j); + } MachineFdRemove(&m->fds, i); - MachineFdRemove(&m->fds, j); } - return rc; + return -1; } static int OpDup(struct Machine *m, int fd) { - int i, rc; - if ((fd = XlatFd(m, fd)) == -1) return -1; - if ((i = MachineFdAdd(&m->fds)) == -1) return -1; - if ((rc = dup(fd)) != -1) { - m->fds.p[i].cb = &kMachineFdCbHost; - m->fds.p[i].fd = rc; - rc = i; - } else { - MachineFdRemove(&m->fds, i); + int i; + if ((fd = XlatFd(m, fd)) != -1) { + if ((i = MachineFdAdd(&m->fds)) != -1) { + if ((fd = dup(fd)) != -1) { + m->fds.p[i].cb = &kMachineFdCbHost; + m->fds.p[i].fd = fd; + return i; + } + MachineFdRemove(&m->fds, i); + } } - return rc; + return -1; } static int OpDup2(struct Machine *m, int fd, int newfd) { @@ -708,24 +727,70 @@ static int OpSetsockopt(struct Machine *m, int fd, int level, int optname, } static ssize_t OpRead(struct Machine *m, int fd, int64_t addr, size_t size) { - void *data; ssize_t rc; - if (!(0 <= fd && fd < m->fds.i) || !m->fds.p[fd].cb) return ebadf(); - if ((data = GetDirectBuf(m, addr, &size)) == MAP_FAILED) return efault(); - if ((rc = m->fds.p[fd].cb->read(m->fds.p[fd].fd, data, size)) != -1) { - SetWriteAddr(m, addr, rc); + struct Iovs iv; + InitIovs(&iv); + if ((0 <= fd && fd < m->fds.i) && m->fds.p[fd].cb) { + if ((rc = AppendIovsReal(m, &iv, addr, size)) != -1) { + if ((rc = m->fds.p[fd].cb->readv(m->fds.p[fd].fd, iv.p, iv.i)) != -1) { + SetWriteAddr(m, addr, rc); + } + } + } else { + rc = ebadf(); } + FreeIovs(&iv); + return rc; +} + +static ssize_t OpPread(struct Machine *m, int fd, int64_t addr, size_t size, + int64_t offset) { + ssize_t rc; + struct Iovs iv; + InitIovs(&iv); + if ((rc = XlatFd(m, fd)) != -1) { + fd = rc; + if ((rc = AppendIovsReal(m, &iv, addr, size)) != -1) { + if ((rc = preadv(fd, iv.p, iv.i, offset)) != -1) { + SetWriteAddr(m, addr, rc); + } + } + } + FreeIovs(&iv); return rc; } static ssize_t OpWrite(struct Machine *m, int fd, int64_t addr, size_t size) { - void *data; ssize_t rc; - if (!(0 <= fd && fd < m->fds.i) || !m->fds.p[fd].cb) return ebadf(); - if ((data = GetDirectBuf(m, addr, &size)) == MAP_FAILED) return efault(); - if ((rc = m->fds.p[fd].cb->write(m->fds.p[fd].fd, data, size)) != -1) { - SetReadAddr(m, addr, size); + struct Iovs iv; + InitIovs(&iv); + if ((0 <= fd && fd < m->fds.i) && m->fds.p[fd].cb) { + if ((rc = AppendIovsReal(m, &iv, addr, size)) != -1) { + if ((rc = m->fds.p[fd].cb->writev(m->fds.p[fd].fd, iv.p, iv.i)) != -1) { + SetReadAddr(m, addr, rc); + } + } + } else { + rc = ebadf(); } + FreeIovs(&iv); + return rc; +} + +static ssize_t OpPwrite(struct Machine *m, int fd, int64_t addr, size_t size, + int64_t offset) { + ssize_t rc; + struct Iovs iv; + InitIovs(&iv); + if ((rc = XlatFd(m, fd)) != -1) { + fd = rc; + if ((rc = AppendIovsReal(m, &iv, addr, size)) != -1) { + if ((rc = pwritev(fd, iv.p, iv.i, offset)) != -1) { + SetReadAddr(m, addr, rc); + } + } + } + FreeIovs(&iv); return rc; } @@ -746,9 +811,9 @@ static int IoctlTcgets(struct Machine *m, int fd, int64_t addr, if ((rc = fn(fd, TCGETS, &tio)) != -1) { memcpy(&tio2, &tio, sizeof(tio)); tio2.c_iflag = 0; - if (tio.c_iflag & ISIG) tio2.c_iflag |= ISIG_LINUX; - if (tio.c_iflag & ICANON) tio2.c_iflag |= ICANON_LINUX; - if (tio.c_iflag & ECHO) tio2.c_iflag |= ECHO_LINUX; + if (tio.c_lflag & ISIG) tio2.c_lflag |= ISIG_LINUX; + if (tio.c_lflag & ICANON) tio2.c_lflag |= ICANON_LINUX; + if (tio.c_lflag & ECHO) tio2.c_lflag |= ECHO_LINUX; tio2.c_oflag = 0; if (tio.c_oflag & OPOST) tio2.c_oflag |= OPOST_LINUX; VirtualRecvWrite(m, addr, &tio2, sizeof(tio2)); @@ -762,9 +827,9 @@ static int IoctlTcsets(struct Machine *m, int fd, int64_t request, int64_t addr, VirtualSendRead(m, &tio, addr, sizeof(tio)); memcpy(&tio2, &tio, sizeof(tio)); tio2.c_iflag = 0; - if (tio.c_iflag & ISIG_LINUX) tio2.c_iflag |= ISIG; - if (tio.c_iflag & ICANON_LINUX) tio2.c_iflag |= ICANON; - if (tio.c_iflag & ECHO_LINUX) tio2.c_iflag |= ECHO; + if (tio.c_lflag & ISIG_LINUX) tio2.c_lflag |= ISIG; + if (tio.c_lflag & ICANON_LINUX) tio2.c_lflag |= ICANON; + if (tio.c_lflag & ECHO_LINUX) tio2.c_lflag |= ECHO; tio2.c_oflag = 0; if (tio.c_oflag & OPOST_LINUX) tio2.c_oflag |= OPOST; return fn(fd, request, &tio2); @@ -791,44 +856,34 @@ static int OpIoctl(struct Machine *m, int fd, uint64_t request, int64_t addr) { } } -static ssize_t OpPread(struct Machine *m, int fd, int64_t addr, size_t size, - int64_t offset) { - void *data; - ssize_t rc; - if ((fd = XlatFd(m, fd)) == -1) return -1; - if ((data = GetDirectBuf(m, addr, &size)) == MAP_FAILED) return efault(); - if ((rc = pread(fd, data, size, offset)) != -1) SetWriteAddr(m, addr, rc); - return rc; -} - -static ssize_t OpPwrite(struct Machine *m, int fd, int64_t addr, size_t size, - int64_t offset) { - void *data; - ssize_t rc; - if ((fd = XlatFd(m, fd)) == -1) return -1; - if ((data = GetDirectBuf(m, addr, &size)) == MAP_FAILED) return efault(); - if ((rc = pwrite(fd, data, size, offset)) != -1) SetReadAddr(m, addr, size); - return rc; -} - static ssize_t OpReadv(struct Machine *m, int fd, int64_t iovaddr, int iovlen) { ssize_t rc; - struct iovec *iov; - if ((fd = XlatFd(m, fd)) == -1) return -1; - if ((iov = GetDirectIov(m, iovaddr, &iovlen)) == MAP_FAILED) return -1; - rc = readv(fd, iov, iovlen); - free(iov); + struct Iovs iv; + InitIovs(&iv); + if ((0 <= fd && fd < m->fds.i) && m->fds.p[fd].cb) { + if ((rc = AppendIovsGuest(m, &iv, iovaddr, iovlen)) != -1) { + rc = m->fds.p[fd].cb->readv(m->fds.p[fd].fd, iv.p, iv.i); + } + } else { + rc = ebadf(); + } + FreeIovs(&iv); return rc; } static ssize_t OpWritev(struct Machine *m, int fd, int64_t iovaddr, int iovlen) { ssize_t rc; - struct iovec *iov; - if ((fd = XlatFd(m, fd)) == -1) return -1; - if ((iov = GetDirectIov(m, iovaddr, &iovlen)) == MAP_FAILED) return -1; - rc = writev(fd, iov, iovlen); - free(iov); + struct Iovs iv; + InitIovs(&iv); + if ((0 <= fd && fd < m->fds.i) && m->fds.p[fd].cb) { + if ((rc = AppendIovsGuest(m, &iv, iovaddr, iovlen)) != -1) { + rc = m->fds.p[fd].cb->writev(m->fds.p[fd].fd, iv.p, iv.i); + } + } else { + rc = ebadf(); + } + FreeIovs(&iv); return rc; } @@ -850,33 +905,25 @@ static int OpFaccessat(struct Machine *m, int dirfd, int64_t path, int mode, return faccessat(dirfd, LoadStr(m, path), mode, flags); } -static int OpFstatat(struct Machine *m, int dirfd, int64_t path, int64_t st, +static int OpFstatat(struct Machine *m, int dirfd, int64_t path, int64_t staddr, int flags) { int rc; - void *stp[2]; - uint8_t *stbuf; + struct stat st; flags = XlatAtf(flags); if ((dirfd = XlatAfd(m, dirfd)) == -1) return -1; - if (!(stbuf = malloc(sizeof(struct stat)))) return enomem(); - if ((rc = fstatat(dirfd, LoadStr(m, path), - BeginStoreNp(m, st, sizeof(stbuf), stp, stbuf), flags)) != - -1) { - EndStoreNp(m, st, sizeof(stbuf), stp, stbuf); + if ((rc = fstatat(dirfd, LoadStr(m, path), &st, flags)) != -1) { + VirtualRecvWrite(m, staddr, &st, sizeof(struct stat)); } - free(stbuf); return rc; } -static int OpFstat(struct Machine *m, int fd, int64_t st) { +static int OpFstat(struct Machine *m, int fd, int64_t staddr) { int rc; - void *stp[2]; - uint8_t *stbuf; + struct stat st; if ((fd = XlatFd(m, fd)) == -1) return -1; - if (!(stbuf = malloc(sizeof(struct stat)))) return enomem(); - if ((rc = fstat(fd, BeginStoreNp(m, st, sizeof(stbuf), stp, stbuf))) != -1) { - EndStoreNp(m, st, sizeof(stbuf), stp, stbuf); + if ((rc = fstat(fd, &st)) != -1) { + VirtualRecvWrite(m, staddr, &st, sizeof(struct stat)); } - free(stbuf); return rc; } @@ -1091,21 +1138,34 @@ static int OpGettimeofday(struct Machine *m, int64_t tv, int64_t tz) { static int OpPoll(struct Machine *m, int64_t fdsaddr, uint64_t nfds, int32_t timeout_ms) { - int rc; - size_t n; - struct pollfd *fds; - if (!__builtin_mul_overflow(sizeof(*fds), nfds, &n) && n <= 0x7ffff000) { - if ((fds = malloc(n))) { - VirtualSendRead(m, fds, fdsaddr, n); - rc = poll(fds, nfds, timeout_ms); - free(fds); - return rc; + int count, i; + uint64_t fdssize; + struct pollfd *hostfds, *guestfds; + if (!__builtin_mul_overflow(nfds, sizeof(struct pollfd), &fdssize) && + fdssize <= 0x7ffff000) { + hostfds = malloc(fdssize); + guestfds = malloc(fdssize); + if (hostfds && guestfds) { + VirtualSendRead(m, guestfds, fdsaddr, fdssize); + memcpy(hostfds, guestfds, fdssize); + for (i = 0; i < nfds; ++i) { + hostfds[i].fd = XlatFd(m, hostfds[i].fd); + } + if ((count = poll(hostfds, nfds, timeout_ms)) != -1) { + for (i = 0; i < count; ++i) { + hostfds[i].fd = guestfds[i].fd; + } + VirtualRecvWrite(m, fdsaddr, hostfds, count * sizeof(struct pollfd)); + } } else { - return enomem(); + count = enomem(); } + free(guestfds); + free(hostfds); } else { - return einval(); + count = einval(); } + return count; } static int OpSigprocmask(struct Machine *m, int how, int64_t setaddr, @@ -1303,7 +1363,7 @@ void OpSyscall(struct Machine *m, uint32_t rde) { SYSCALL(0x177, vmsplice(di, P(si), dx, r0)); CASE(0xE7, HaltMachine(m, di | 0x100)); default: - /* LOGF("missing syscall 0x%03x", ax); */ + LOGF("missing syscall 0x%03x", ax); ax = enosys(); break; } diff --git a/tool/build/lib/throw.c b/tool/build/lib/throw.c index 53414fcc..87fcc2f6 100644 --- a/tool/build/lib/throw.c +++ b/tool/build/lib/throw.c @@ -18,9 +18,12 @@ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/log/check.h" +#include "libc/log/log.h" +#include "libc/nexgen32e/vendor.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" #include "tool/build/lib/address.h" +#include "tool/build/lib/endian.h" #include "tool/build/lib/throw.h" static bool IsHaltingInitialized(struct Machine *m) { @@ -40,7 +43,15 @@ void ThrowDivideError(struct Machine *m) { void ThrowSegmentationFault(struct Machine *m, int64_t va) { m->faultaddr = va; - m->ip -= m->xedd->length; + if (m->xedd) m->ip -= m->xedd->length; + WARNF("%s%s ADDR %p IP %p AX %lx CX %lx DX %lx BX %lx SP %lx " + "BP %lx SI %lx DI %lx R8 %lx R9 %lx R10 %lx R11 %lx R12 %lx R13 %lx " + "R14 %lx R15 %lx", + "SEGMENTATION FAULT", IsGenuineCosmo() ? " SIMULATED" : "", va, m->ip, + Read64(m->ax), Read64(m->cx), Read64(m->dx), Read64(m->bx), + Read64(m->sp), Read64(m->bp), Read64(m->si), Read64(m->di), + Read64(m->r8), Read64(m->r9), Read64(m->r10), Read64(m->r11), + Read64(m->r12), Read64(m->r13), Read64(m->r14), Read64(m->r15)); HaltMachine(m, kMachineSegmentationFault); } @@ -49,7 +60,7 @@ void ThrowProtectionFault(struct Machine *m) { } void OpUd(struct Machine *m, uint32_t rde) { - m->ip -= m->xedd->length; + if (m->xedd) m->ip -= m->xedd->length; HaltMachine(m, kMachineUndefinedInstruction); } diff --git a/tool/build/lib/time.c b/tool/build/lib/time.c index 87efbcdc..86e81cba 100644 --- a/tool/build/lib/time.c +++ b/tool/build/lib/time.c @@ -24,13 +24,14 @@ #include "tool/build/lib/endian.h" #include "tool/build/lib/time.h" +/** + * @fileoverview i am the timelorde + */ + void OpPause(struct Machine *m, uint32_t rde) { sched_yield(); } -/** - * I am the timelorde. - */ void OpRdtsc(struct Machine *m, uint32_t rde) { uint64_t c; struct timespec ts; diff --git a/tool/build/tinyemu.c b/tool/build/tinyemu.c index 93839ed6..71597228 100644 --- a/tool/build/tinyemu.c +++ b/tool/build/tinyemu.c @@ -30,22 +30,17 @@ #include "tool/build/lib/pty.h" #include "tool/build/lib/syscall.h" -struct Machine m[1]; - int main(int argc, char *argv[]) { int rc; struct Elf elf; - const char *codepath; - codepath = argv[1]; + struct Machine *m; if (argc < 2) { fputs("Usage: ", stderr); fputs(argv[0], stderr); fputs(" PROG [ARGS...]\n", stderr); return EX_USAGE; } - m->cr3 = MallocPage(); - m->mode = XED_MACHINE_MODE_LONG_64; - InitMachine(m); + m = NewMachine(); LoadProgram(m, argv[1], argv + 2, environ, &elf); m->fds.i = 3; m->fds.n = 8; diff --git a/tool/build/zipobj.c b/tool/build/zipobj.c index c12282ff..69ec6152 100644 --- a/tool/build/zipobj.c +++ b/tool/build/zipobj.c @@ -279,7 +279,9 @@ void ProcessFile(struct ElfWriter *elf, const char *path) { map = NULL; } EmitZip(elf, path, pathlen, map, &st); - CHECK_NE(-1, munmap(map, st.st_size)); + if (st.st_size) { + CHECK_NE(-1, munmap(map, st.st_size)); + } } void PullEndOfCentralDirectoryIntoLinkage(struct ElfWriter *elf) { diff --git a/tool/calc/calc.c b/tool/calc/calc.c index 0a6c1755..94e9594e 100644 --- a/tool/calc/calc.c +++ b/tool/calc/calc.c @@ -368,6 +368,11 @@ static long double FnBsr(struct Numbers *a) { return bsr(a->x); } +static long double FnBsrl(struct Numbers *a) { + if (!a) MissingArgumentError(); + return bsrl(a->x); +} + static long double FnBsfl(struct Numbers *a) { if (!a) MissingArgumentError(); return bsfl(a->x); @@ -610,7 +615,7 @@ static const struct Fn { {"bsfl", FnBsfl}, {"bsfl", FnBsfl}, {"bsr", FnBsr}, - {"bsr", FnBsr}, + {"bsrl", FnBsrl}, {"cbrt", FnCbrt}, {"ceil", FnCeil}, {"copysign", FnCopysign}, diff --git a/tool/emacs/cosmo-cpp-constants.el b/tool/emacs/cosmo-cpp-constants.el index 20ebdaee..f68097f7 100644 --- a/tool/emacs/cosmo-cpp-constants.el +++ b/tool/emacs/cosmo-cpp-constants.el @@ -143,7 +143,14 @@ "IM_FEELING_NAUGHTY" "__REAL_MODE__" "__x86__" - "__i386__")) + "__i386__" + "__W__" + "__PG__" + "__MFENTRY__" + "__MNO_VZEROUPPER__" + "__FSANITIZE_UNDEFINED__" + "__MNOP_MCOUNT__" + "__MRECORD_MCOUNT__")) (defconst cosmo-cpp-constants (append cosmo-cpp-constants-c11 diff --git a/tool/net/redbean.c b/tool/net/redbean.c index e01b2fcc..362923c4 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -62,6 +62,7 @@ #include "libc/sysv/consts/so.h" #include "libc/sysv/consts/sock.h" #include "libc/sysv/consts/sol.h" +#include "libc/sysv/consts/tcp.h" #include "libc/sysv/errfuns.h" #include "libc/time/struct/tm.h" #include "libc/time/time.h" @@ -1136,10 +1137,13 @@ void ProcessConnection(void) { } } -static void SetReusePortAndAllowMultipleProcesses(void) { +static void TuneServerSocket(void) { int yes = 1; LOGIFNEG1(setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes))); LOGIFNEG1(setsockopt(server, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes))); + LOGIFNEG1(setsockopt(server, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes))); + LOGIFNEG1(setsockopt(server, IPPROTO_TCP, TCP_FASTOPEN, &yes, sizeof(yes))); + LOGIFNEG1(setsockopt(server, IPPROTO_TCP, TCP_QUICKACK, &yes, sizeof(yes))); } void RedBean(void) { @@ -1147,6 +1151,7 @@ void RedBean(void) { programfile = (const char *)getauxval(AT_EXECFN); CHECK(OpenZip(programfile)); xsigaction(SIGINT, OnTerminate, 0, 0, 0); + xsigaction(SIGHUP, OnTerminate, 0, 0, 0); xsigaction(SIGTERM, OnTerminate, 0, 0, 0); xsigaction(SIGCHLD, SIG_IGN, 0, 0, 0); xsigaction(SIGPIPE, SIG_IGN, 0, 0, 0); @@ -1154,7 +1159,7 @@ void RedBean(void) { xsigaction(SIGALRM, OnAlarm, 0, 0, 0); if (setitimer(ITIMER_REAL, &kHeartbeat, NULL) == -1) notimer = true; CHECK_NE(-1, (server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))); - SetReusePortAndAllowMultipleProcesses(); + TuneServerSocket(); CHECK_NE(-1, bind(server, &serveraddr, sizeof(serveraddr))); CHECK_NE(-1, listen(server, 10)); DescribeAddress(serveraddrstr, &serveraddr); diff --git a/tool/viz/deathstar.c b/tool/viz/deathstar.c index acbefb2e..6a887d2a 100644 --- a/tool/viz/deathstar.c +++ b/tool/viz/deathstar.c @@ -35,8 +35,8 @@ const char *kShades[] = { jmp_buf jb_; double light_[3] = {-50, 0, 50}; -struct Sphere pos_ = {20, 20, 20, 20}; -struct Sphere neg_ = {1, 1, -6, 20}; +struct Sphere pos_ = {11, 11, 11, 11}; +struct Sphere neg_ = {1, 1, -4, 11}; static void OnCtrlC(int sig) { longjmp(jb_, 1); @@ -128,7 +128,7 @@ int main() { double ang; struct termios old; if (cancolor()) { - ttyhidecursor(fileno(stdout)); + WRITE("\e[?25l"); if (!setjmp(jb_)) { xsigaction(SIGINT, OnCtrlC, 0, 0, NULL); ang = 0; @@ -143,7 +143,7 @@ int main() { usleep(1. / FRAMERATE * 1e6); } } - ttyshowcursor(fileno(stdout)); + WRITE("\e[0m\e[H\e[J\e[?25h"); return 0; } else { return 1; diff --git a/tool/viz/printvideo.c b/tool/viz/printvideo.c index 92a2b32e..dda19279 100644 --- a/tool/viz/printvideo.c +++ b/tool/viz/printvideo.c @@ -144,10 +144,7 @@ Effects Shortcuts:\n\ H +Hue ALT+H -Hue\n\ S +Saturation ALT+S -Saturation\n\ L +Lightness ALT+L -Lightness\n\ - F1 {Unsharp,Sharp}\n\ - F2 {Gaussian,Boxblur}\n\ - F3 Sobel\n\ - F4 Emboss\n\ + CTRL-G {Unsharp,Sharp}\n\ \n\ Environment Variables:\n\ SOX overrides location of SoX executable\n\ @@ -1157,8 +1154,6 @@ static optimizesize void ReadKeyboard(void) { case CTRL('G'): sharp_ = (sharp_ + 1) % kSharpMAX; break; - case CTRL('B'): - longjmp(jb_, 1); case CTRL('\\'): raise(SIGQUIT); break; diff --git a/libc/log/bisectsymbol.c b/tool/viz/tailf.c similarity index 51% rename from libc/log/bisectsymbol.c rename to tool/viz/tailf.c index e94f5ebe..e9a663fc 100644 --- a/libc/log/bisectsymbol.c +++ b/tool/viz/tailf.c @@ -17,27 +17,86 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/alg/bisectcarleft.h" -#include "libc/assert.h" -#include "libc/runtime/symbols.h" +#include "libc/bits/safemacros.h" +#include "libc/calls/calls.h" +#include "libc/calls/struct/stat.h" +#include "libc/macros.h" +#include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/o.h" +#include "libc/sysv/consts/sig.h" +#include "libc/time/time.h" +#include "libc/x/x.h" /** - * Finds symbol associated with address. - * @param symbol table pointer (null propagating) - * @return symbol address or NULL if not found + * @fileoverview tail -f with lower poll rate + * @see busybox not having interval flag */ -const struct Symbol *bisectsymbol(struct SymbolTable *t, - intptr_t virtual_address, - int64_t *opt_out_addend) { - const struct Symbol *symbol = NULL; - int64_t addend = (intptr_t)virtual_address; - if (t && t->count) { - unsigned key = (unsigned)((intptr_t)virtual_address - t->addr_base - 1); - unsigned i = bisectcarleft((const int32_t(*)[2])t->symbols, t->count, key); - assert(0 <= i && i < t->count); - symbol = &t->symbols[i]; - addend = (int64_t)key - (int64_t)symbol->addr_rva; - } - if (opt_out_addend) *opt_out_addend = addend; - return symbol; + +int fd; +bool exited; +struct stat st; +char buf[FRAMESIZE]; + +int WriteString(const char *s) { + return write(1, s, strlen(s)); +} + +void HideCursor(void) { + WriteString("\e[?25l"); +} + +void ShowCursor(void) { + WriteString("\e[?25h"); +} + +void OnInt(void) { + exited = true; +} + +void OnExit(void) { + ShowCursor(); +} + +int main(int argc, char *argv[]) { + char *p; + ssize_t n; + size_t i, j; + bool chopped; + if (argc < 2) return 1; + if ((fd = open(argv[1], O_RDONLY)) == -1) return 2; + if (fstat(fd, &st) == -1) return 3; + n = st.st_size - MIN(st.st_size, sizeof(buf)); + if ((n = pread(fd, buf, sizeof(buf), n)) == -1) return 4; + for (p = buf + n, i = 0; i < 10; ++i) { + p = firstnonnull(memrchr(buf, '\n', p - buf), buf); + } + chopped = false; + if (buf + n - p) ++p; + i = st.st_size - (buf + n - p); + atexit(OnExit); + HideCursor(); + xsigaction(SIGINT, OnInt, 0, 0, 0); + xsigaction(SIGTERM, OnInt, 0, 0, 0); + while (!exited) { + if (fstat(fd, &st) == -1) return 5; + if (i > st.st_size) i = 0; + for (; i < st.st_size; i += n) { + if ((n = pread(fd, buf, sizeof(buf), i)) == -1) return 6; + j = n; + while (j && (buf[j - 1] == '\n' || buf[j - 1] == '\r')) --j; + if (j) { + if (chopped) { + WriteString("\r\n"); + } + write(1, buf, j); + } + chopped = j != n; + } + dsleep(.01); + } + close(fd); + WriteString("\r\n"); + return 0; } diff --git a/tool/viz/unicode.c b/tool/viz/unicode.c new file mode 100644 index 00000000..8c1849a2 --- /dev/null +++ b/tool/viz/unicode.c @@ -0,0 +1,92 @@ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/fmt.h" +#include "libc/mem/mem.h" +#include "libc/runtime/gc.h" +#include "libc/stdio/stdio.h" +#include "libc/str/str.h" +#include "libc/unicode/unicode.h" +#include "libc/x/x.h" + +int a, b, w, i; +char name[512]; + +void DisplayUnicodeCharacter(void) { + int c, cw; + c = i; + cw = wcwidth(c); + if (cw < 1) { + c = '?'; + cw = 1; + } + if (w) { + if (w + 1 + cw > 80) { + printf("\n"); + w = 0; + } else { + fputc(' ', stdout); + w += 1 + cw; + } + } + if (!w) { + printf("%08x ", i); + w = 9 + cw; + } + fputwc(c, stdout); +} + +void DisplayUnicodeBlock(void) { + if (a == 0x10000) { + printf("\n\n\n\n\n\n\n " + "ASTRAL PLANES\n\n\n\n\n"); + } + if (a == 0x0590 /* hebrew */) return; + if (a == 0x0600 /* arabic */) return; + if (a == 0x08a0 /* arabic */) return; + if (a == 0x0750 /* arabic */) return; + if (a == 0x0700 /* syriac */) return; + if (a == 0x10800 /* cypriot */) return; + printf("\n\n%-60s%20s\n" + "──────────────────────────────────────────────" + "──────────────────────────────────\n", + name, gc(xasprintf("%04x .. %04x", a, b))); + w = 0; + for (i = a; i <= b; ++i) { + DisplayUnicodeCharacter(); + } +} + +int main(int argc, char *argv[]) { + FILE *f; + char *line; + size_t linesize; + printf("\n\n\n\n\n UNICODE PLANES\n\n\n\n"); + f = fopen("libc/unicode/blocks.txt", "r"); + line = NULL; + linesize = 0; + while (!feof(f)) { + if (getline(&line, &linesize, f) == -1) break; + if (sscanf(line, "%x..%x; %s", &a, &b, name) != 3) continue; + DisplayUnicodeBlock(); + } + free(line); + fclose(f); + return 0; +}