From 2046c0d2aebd9fe7e23755c9daec4234e40b7dce Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Sun, 24 Apr 2022 09:59:22 -0700 Subject: [PATCH 01/18] Make improvements - Expand redbean UNIX module - Expand redbean documentation - Ensure Lua copyright is embedded in binary - Increase the PATH_MAX limit especially on NT - Use column major sorting for linenoise completions - Fix some suboptimalities in redbean's new UNIX API - Figured out right flags for Multics newline in raw mode --- build/bootstrap/compile.com | Bin 94208 -> 102400 bytes build/definitions.mk | 2 +- dsp/tty/setraw.c | 7 - examples/examples.mk | 8 - examples/printargs.c | 24 +- examples/time.c | 4 +- examples/touch.c | 2 +- libc/calls/calls.h | 17 +- libc/calls/chdir-nt.c | 2 +- libc/calls/close.c | 4 +- libc/calls/commandv.c | 83 +- libc/calls/copyfile.c | 2 +- libc/calls/execlp.c | 4 +- libc/calls/execve-sysv.c | 3 +- libc/calls/execve.c | 2 +- libc/calls/execvpe.c | 4 +- libc/calls/faccessat-nt.c | 2 +- libc/calls/fchdir-nt.c | 2 +- libc/calls/fchmodat-nt.c | 2 +- libc/calls/fileexists.c | 2 +- libc/calls/fstatat-nt.c | 2 +- libc/calls/getcwd-nt.c | 21 +- libc/calls/getegid.c | 8 +- libc/calls/geteuid.c | 7 +- libc/calls/getpgrp.c | 2 +- libc/calls/getrusage-nt.c | 15 +- libc/calls/getsid.c | 2 +- libc/calls/internal.h | 13 +- libc/calls/ioctl_tcgets-nt.c | 25 +- libc/calls/ioctl_tcsets-nt.c | 39 +- libc/calls/ioctl_tcsets.c | 5 - libc/calls/isdirectory-nt.c | 2 +- libc/calls/isregularfile-nt.c | 2 +- libc/calls/issymlink-nt.c | 2 +- libc/calls/linkat-nt.c | 4 +- libc/calls/mkdir.c | 2 +- libc/calls/mkdirat-nt.c | 5 +- libc/calls/mkntpath.c | 22 +- libc/calls/mkntpathat.c | 4 +- libc/calls/nanosleep-nt.c | 3 +- libc/calls/ntspawn.c | 2 +- libc/calls/open-nt.c | 2 +- libc/calls/poll.c | 2 +- libc/calls/preadv.c | 2 +- libc/calls/printfds.c | 5 +- libc/calls/program_executable_name.c | 19 +- libc/calls/pwritev.c | 2 +- libc/calls/readlinkat-nt.c | 9 +- libc/calls/readv.c | 2 +- libc/calls/renameat-nt.c | 4 +- libc/calls/setegid.c | 7 +- libc/calls/seteuid.c | 7 +- libc/calls/setgid.c | 10 +- libc/calls/setregid.c | 35 + libc/calls/setresgid.c | 15 +- libc/calls/setresuid.c | 15 +- libc/calls/setreuid.c | 35 + libc/calls/setuid.c | 10 +- libc/calls/sig.internal.h | 1 + libc/calls/sig2.c | 1 + libc/calls/sigcount.c | 21 + libc/calls/strace.internal.h | 6 +- libc/calls/symlinkat-nt.c | 4 +- libc/calls/truncate-nt.c | 2 +- libc/calls/ttyname.c | 2 +- libc/calls/ttyname_r.c | 2 +- libc/calls/unlinkat-nt.c | 5 +- libc/calls/utimensat-nt.c | 2 +- libc/calls/wait4-nt.c | 6 +- libc/calls/writev.c | 2 +- libc/dns/gethoststxt.c | 4 +- libc/dns/lookupprotobyname.c | 2 +- libc/dns/lookupprotobynumber.c | 6 +- libc/dns/lookupservicesbyname.c | 2 +- libc/dns/lookupservicesbyport.c | 2 +- libc/fmt/abs.c | 7 +- libc/fmt/fmt.c | 3 - libc/fmt/fmt.h | 4 - libc/fmt/{kerrornameslong.S => kerrnodocs.S} | 10 +- libc/fmt/{kerrornames.S => kerrnonames.S} | 10 +- libc/fmt/kerrornames.internal.h | 16 - libc/fmt/labs.c | 10 +- libc/fmt/llabs.c | 10 +- libc/fmt/magnumstrs.internal.h | 23 + .../{strerror_long.greg.c => strerdoc.greg.c} | 17 +- ...{strerror_short.greg.c => strerrno.greg.c} | 17 +- libc/fmt/strerror.c | 4 +- libc/fmt/strerror_r.greg.c | 2 +- libc/fmt/strerror_wr.greg.c | 6 +- libc/integral/normalize.inc | 2 +- libc/intrin/asan.c | 51 +- libc/intrin/assertfail.c | 4 +- libc/intrin/bzero.c | 4 +- libc/intrin/getexitcodeprocess.greg.c | 36 + libc/intrin/getmagnumstr.greg.c | 29 + libc/intrin/intrin.mk | 3 + libc/intrin/kdos2errno.S | 2 +- libc/intrin/kprintf.greg.c | 16 +- libc/intrin/memset.c | 5 +- libc/intrin/nomultics.c | 11 - libc/intrin/nomultics.internal.h | 1 - libc/intrin/onarithmeticoverflow.S | 3 +- libc/intrin/ubsan.c | 10 +- libc/intrin/waitformultipleobjects.greg.c | 39 + libc/intrin/waitforsingleobject.greg.c | 37 + libc/log/backtrace2.greg.c | 10 +- libc/log/backtrace3.greg.c | 8 +- libc/log/checkfail.c | 14 +- libc/log/checkfail_ndebug.c | 2 +- libc/log/commandvenv.c | 4 +- libc/log/countexpr_report.c | 2 +- libc/log/oncrash.c | 42 +- libc/log/printgarbage.c | 14 +- libc/log/vflogf.c | 6 +- libc/mem/vasprintf.c | 31 +- libc/nt/errors.h | 2 +- libc/nt/kernel32/GetExitCodeProcess.s | 4 +- libc/nt/kernel32/WaitForMultipleObjects.s | 4 +- libc/nt/kernel32/WaitForSingleObject.s | 4 +- libc/nt/master.sh | 6 +- libc/nt/thunk/accounting.inc | 3 - libc/runtime/findcombinary.c | 2 +- libc/runtime/internal.h | 1 + libc/runtime/ismemtracked.greg.c | 8 +- libc/runtime/memtrack.internal.h | 1 + libc/runtime/mmap.c | 224 +- libc/runtime/mprotect-nt.greg.c | 63 + libc/runtime/mprotect.greg.c | 63 +- libc/runtime/msync-nt.c | 28 +- libc/runtime/munmap.c | 134 +- libc/runtime/paginate.c | 57 + libc/runtime/printargs.greg.c | 33 +- libc/runtime/printmemoryintervals.c | 4 +- libc/runtime/runtime.h | 1 + libc/runtime/runtime.mk | 4 +- libc/runtime/vfork.S | 2 +- libc/runtime/winmain.greg.c | 14 +- libc/sock/describesocklevel.greg.c | 33 + libc/sock/describesockoptname.greg.c | 45 + libc/sock/getsockopt-nt.c | 44 +- libc/sock/getsockopt.c | 22 +- libc/sock/kipoptnames.S | 39 + libc/sock/ksockoptnames.S | 49 + libc/sock/ktcpoptnames.S | 48 + libc/sock/recvmsg.c | 2 +- libc/sock/sendmsg.c | 2 +- libc/sock/setsockopt-nt.c | 39 +- libc/sock/setsockopt.c | 27 +- libc/sock/sock.mk | 10 +- libc/sock/sockdebug.c | 4 + libc/stdio/printf.c | 4 - libc/stdio/spawn.c | 4 +- libc/stdio/spawnp.c | 4 +- libc/stdio/tmpfile.c | 6 +- libc/str/{kstrsignal.S => ksignalnames.S} | 10 +- libc/str/qsort.c | 4 +- libc/str/str.h | 13 +- libc/str/strsignal.c | 13 +- libc/sysv/calls/setregid.s | 2 - libc/sysv/calls/setreuid.s | 2 - libc/sysv/calls/sys_setregid.s | 2 + libc/sysv/calls/sys_setreuid.s | 2 + libc/sysv/consts.sh | 57 +- libc/sysv/consts/CLOCAL.S | 2 +- libc/sysv/consts/CREAD.S | 2 +- libc/sysv/consts/CS5.S | 2 +- libc/sysv/consts/CS6.S | 2 +- libc/sysv/consts/CS7.S | 2 +- libc/sysv/consts/CS8.S | 2 +- libc/sysv/consts/CSIZE.S | 2 +- libc/sysv/consts/CSTOPB.S | 2 +- libc/sysv/consts/HUPCL.S | 2 +- libc/sysv/consts/PARENB.S | 2 +- libc/sysv/consts/PARODD.S | 2 +- libc/sysv/consts/so.h | 127 +- libc/sysv/consts/sol.h | 61 +- libc/sysv/syscalls.sh | 4 +- libc/sysv/systemfive.S | 2 +- libc/testlib/testlib.h | 7 +- libc/testlib/testrunner.c | 8 +- libc/time/localtime.c | 2 +- libc/zip.h | 9 +- net/http/decodelatin1.c | 2 +- net/http/encodelatin1.c | 2 +- net/http/foldheader.c | 3 + net/http/hascontrolcodes.c | 2 +- net/http/parsehttpmessage.c | 2 +- net/http/parseurl.c | 2 +- net/http/underlong.c | 2 +- test/libc/calls/commandv_test.c | 24 +- test/libc/calls/execve_test.c | 24 + test/libc/calls/getcwd_test.c | 7 + test/libc/calls/mkdir_test.c | 22 + test/libc/calls/read_test.c | 8 +- test/libc/calls/readlinkat_test.c | 14 +- test/libc/calls/seccomp_test.c | 1 - test/libc/calls/sigaction_test.c | 1 - test/libc/calls/sigprocmask_test.c | 1 - test/libc/calls/sigsuspend_test.c | 1 - test/libc/fmt/atoi_test.c | 7 + test/libc/intrin/kprintf_test.c | 27 + test/libc/runtime/memtrack_test.c | 4 +- test/libc/runtime/mprotect_test.c | 2 +- third_party/argon2/blake2b.c | 4 +- third_party/linenoise/linenoise.c | 163 +- third_party/lua/README.cosmo | 2 + third_party/lua/cosmo.h | 9 +- third_party/lua/lapi.c | 41 +- third_party/lua/lapi.h | 6 - third_party/lua/lauxlib.c | 40 +- third_party/lua/lbaselib.c | 40 +- third_party/lua/lcode.c | 41 +- third_party/lua/lcode.h | 6 - third_party/lua/lcorolib.c | 41 +- third_party/lua/lctype.c | 63 - third_party/lua/lctype.h | 116 +- third_party/lua/ldblib.c | 49 +- third_party/lua/ldebug.c | 47 +- third_party/lua/ldebug.h | 6 - third_party/lua/ldo.c | 40 +- third_party/lua/ldo.h | 6 - third_party/lua/ldump.c | 41 +- third_party/lua/lfunc.c | 41 +- third_party/lua/lfunc.h | 6 - third_party/lua/lgc.c | 41 +- third_party/lua/lgc.h | 6 - third_party/lua/linit.c | 42 +- third_party/lua/liolib.c | 41 +- third_party/lua/llex.c | 40 +- third_party/lua/llimits.h | 6 - third_party/lua/lmathlib.c | 41 +- third_party/lua/lmem.c | 41 +- third_party/lua/lmem.h | 6 - third_party/lua/loadlib.c | 52 +- third_party/lua/lobject.c | 40 +- third_party/lua/lobject.h | 6 - third_party/lua/lopcodes.c | 41 +- third_party/lua/lopcodes.h | 6 - third_party/lua/loslib.c | 41 +- third_party/lua/lparser.c | 40 +- third_party/lua/lparser.h | 6 - third_party/lua/lprefix.h | 27 +- third_party/lua/lrepl.c | 64 +- third_party/lua/lrepl.h | 17 +- third_party/lua/lstate.c | 41 +- third_party/lua/lstate.h | 6 - third_party/lua/lstring.c | 41 +- third_party/lua/lstring.h | 6 - third_party/lua/lstrlib.c | 61 +- third_party/lua/ltable.c | 41 +- third_party/lua/ltable.h | 6 - third_party/lua/ltablib.c | 41 +- third_party/lua/ltests.c | 41 +- third_party/lua/ltests.h | 7 - third_party/lua/ltm.c | 41 +- third_party/lua/ltm.h | 8 - third_party/lua/lua.main.c | 42 +- third_party/lua/luac.main.c | 41 +- third_party/lua/lualib.h | 7 - third_party/lua/luaprintstack.c | 31 + third_party/lua/lundump.h | 7 - third_party/lua/lvm.c | 41 +- third_party/lua/lvm.h | 7 - third_party/lua/lzio.c | 41 +- third_party/python/Modules/termios.c | 2 +- third_party/python/pyobj.c | 10 +- third_party/python/repl.c | 11 +- third_party/zip/zip.c | 35 +- tool/build/compile.c | 4 +- tool/build/lib/demangle.c | 4 +- tool/build/lib/elfwriter.h | 2 +- tool/build/lib/elfwriter_zip.c | 17 +- tool/build/lib/ssemov.c | 16 +- tool/build/runit.c | 5 +- tool/build/strace.c | 3 +- tool/build/zipobj.c | 14 +- tool/decode/ent.c | 2 +- tool/decode/lib/bitabuilder.c | 2 +- tool/decode/lib/disassemblehex.c | 2 +- tool/emacs/cosmo-stuff.el | 1 + tool/net/demo/.init.lua | 4 + tool/net/demo/crashreport.lua | 2 +- tool/net/demo/fetch.lua | 6 +- tool/net/demo/redbean.lua | 20 +- tool/net/demo/unix-info.lua | 266 +- tool/net/demo/unix-rawsocket.lua | 13 +- tool/net/demo/unix-subprocess.lua | 33 +- tool/net/demo/unix-webserver.lua | 26 +- tool/net/help.txt | 3047 +++++++++-------- tool/net/lfuncs.c | 562 +++ tool/net/lfuncs.h | 84 + tool/net/lunix.c | 1007 +++--- tool/net/net.mk | 10 +- tool/net/redbean.c | 902 ++--- tool/plinko/lib/gc.c | 2 +- tool/viz/bin2asm.c | 2 +- tool/viz/derasterize.c | 10 +- tool/viz/lib/bilinearscale.c | 2 +- tool/viz/lib/convolve.h | 4 +- tool/viz/lib/unsharp.c | 2 +- tool/viz/lib/ycbcr2rgb3.c | 4 +- tool/viz/life.c | 10 +- tool/viz/magikarp.c | 2 +- tool/viz/memzoom.c | 6 +- tool/viz/printdos2errno.c | 10 +- 305 files changed, 6602 insertions(+), 4221 deletions(-) create mode 100644 libc/calls/setregid.c create mode 100644 libc/calls/setreuid.c create mode 100644 libc/calls/sigcount.c rename libc/fmt/{kerrornameslong.S => kerrnodocs.S} (97%) rename libc/fmt/{kerrornames.S => kerrnonames.S} (96%) delete mode 100644 libc/fmt/kerrornames.internal.h create mode 100644 libc/fmt/magnumstrs.internal.h rename libc/fmt/{strerror_long.greg.c => strerdoc.greg.c} (82%) rename libc/fmt/{strerror_short.greg.c => strerrno.greg.c} (83%) create mode 100644 libc/intrin/getexitcodeprocess.greg.c create mode 100644 libc/intrin/getmagnumstr.greg.c create mode 100644 libc/intrin/waitformultipleobjects.greg.c create mode 100644 libc/intrin/waitforsingleobject.greg.c create mode 100644 libc/runtime/mprotect-nt.greg.c create mode 100644 libc/runtime/paginate.c create mode 100644 libc/sock/describesocklevel.greg.c create mode 100644 libc/sock/describesockoptname.greg.c create mode 100644 libc/sock/kipoptnames.S create mode 100644 libc/sock/ksockoptnames.S create mode 100644 libc/sock/ktcpoptnames.S rename libc/str/{kstrsignal.S => ksignalnames.S} (95%) delete mode 100644 libc/sysv/calls/setregid.s delete mode 100644 libc/sysv/calls/setreuid.s create mode 100644 libc/sysv/calls/sys_setregid.s create mode 100644 libc/sysv/calls/sys_setreuid.s delete mode 100644 third_party/lua/lctype.c create mode 100644 third_party/lua/luaprintstack.c create mode 100644 tool/net/lfuncs.c create mode 100644 tool/net/lfuncs.h diff --git a/build/bootstrap/compile.com b/build/bootstrap/compile.com index 89c8adc0b9e080a75f65697c240ccb5e4697f71f..df9521d24e1c2f14132499325e3fb32ad9f48f1b 100755 GIT binary patch delta 47898 zcmafc33yXQ*Z)o0OIv73fEEg+P-rVHP-uk;p$#;&m79`6+4n`+K@=fDlqJQq==B;9 zM-UejcYNJYT%ba_(ghc`mt7PPbz)Q&wIB-l|IQ>W>i2!mzdq*9oHKLg%$YN1&YYQh zDLyL~Z)@?yySn=K-+L%j_r%Vg@;H6!MvJ7BR@g&oCH+E4QNL3(`N}{YQu)$C@!z{=`%wAiuPY zM(zjvhJuGW`6N=4ky7g950E6KL~;X?(w!)Q^i!118EQasEhQx>ftL_%wT>oyJN}^` zospjb&ey^pwGq#8zuymVCV^#ul`SAs zN0?CoOxzbbr^J0XiOULt4+_9TQNzPSB#Cd)iMBOD#s`o;d=4bZh_4JF=Mqv1Aon*T zrw5QGLVgRAqiC7&6*{T>4h#pktd^tqP*3(x)`!@9lo0_Zv#<%HF7>dyYureL&p{Mu*G+pmO(@r!E@y#Mar4@z} zOQ6GJsNRZ)30KEkLz12!!4)!AwT2%L6@~4_T3JOoU9DV958wTsgM!l^_(%8LDl#un zWU?qS(sL|Wgau1P)XGBzs=wz<5VhhKs+T}@^jrv{rUg-9wQ_4hL9Uk1SbB%soQo~Q z-$mXP15{FBzY}Q>!T7IY#4e)$9r??1C8E!6?`3<}hn)HZ7 zx(R5{r66!_5LisWn?c|x0d%ybS`s)Z*V!zRcOn&uK#IV90!7-DM*#p13?(L@y%|)d zJwQ$>)G|;gvQ|FuJcQ%zOxQL-3O`H4jswW|1k#U*;OP`Z9&ARg7s&I3ObjA-HX|Pp z$bE!N41gb&6*Pf-$S_#>rFrXKm1kxVs z^mGYAA_B$R)yn&+c&%J1YDH76^kA9qFr%Rh=)v2BG?X;r*JXYP8Fauas=1KSdCws2 z$qiO}x4D`lP>n2?%bbgh6ZJk2^@fXjuT#A#!Fmrg*BczDN8)suBauP1m7-cZQSCme z_E50elp8d^>|H$TgMfStEGLZ?OwT(( zK+6Ci$@6{?@Ee3hrm?N3G6*>zfY`eho1dgM3?*No5tn%;GGO)qGTtg`c7L$K$A1x* zPJ5)HrhT*pcHh$QVp^Lq7AGZ?Sp_nKogA` z2)X?(XnH3gd?A#523c4`@5|R|F_WV~oJ8DCahW?%Lk<({VQZv(=|IatbOeCjrvZ$^ zYF57;<6oWzlDzkF5Dz06eS6LCFE)=Q5{8mB_$@YDD1+HJjY@XE3niVDilx*}O0`C+ z*nDp-iaFY2iJy-alv2M#F5fp4=!-K6eFH$rzPi|anGz0rIhjB1JOX6-exlMKiX9Wh z;$Z_svFLKLX8av0b%aX2B1*j}N=28GHRDeZ@;gGV6G(RuNk)<{Bjj~LE)d9N0@<>h zi~=7&(@CsN7qBknG~szJRS3tb$xW1sqf}o}H9c6BW+U$`K%IjinrS>jfWm_ynnb*D z2GRdxGj%<>oNOfj7NGL}MD-+6&SLXM{fN599UKCNW<2RQ}c?p{~#p4j3O+L1Y|lQ04^ko}a-fUqxlKwBrO=3o@jQO9HIi z2>0vbJ>biYa`_n*HuapM z)|SB|l2Bz>pbB3yUBVXS-TUDa8%m?W3)mP%Y&5kRhE}n%m*8=Wx1$2+h|D;wsK1mG z$G&LdxywBu?mX<-Bv`9MJ7*UX$xQ!K5Qi%7poeCD6AwN!AkTJ~O3wtar||F=qaA?0 z41n&Vk}f!V<7A)}&<;GSpE9(RbQ%-K$$%COv{!CJIgN+i^e z;djIB11o8-f!a2zuyLs%HJb{o_zIG`3zfXtztilG838Ex=(>>@H+s&#ZwWZ3d{vnw zS=5SMkdNY@*osSmM%$>SxSKZ8J?;zd|Z*?7nL5B$AVIEav^?oJO`Cgr>!Q3 z{iBW}2BT$p57KKaKMdX=xufOa$oLNUeD@?oPaO(mPwtl_E%S|~=!QTyAn%E4MwdA$ z-?};f7G+yCXP-w_(cfLCxd<%NbH;5YYkY$iWD|tRW z>f|prp!o-LXi&dIGUKrNji;DOFR68+;uy5&ht^#A)$ezi6$W9g=>g@XBIU#zNHEl+ zKO~Eh+y!d9UoB?C+n7yiwd{fvs_^qCpg-@}r}tHuL1vl;dr z!Ojw_Wf6ob$}I%UI_tM`$%$FP`(FW)=7&HkX<`bgRs46%Qof#Q4nh-wnj=utI|Qw| zZs;A8p@l@xvWHe>ZeNU|9h)0zK@HCr4L37m01W;7>In$r9i*KQ*Fj{;ArFYKiXTUL z@91#iwy9E{`?X@EGfwlvXn=n=4#=5JNbVt62(^;Z+_D=mSefSF~uuxowR%=CiO5RY+C3E8^(70R59?~Dg7AI0a>a0h;|pMhXy+= z!(d0*)Y^co!T|OpG;ylgNwaM{tH<+b~HikG9Ppot$0H{$T{MLMEUDbc(^78 zPu!H@!4Z52BsF;#>VU+CpCxG;8I$d#J};B8cbT(^o}(4!TVJFs5D$O`0c;JaG_Hq| zsdbSXSvLJpwGXIdm0(njo2;J7?SyWSqNJv3Qk6(j+ zpr1YEqmKhCECb1c)^$D=KO~FMec9yH-kogjRl4v()6Hekw{+g>ujNGxm^&q2Kk9Mz zc1jOt!YCmQ36H;Z{OEkq@tuK=^KX&0n!Z^k^Oh)Q)BOxlKnmYzLJA#(6fz$wfzFVr z;#Yn`>{G(R*UTWgvlE3ycZmH8UUd~5Td%^f5X(dQfaR<-8ZcsUFxvC|U{$}yYyl&} z7HLu0pir3li`8@n>)>%0t4h7Y`R7Q%{%Jh^#{Ld6)Qb|DY@`QSo9WLrGXD< z42-XN21Oh)Ift6V&6nB1zVmh(x0nEUigiQ7Ejq_kG6;t)HS;Z(R+3x`E3E=pHAW!fp^vx+K zjk)1V1a>K5rw~@loJ$C`%9pLU$J{`EI`RQKnK%c`_UY3nm|cr-jxPbVv#EAG)mE!e zAx%Y$GEn0RR^eL<1TUf*MX1q=YW##8)i6sK?d6FcdIXHi!sdpALF$NtWrPLsL)c#j zXm18>zGX5&&7T3vpC@`+W;xP5o7bt@5dgwdv?#OWrJtZ@S+Zd+mgckAb=i%c7P+iT z?_}qxLn!5{iOo~bPMKzzYMEx4Zkd6}X6}crvDbisM0(zvI;l2H=S5D0r>*K=Mgny0C?enC8{{EK^TEQ+P3wg;MzcRX+|&F}2EV<%!m4_A zzDoz#vJOB>gj9VtHHq8QwDa4s6^-AG7NjzW08w6$Ex&{xp2ujZb9}Czm>l4`nhruN z>R(p%cdL5Lsvh837ySNRd(pGEn!9S^ zlxfwCa^2nRu0CB_j?k-U$Lf3Bb^Q6}bIh z`R@P9w{6Y`>g)fNzp}H5!9auOoAZJ4heduW^&n87s=2^df}szZ^Zyn3mz(o}9zNNe z5A^Wi=6paROLwwX>1*_rZ!j%=gue9xb~?RdYR)k@c%!tuq=ESvKj-O=6{%M&6Y?}$ zQg6QfIicPOm;{$X}tThwS^H2Knlx@|@~?EJzC^1X>eRn>MDOj1buE@UedHE1P7o3{+N$P!1Hq3lCmwkx&oj8nSdwsw^DJB zP0I1dl#GP-pURUYp_@42p_82n+k76 zz1~uYJxu%Cb0nevsvL{r{un)=u!U1ZnbpYDyhhvh~s|OwTLzMX!3{C;DLB=!0J?-#}@6hDt|3VW}{OA?vJrj|1@(;G-A^- z`yr{P zFk+05%t6u!DK*O7rm?X8nffKu*x>$&J-#1~X4Kh9Dft-<*x{p(7ha;SR+e=aGF_FF z;E(B8$R6#V(D7+vNjLsPpn}GH+8Wq<8KYwsAYkf!e z_-;9n9j0qb3a_-p8iI-YGPGez^{CySu);gy^g}1Hg?A+CCl{~{cf^Jdg$(>LqldHi z?oe9xos2=o;o5sccIpP)fyH4G9jiTzooO+C5PwnD{wPwKdE9@{UoCz-9%^1vEWdEW z?=O4m2Nb2_-9S-F@y~#OuE*|9M+Y(v+f;KWsw2M&-wVgFm;%bx%ZT_y0*XV~ty@Ws z@_Ib-;#8lKH8m-H^+09O>2#a!SHs84QnZyu{-&&BhXzK+77rF7MVVjw%kQ`N_xU^& zBWjVwZVc=gHcHV+pPqe&#hWrG&>?9dwg-uHQi`M0WLR#0%siC9$Ox77$Gm?hTE?NO zIP_IY8z}tvQ$iWpbAOBtMF~k;0kN*<@UAU%>`ha?ZY{fLijU)O6MSzGM6H%)<4{hm z<}1S_X+AWj$Dl<0y@Qx-P*42{6I(GTxz7mFJonz|D93+2Ln_fo+ud{6%_?p;2RNg# zY(6!Gr9oq#4eFGi`ZCmCw62y8L@PX&0K&6UOLGVi1_0tGm2aR|n)#t|B*;<)7)9Nb zPAzqLgZ0cxN?S6`0wr?0sdaHnLdQGNn(eAdr^rDMG@@vG6{_**K(;a~c`8{flI#G) zXR%lRF0j7QQ9VJs1)v9%MKOyJKu3r+b2*-3D1bsl#&uR_opn^niWvd;vUHGmNLh3^ zT?vUPe3t!{mFoPTD#qXVL$sClzbe&Z0i==I*fu~Y!4#|ObA*rf=74Vx+$xE`I7oDa z0DL71`6AV=G%{A#O}`z{#H)M`pjOvk{y571z$YN<+Dk?J_K})M@LySZcF)M0Q%RUb zMlB|3Jo`O6O*e`qnjQL?CwD$;9-!B~%nlD3?mU-=m3g!YKF3&?(BH?XN0hZcC6Hv@ z_N2~y01O}Odbq3J@4Y0n=)8z*q~3#4%t6~5wANrMR`3^t_akJ*b50VyCI z1igdW>Iv7`P(uk82dT)iyBH7ld`@g+Z@#4;)!UQQCzMs>+%{!CvXdt%ecRNzrWft4&IGrEID85(J`NHgUXM3EG)aPJv4i&VfeQsL)ACqoQub63=<66@c`PqU z7o^t30{>Y*#L=FBA=!NmB_BW_Xr_(#O-k4u;iAN1`<;73q23v%5k1As$APxu|AJpe z;0OLK?6~QuBaScq$?x~AnQL*a%9r}GZn@D;-(6HaWQpSgY5>g}o~q2AYIv$D{E#CQ zj@@%KZZ`GD-9#M5VsK!rs8Xn8Pg1Kzt5#Qythhttl+-g;-9GO^sM%h+8RVUgRFh-q z+VA(4LIW)m;1ycN!#lK$iw#I|4Ae;x3S&=hM^Kw7gDTHU#xX>5Ce({uKQ3 zj8;N-lu))QFU7e7tF*9SzE~~u70O^5NWNBD=4Q%}vE#ELAQADMT#fDzMDvzibbTPB zf&803AO|(>l409 zd0Nue;!Uu?nk>8>O(t(BPwTz4_-PzFs6SY)9=CNnTiETIEuzBSsxDN(K@H)rjiYr~ znrx-XoklR{o%#K%!Hd1SdT^a=`dbSb6?umD1LSJuu0Wu7`6Z3^)~NVQA-i^Gs`L9% zw-~~a#G?JN%B-s5-4$mb3daCtQ3WoVL|xv5p>aR43qW;O4gKz@$FHJ3N}$?eDy<_Y zDy9x4eXSLL6}-Q2#}4lP!SAn?i_wTw&2x~cwv4r?zkQ$^r{1s>sRz5Pljr=61DuF# z11~&;19g&KLb4vNjQ*4-$sDHkQ-J_Ti7CuAbPM(1!Z7%H&$C8SN&(+e#0Kt=5v3*L{ zmc5X+Cuo_&3)nA8`&JRi`u%xo^37+M!5Y(I40zEbwlTlG^NYlPX-oEZ=;$f^pm^$(7Ca(H|cIV6RPD$;Es^2iaa=kP(g_t~7`w+$QLle$w)?(A(A!^cd63QVal#{>WC@bJ5)+u_|=)MNQj zoU2DIrAO^8`6K83ey;=CL6XffcfurC)=gRT4V^K^AOlUasP$TCth>nXx~^Mpa9s;U zH6vAng_kT?Q5_xg;J5w8Hriqbm>_moR-bj&PoPj?L+2O$obJj_k?i5v!}Aaqn$qMq zfT#;ifnPwmXKd`O?Y8Ji{Q{*Syja>$Xi+n4tW#mU;m=fBzafF;DBRD+?ku!KU&8J~ zQS*An`a%_T1*Vw%^>FrFVM5qCtd-n-iB%OA-S+tUW`D6H=}{W`hX?}GiMl08PY`7O zdNy!GALnb>E(W|myB);zL|$8AE{Lv6Krv3 z92P?Gy)a`(r#As0)QyyDEUUH;8I{7YLw!XI^$1myKXEw}T%H8X^>Qnefk1 zTAJ}GM1OED0CvCOcYKB?q>wzfE(m$~HdKH!)2fn(x-RM9B>BSCw9_!>z6LC&uoUEZ zAnN1yJF;EoO2&qdOrP*L@FW78dUi76N7WgJaSw3qPbo0z*hwAsY?mT%bJemt768Sa zmlRoK!*47ffUZx*W{XC`F;}xEwKsf{mlQqs&@VRk*wD@_WmJ*v+(Fba?5(KeAr@UF ztqL#aA-=x0hNwe}8yzk9ysw23Q`A31P%1E?m-u5wA^c5pQme?pTdrdjBRdbku2=HK z*5$^&+6`U;6c`bm*jL*iJ%~h2UM2ZS|Ai!|Rx*ItX?14pin5(4yz+CDfsE4;9#Sw- zi}-s%NCF^mz{bqvTLIDTNy=90atG4Z6o~owe+9_oD3#x#;Fu*!#6F5uC@L$4;IQ`F z6m=1Z!XoCT0HY|CXA_i&gUE&>n}?@Si_eV>p382-N)V%z{_Ge-yk5g@P_1C=N44(| z?x86_z^@=&$W>OrN;vy;RJZWCKYN)wMqbhTNXintui5 zB!Qj6GD-{m8GqPIU~Ac#tL?Zyj)CGk)e>8^|Qw&@b-=_W@M?QzLp6 zFWiqoju_#7Om#$A>Ozyq+f%RqiDLuS30F`C}{B^*BdYi|KbFHI~(n z?&B=~f(EBfy`=sKR+hD}JenQt--nn&xZ`IZQ%q-;g;{Dtd*k$jxBd$5tIUu8h!e4l z3&I@{`W}ot7Mma507Y?(3hYt-9JOob-8o`zQ5fcKUvnB`W^Yqwv=YE@*d9hAuJGd? zP}wwvtsB#;ZEMhhRvStZNgFkJVLq!KlcL|%oBceddvq>rj<5z0Qg19W{+Ls}SmM}T zk)c4qxupH>4mNpgx;}F~dwgt~(qcWPZclFU(oT){JSBRu>?BMRI_kO+f?E=BxzIBw zU}WFd)?9)Fo5H}=G|^k|hfgxyxY+r3yP+A^DzA05WYkzvYxqvcFVA$=eij2{omFEu zf*`aeOFoyYla?yJ@fkn+Ul(#i3*>^-X0h0d?q2F;Sa{6QwFN<{-~JH#Xw-;j3_;9 z-^eGRL&6%t5|I`{ZLz|&=8-!BCN2rvGOYBi`2>)`4Ty}2e0V0U$pEw#u^%J0!@+^B z4#e%C)NmBy6xSfh(b6!XLQ&5f+Jq*dX2xO5r*QtHoq6MX>GfNVvim2-hSJ1f>cqLa z_t>S0vAWGWBXIFofAt7!J^2&eP*yv+w?)PlgC@mh)TMFt$6QVd*eqOm(35h~bV%Vh zItaC&Po@VOO>7m!@ej`M`c3IO!~v{W&bs7jvy)k(Hq=d3jQxnavs>DAOx84tCW@lV10waQzv@)QqBMY;vPd58zYDay$ zY<7KW_ih)l?Sa;*Uf21*JYRwcZojKzlfrGfYl<40#Ghj3X|er^YiZ07cUXtET>EvZ z*K;&b))r!Z1|;|-Y&os!uvleLH7R{*7JGDBkM`XFRkZ#|ih8FJF=+cnw6A2beba{J z?SvOZVMm(H>OOJfglGeD+AE_X)q(x00#CA>} z9^C^7aFaI2ugj* zE53yCL3Oi86%bweh5ra`P_$MSmBYC_UqqTusx#0Zb6Yo|{^?kLT0lF(ed%0fhh{{_ zW)qRgyVrzz%A}nm z$pSo{)EXL%l@4~M@l~Od-qBtQN;t8^RAK3r=8%%J_!Pe^~Y4D>7>(Xc7RTJPp3`A^JK#}m(QG7e!G@=46cp1 zd2~8Ei9ds5uG77fip>*5^;Tz2)XIgF^tC)~?vI2|f93?#J+>MST)>E7A0EZKevXic zTH{5~7`^=q&Pd_ihK><}h{GQ29 zz9%W+WW||6<4JxRN<`fJ@wNoEZH?@+yC)RnqP7la*)gYZ0_X@+Tr0yRdrvy-9v)sR zCjl!C?klbsChfmsm{@GaZV3_{upC#2*zAR z1@OASempS|YmC9;OZX{KgZn_Z*npT0;CwO5JOo*>XX}H6p|lj^ZP}rNGr3OQ0|N=w zq22#Q1q(+@LyBC#0;SsMu?*YCQ2gqE14qVBU>7_`Qd&i~@O1>qpCKGYi*A8!?A60c zH^PD&MHLCfRS}MvXLQtHZV&HEDqSCeJ0VIpc`;~bRMg6OXcznM(EV^-G2AI=%!*g2 zG;?HcwrI9l7suY5o#1?|Eu5o2C8(LRy?GL`QB#4i(0lBPD5@y^R?rWt+XFuY(%jfkwyB zR2OggVET4w&hBh2);)2v@G2s+9|}&CS=aa!ub5fXjVaZ)HLtts5z)H@F$-zo2}9SQ zyJRD;v#;lLclO~^qJ#mX_2HV|5yi`niz42=kQ;>|rr{7=BE>M}hTDsjtlU_8fo)c1 zU?i7?+uXC0B5kwMzQf2`O^x%MnVZt@O0xjQ~0IoUcnpgU!V4INM%nRJ5hxd^M3lP24fXN(lof0>o&Ir-?Ce zgmWptIyu@Vh!&<;r^BHr#gV62 z9B}s})QD@0kT=5B*z6ffih-gfW!)N>^C}w0?poCIJt3DZL0U6Ft4^ZjK~d)$RV5Sw z3UG3FD=1w)Vo`gcx{?weaOL`Cu(EkQqXxlSQuo1wYRS9Tvdi<@wYvVJFv=}?QY|yg zPaN?*bj7SkRYylJHs%uD#)3uuFsT?Bkgo+^aAQ$x3$(|7BQUKDq^!ODF%Ng3X+*Jl zG?9Ch70QG5hQ&D=4C@^DmW4jB19@(kg?AOP_rA|@gX zK?ic?KXA-LMG~;g;`Z`Zc4dJ>Z=B2)EL;|T05~mvV+y;lFg1HKGM_@1C10Kv^AcsC z?~-qT7BdzZUl%QADm~k4F|+B}ItXu}#bozj)X0bj;x~|ENF8qy3%>B+< zru8!#kFG6~-eDK%1(A!SU9-|5;&AjG;R}zqdiJ zyN_+Yzi8<5OQ2hb^x+aLc!Cf@Kn1HnOP;oemc$C*9XPqZJ?F8^Whhyw{$w+Z_}0~^ z%Q1Z1NTdC=%u<%Fb;7uR+8Q@x-h;5P@uL@|vm%k-bSvMF@*8gDdr^Mnt$ZTo@4Jl*@XW4C{c!NfqJ zW(F5DFuT3SfGaHnJT%o!|KI9<7bw(Rw^aknS=N5wz96`n|MT@?zfEC=TMfVQugZJt zS%M>az{Y=ppGI90*gxvl+$tmlw6vZra&&Fi@81H-sb^aq9oriI)yJ56R`2NNJp4C4 zg**=+l!iFi|Jjx(^dA)Z8$;@qz(C9YYQy!h6>~u3oAhiCpbbq$E>aP#sfZ^3K>3xo@?jCkFS?a? z3c!?G06$e2ek*^K^4JwM%@mRE)sz>C{u7D>8EE3kdkci?L_~sY-aptrCW<)!0sHk< zH9`4rc};FjtiRhtMw44m{)MJ|Q_CrUHcO)y6`}L*pa2soe_vCcs^rm3jsJU{SSn*{ zDx=9n(3^cLpFsKExAG{@&a4>eoLmd*ryfw$V>Y$QrZ)HwD(=kSVbOK!Lb!qf33)*wFcZg%vcguzq=v&Kj#xbUHY(5OD#SGa>M$Q#-e zf)^R=VSL9HkfJk!W>Wkl>$h@}PGatrsV#CQpzSGa&&tBEZR3!<_&hVN>g?Q2^x?W( zKoG%>ZDL=i4A)O8_w0(-;Ii5&&2|5tcw} z%L9Ocp4+2?kU0TJ4^L_kFg5^ak2?w%r+D%K#WvM4&6~~6tm@pcB{Uf}FBJO+cb zV+-oW71nn37+n~fyE>uG`xePp76|v_+QlgTFnfA+pMk5$al<|R1oSCzS@lKiMBoDj zG7ieP%;Tw)qZ5(LLuAV{*}`tD?&{ozd(30gl}*m*@$&}an9ZI#0Yg)5UBWuCr=ZBifARPchWM% z#sO2*-btLH6dZ$C?Ei2Q;$JhNQNE03LmeZ=;Y!31I`W}b@eW+Ob`YnUjhGVxAGGZV z=i>uS;K+|+AyqgNLM%!Uzo@iOJ16lw=CRJjUD`bYijj`in)!{kMB{rd&trwfaS<;E zvuD};#q)HJvXjL#o&DNV=os_LX2?I=v7a6+$^@u@3l*vz>o0-a5A_N8ZL6drst3X5+r@G0Ag%vhA+#psaB zT!bV}FR|hCw!$tQuUQMgavpmXx2#q|X#=+Kzu2_#I|$}%hE>o;ZdPV{)Cjy)(D)># zLhy$DFiY^=0^Gad!=iwAvbo};MC?!_h{d9B81WkzaW7nW@b(e*du4P|B?$42?8d`g z=#{Gg8{d&dc9}<@5MJ&`rW+cXxeapsJTYUEGm$p9B7yL94A6cQwE6AS*h~NdWLgme zMFiX0%+x9H2x2wqAhrp3(It9Ssq>SVT20;hd#@&e8PE1+0?!HpnID2cKpYn~{_pj= zQN3;nP0Tn^kH3XiBk6vd?|vv*)C`PWmi(V6^rZyq;q>!mzq_Y`Fp02&{L_?lZMm5#O`7!6RkXv!2ST8LR7<9`U*;e zvE^tFjI{$}jzKQ79@TVfKIg}AyzIH!%p61i_*VqOrj{BBMQ=}K`xa$BDGhvb~i@7H;0L4BBN-|Eg#)W(`hV~%a_*;be$GtmbgS3vqa z1+7Ag(0h!o{492`!Pk4z9!C0aulMZ3TTOkNK&kssI1M@RKFn+d`L#Ew@bY1v;Z1zg zc=7EJFxhAET214IfAHPRc0aJ~ncY;6+fm15u0r@nx2BzEO#sfWmv(c~>ot5lk<{cP z*bW9Rax94Y6zL{jZz{S<@VcuBlv;_xA0rpI@R3?cpC$Ri^G0)XB(!E02mBu_+A&t1 zdG9w@`iNL2T@W*-Z1Zq1_A&N?x85O{=6x4sEMxIHG!C6E2&>9pn1rq&jAIMDCM@)* z4zE8wi&==<6^=B#S^(ct_ed}2TMF31ih<1s#jVG|TKUZAg^E{Q%=bq?BMXEeGLHgU zo?HY5OaVZRD0Y$e3}ind{C4t+ItBo*0)YFmlR@Hb>bsWtBo#ptYv!|f;6n?ObDqBl}$z?QBCu@MGA%)k!;a1~z&O!k2>YFM%kifm9d-1^arZITnqRJpUV$L`- z|Av2s5e6McHw$i0J6iZ$CgSnFG1!-&0P#SF-nZ znGXRd*;|(Hq2c)jtDa@g5TJZ*P+>Dw2ty6OynP~@ttKWd!9qpa(@KpmL4(jL-$Lxz zVR@*}usox~b?hy*qtk>w!t*w&r}F5Y=T-g;Ccj0kqvS&{Zx(-@y|WtM3$;){c%H)P zq?d`jjo6G#hSoJ5EaUwmErxEwd&htZ?W%BzH~6Z|^rQu*E#x`O%D0OSYKCF!J0w`_1~c&dLA4oi%nwS^wJBCBI^Bx*u?e6Q7`|Vt$pJ0{xc9wrEoVeG}r@(Xg*+8 z701feg{GgDw}*FH;9iv`*;2WJYa%?9SvGEualzXDts?yIi+lmQxiMWgkoA4CJbcwn zjHTSYh&4WW&)xRTC``uV1ZG#jU?3KJQAz!gXDtlU!9heSjF&o+rc%spsII674JEHr zjjR~Y4Xl%;k0IlIiXhs`SdC5<`iCj0RebjbR>S5*Z6R9NuC>@X9z|z>HndDZa^N2BkiGV(iGGX(Cw~@}TjHVPF z{+=Y?HuEv!Pvp;HzT(A5@b|a4_iA#ql~jlT*Ml5Zir(9kW{S4>8z3dZ`LhLm(HLg{ z;(KnO5-6L%vp|A`S_~cn?(wfF0sE6AZWf8f>l+?EUu+C;@l}4VP*|aX=%)RvCZB_~ z-_%3-6rd)$m9xY2z_}NKe`1$>7fGuD;30DhQ1Uppe@s{uPUBdi)Z(lvjXRU>Hs=m5CQdY1yT1{ zs*`}GB|c{;nq6B%t>at{U%9y*%UR=Kbi+L{@Xn(RfZqwAWuKq|vcfiR&e5Me%1&$^ z-@0@z>cb?{h#w!$vY+nieEAyWk^HLorKrC@c?W(+@2Y<~hg8rWf#v&V0hWt{Wdp$i z?#rBojpvgd$KDm^q4Fu<;HiIx2VH+}q$T*Mc%$ORn1BLOpj9)Mp@8+!PgY%xver)@ zkF+Q4I`wo8JN0xo-IwgIr#oAx5hc79OApAU^1RH4YZ%M3;DqZ`5_r4?NPMF1c2P7Y zQ9KGErGs<{x$>-xyAWLp*j-zqBd%FNrY=AH23xabv2)BWXteZ*+;aqN_^^UK@dG-6 zgMUcPd;xkOIhK}pArZgQhZ?F@+&P%wCt%2rBmz(;e@=xYVl93z=}nznB_N@cEEmZH ztn-9}@gU%!V3K#s>;XXLIQ%KDv)5odz_GjpXu(yTyirt#Zn5>x^hlfmKY=!zPm#e- zjrZ8Flszia#}(Z%91z>B?8GyD^hs70{;U>x?K_L)=vSadAKm^fd-2&G`uPg0e>PvA ztT4lKsm@Lcy!(8-XZ^=8V&2>mc}30iQO@*Uh_&>xb!f=TaT zpln)94e~bAL0F!U%x$8c`F-)Y1ezNtg|}A~br?F>e*k^SjD7N4AASBX_QP|J+~V{M zIDP2(9(wnk?3L$p^|m`1e?BuV>Q00lLnDrSj|js){;fqSz2b<9xT41Vmd^%movME} zpKadSw+r>RK+WDqPc>#rK6PhUx^HxW77Z9{vGWer@r8%<^Axt}h0(1R<)Qnz;I<%- zU3sBT_uG*NtL^accBDRK$sLe3ls-xw4#~Wd%Z9#~7x6ayNfg_g%Qn2&C2B2smjQvS z&t+vVj?>5GvNqeAdW7%?_2|`YJ@m7NvM;u+*T??AroGfbKRBN)f9Zkt)8KvJ3ygJg z5xF4&I%yUb^786tk%SU~$6xNDUzx)`dU=2@h5h_;ynaP4i+rU(e_{xm_{wdfAALAX=C_KO-P<52f8JixkuG zrBvht6xkb#Ii+ZY67#)!yZ(%Yb$uZqk;K1#-<~RUQBid*>J>>C*)2l6yM`4;#dO`#%CPfGHM0Wcd{FTTv5NYTvGQDqy5F@w z1n-Zj6LC4i79Oti4uBCrPhTL4rf&?c68H$Tptk_~Wuyg1&RW!imLq3}xg+uRN4@3h ziTsHAylxe0<<*nj>J5A9F%@j1gEPUZU=BvPElfc(DmheVrXMx%uL+Q7V#*Nevi{K|ui#mc( zKgRYadZ>SZ;1J7^^CN;RScvm0reGg*`|$#NDo?F=dW2)PvItA z0=?83XVWaWM{l2eI2A5kJ97q(!+O!;I+&(3)!oE09n>f(w zUQ|$E3Oy6Bj<~1yv#>c`oO$a0Jj2dYYRt+(=t7Fsl#}3Sflef#J8BW=9d_))enwM> zv>hCw4Ruty)pfn=1GuJl#QQc`4fU|i9T}^*J`2HX3ov*S!>#J5@R4eC8xpHkh*jtz z}DOXRawNUY%p03aYv)?aC4|Kr|_uaQ4 zYgJFXE_F?fDa;Tji`2vP@R`|!4mk7ob7fEFS(TLg`lsVXx5TQ z!6Ob9Zd0`T!NT`o;cLZoY*`Bhr<_m;?S5!Xqazdv4B2JUjH77MP+|um+}v6-5V9k# z_ZwVy36MfslMeO}oz|8Zor*dF`g_08mq-1uxrGkU4J}3JVjn;f6!ptObwB9a+#^yH zZ8_AW(E*cqKkgY?1p{ZTb)YWc1?CF-!aPkGSm+)_x+n~{BfUV4#7b?ENHn;)vn0W$ zTGHu4SQ^?#7t|5yzC@_#0oA;Jz~5C=5h@zssh(7H3AIfsO8p&?j+d~yKG0Fo!3>>( zR^q;Y5m}>H%=!-0UG-3EY(9+HQGov&#A1v4waD}7)pC9IPJsGzB@YN z$PbD;`Y0)F=}oN2cnj%}n%RqZgRI4@$IQ3HT49ASg<`$`L2Y_d3Af$Vo0ty0ggUOp z&<6C;FZ2-s8rRjXseh#2RF3#ybzSG2LLo0}=KtOSXHs1<(6XEOupBvOt;h9)f`yc9HW=E0*W?4 zm^_<0Oqf9why`!hnq$}X1{I)JJhhmXUvB#lfTSl&UZRG-=I>aQ_IQc-$`khO*LjC zrN}6efl{NMr7UcduvM^AHup?pqx>1>g5FT_pKyvzl_Nuc-sg~oA8?cMSYt2anpLB` z4LO~m#7C`X#s7(%;o+|-)6r#4264$ya*(o`d;&PkvjqcL`nxgup##~lcawD=u*L6Y z=-=$mw!NF`T!X8LwvgmcpgXp#FZA@eFS-n)QT@Z(Yq$uAZ=V z+h;TR46aE66;q00bf>ZlwTHCTofidJR2p2UgG80#VTu&f7qHJEfCS@2+a9N(Bq~7q z4%D&fIAT!!iIOTOK-t9vLks;$L!!GMj|RlGB9pxu*teYiWcyW)Pp=sGVDU%S3Hkkxbd$9fWtnRmsS zT1BfSEl9K&nJyVh*I;e1g~Wt{9jHYN9<;zT;(W&n^{}a9C6da{S zIRhH1wf~ei6(Ygs>5)XCfl8g!3U%n_uhmq|B0k89`vpfe|La!iXgZtm!BqXq zbhh_{USh`p%XK5LW5_`ztD1wY5?n68uvKz6>o83e(-DK~7)8i%`b>FYN8X`SlN}Ly z74-pu$d*9Gyf0FPtjx`TIZ~l#4uOnWhldSIjT$qX04`c-3c^RI(QUnpMH6Jz{&1ZO zQTz>t($-zjSvpLni!R?@W4WIsjtWCP?|G6E>?zq+?@{W$wg#*9omj1J(rP^dPUnNr zj*&*(8`aiev7V0A5O;HtNgyArFCYmZTd01;W^Rj5vEnr@kP)l$l00qtKsY3Y?pXx| zS^a}fA~u!Z!AbOg(j#I74s+leW-Vb|%G%$Jbsh@=*dCdJM$;AD*H+yr^igrm4Zqpc zL*%KGw2oSX%jlhW^&Ks0<{#5AT>T^9{$a-c9`OF^$oqp*URKSYrLp@y>gYU-k8ePI z{y)p6?SCwrO{=CIt*A=}1{$Td>V~FJkMm0F^5B`(4NagNJiEZ68StkNHxPc6YIbI# zKE_IHr32e*S~FW6`o$7(1ye8E)MHqF|F&?#5Yoc=&vi2t>n5xKrkoFND@Px|9x1(P zDtmts7hFdMw+s{TEWrN{A;uLxG_Y`f!M1$dO@FN~`}E`63`e00Aa-1A;`s0E(#Oe( z9k6|)_{c|r_{efnmp4TkXKB|IQ)chpY~Uw-^k4O2^FO&2AIYZp2nJW5(2J$)%GaOi z$?n_L(|HK1E$t-yz zl7@9Ym=#?5tnk6ae1kMXCi8)?nhsOQM9xDRZHyBSYNMa7C+oO7NAFE#Q+7AS6QGC` z4)|qv4}C@|>rj@fzm&qJmi3SOASE#E;g1C)B-i1OUB|25DeSGXsrn%)EaKCq2uc6o zxJsym7lN9Hm5u+@G5Wn8=(Wrl-dS&yPl6p`?nxH(HwABV;EhWhaQsel3IlYNpT+HY ztVQ0>spo5N(xzlN-jI{!W3+U%>cKkgNf>J@(a53N9?Fj0!c+$Q#->P!k zWC+$(Gq75e!|QIy>UpgRnt*q(XLh9En;a5&G7pVeoFtA4&p2HK=i@o z3-`D4UF74@1N3*&JqXGd@iF&cs*Y`x=NzW^a#ucm0ZrYSMtLj%;r1bPu6Adh`)d!mKuXN zC;V>IERSc|htf7;I$8e$k5OTDvc+fG=rhbOa$BG#W z1T6lXO6nz>seWnUT!c{^!whA!lOp4m#VaZuvAhQVWfmP87{yKVOZeAry!&8%EcC+V zZPreIIPMie~a znzlN&qWbpsgQzgs-;7`jd0OU_FIYu&2WN^N*BsSf=9d19-YJ#87yYPHaX<(|WGLAJ z(JJmo@Vi4nc;iN2!@5!d3hj$8mN>!od$flIV)^|zHiMdw@J2c#0#K)OisJqRQT$?M zH*H4gVv=I|a(TXjrH^`CzkHCWa?|NF2+=zOMXH=Qult3BoxuNu7lTWI*niVoAkZj_ zdc)fdY+6jN??WwnrD6$Z_jpqS8eR6j-*5IukPSjpd4>_>kM?N;yFuDR{lNYOd3LjA zKrdsS?#HZX(<*!`9q-G`I;fZ~+3^m}!E_wavT%FtoKb)JsnvpEU$xwqc-+Z5<+0{?CYIeAF25BGJGd#{P&>)5G% zaiiOP23gos!OUUUcf4?|l-AojT|I=NDOUAlzIrXM+c`+Urhda)wWS-0=k|+(!avY9 z70Of3p%99SLRWEEI;=Kf!A^`Dc!-tkP*ghDCoW?7B^qPAK-fOQ)l8Ei}co*!o?N?<_s%Fdu0~hl%W&ZA8;oOr!N%1 z20IFWeLgyh^0a$`iN=xa->wHh_;~aD19op+QZ9)Le_*KcEOaO`3_-02+6JtJ?hO*@F7K&XEx9 zbnkPJ6PIvBP1)j$o#Y)y**6P@BcN2BPzcN3=7u6lO8K5@jDdKfu- z;wXFo6`z+D-=^mY(*uW^p~{xY)wo%MahT|9p_%6+;RRV(eca2$Wy-F2(G-q;62kxR z(_>QhvqcS^oN!3~zLF9*^3LvP-Ju^5kfzVNLK4*wFush(xPG#zA>ChPF-1q% zPx)lL3+W3%b-WN+tw2GT+VD!3hFZvq?ETx#vqQQt1TVsz)uQgLX9fF{3~z}|JAFz9 zHtNu9wtD|C{kW)|wfl$b^cQMayU!CN%4>*982^$D`MjfkYt7EdpMR$_9^6hEo^Qmz z*pv{?U9Yi`U#@YQ&>$?M_zn}^nncbdCs!fHL3`ic$ilp(;o+N)_R_J^7Xe9|h>rZ7 zNTkqPC|;zEw*MM-HOnQ;=sq^)K%#RSP&WJ%YvO^oHJwOEldCC&c~dh#iQ*Xz-WDyXebUllGvGu7K9w+) zlH`+(u@p7R15h&5F<6Utfen5NC-Y!&0DUSKUXvQXn4l#SiA)7;UGmY3apB4W_WOah zx~Ew9!7dZOpAOUi5@`(HafkYfw_1fh_993e5b<7LEwG zDPzze-g~YnKBspmI0afZSXtypHS?ebw&q}8=l%!9LLZAZTxJbR2)$o=5w<9j@4}4% zS_3dKjksQHnUGOoRk8EI=hMjRw5p$DPvsY@Ew+Dn7f#WpCKX+I*XqvetQTKaUx1S$ zj=5=g>ek&R=Q^zHn*0h(JU$-?>4iJu%6pB4=QXp4I!^z%o}u(AD0tgL#Q7yy zBi|A9zJ$Rj-~I%7W|#5tcupz$DE%m0g8|_G75C+FOf28$JUz@;i`txIb)ap_{90f(5OwMH9fd0V^9*v+b4L~9X6LEGB8v=&EealbKc zZKsIq{e7R4YjygW_nqH=lg}sjIs5vY^E~IA=RAk@%W-K~*@vw%i!^k1e%7AFOCYd7 z;qlxLN=XXsny_JX+L9_$QEC3ZT3CQ-J3{`+-)Kdg`6@yErOHmozLw{L_)j3dzlQ$! zrcwVAeG5e|F--&m<~@bdVs}Z9R*?Abtj1iH=8t+6nU3fAJFmw|yD8JXK z&cC2_KwpBH>7xPGZnfD|b1)XZRkScQ&)S>V89XD4^(#N9!gHe7|q|je{(`a6d0!~V=f(V0uS`QZbemW=71r%Z^LX1adh;Wn!swH%~QA2eA%sb39arH znK+LV#cecMPUj9%g%%C#%1J|5zs{yBSZPDz;b?TuH(2O^B?~b@mxq@aQ(zrt({1R= z@>2?;VMEc>vRAMMwu#ySM_i`ZMiViFj%Bxd^U)|lbT=ODCD`1*9yKP9H1H*{p%v{M zvX`#Mc81X$BKz9nezYn)dHHeor0=`fzXO@t5NL}sjUG{2XBs7c_bBGDiFgVYTIC`( zXuv4+P65dhJ_>;S(dX9Rt9Ykeh2SYBS1zjAaQbl6!1jQph@1{2!odNK-_w2Q+IpK zIL_eG1szof%z`=;CzC-M0%{+!;)-Eia@LpRGHNTu3KYwFf$sJ|*9=5+^sy#~j|Sq*L_W=9?vP{M?J+z&6!h##fdU5l zb|hXC2B@vS^(iGLde5RZ-kC@Xg!9fAe=6mdf<^RwT+a|R8tCo-zca>pFs>xxvQR3C z|3MD|+L3!H2NjTL+kCWiYcOx<@3!LkF57StW_BZ|4I10o4O&|?Zk)h|BW(yM0_U&D ziwn@{kmmYWf0xRSA-RdBtvc%`mg*hcO)mNirm^)HcIZCT$QJC3TZ2sCP`-!Ci6$mw zxJ^RHmHnbnAZsLvOxXRGcYu~a-m8ybeaA27Rw_%AEv2;N{s;qLaTitd!A7FtV5i!U zC<#Um#>r_gil1<-6}@sYRl+KIm%)G}VkwDiaRFj2icpi7%ag#^k(WTUx!(Q$Kf2@x z+OTn^EDMxhB&QZ}0TR^O(8oV}sp@`JiIIr7P?UQhh{~mf!o}jUM|huEOn4CW?#J$k z4xj`r7 zpdPW1KG8}mR-l@-)qH>GYQFQ5{7w#fBM-f>0dG#rFQclyX!&zA9X!$RU2Hvk6R3}R zpj5PUgSLxDk(sR%7DUa*W3a1q;^|)0_|!*!L>qu){MBtm8_@b( zjDDNS5BY#RXuYhLzaHgYeWJI0AgBv*xmZ{SL_G|PV*t2Z`4=ST>(e66;OTXs<26@5 zBM<_;duTY7)H1h~I9x5F$WNh!m`ol;Bp`edFa!*1`K4x57`->dj{t`;!!7Qw_TB+l z2myK|@!ogEjS_*$Bv<4-uN=mtX?Dg9f)n=Yyx$`kSKJ9KQGd_{wS}YnHpJO}m=ymW zre5E0>$!o#L;p1d9 z60?OsFKTXpI#8}5O(HPpkQ1AzLqchYFz-|gKI4F-Xii$-T!W`ucUw1hUi?ml=1H1= zlse2rVb-V?v69E7DJ6zaDKYn^lo%vDn|q30fzoV7nl0iuIB77;Jedcl6RVq(G^01U zQ<|TUp29HwZQ#tQaj$QF$i9yl0$a2tyVq+U!q66yK-knF-4@Cb-^22?5$Mu%mpB@p zoV?)nA}C6bya)C7MROXWCu5lW@Uy)4yCMdPy3|iuvG*H<)@viC@^2e_uOFwJ#x33k7Q29qjJ;>&m6@dOIghanuUZ$1QoOCn*Y zL4hj;?+{XSYpndnbU*9xA|m<1G3T6z2hPVF^;p{C0&<=XR^$$!47%v4`b&Gmi=I+BF4*%v8clMbcDW~7}FKL71 z8Z%;#L6C((@*Bg^y~D7yB~pp)fagAOzj)@MkjJLMIZ*!daCgI*DVm%4?xeF5G`HS! z+s{6v`O|yuO=mlId~zrXmfv|NQJC&vP)G&tfX?pbvvES5`|jD8;)|GFu-o7RSd0m! zKHcOPh#m{V##&E^TYz@^j^&rPw*CnM(!Fa4K%j4%9`(h_hK`FHxnwpb;zsceBtjuz zeCZ?iQ|DrOje-mHEh(G^*35yGHuG?jJ(JJh)WKLgoWrx><3DnLd`{m#9YOF2Z32ZX zzr(Zbf`y)%3Di@*jX{rz?nwab-Gan5h+l4Tw>=--{Ub~Qzhzc>tMd4=AWLb^=Ww;m zy^bB@@a<%TB9krdk>?XNv$nVwo$qF!4#$kTU+6XW$6;*LrU#>P4I=4prh7bfAh#`| z>m5Ro_8>zbiLaTcj7Pm&jTkFM7*&!}23N=FVuLC0c{6e2F2DA>BA zQHh6b5eLF4*!nPrLq;D6u}pXm!@n1{iIA+g@XzRY9b8T1iX*K+%)oFjA@^XplRNK% zp-UX_#N@nTo$rb}yn&2OCP%Zz8ZLJq=3a4OXct=^GT(-Xj+}a6Jye3>5|;p}|L5}D zXD{?_^Wm>XbQ88YKm665Aa~Lf4t5WeJ11?qYGl6TmU^8PkIlii_a)e)Tk0dEinf?* zx!b;<-OtG{3Afx~-X5A!5b*WZ>esg-k1c`?(sL`sH5l^0^#kstL=P|3ep2RfcexlZ zc}#4G6SlQ99t$_R^Dm}r&JA@xdoj-bCMX!tVX^;m#K(G!TZYB+ifU}iX<)WaqBEum zxZ>R(HFgaAfb^6Li}PHOA5e0Z%BUo?s+9b4UA9g|2Rs&(HWgY`^p{7|DIIGH$IB|`LHJDcX_E9^?c2J z_n$*G-@N+K&|e$0nsI&IZEouO#=OEeh6@Du9dS3!>qYDN z>@3Ayek*e98C?CrZenl{rzp-}Gg?ezxuQ{5+@T*x3?jQNH< zv&Hw4l{w?chXF-v3G`-uesZL`s*5 zuJEm^?~#gLNXD1%ClyQ5-S6B<=sgHAFN)JL+JN6rR3uPdmd<+xDiR%op;X5k zh)}XV-F^Gc4Dhql?xux=pQYxGdUv&Z-Q77tkvl+{EWGKSrgUkqJHtET^Ou1FJJ2JO zUUt8ztRB`O2+a>f=-R>rYo~%NgZ&f`P8w=h0yv`jt)6rmgok>{_maW46Ab=j_~ch> zW3h2#k%sKX{PxJHwT}y3HID_Z{euvvc`0!1--M!s`}>?@u=66fF;Ig-rU@+MBnazXlMfu` zg|>+`bWbYZ8R!ejZww^!2oEslVxzYZ)T-C#KSR?d(-1Q^kW1SD#Q@RNUrZYi)6N({aAqG(KZL_0F%gpH0SeNWvMoeTunI#9SEVMf2nJKg5@dJ@W&E zP7&`X(oT$DOwkp$@sFNY0)!WmLRDAb6~Y(%k>&?W^%{LM)eMW|Isqw&o)E+J+hK!J=g75NdN*+~6N5E_y@ml|xJdR_gTJb39eDt$g?? z?9rn0f=TZ?SNa~;77_UBIzsDtNfhSTDIK{}Hs>&?%+%0+35BOqkn^-zj_27Vsp54w z=a=rqI54jSr(A(ifnb-H4us2gjLt=U)TaLg76^jcxq`#^HeLibXgKH9+RJp0@)9 zod%_E3>1u-c;*QS68fZ_2D;1rFrQ##2FVXo+z1>8<2M58)I;TdaG`uglZwRGJadAC zEKSkNp0|U9p7uFaDDfEqXBd&Ls1c|gJ$VpPmh=8U5FL6V`A*P9|2o4vzbg)Wb5?P$ zA=eL^6E4ana6;N>XsH6sE(kfFhHz+zfrvhug23{*IF1sP>nK3nBXIHpVQ6ZP#7=UU zt5+7Bh~?#UA=on`SV+i!1(B%#5;h3dkOome5W&ayKOWrvSb$K@UMG(cC-(!sf-kl_ z=SvvC!?Wes2-|ne^L4P$D`^efWEVpFOKTeda#IF3bfFDxVC!yn_F8bvlMo_A=Y4Y! z?8LKL^x0mR?%(mI^Dw$*y&q%!SG}L+#emcfBvP9t()RG;bQKlsFL{=S2&s1W3L09W z`ObZEKHAEcgYej6p@$?g5T0l+KfdY-i({~W4$MT#pzpK-pg}%XAIzIn7RU2ZiRt`) zDGKhDqJ~|A907z=2uQ;YVodXl^LWn9s5j5Kub(NX!Khu7qL%a{SO{scZT0&s)3C|| zrvAN~XI`iflm2NXPU~^*fQ{(N<9t%$(xsT04$8N>AdAk#H)~w|iM5lxT|6I$3Tc|+ z%B^QZg-11-V27t~xR9LQ_d9gOBb|8n+MY*0bb|x>A*KTTaKMt)>*@pVBZg}CfU)X$PNBp94x_K+AW)~wG4L)GaWl(8V|s#TA4 zZ@_HHYE_S>uq?$ceuVtD&Ysb21&jSp+_#Zpk`_%f#1!&7BLYk*cmvK-x))Ok)TflA zFwpwS&;BpS$5C!yH~EWrv`j3#UGT=bm%K3_&yRtxC{KbfhEArsKF5@KfY>E@V>HqS zryK>u_H>ejZc$sEaPd?AM0JJuzSA>G61oigrp!Znv%P>iI|#x@#tvd480HD9x~ro)$@n@0f(d zvB!XOp-VBiaLfnZ3(=k)5kkCXLRU{ggkXsN!j3VzvS0jR8eB2>3gF2NWuB@CVNTad z3lss!bp7Aj@rPrU)Fx6R{$o%5vd|`RzQq&KPDr&(IfRZ*z|1aX9M2i|6?7=~)bk_j zklhVpU)V9XMfD^1oMHH)#nSyPh9e)a0i@|EqAaJ)SDx~ALT5wsFjB%C>JJz(@#KC; zA>3Y$+G6>HxR+wea=!Y~Q`b)DWZwQIT+olfKjNW?Wr0oN%RKI9a6Y;JcrQ0>1ph+8 zFvFePRX>J~uW!juKz0Fyq(zKh?ulzJbTVm?K``md9j5a?bj;srb`H~FttdZFLtFHo zib5fbk5QNVB_M1&6oqm=5uP;6v!uPywcSnZ6;Q_`A!2>EgnItgUNDTB)rC(YgW1wh zOSwH*+5hM4#ewnya&?Jgko<Dzcon6Y^U2jnurP>LKuc5x?v&o+A9fWjE zLV)K|2O&XoSL2Z)g#nt~8qXt#TKThm3q_-vb5YQ{F%hzEQPV1cp}+1qWbXlH+XJHUq8DAkJw7_|x?p+d=^yb?H^& zc(^#5;N(1lPm-fPy(%sjZq-BJnGFM;2{$3Joo`ZMKF0}H&HB?@7j+R*#n8prK!N%* z!}x)ozv<9)*#kZMb%I&YdDb<-05Dvr@m9IWSnAOl^;afw)1ef5W z4(+5}hQud5LN{SuyC&G6ksLQ>=aX(jdH>dTy9r@}{kHY~LLKC%kDvUswSfpEt3nqc zuvoHkenv=(l$Uc6klG41XU-3>G>D(#%YgM==K<@jll=?26eVisL<9n}m@ST~Mec($ zj?;@{b!aXO2OXUWjl=`1JsyL8mMHxsN)Y!rPb*RD$c%XOo~rFYqZonM^edv(W<9jC z-q>!O3Wl@hAw=EQ9zL~3e0DaU+KLx!!UQ~M>sg&7mTx>4ohT!#Zw{`AHd^I5r*etGGR90b9u0L?HaqCp;UIg=qU@ zaGzW+#ha--XRj$QwVkl3vtqFD-~m(FbQU-j+7mLlJ?|&wwQgtv)R)^6){Ae5CF`la z+#a3~x4LWADI_1v?O8gO$TT{{Z=Fr)k60C^>AHUv)tB3qo%Z0*7$QU(1=N?@r|H5H!*=VC;5u%fY780@G*pN1Roosi zHDG!`|EcJprQANKP%~QdOd;BDHn(f+o8vU`gASp@Mckp=HARvpHd705d%IO3TjD-A z7>X9)_V(*TcE@dOhCPMbSvu>@0@6{#7;X=47dJY7=1Y zpz^qd@wrcD#+_9HCKi%toGE8kI}h)<6=MWGquGLK6v(tjNFbjWvQlBND;1 zbN|Ibn*?tv1!>%k6CSKDb0NKb3$nH+=nCZZW0r({n89${CL-(0=Wz>Aw4-E}(jyiF z6mmkw30^mFY3$JQ7#|LkD?mL#`FF_?S@qJ3-=HyY6Em`T(AFzl8wJnUXWEDnO`{aMl*lp!PJoWT~2;6c2StEhQGoX^HxUXNJ zQ67Ln|XNr`ikycy*&EqQt*XW4sSdH|@AK=hp)iABaX$nrOClplYygnNJ zKE9y3-#Yl3#z8`k#Mk~guE2Q!-Ss7Q4q6E(vLKU-y$D+0q%0H*vK0G5TuLTsqRvRP zq7K;1F{hctIL{VjK?gqW_GMWC@XUj_=OGl2IQ4?m%QaCwL{<FT!B-s zFTj0)2#fUo!AOX>@^=Z0l$pi668&NV=ZTk3b1N=O6@}p_aM>NZ!P=o9Td$WKWkFcp zSM}6;m%z|q)=$LIDE1$6Gg@FN<{KCNo=5)1tpXfMD2R?eMO=R%l^a{?F$66NG9fXo zbhA`DTS0a_|4l!LxR zR|m(C4+-|y!*Co84GP3t>)QkM882O>!VZ+64TG)za7X(f6w;lM(|I28xlM>i{Cg-jE>!(bPGj^z? zsGcBT>If*9D~Fm>y^dY!yl;n86~r#EhCm!YOnPOPkL!G1v#VmiEfwCy^dgOO7w)QV zQ5qa~18hGVS6cPOj?;Eyl{Iof^#n~uiw`nrDA`JsF%)RF3HJkXTg`U590_UE7F?mI z*l&%)$xbI44~w+9Rekl2JA&go#kA{6aK5s~cgzgIsr|rP*rm0*gk4z&jFP(n`RgFA zWW?LAg{Cq^aLOjdE|t~q4#B*T%2N!ewpdQPwSpy**A#0Ks90-<>U z*659R@zB@qe%5NH)M^mDy5Kwr?mrd+q}c=YizF=8a3E&STWQuVFjoa(VFS7GgHZOw z%OX{*g`{2T0h9 zhG~*vgaSozY7Yd^Qh-1NSPI0*b|{3acO8wu)kP7`Y{Jz=z*V|#`YbKkEB4#id73q{1b$0i^W{%n&zUzU?i~O-375fo@n z^4$K@9A5B!&WsT{RQtv_7UE-r3f}Lr>D9UURFJmF2 zwI<%(k4>6tanqORA=jUsXUL&0TrE8a<-!l*j zZ`$x}UpwqncBu5y>X!slVG5m*p8Py8~#_tg|NbWNi=BLx++R+cDczn~*@G)ms8Arx95KWb2HHHV|^jfxgH} z`(nc=ed7@P;jK-albdfy7RG8o>faV;I8tJ^jPG@+fER`kMP)-?lT8nS4^B1Kd7Z)a~hrQ-# z?X)Bu#hb|J$oi_zmk&!Zvkt*#(Yq3*)8D?iI&vKU>i2W&36^0c#IW7dzYr!C* zj^-dLPJrsGaWn*Yhf#Vrl;`!*ry5KYVIhrD#R#}rs-J;2mZ-q)=pkwAz#-nv)LfNs z;S2i!cx#aOECaheD~H*eq{`Ls;T1%~fJe?ocS{Z}Qo7#A`_)MOnoa#V3H@0}eW`XT zhLgPXXX;d#{GG~&40^VZIu?Dp164(~CCIZ$&GUqHeUbUSau=EHpsM-d?{?G9G2Iw~O%J`P1#o?=}2G^EwOFqYj7wRR!xn+`=|ZC|$R zuE7=t$0wmaTd_#yLmB&xD|^GAxY70&pmN0GHb_gV4K`M#`_7b~szYYwH_GsZ?!Roa z9RXsbTHM`L_BF7y^3&Y^Ci(k*#63<+4jvk{h6Z_@=QwFvBzFif3Mgv@^DHXMdKyPcjCUsVR_K+iZJzoBJk?~#CP39 zo#+P2Pku>KtoAp7Ajmf_6SITEqT2Pa@>+g}WHF8*^9PiXPhGVu%J$dbY02SVC^eMw zns5be40ffo&}_%G3;fSe1Zdm^>VXsO1|@1Q*&TZ@G;yu%;v6*LE(%C5ui`m3j~Uy^ zIKKT!i7J4R_O5(GaDdW%`<(#d-!3_tb=zBEM64|PFV5{2}UpaJ|5^V8xxZ3-UBn3J5_J{O#E4!H&RQ1)U3G9dNJVX?jWLJ;XbQaDq78 zU~s4mvL4hM+lZ5PK(O00s0R)yKSuRi#L3uG(OR*sb_pBq#Y6m6fy`Vq;yJ}NZ;=BY z&8&BdRjRq(*$ReVs3$)E5U$JyTB^6iOfxZQ>uLhCpu(y{)^1!1D9zKj%E+l6`v^y{ z&0K*rv6a4R7VWEhf_e%A!|uF^w|JEzoJn0By>(= z`YluKuc&Z~3J%Q` zv_GRL6{e{0VHJ*0;WQOis&FlbcE&cU4m(wNNQFPDutkMIWBeKERG6y53>6wxI9`Pd zRrsO`Usd5o6@E$(`LhG6!)X=%qCyR>Y0|H)3S(3lr@~|vrmHYxs6RYi@1)-V6%JBi zt_p{%kiu-8WEM(<_A}BkhoBq^7Yf7r&NzW&_LSN6tTw#3p{c{;9-sl;e zCp+{0_p%+7GuOR*1#Sz)ZN`!OBAi zI2LNn*gAMjgvWZosemc)m*e+AqJ`+C4&*xwc+A&vpVdvMqQkIjIsnOkpKgfzjPkT7B7qhs?% zjAiK=z5Dd-*Z<+nEYpyo=G;6sYuda;Gg)|-iH|VrLfcIC=(vIc7Mhlp#-w=mPn->e z9|8#jJcmEQd;iN1n7bnkg(bI>_;``RWoJq;XN}l*ruB83_0w*=K)%C^u9Ux)O~Y1-+i-i z=f1ft5GS?jFCy~3*%+^ym$TU!jCEw|v7H^mzx`MtT!!-R>vj$XFe97_*);?j%>#WX zPKC?2+JkWUTC0P3zqSeHXzu$mTMC!HYyrk>CcP!jn)G9_juug85J=m zh`y_|RHs*Y$SD5p&C1}?o>%W(9{x-~1YBad@B7?kqd3(LCofx%?_q5-GCRaG=)ra(ym{=wiQJ6&hYd)|PMVoCE2%VTYEq)E z$?&D&m|;kgF=vcAwF?U!WP30gAT15 ztLuivnSmv-7+nfWwX^Q52kVK4YSP$4IIzvYk!>IJY(LhYJj zy}{mOZ?pGxtA$hSGW&(yWIwS(>|H!lhF2pZy1manXFJ&I>^=4wd)cts;5KLsdc!os zbi;VVI76vnlwpElvSE^8q9MW%Y3OU{X^1s2!$89n!()aL!&t);hFONGhC;(U!{dh8 zh8c#LhGIjJVYFe4VXk40A;u79s4`R-{%BZbC^Kvh_>s8->H^*js1Mi?uqPn>ZvpE9 z-U;|Pplf17z$XEziPI7{vzduA5_R1Yx?N|M#7(SI;`gj`qFuLztv7rYu=UUgp)g=; z$|yk=vu8hvX+{0LQ37+Bz}7wBwr4sNIl--I!ToUS5N^Q>g8OA5%AQUqw)hQFVWA3V zsBp0g)hyU5)xJ@MJ5;z&g(p<_vkKW0{`4bNn54qKerRWTszZ?qXQ^ue%Bdsc@_cpHyKLhZuaXst%v3@GBKwRADIXCgB&O!XyG6 z&Q{@bDtu9euc~m93U{mUFhSxaW!0foh3)70GfY%rUlkUpaEc1&sqh&Uu2kVGDtuRk zU#akf3je9X!1=^Q;TH=7{d%e}PlY8aoUOv8DlAjsN)^7U!c8jNt-=#3{8@!O6Z+i8 zPdYt;s-&Jq4(F(F?ls4sQvmlj}*+VJ??JPl(FG_Cl^CS&YdBQCI z->6ybW(hN-)GeDDKcjU{)d>7w{Zp@I3EGH~_3)`?6;TwV57G%6RIm`t54&i8jF+1m zq!a!M1fBxTn)-Z@UeNM{vZ2X3&1uCLFpk=>Zd7C`bYJPu9tm71Ma1$elhjX`j&u0 zBcO=(8x6EkzZ*4f}6LlGT)|D&z=(xC&&Cs#y zr|TJ4cGl^0g;D|RcNIOVJQUq#+7yitW^YW|m*n&ES+=#cAXJKb2<(4(6#A%CK!x70 z@n(Q?z$mmz?2E^VasWb;lDC-!eN|{uI-Xhhf0kyfEPnW3jUbB~@LMG=#cx)*QWpJ) z^Zo6)g7neOn)mqQ#25VWNi<~HiJv+#Zn8$uNV>do5MACwSewk~=QjOhjs&M!x#M9o zO@?X&76C!kY)8ue*-8+0waLwe1mh*w<-|bjNPs2R{zR)g53ELvRyH&nY%4=M{Jmy% z_3}hlByLUwq{FOn-qVwiXgSO1?M)E<07wXiRAV%y?NK z-J$eu-o=UZDvi5wou-i^%q_kOz3D{hqcxH{>gFd@KS@$BM3rQgX58-4Ry2%5U$Uvt zF{4=wgr7WRXYNK);A&x(c6Dy#ZZb-_Aaqym%cxeC;;V8K$)+S|c8x~ZS+tdioexqL zq5R(|cWlrIH5KMD=FzsoTctO;n+5YI^T<&nB>7he5zme_g$yNS4smA@aHZ*1Vj!+{ zQqLX7ZzyD_5^InekCap;zJnwgC6Z1gg@K?zx|q_r{dGt_O-VsWWX}?BxrzpSb5`*e z8Y9ZU`5OhImf^X3Y!+2^#v*hlF2 zQ6Cda(YqA+H;~U$^Y2sMT`4vNoyPyXJ{?kVq!OKDffvwl@x}PItf{LsnBLsN1pLFM?5fY!pw!v z*n22aN=o+E*xacVOCW?b$1K?7YiwwUMu5Jfy{kY-fQLB1(ONp3bP$Ai>`@;d<8e6Y z2!4mS4L^Fj3#i{F|7??!o&$`%ss%9&qXS^xqL)x>GcRg&qE={bEb%BQ(-jX?+bU6! zrq2_uigkbzy&pm#WUTUjb|jP&+x3;=QqXBCMF&0nPkYyJA%A9@Ifb)~NG7TzLpjM% z@2f$Q7lTZ~E5$w>)yum+h?;*NmB>*oy&Hq5u|ZVhN-=~`P-&+RHA24el5Y7ZQ4ofB zaxS7pcy`OSD*16uovCRg_chiEeO6p%ABeP#3^v#{^WRE7<~^ zvrgo5p$I)BmK={TINllO> zMI~uo`ZWNMKv!%8+Lu8z-vQ*LvZ6{9St;&E12n!iggqSOzyuP<0pv!G3}9?{PX>`+ zHXvObd4Z5W29cjOAfMpK1BARDM6OklPVkjDb`xO*qpAaYL(w#jbQ3Z@h#b>^j0RHh zJxR#aAhK@*^6InHmSKd<4k8m8kcWX3e7!BHs**Ra6ibP`Qryl<#Zam1L7H>$q_73F;B7+citF&}GChS1 z8sOu_79peY{eZMLq_5hWA2bx3r4}P+<}zg=<3zd7dAVF(?tLoPI#_OAL%E)6IZ~&~ zq(KJ7p5w*Bc(JFbSl?i=(eOtY%5A+vf`D9fEX_)hMW$aCfzr1VF2FmQuuhnNXHM@J z=zTOu-$DgsdglZIq6#p0pAG`9L0RN2n|YT7AwQ@Pd%GgjibbeHS9}_kxJ;vvfv|^= z@$I8(R|N}v_9sc{v`0#E>W2&AH$EZkTS4s09P5boz8wV2fC#8Cg4^HoAoVXIhm!35 zB1rHkFJX@>JwyPHLz{6Vql_g!$$hkcNRjCVDiQ4cz2pgK)Ke=<;DbAOj|5pzGx=ol zT@dgac%o7rkq>wTmVOcle+XrJAq#Kld*=?#Z(=x@lZu;5E>k2`f&#HrrmR57LG%~)4LzDLOR9Qk|@NlubACFE{GhH~T# zj%-p&PJ!K+;^dNZSldz>@a)VK6sV>G-%{!nr4AxhWJ(GarP0WCa8Ps*L?exD2|awu-q$-ll?>=C`~43-&LSjNg(%4hJ;2B2ew=~|_RU0&3ZwMV$>8BS5a!=o zY9O8!$d{&4YS&~Q1DOun$vOh|6gF@+H&9>|ps zy1a}sn|xl9D|aJE5>VvAEG`v`faG=JmSG5!btN&71!4>&G3slLMy;6Hi}AQGTP+7V zA~TLi^3_t3*dIe8?t&m0&LiGYT(m0G^BmRV&irjY)FF%S(Zg+e3lDZz)n{`Ir59D~ zAv}CVs0W}574#TUE=BxTDlCB(3a_(UzY*T_Fc29v8D+Z(?j4W%n4VW<%5Va;8`C{1 ztVEs95{pl_ArvojbcQI+iotCv73emPvl>SPub+aZ9zh|W`F9%qu>$~w9EPV!aJ~23 zTC*Cjvh}+J!7P{Ugsud0pv>OFE%pOZl5kxROhO{;Pxo?}ZhKiqfEQXRTEVF(sXu-- z>=6`3nPzS=@^57vvFI)Rf1$m)(w86`G*{<^M0yY8Jahu8rv|CnJ2isf&ir^dn!(Y( zAn%Q8K%a6_zG*}LE6PSRWcMN~xf2%D7njel4*_zUCc*}G3#iY_xw=qk={mHUZRRCJ z%Lr&9LYza7D)9)Wr%})3Ql}n8GUJGR)>}l>YY#Y4a0KcLz-n&&8VI;drg>1-(#|km zA}J-=z+xTB(LW?hkQ@STY~%rqhCvvia)s-5z@EFw>B!M--|xEZbo4^6e+H#_-=w}9 zQO8GPyV6f7*JTIKMe|&1rbovzE$0H?OMmk%6gPo=Y}hN+tZEa?DS$$hD)~l<$>Y9lAIlLK;5(G8J>1%80xqfN2tW z*8V|;;hdrAEl_wj61EuX^=(DU+XN~o-K3ZWHW(8dn=~4*sSU8+1bdEP2hmWKP6W(a z_*<#qgzYow29PugI8sXG14Z7));xidDO9pAs!&V%=AhPosMU2(>lhDPqk7F>(A>*( zzwd_m75hH_%|on2K0v&8Q8`rj39QB+Ws@sa(cS^rn^-TY z6sTaI0ODV7{$d<}c_lyDn<{4wEuCGU08L)TPySvmQw-9f&e))0*@tk4m0~!6f}bYw zrxa6t{WRywr zt(%{6M$)arl4o2%lD}qd&hl>4g{{zj$#qw6@2IMTSGWpT|IkbKA(HH83#f=Y^A+${ z#gYI3dsM=m6x8qxaP@V`)zYn6o9Bs;0LuX(o1)}(N_D;i3PJZ`8h#nV_fhnss<5N_(ah!0b%`3SMf2UzAE zRQLs^*_%3j)ZzpPCF#0VPt$c*W~Q`JqB^$-r{N(54zL2HTj%!L>L-+;om)A(3`es? z)&!6H{~$4A%mabEgZ{Rm~h~6$Q6?C4LxR_ z;!SglJ2_A+r-!D(k%Ly2ApICug)}o^4l;k!!c1{iJqXQqi!-PsR}t3&*B*<6&7hrK z#DlYeZO{eH=xhvrNEdN!lqo6Qltsz<6w-h-P|5r=(6XQN<-c~q_=WtGtNF%zHr=4> zh=M|-nNb60E1xBIcJ3+Qs(AyC2b3@uWub&EsuI{zWUa=t^F{U^=xmxRPzyBj?JTa5 zIIfXQ=VP!Ra%HU1Wo(KHnEo1sMbkEcm^Y1@QpWb)g7lVKa6BY_Xlsa{omwyY_wc>` zpRMv4F1nZdDtA-{IG`C;;}4h(PZugxDZQPq4&~x6z~ce&A0|gWJF&rKA!{?V=o&3K*W%b+6IFnESrP3JVJX*I550^WMf# zuPSKJrz>l_Gy^ zbLdzqJRy1VLL}pf`3o`rHW4z#&$^*b$yhPJun2^Ly*E%LK(Ux0JPM7mb4Jb)_xv4! zo=)gGR1VAp!pQslIg)3@J>*9quX;`|rdDrST3RsM2K4?!l+K{iJE^q14+T0j6!1`i z`_qgc`#$~x6cDJu3KYPW&OXT`pnn&1^wL9hEg}fjinu@8itKK}f_dfidTBDiob|-k zFBp~q%KE7CZzYQQiR+Oez#~93ONHW?SJAL+F=im<>J8nogwT7nmEDz&-I6-rtp%BD zf4oILH(`=_qIr^evUv)Inkjw}LRTHS4r$v;<*9CQy#}v@K{(oD$y!(X^wIPXAyMLAsNf<(FMa8w&jI@e%x5eRTk#|g+(CC z-nkSAyvEkA!A2f!mtM8WH*NAC6{S%Za|K^}r6_fv{uyxa42T~t$piGTvjNI?sX5vy zJ(ZZW-r7SJ#g-PY{i?q*Ev;=M(F_ruR92;RbM90q)cp*S*L z(;_n*E)B5iyEK#4^rvH%g&>&~7+c{M%Xxl!L;f(&k7>xO^n)7mK929B=AB@0g9AGM zgCOo7`Nsdq-+HND0JZ$j|HvP2$g34rHRM(L9XvmgTA&jAr-9%+7wENy{P#S+q9L!g zaA8AUZQ=BWysD8gFDd%;rP@pHD{In+Xq#J=bLlO$iQAOB=`EWse1ltMBJ6iDYpHa| zNR3T;AD}>)Bpy~+*GobCSVodYtE|eX(w@juM)Vj!k99rTYWJimJ9{KHmb=3w z?+sAS_J|wM_aRylW=Q215-Y_gR-*@{@O_dTOFQ_1*soKuR*3GAD{ym^#RO0XVjBYr z-;>Kpo=;VbJ=-{^_J-x7)3t%x-I&3J$0c`Y(o`g{MMQ+|qIh!nt~())r(L(hmp=0Qx}h8^p;NM0!8w}Kg784>5F~AGlMWTy+?iR(Njk4Pc`y!IVn5_k+>u;W zp;kExd)wvaz;Rva%dLOOQ_lB{iOX7s!@O;8fn)Ej8)Y7oCj* zuCE{3(x^{wjZ-wDd)GaRrCG#?Y4s&%ZSI9he|B-sVmkovab|oFflcRbB_ZnFHS^rI-T}#GrLQqz44rZ2 zBgpSaWpEPO1abyqpFl~Qe1V&NAa;8vRlWSoRw}8Xt-{NRyU9fdVuyj0bnm4s&BeLw zr<;QEsWH#E;JO;uSCwSpC=F=AM?2-{cQeA$@Q;apZvn)SOL9OUSFnW$3ueGNyY=m; zeZi;<>)Sc2B8k=%PFD)D|Fr6@aTVj@N!^-dq`v10jsQJu=@1fHPn zRp=pUcI@zHGy=9+Gbi9ls763xSH*QsV=X);WyjL?`TTUScvhNyBwY%L?eUDF$w_hk zpH|EM^9Qdh^?%ywDix`xI=sEPAx*HlzQPvJ-dxD-bwYJ)uaP%|0PJ-T`6K1^)H7Ds zUjaLI1aC1HpjOwPf!37$nK_Vk?In_ceW;r!*i~hJPM63KcsEHd(7R((dMkHwQZ+Uu z+2qjX?%eWkQy;Bny>h(Y!_Idsn1`=qVS{NTt}pyPLOv=jyL_ESug7lCGdin*vk!As z2YiT*0o`h#`yRB4JJYgT_F7!zB4aMjbUf zAI-iGA#n!uA(Q->{h6VP_ZyxWWmrDhC@5aG|9Xfr(EOOT>pV6p?V&jn z9b>WLqbgI6^WF@1T@0~z34CowpgKRC5HTvEkW5U+|kIt z>|&4~8gHv=oC<{s_6c&uUP4%1`$fqU+FDBa!KyjnZ_I%ir?9087lVUuDeyI`_@4CE zXCG!E<1t5TX1yE;_*+jiyB6mO-5p`m%vF{=VIrUFjkS)SH7CZafoX;7V8FLWSvH_$ z(=2Woc61YrCs)}tpliqEDvKdG9wNBD3`j_mj~4KR#oyGI(*E$(7Y zCb?~fEc3D&zNmv^oXWO$SKhR=YWs80NH(OQMB!a7FT0C{0SqNet9)xoc3#6=&s~*2 z-ko`YG89$#BixzCC_`S2rJ=%|lp0@wVUvuUc_&u+*e{-jHWF{oNJCm6wr89Af#3** zO+JY&^D=p##gnVi?N~grDR%FN%Xi9uiE!O%?P$|qw_~KGsq#r)`|i=FF=%cEDpx)~ zbti7o{6+1szay2iFbA{9W&VFxE+>SrSp&Jlw2Ee6>*iAshD(JgD3lp8Uu^^w%~1>lU|ReUSr>70YFm#)9HAJS^^R>x)opp8T`< z*6|Lu_UH23-?oL9*(0h#IRfmkY1Igf1Y1ec>NMrIfqA_s#IkpgYnO|k_PAd|MwvzT znaZwGd>IHdIj^|R-V_C!rYUmET!S{!#kx2w?Z&o$J< zrynSR@NypT2v=;lZiFDLpL>3!%49cUI667D8qYW)B{z-`#X)xE3#p?vIzu@x852tv zk-C$%Vy2qrx*M>MB!do{=1$!N1Uu10>1%D*zX)l2qC2w;p;~Dh>dTgxHT+Gdh5z6P zpH?MK20jq5$Vnr>1{15E#&?BOSOwi-+7qkOl{c)dW|%wv!@fSg2xaWl&cZ~prMn=E zkO~pp(1j_zlx<2wY~pA`K}i^!N&~`8ssDV7lpNxF9a4bk4@}?oC?swpp(}BdY!535 zg9~y(7eW+c_2LLeOUW4f*8+{O1h+tOs zJGT3}1#b)DQb(*i;V!XrFVVBLU0}rtmlrDj!5uZr72U&~HItP-59f5d%I$ZNzUaJG zS8|NRb0Hci-i}m$VVRf0kbJ1a79#FXQ~v#MUrj&d~%JlNH( zW9E`$_9pDsg+RaugT%lNKuxPLUgx)$W}l_g;Z?}M5Y2M6J2c)?5OCemEYP`bhoV>! z6?+K9%$P?VEiG~tD}l<*GLAU{j(%nYAHEJiV`|sRE--YVkVjCc;LX*QtHEywKsxm6SQ_MHbRccsGJS}6~QQvk`R_7<^Wawr5IA~=7 zh&Odp_T-ym9`6QX+0rH6A1cX9ls-cWIwic=5IGhbR+2$P z5Ck3;W#QZj@`p$HWJp@>soxMy^El9M2eXA(Qc3PbdiJ(~i#T1)=d!gtzu1t>Rs>Ut zu5WM_rnDZ~dVEiKL@-HQw%E`n1S+xDkfkQFfgnYq4QmHi3@5t@6{E){Osb%3fp>?w zQy-s?-J6MAuCwgzFF}RoCh>TJCa!d4&CuA^=^*mgV8(>kpjcuUnUy2QWoj{BDI1#Z zJU>rEVO2u}l5qq#_)6*!g{(ZwU5zCFLFM>W#ATPKf;JFqk5*-t_fde=60lv6mbSp< zv%h}ix}&x0u-dh^2prUv5O;rk)qwbo?Xd>%p@o!=M*0V&Tav#Br642)CQ00?0mY&! zROvk|$Ejy0zb0KSHpFAuB4Labu<9TLA=v=LK_f7=0^)wcV34W?WT{s#2h0F+5!fV4 zPw`>U*C0kvgj>bD^)Pr#1j9A28iU%q-|uyntT{ zI+_MU(~UJaLCOG6d7o#l8f`R08f}&05t+_K-=KtCC11B-k!w6Tzp;5`P7IElrSfZG z4!`mDg|Aq#o7;FmWoRf)99Z>f%o!pZd4l-RRINu$^B$ zl3TwkvvcV$4QceBJ$m;WQmzm!l^n4*CcTseaOY+>8>9>x*|ph!pM-6f)ai;lf*(e8 z*G@|D$Ykv^QOc(yJH#ZyBT(Q=ErpGg2V&odRDK%SHS+ul66PwMtiG(Y9+j^BZjO>a zD%JU)IT(LkSQ$KwKayQK$=lE!51hG3u6rT4k$^=2+La9$d1R~?pvn5J*9sb~jo3p# z@@XH7f#1yqSS3V^ldoZx^!4+g6Kgq=6M+0c!{FIudr6cdcaAJi6Q_!tHbga>*z)_ z+xth@0E;C5MC(~}EYXCbcSZPPtbR@w?4meh7D75i4;Dxq4^J5bU!kW!Dv|Z1THyrX zN2{0_7ay=#rubhFxCCRY@-6ddv#gjW#ms(Nrl!Hjs{>}&J%ln-&^?e9QPZRZ4Nmue zaGeWr-A1%S2JfW>u1!V&7h_5NF zzU5%=I;fF-UWZC+3o$(l7Hk6TeaM62rP&n}gHCIs%$(d=(@|MFxw#fcr|(Z5WO4t9 zA&ZSn4V;2}fOdm!5;s7u!5gT-j;hEAH~Z)OE$hD53TQ?06S4xtMHrh9H6aGD0qOg`Rzlbt$w@V`HdR9%N(&CXfopcON zLqF_2Zn`2_?!)i~o)-+)p+HP+uvq^_Zm~b|ok-doA{2Rum(ctM4}mR?mxCD9Q-ojT zo`S027Aqeefvw&T;%WI)ChbghzG7>V`rELvlJ37^H;rO`G*%yrYt~r!!sN?s?x^YU z%Ctv^IwzAc+oAr;@qyS6nrehoso4OX@SRHSi0Ah;XK(y)VnGjg=3azK{5H_390QBM zg5^{&Y=ZC{8MgJsl<)_a#b>j}+ z)Qzpasf&&@{19q5W!em!KZTG3;w!~+S5ktOnm20@B`p`P} zH)$f3HIFB1b|{}c9_M_iIr{Y&rne9nFa_XPpPqynFS|S!PMbWhft}vKXE$9jEi0sw zgrQK9V>(TB7ig3l+zCb~iuo}BF~kPCLk&rK6gJThwibwlY@cp>vl1N*fGX}0+OXkH zWU(1VJ7cCuLD*oAHo@pO*qiucZ)RzlKWK`&{FWFjl&s%S&PM2&fUWouE~|o1WhIXRO;;uggl*jLs82 zLwZ50U`X^aGH%2yA@<2Mf8pSg`+Z#X2-V|OGc=6-Wh~Wn#YI5TZ1L_=a+&1GJGi6P zM?e|tg1n|SQ&!K2jawW6Q_91x(q5P!R>yo-q*pU#&x|fzMzD_=c1V9%E9gz zej_k>d>-)zVqLc>yFZavsI5?z&3r^tru;ZFcTisyLvsP{QP67FyA`xZ!F6JpH6o0C zLI5nRkcl7c{(|#n2f_uRzx#`=qz#t0N0~J%d&v4_Fy71_O`r-s*=+>UZE6)x`H|oU z!_>306!jrc!6v~k*y*4-FN99yOn1VF4p9oGPuv&Yq5L|_q1`-CnKgTUSS@hwgqOmV zi?dU5UPERNj9Tzp+_CRarV(ZOxMN2l<8O;dg`O?kv5(WUX%ODX9cv0x20zi(#AEdC+Ol`|VVt*dXAW+R8406OU>#23tmlaefJGKoN^BJ8oC8{6(3XKuYBVj> z8TC9Zn!1-JyE1=oT*m8> zyDD=mDD&mZHrBk!>sq>dPb?q#qA#u+&Y$)WR_vlsFdSLG>0CxHZ zFol4v{{d`6KJG$>$g7#AYzzmrG0JSYd1 zB;Qo&u3r8-U8|kSP`jadwF=I~qKPi(i0sE5%5r;RbB_v7CphQ3d55ygp4|M=f6_a4 zD8Jd$n{`&<1@&U|+@Y8kHgDEM1vg07bcZs*(Ye|8elARIJ@v8eMD6@a1vgM{-u}Pa z@^O%^LFg4A#B#i$(Ofex{)5}24{($JsMpJ)`&5m2LvvJFKDrO*dWn0Ww0W3Wu7PsR zXl`viqgMaa*hP5wdZ9q~U}x0bXU^-~4N?RnkRKN~Vv?p}bER}a$0@slWDT7_lSVyD z;>X+hfTWz+&<1AQPU_{m{Es@5xA7i2^bc^zHf7qvMCW@Cg6-k%Y_{c73@?#Y!;eDL z@e?5IgU0!>560OB^YMLPy@0O+Rr5FEDkA;^qwqelTii{+pH!f;VIUnN=%Ig*))J7y z?+43Q5b(W!0Jjrx%|C#j5%9Uc0l6mM23!?1IJp1sEm%z~jDKTMPrSH+i|P#O;V=IK z&Z{Iof7i`>#Mz1a1%GSYI)YaI4Oq{2+yB903lURv{-Ao^Cm>df4*=^qyzzf<=)abz zC;W}tExt*>f&TzS-b@UT|Okq-`V7SHn|3OL_AB1*fxOIPo6TN zrsAuB_yyL1xY+d>9-ab?9J>h#7_#1N>VVnJo9Mx-lAT8*_AN49k)Uyh&RdXbR&PK0 zzQU3Zh;M_4Q4NSKguuH>dxDrr4VZrsroj7XJ%GsoU^2MU5|b0%YGsQ6@Xl$#&qS7O zfwSi~W^wyt)4ymryd)@tdU2NS=U{L+(s1ay%IvNN;PIF#ERCM4Xkh`4gya7U^zEc5N-s#^4AqP}QXRj{^*s20r;LgOg30^VH zOv`HXB;T8WZ`RbS6+_^6ta}n+I1~`9LA(opP6yIh!{A&Uf>Y}jDT|kk!0o--C5h2% z&4PcodSrpZ!`KByvotO1Qgg7t%L0igq27=EH&$5){?v?vGA>gYQ8`)>%e}8aC#}+y z=}X%=*N|C_0?Dcb5r=cdryMPcOm(!Pb3_%Hu2Di)j4+5IuE)RvVa4Nk@b}6Qj@8~~ z0oWV`?sy2^bTpQ#$atOWv6HuF5Rj+>V!c^GfDVAasv{iJQDq?&X{zX*?R8VBMqK9@ zY4;bIM#n0-&LkU`aX~Y+O*~^m2FDy%CzNhp3+?(0*q3sP#v6i=6mVh?;boi`706R3 z{stJ{a9fUyTa3RUD4#i748FCWE{snupQcO3>b*-9mgnWH!`sxzgdcyUdM@46bt{w4 zN`(wt@9QMFn}M%}SikAKmpXs~r%4}8RHBPoOlb)qZBSr2OP5uaj;RwTP_>RBqj1fm zADv9m7{Z0$C7gK7;=ZIrT+QE?~@*NzU7jS|$2KL1?WqDECJ~yrlLZqXq z+Z3(J$J{7KmC}+sUXqC?&T^@G5Jd)wrYWb2rfXcvfMrviX)S069mCG1%R5&;SCC1p$7 z;JhanUPM?JS$qi(*7$L-T7j}oT94^UUZjpNrK1#^iggKg3EEKlx|$B7v>BZV z_;1)GVueWK(} zN6*ZKc(6n`?NZo@z_!7oxlNx^Ra^5B(VIR*5=X39?)oASGq`wp!M=dS-@>J^Y#6WV zLchh5mGvfAzXn(tU{Yb`T(D4IVR-5#Xe;QB2Vr0|yrVTJ3W(ti1y>TY!?=;?jfYS$ z0~gG|M=od7ljw@!D{G;;`er@Ys&Np; zJEMWYzc_=;reL6|W769HU9LTqYoAyzj1%S9N4S+q*YW&M)6M*c(090*f0djO%)=RA z^MUui)KFJjA>U9 zjEN?uNMPqIqA{U!mb?pU2v%m|f-SiMciGovnuubWrC+h*I0N_YXb?_8``BrMg{zgk zh)Sx&u0#!h{|)v&Aul!*c$9jzF!NDxS6dVP_trQY49Q3H?ekRneslVxG^SkYbFBLZ zUq<7mq77{5V6x|J*fxR|VfW}=F%(Pq;Cn@>m$Bx2;Ju;)c%AC?dQi#%5Wj{TfBD8K zA;0V%5ic0zjp!bTmwfcYeLQjb8HPn}5c9e0M+CVbR(&pZW>fxN< zSm3aW9!b7#k|ms$BBK=N@?_Zz6^x?uR&1-D27=f--wWSLay?LZG==&Cq`QD7dZ6 z1gKud>o%rS2zA+D-t5eFfRCz zEp=y}CK4nuiT(=@I6x!cEW>^#ziNs5RG7bxI+|38FQORPtIOm>N^oF1Mx2IJQI?=9 zd5i3+F!o90tHgd z%JoO8qeEQLvs%INc%V!c69L10{d-ybJ6s>FZ0r19i|wv7_%0tJO+3wQnYDX zfQn+b0)RL2WU&+p+{W682QGE3#{>6o`r5I_j@tldS&qub(cWD2kl<((5EW3kOl$Bf z*que$eM{#+fp&-gN!-}#+Zb29tHeoJ%1}9Oh6Eb_2)9^(CrL|rG*vIHv$`z@(0Z{G zQDmS*G!j{Os{#DXU-X5X_HHgyYXIr<28*eOu3`o>f6|wFH*&{^ z>M+5P{Y1zK-=G{ODE}<%QospO1K}ONVCa)Eh@{N4eTr$93>ncjPS9`w+kI8*LI?bu zIZ$P9BPTde#v)(k5h@Q(>4hfWV$?AnR#$(}j(42gv4cVC8v$QJYc^apF+*rvuJYcl z&czc^%t@Ay>49{4M&9ztsxF#gT~HDF2Aej+-oqnirLB~(4 zlnPHu^Ysc;Gk({8FgH)YipdMs0Z&@9iGWYpb)3M6H#xZ9i%I!O0FMWaS%3!j?eYOn zA?$hdp?X^NT&{X@BnkEz-amn+eZUm#ec~MkiL@3nKbO4#a&Ks!^4W_WTlCm*U-;30 zgWlUbPxl@doLVkv-667%?q*PfuUW0V5qbE zP?fpL3v1hI9#cMAyD4@lT<8OM#N5}|W6F$|(ltGm*Iz0PyL1LUDn8L&nW{XY`9}F# zX_ZnwGgt)2Dk*SBN7HC%#I6A)e37pBeHs-L3h448H5`=HwPvi0G=^s=siNr`Ec*f%1A#U5+0I(3iopXW+ zL@Ds4|DqE9T8_4Ct1{@d(M|Py=>!u&k9X>-eDqp7XY}_7?2?-E-&p?J4!^^9j(aVa z?|6h`=FP`VUA)bf1u<}rR0eO(yf0uKic?xKgkpwCd+4LfJQmaACTwbx8hX0K~w{Wyh0&-{cQ&`bFRk!^<0 zEPWc1uxT>sJ7Eow_}JYtP8vIpKk{*2S4$V9%g>3pnlUFusa+QnK8SEtd0|(SE9>Ss zhwX&ggcag5skCUr%rmzVjraz>8Ec5101A$$O52i}$8<+UxV|ct?1KY-9)UaNq_#wX z5C9V!y@3Zv42OhLGMXn7F%=W8F$e*C0ft6NHYpQ%L*^*_lU(O+W6VQbaUalJq$=@8 zBY%!GtQ6m@KQFe6+?E@ME=M$(ZltP2JHHC z$igsP$rbXTRbnat^bg4Ehbe^ zqnoR?Y;h*ix{{Togpf>2M`i23E#ZIRdNRm#9Tm-nwoymvva+hAa~+g!8%Am8bWmIy z>RX9D32J4Fw>xXMwO9JR{i62v0p-x!aoRNog}uG7#eWkaLu+XG9VCK$oQaC#oh1!w z3gv2gr?d9!c1q`W`)C#_Q{GL`9_*kjeRr_7OFQM@yPbH$CArPlYQuk{fiDk8_m9PK z1z<|kjc;g|v{gRZ*fTea|DW~f|t^s=L#g_@p+9iPVwXWy=df^_p-Fz+be6|OQ0rielKyH z*g-Yur)j8_oYED~phU_f)SlUlxVTLn>5-CPgEFL)Gi*BygYCQvDtrQ>9fXV^yixoK z@w#q#BPw1c{@O-KdB0sAZdY&#!*OM(PHc>v7T*L|b`Jreu%L@?I_9O|z`x#(5BwiX ze>;FTL2#t+y%`9gHAj3)6wmu{PLmbvCHZ^2lNuu5KFT(Q*dM-Ci?_!#<*-4?6T>Y~ za`#8tG1pUX#b-_J}PkOykReAHW+a z*IU!Ag%diLjM<;UcLB2RCIdKnPIi8G^peFB+1nz2bL)&H{G0aItKx3AWHQO&FG z6|8=s-sYC{Hsd#rrbjQ_I@12QY+4Jxe%y?BI^HG^(R*^f<`yOYh1m(}lw5zcTNv}8 zNLdi+Lmq&fqz{Pzg_rZ3XaY_p4`Td|pz)iP58Vv2;Z0&Lp~W5571f~UE%bnnJUQX! zqqvolbB{)5UddsHE-(Br4!|sOlt2=NO75XJ*&l=dhe0=k@q8A~Oh1ki;K={P!)JgP zVEt_?%_Cre*MXi!;DamAw@z57>1A@6D|^eh@A-W)BT1#-InjsvSTf|uQK*ebTqQc!xQ~@;d(f) zrV48pw}fO3juGU+$yAJVuUq~JIaYjaz!MYilw4nis9GM29vhDTd-Whq4<=3TB3pop zJ*b3KY(dYPkDebA^nR9-Lyo4F@IxS#O;!>l)Cya$$^{K7w#ofT#l3*E$+3BO_~y{$ zvFx4`!$!BlxeEir;uiH(gJ)8lDd zzV5=-xR<`G6A37vbX{+kQl3&J9sR|1LF37JR=$mZ%pIzB6w1xdsBs0_VZC-|mk}in z|B?d3IGLAm%88q}60Al(j7~wvT|md3#8{diLAvjWBPpxA&x&(gcR{kX+}F?QDi0m( z4y`7vC>i%TLTvI$@56HN-(tu6C+`o9>D8+~3<3Ql%ckK6ZP#%f>ozC?X z2Nht0dr>;%Qx?71U8(+8k`vJvS(=oU?-@px#(lISL!GD}ur!Q`|6ys>7Fjf)7Jss2 z90W_lD1xQo_y1{W3tLK>U!iB%my?Dxxu1B9bpyA*8*pLqs`=Gmki$927&0^=LqV1V#} z@xCqiCNqM*>sFW&UiE>2+0(ftnU~?Skf@_W-hE^G)eLL0q?B98p8RmYuJbUdXW&QN zVh7YAKbxfv42%S+sz1Ve`Vtj1RD)b1<;?OQjzn{K^5@DgBW<|eM=JsvhUDecp*UD?n@Quasy|lW*v$S9exP`4PsffSaf~PDP2nRz zq!S4b~NB2Y$*PkgX4?{EIg3$jq&s2S#7$1)*Q&+q=BTWw`fcCBW?9qqM-6}oy$lB z#(lcy(>cRy>`U6J!8!w5OHN%e?G1yq3uSB?hD{|ha70A&@K}H=q2kko!9`F=y7M+- z2)J!sF(MO`O6T6p6WIc{Om)TcIV@LCyF5Q4#J-M`oB7~iqAP;D=nzj#cbieKEAfET zEybIia*f$_9zpmj0E*6Qc2fnq;$_I;Wh!+3>w z{VB`M?pbaee%j@X-A6kOcX1Mfn5j-&0_)P+u_NjT+9ACduPbSWc^sQe;7Upf<-x_Z zK&|f%?PDPFL2>fhaN$*46K4tF7K^}ScQw4R6V-P^2H*Ti#iL5>iC;9HRw1>Z4d93Z zU2qu5o#crmITt1k6}!q1itTe;vw9qx{n=1uT{&ryj?T`*R^ihE)Z*tCAz?FC>6Sl5 z3C%AEK1YHXzKutY(&p4$WdX#4K6-Ona`V2^l}rLH?+Kf`S-!D$Ubrn}co-${0ng#+ z-Xo4nnDvsV-MR>?r~hY|jDp=j{exV-$JXp^t0yy*#WJy&5b`8;OWnorK zKl4A&Zk>fgr~kQXYNuCfKA)&Ppi??;>B<)mHaYa3x_DTH0~xEFi&YaQNWidaayX*_ zl#EApu5Ym$hVE)fc}#qA>@AFz2G_#|uh^(R7(bbrc&4R|Sv zN0{035}l4lXapvH{BrU3QHVK24KYzTnF>pqGm_>kzjrn*?63POg_3jns`EPk+{*oV zJq;E7Kjw8E)z9a349b5@><91AJ21D^dA*X)>)v)~{r{TR?>5ZqmG|fMUQf$6Ft2Zp z#JoN)OI=#fDxzUtueH#;rq1h5vwX9DVn0S^O3@QNF|R)b4t!o8j(ME}5o+5b4qr9% zY2BQHxaKd45sqHEKeZpCsr`nesr&yowGZ^2#*O0tV{U(NYTpBb@wiZ+o*K{%2wiaw zd>%IR|ByTjuh)q^bV}@wPC6Zi4h-NpUwyK3%HSPI9qDE@+-1o&b*pDHS?&isjkCMk zk}-2yg!0mkG;NCrW#5kbTRl0n)zfubXYKeV%JVz(v~f+8nw?#o5lt}G#$x;HmvB-| z=3V~S2e0`(r6>+CW}e}|(P8+$c9r-wGV~A059^cJ;+IJ3miyrcaS3-5E{prlB8}aj zcYv@OkI;A)mm-ZiR`UndF-H@w%-@x(?HI0X+f}~}f=wCP#gKM)*3J%7p58q`+a*ld zzPndzI&LKwg%@Ol8#{Nfv2#a`J=$0i_e|72(^x6oQ@^p(i^uD-sV@pi@<6_^Lz_Ly z<~@#~zD8)Z$ezQCG<9NgsDMvSqzn2t0q@Pw-%X+GM%WIGnuK7FUq`CoFEIQ?i=qX1 zc|fFVs5r!Gq%8a*(fJu#rFA6GcT=0Tg@ z@igC2sH6j`p%h+G1b|c4Yb*=(sTW4=7x0HBZsP!hzZ_bQSKj#Y6(2N|TH;GTh|Zeh ztrHy98hw&((AU3iq`wAoTC>g^3tO`yii;p9ZOMdhk9RwA9qoIxp zI`u5rIj@76&)A_Efk5pZ{%yTwcj>VJ68gge&HdNdp=nUsHTKmLi1rIuC1fyo1#9maVC4(aN|;{b%&`_Td3K#S+mfVusCe+GN_IsOV0>dn9v9Nd|H6G=QQ#I953a)B!3z(<{MJD{a=Ey- z@)6-x)PqHVdVUR1c=gQwD@BKQM`Rqamfyu}D9#7KBAh#uFNSm?5&PV8f)wpX?9I|p0Q1svoFpgAWZpfJv=agX16UY)L6Z= zrDq;y>6=#L4STFKyDmLYZZ<}y+fVu%b9wz*yv27;Gob=M4}9R<8s2pLyaBfN5RlW1koBEU@gH#G}vc*D@9h55GGQJ><_ zJ{_I40~hk#6+N6?h?u5<_Q4t~cnk9rEW2W*Wd~^Lo|bXNTvr)t7hQN|1lq>Y_B4ES z;FU6|{Hk7a9d9ayqM=FG0)bnuYCVP=a;|fFz{Bh*R(xl#{EpsSbSX4XKBg68WO$|w8dq7b)VWv?RI!(G!z<<$OG z-SikUb(;eLQy`pH5h!TU4Z+JTnHkqfH82no2!iY3 zsMe0Is7=RFro}xn`dfEQv?b+;aik%1$vJ-b`upBK*L;ksg}O?*T&vSqH{Rb9wJJ3e@CwzKhR!mvowlrRPG&UJ-jzYs@|Rg z`F{Tx1F{h7+-iHPTZci45cB&y`L?C~c{JK4pJhX$>0)LmO5kGVAE=v%Eb@5}fs_;7 zVkye3%0y>5&aWW@-oH#x;Uq;E8_R7AFFBFPtOpGVJ_`34@Ucs?Faj?Idq#@*Fde(r7@8E)S=@Mv(gi>#jIybpIP;Z@dtk*fS^{RK35 zi7RBzV*9a;PfTf6bL~ec`ryq8bQ(TVP^+x1GB{xk^baDGpMxRAG?t797h-Kno==0W zt6nH#VB4Fp=ci-n{0BOL&0>5Eze0U(nt%sf6>K<9K5CIGjg|0hk&O=f@U}-j+|Ak~qasZOO0t5HD)`aJN}t zS-4myt@Fo^^NphHd1PgLvZ*>ya%5=;Ue&{gxp5?V!*zfq{)j4<6 z#!X7mL4!_%JrXP4+`^REglQjFwj3O!{U~zFp9de-Xj{FfSikCM_SKg+*W zore)k?Y%i?kVz?;0=kif^&a0Vj(#XtLQ@m{{e{R zmd1RL;$6h3V;kT^${RVt5qv!}2AUa~QHHNXIV2p$&3{Om9Y8VD+z*tkwcXpepW%Uu zbu#@;inlTK;7tw4g-eBaRWDE*H?xW+Wvl!(eXKtCjyaBzFn8Sibg;+LM9aU7KNCZh zNv7Kq?HT}%uRG>YjG7nhzCM$jD>c#}na7Xs@Bz%RZ{zKNj zb(s3_7XsdblFx-wyB)1do&H<|g>My*#n9hDRIm8?_M*zX;(&cR?sZV8c#S2ZDwR7i zPhl#5t6a|dgDo1!L!>-K4I{Dh-Q+X z*#&5j_>%AO+2COLTw(saFaBIXHeeLV9w!Bql$mv}>@&Oe_4S9FOG5_YI@jlYp@9Lb zLUltuZFH8bT6-J@U~}uTKW?GmLb3nDE3s#44V3$__d%*_(40-4i0NwONJE@W#uUW< zzv8YuF3RKlzq9Pijf={qunNcmDj;}J5my%kS?~z)jwi;WMw4i8*P}IA5KY~zB&iTwU zGtbQCnXt!?brJn3(2Iw4w|5^)mvHZ^U1MNgRT+6S!7+IpcDJBz>L^toDL!7&&s7wA z)0|a2t=O<`JyJE((HGkj=B#;3AF-`8XN|gVoNXCoi$N==6dB(46g1J>JX}R2n4lu3 z$LhqPA%5&%+ks42ajO^`%AldpDfzfNC2!9tXmTXts}Ts{`N1>jRK?pAJ1LQ@qQa6g zS5X>qnf5MVuFzh#E?JbhCu-fLHo>YI1L*fuObwMb6A=Jj~agi`*X+x!m)2s-S5q3XB45yNNU7 z;I$6nvO7h_<2gkxi+EJ__qh@ne*pNQWVrKu@MzxOa>siSU-A3XN;m^&Sib^=^0Peh zz~MY@!+Kg$<~WctvgZ$aF9E%XW6+&A{xkJYMf;9{Be;C}6u`>^tufyZ&7nlG|JhnY4j9fQ8CWi0pk9&qPfxAp9h9SEei z2jv{`V8Zs_;rC3pfd%PwlOCX>yt!7HH(i3OeKI;vxXV}t3tgPiz8jAqI1`qId2-2O zP{BJ?;cM)@s9T{|;qk$#Ama(p`w5)J(Ex8si(E)nsuH?+T4f#xs{~~leu8Hk;?)|F zeInq@@eHinW_ot>h95l@w-GJJ`#BUe5gq^?&aYah{3T>(sDyJIP^yt<%!761d2g!A zqF*o1N49Jioe+3}8OGLSidKM&D8!bd`wvd>eu{?OA&=WirGXdC=wgy57{w)wr%)t( z9;NBHltv@PgFJMqsnsLBZhekWX+&+4C;G#xoaj@e{lLSgPAK9qQiL zt|19Tnd>JzzrmeK=ub-HXB(k=f|GrReVFm=kD!$4z%mMX$5s?qnb(1X)GLungU)`w zL2r+BnV*9#=O7)j#2|NBD;tO62LA}HHYi}~yOSS6zi76-XRY1EY1-uH(Oc^=faOQ8 zqgaET#%V|dgz9~iN3QS>x?<=I^!gz`!iz0xCu)N35cMaq!9_G&_gFHKaAm!3RT?SW z%#&42*(;!ui0hcx{Gwtx5tX-V3vZ&b=WH6~+9rFJ!+7x(}7G-nTkXdt?$gsSbG(V$<7EoB#IEhZ6q-1)P|XEAiqCyax9 z6W!jWF8TG}LnA7tNEEplACZ958hXQ%Pg0rWW7t43mpa?Enh_{_65=)By=~2f@ASrM zn@~`2L>&*d_xIAtb8Rzn#YKE4?BkJ`y~o`Hlro%she;?n1P7q(**ljgU!N?R{CAA} zR#!ERi@&SrQ2*(}8D6_yTJ81YSNLKTG8!d%7S|f8m@&iP3X<@=usMtxlW| z7!6&cc&%xIvf}H3jzq*v{38~`0nzcW)C>dvs2xFc-a#F*mB-f|<&8CV3R(hc{*^KG zUDhR?#5-yj&-nLXkjX?KbcAUbMxcPGy1fm>gNiQnH;(t=b49MG=(k`YPIW@+PIyAE zh`vByxC%}98{ZKOrbSMp4kVO>=PT}^Ss_{7$%2~mI$K!5E^3aP%j$6J?T;dqEAz;C zWMy@!vij7_kxOCrmLc)c{#JGl!}juFBJoqO)C|R}IU5}V&CQ;qb-^5jE}qEM@^!3L zqE6=q+&CvG<4*TCe|ipC%a1;Y3>em90V8={Ajv!A3aJmHGi4!g_z2mIgd zjN`1zd#9I-k4WWnN)L|}3G#lIi^%LDP+J><$a%-3-bynS8hJC$k?QTCyvun8mm41kNNdE0dSm&-6HCT$Z5U*?Xd1W1@usm#%*EiM zk)JR*0)-VOUxE4h;q_P1?SFtjw-t+gES^3gbBFxJGyW#M7Tw9SaSdH`y!=trYb$(Qk|VRAeFzjR8eKi6}?_|1F+q353Y@JaI|o~`*kT9;4zem*raZx8Kv zsD5uZ*N_iTO+4Bwvpm&sB=an~{JVi#Gcl)FH}A$cfxa>}uX#BnA_vg7Bi`Af^giD| zy`|Q_h78g+79+xm@KPD1RgXc%mS8POGb@(b6iOBa<7GJ>0n( z2g?UG5#U)nu7(y%q3>S7ZVn%iF&*repF}L=1IFbrMj{F5v6}ogBVGx*ZSNq>dg83%x%7E^7D{CA8R(%p;kmiO31DD!n{Ps%DB?xsKQr}ZaR2U+KtUjo&-sEhcPhj*JlSKofbI)7P{!v42GaX zUiuW(UN=lPG>3yJ#tA%+s{oyHGAe=(`SVWK{N%9B=38hT7!I;9-gzgXHx5QvjNE#A zKU6^p!gk1sAO+igh1oBMw7KV_ZP+s*@jlIZD~`uSz}Nb7JXLI_A9#(QCp z?QlopC=f<;PCI{1sqj3*yvkKif1Syq7%%Nkncs?Z-)454IDArGg@Nd99pNHRY}q{h zdsQnH-=*G$l?76v6E-`I5`&*nGQHWleMQO~uhCJD;Tx@Ih5wEU9DN=tXh+a*w-wtc zwa#}Iv`-Ddx$_UN#D>F()BDKXo_we6$}Qdh4tQhjJDvYNqKo@k%p%&zD&_1Cqjc{$ z-burrhHmf><;sne!8cb(QXps3Y!E)3#W3+}@VE4wFM5e~Z5k>)2CNfjE5MZ*ijQg$ zO36<-(>~o#qr#*V3%G(DKUZLOVCpl8?;Z*Zu_Vz><4f?0K{6e>K(Scy9K*PRJ;hZC z)Ti_1APRMdW7Rp7!#@>;WK)Rz=;D=%@n&3DJC>;=eu`YQOtIYTk$&K={c0L!7AUNz zF&H`t#GFoVVMBzdQ>t%v&)eRDsQ6kd2bIO875Fl+L?dt|Ng>C4xekS3&vFg1uH+Li z6eSy1w?@eEy>Y}CDF56eW4epFd- zD@8XoKylsbxo9zW`5!BuaUvKtKqSw>MZ{_h6C@=Ar0I~<)LiuvZV_dHJP6O_0C=i#qvH;(We+T|v?fL7|%r{Fq(xQ;f?`EW{b zDejZ;_wK&YEJ%}UptRO5XhTP!_xzwj-gyiMv+>>zBDX#WU8@E?Iu6F5Xu3y>w|&qB zQTC+!@p-*q<^GkRQ(QuPZv_J&%H!!sLLY)_>c?+m)f{s^(1b=O%(2FgjRNypL__ z(VtQSGr*UosgxSY$+br^)Lj8=uw(KbL_kWR|HEe%0;GGI+P>FHs#X^AC@p$xT2>p)cn& zVW+euMRNSMA9@BttT+v!kqB#;uR2ReH8I^?b6QdJegBnR5{bt@NKwX@i}X z_|5RGomSIj_HKfqJ7}0ryJ*+?A>*<(43P6J36O59oiZCWg6hF#4EYfL&^P{N=3?jV z3|phLZ+KBVz7)$LRAlg8O%K7Q7jl7@Q@4f_SeJE&T0Sbjq=6wq>Y!WDx z%mlh1{<=CNnC0n8E7dK*EWy1fGEG?ztW6o)DJlSOA*#Fns%SYafO|?|#rwe+y9t_I?m5lYVKs z&)x=h(eKUH-Ib4;J9!uk#&7j79f9fIRe9PKquc5SF%q1C78aT#<1if$r|CFPwl70v zX7h%j50IBFQnSVmZCMluLqQV=zm z09u#O7%(X%m>04ZJ%x8#%mVkaG4xJ0nxv~ZdVoVT1cs8s9dZ~?IEAOY7bU?{9_;5F z{)SGyJYBTmKQC4D!&r}yUbL$zYf6ebD~!cu|8*gj3z#jOXTgu~0cr>F)mB5%VeKR) zDsNBXrFE`cV(qj~64Y11SeCAAVdJqdHeRRuah_`G!qT#3bTKWXC$C-m$&#d>J2O%_|HBrLk!-R!RYRq0?BW#rzbMyG2xH+hd}M&-e`$f$lB17xJ;`I zQF@9lW8-CU9Rl`ZZSlMBrY8tGfG#e99- z+P9Uid7sWmSsmIc-yWi}Zs-LA5JsabmWG@Y=G51rha0-K@cJnBu3Kaob_{#bfkZdQ z;UC4B?Jv%P*|-4CHETsynNPlks>K<|!zAv)23_!J%=l=Xs{>o*$A*w{?-THWz>zaf z9mMg0Z|9&SC=5>WwZ~PN|M;2;eZ@VBT;vNi&%;yTWFB}U$fpjT$V`Ea@L_*sai{jW zC(XHkHd{v(vM=0i;Owc zNc?9PvOo0sDCZ|58V9S3y0czgixM>gqR16y~9xak=yFqZ`eK6=WTH}?Jrkfi(=WjV7>Zv6id=w z2-p(MhUxwmpbn2_JrbS{;G#hzNqaAOQwj{b*CasW?-HM83s4_}k5J+%ynzL~3Wqm7 z8_g=2!@A?MPyN3;$KV{3=^_cPSc$k2PN z({WoXj==2R_Y|i>Q3dTu)wr3K1+wsIBcIo`1W@prDK+W5)|GFeVg3B{Pfa`8pKohU5`vfd| zA1DRL+e&ua)R;H;vvzW+ZXp7+GaM%>p$+mQb9t& zZjddYI9ow-eu=--8h7>v;{ecV3*ku54tou|&mgirHI$hT(GQ!d_BK8q&-%)tiGBG4 z?Ix+Ox;>GaO1k=GA}eAsYFrXa*KJ8x3zJx>uBBA1O=6X?e}x#Enhn%G$STYY9~MX- zG<>MWB(sd*`5-`<@(8sonHA{bpI2*>+2p==^fcheC9X-)E~IxehH3A!oR7su$p~{w4JC^s(sWwpp~!LG$%RAIyg*u-5rO*=I`Ub~!t`GI>ZhzoSrFD9rN#k~QEn#5qf&$|tpQ)7-V(N8glY(B-`Ai46-E_@&C22u9 zGStNFUFxC=qqEDq`AiMm9(T<*GI>%q65Pt|=4Dc)6c!2p8%ovPX*eId6!ckczgF9( z^H+V(beX!nr_VHvdk?6P9+Uzrpx?vF?W3>xUhIP+!1r={Qoa1LJiVU2m)j$fjGKGq zok8?*+@6;xB}=+Qgb(KSadqLkUI**wdm)F$$+A6;;IrZ5mLWln6Yjg}TN;&Jjr_NA zdrF1;pgg)B?gzL%&B6*q6c(zHvAdCBW_7 z>O(#O+k?9rC-ffm5|seA$2AAM71i%dG)jQmV-L$m;7HAu2g=2DAZVOWpB+9h+?|atYUTEbHl40#mIVE4iTTLYGgn5R%Q+!VA&mru0m_X+09_>&Gfy* z>U^UOhvOb{)T$c6D}qxDWf?{6o!?(2gw;^Z%l)pmPhT0P{Mx57d>MLqC;n; zSCMZRSVD>+%zIO<8o{gsI}o-*ezBNFg(Uxw@VxL1xo0era?0;-q2PEL!|jw`-J;$Y z!QxG0aSO6>a~xnHE?9NPWv?*nywN(mpbnB7CzQT)FU(Shj$~uPjR?LN&w;z-0izqA z8OgfFIKs>Yh*^^Q3k(vNCUE;uzk8mrPZ3xtuukBg1nv@eT;Mr@?E-^F_~Yy0M~9Ry z2%`l~75I?A-w53DGyIFNHw!!_P!o7vV8lp&f=L341y1JZkfcWi!69&yz!wF+FL1xW zHh~_2*978~B3_a)0tX5lFL1WNM+L4RMEX*#AiON_9f2PS{9ItGz^?^f6nItOwE_MN z(8)ad-4J+Npj_amhYBP=joYTO=}ZlIfDK}G>hK5H6t(^VwydmpiG`dclR&e;JM_$- z*@yhh{!d{KTcS>%&MrIRG9)R1;Z#2`5m*6C0VV>61M7i(fo=UIDGPWNI0zUC`mcZ% zU_S5w&;ncqECwD1mI1B6`+yq4Ie}YYH)12R3OI?Om^$H9FdbMATm*cDMM=^!7+wP* z82A=&T1MmanJkMnX3Sv`Oqpp2b<}|@4Zti-Kaz_K7iNghq*7rHhIy(mL-s5!5avXf z9}{K^%+PBpKk)_|03It|J1H= zaAgwx@75EgGtiL@%Y}eo{3r2BcknD))abAQF@W?)o~XyvhGPQoREXNAzt->O`s%la}iHIpJ(w69#MBrPv-x|G%9d=T0Q*tN~5n&O`|WKoTkA%F^w|)NSX!z zOYn~==HWD|Cs-F$KDyxHK?AeqKm4%N*fyV)2grurRTwX_7O_ime#KO@B=IXOfw_W7 zb^k%zH;zf!pdEhTp1+~+x9mZtY+})l^!xqvT!B`BGX*{>@J<>Vg#9l9-xb&_@TkB` z0y_nUPw=PHOJIf{9nuIvC>Jj9T=I6xd7PFo9(PD+JmEJ}GdEzy^Uk1%5(Evrd~JToo8P*`Huf zff)je1Wpk+Utp!cwE~|O__Dx#0*?x87kC>;=}S>l{0XKA93gOmzzTuO1g;XeR^S$a z4FY!xJSy;#KuU{#o%ig#jY|(?Sh&LiflJQ#?Y2xm&T0&Kj9r(pE?UR^{X&h_A&FM| n3pH9)mcP~hLXDOh3vac+U!u|S#!9W0e!oDYZCt*V)kgduXeGXy diff --git a/build/definitions.mk b/build/definitions.mk index 7a08e72a7..cfb7eafe8 100644 --- a/build/definitions.mk +++ b/build/definitions.mk @@ -169,7 +169,7 @@ DEFAULT_LDFLAGS = \ --gc-sections \ --build-id=none \ --no-dynamic-linker \ - -zmax-page-size=0x1000 + -zmax-page-size=0x1000 #--cref -Map=$@.map ZIPOBJ_FLAGS = \ -b$(IMAGE_BASE_VIRTUAL) diff --git a/dsp/tty/setraw.c b/dsp/tty/setraw.c index 72eb4a36b..6d2b8d273 100644 --- a/dsp/tty/setraw.c +++ b/dsp/tty/setraw.c @@ -31,13 +31,6 @@ int ttysetraw(struct termios *conf, int64_t flags) { conf->c_cflag &= ~(CSIZE | PARENB); conf->c_cflag |= CS8; conf->c_iflag |= IUTF8; - /* if (flags & kTtyLfToCrLf) { */ - /* /\* conf->c_oflag &= ~(OLCUC | OCRNL | ONLRET | OFILL | OFDEL); *\/ */ - /* /\* conf->c_oflag |= ONLCR | ONOCR; *\/ */ - /* conf->c_oflag |= ONLCR; */ - /* } else { */ - /* conf->c_oflag &= ~OPOST; */ - /* } */ if (!(flags & kTtySigs)) { conf->c_iflag &= ~(IGNBRK | BRKINT); conf->c_lflag &= ~(ISIG); diff --git a/examples/examples.mk b/examples/examples.mk index 3d958f8e3..0f6973377 100644 --- a/examples/examples.mk +++ b/examples/examples.mk @@ -151,14 +151,6 @@ o/$(MODE)/examples/hello.com.dbg: \ $(APE_NO_MODIFY_SELF) @$(APELINK) -o/$(MODE)/examples/printargs.com.dbg: \ - $(EXAMPLES_DEPS) \ - o/$(MODE)/examples/printargs.o \ - o/$(MODE)/examples/examples.pkg \ - $(CRT) \ - $(APE_NO_MODIFY_SELF) - @$(APELINK) - o/$(MODE)/examples/nesemu1.o: QUOTA += -M512m $(EXAMPLES_OBJS): examples/examples.mk diff --git a/examples/printargs.c b/examples/printargs.c index 11c057ce2..60ce148ed 100644 --- a/examples/printargs.c +++ b/examples/printargs.c @@ -7,30 +7,8 @@ │ • http://creativecommons.org/publicdomain/zero/1.0/ │ ╚─────────────────────────────────────────────────────────────────*/ #endif -#include "libc/bits/safemacros.internal.h" -#include "libc/calls/calls.h" #include "libc/runtime/runtime.h" int main() { - int pip[2]; - char buf[PATH_MAX]; - char *args[2] = {0}; - if (strcmp(nulltoempty(getenv("TERM")), "dumb") && isatty(0) && isatty(1) && - ((args[0] = commandv("less", buf)) || - (args[0] = commandv("more", buf)))) { - close(0); - close(2); - pipe(pip); - if (!vfork()) { - close(2); - execv(args[0], args); - _Exit(127); - } - close(0); - __printargs(""); - close(2); - wait(0); - } else { - __printargs(""); - } + __printargs(""); } diff --git a/examples/time.c b/examples/time.c index 2213ba405..b0ee8b974 100644 --- a/examples/time.c +++ b/examples/time.c @@ -67,9 +67,9 @@ int main(int argc, char *argv[]) { char *exepath; struct rusage r; long double real; - char exebuf[PATH_MAX]; + char exebuf[PATH_MAX + 1]; if (argc >= 2) { - if ((exepath = commandv(argv[1], exebuf))) { + if ((exepath = commandv(argv[1], exebuf, sizeof(exebuf)))) { real = nowl(); argv[1] = exepath; if ((ws = xvspawn(OnChild, argv + 1, &r)) != -1) { diff --git a/examples/touch.c b/examples/touch.c index 0325bfc92..58e179f44 100644 --- a/examples/touch.c +++ b/examples/touch.c @@ -20,7 +20,7 @@ int main(int argc, char *argv[]) { int i; for (i = 1; i < argc; ++i) { - if (touch(argv[i], 0644) == -1) { + if (touch(argv[i], 0666) == -1) { fprintf(stderr, "ERROR: %s: %s\n", argv[i], strerror(errno)); exit(1); } diff --git a/libc/calls/calls.h b/libc/calls/calls.h index e50c44cdc..9de7af625 100644 --- a/libc/calls/calls.h +++ b/libc/calls/calls.h @@ -75,7 +75,7 @@ bool isregularfile(const char *); bool issymlink(const char *); bool32 isatty(int) nosideeffect; bool32 ischardev(int) nosideeffect; -char *commandv(const char *, char[hasatleast PATH_MAX]); +char *commandv(const char *, char *, size_t); char *get_current_dir_name(void) dontdiscard; char *getcwd(char *, size_t); char *realpath(const char *, char *); @@ -120,13 +120,16 @@ int ftruncate(int, int64_t); int getdents(unsigned, void *, unsigned, long *); int getdomainname(char *, size_t); int gethostname(char *, size_t); +int getloadavg(double *, int); int getpgid(int); +int getpgrp(void) nosideeffect; int getpid(void); -int gettid(void); int getppid(void); int getpriority(int, unsigned); int getrlimit(int, struct rlimit *); int getrusage(int, struct rusage *); +int getsid(int) nosideeffect; +int gettid(void); int kill(int, int); int killpg(int, int); int link(const char *, const char *) dontthrow; @@ -165,11 +168,12 @@ int rmdir(const char *); int sched_getaffinity(int, uint64_t, void *); int sched_setaffinity(int, uint64_t, const void *); int sched_yield(void); +int seccomp(unsigned, unsigned, void *); int setegid(uint32_t); int seteuid(uint32_t); int setgid(int); -int setpgrp(void); int setpgid(int, int); +int setpgrp(void); int setpriority(int, unsigned, int); int setregid(uint32_t, uint32_t); int setresgid(uint32_t, uint32_t, uint32_t); @@ -206,9 +210,9 @@ long ptrace(int, ...); long telldir(DIR *); long times(struct tms *); size_t GetFileSize(const char *); -ssize_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 getfiledescriptorsize(int); ssize_t lseek(int, int64_t, unsigned); ssize_t pread(int, void *, size_t, int64_t); ssize_t preadv(int, struct iovec *, int, int64_t); @@ -225,14 +229,11 @@ struct dirent *readdir(DIR *); uint32_t getegid(void) nosideeffect; uint32_t geteuid(void) nosideeffect; uint32_t getgid(void) nosideeffect; -uint32_t getpgrp(void) nosideeffect; -uint32_t getsid(int) nosideeffect; uint32_t getuid(void) nosideeffect; uint32_t umask(uint32_t); void rewinddir(DIR *); void sync(void); -int getloadavg(double *, int); -int seccomp(unsigned, unsigned, void *); + int clone(int (*)(void *), void *, size_t, int, void *, int *, void *, size_t, int *); diff --git a/libc/calls/chdir-nt.c b/libc/calls/chdir-nt.c index 65c027ce5..cd5f664f9 100644 --- a/libc/calls/chdir-nt.c +++ b/libc/calls/chdir-nt.c @@ -29,7 +29,7 @@ textwindows int sys_chdir_nt(const char *path) { uint32_t n; int e, ms, err, len; - char16_t path16[PATH_MAX], var[4]; + char16_t path16[PATH_MAX + 1], var[4]; if ((len = __mkntpath(path, path16)) == -1) return -1; if (!len) return enoent(); if (len && path16[len - 1] != u'\\') { diff --git a/libc/calls/close.c b/libc/calls/close.c index c8137e0ee..13cfe7b21 100644 --- a/libc/calls/close.c +++ b/libc/calls/close.c @@ -73,7 +73,9 @@ int close(int fd) { } } } - if (!__vforked) __releasefd(fd); + if (!__vforked) { + __releasefd(fd); + } } STRACE("%s(%d) → %d% m", "close", fd, rc); return rc; diff --git a/libc/calls/commandv.c b/libc/calls/commandv.c index fad7460f3..79ad7b629 100644 --- a/libc/calls/commandv.c +++ b/libc/calls/commandv.c @@ -43,15 +43,16 @@ static bool IsComDbgPath(const char *s, size_t n) { READ64LE(s + n - 8) == READ64LE(".COM.DBG")); } -static bool AccessCommand(const char *name, char path[hasatleast PATH_MAX], +static bool AccessCommand(const char *name, char *path, size_t pathsz, size_t namelen, int *err, const char *suffix, size_t pathlen) { size_t suffixlen; suffixlen = strlen(suffix); - if (pathlen + 1 + namelen + suffixlen + 1 > PATH_MAX) return false; + if (pathlen + 1 + namelen + suffixlen + 1 > pathsz) return false; if (pathlen && (path[pathlen - 1] != '/' && path[pathlen - 1] != '\\')) { - path[pathlen] = - !IsWindows() ? '/' : memchr(path, '\\', pathlen) ? '\\' : '/'; + path[pathlen] = !IsWindows() ? '/' + : memchr(path, '\\', pathlen) ? '\\' + : '/'; pathlen++; } memcpy(path + pathlen, name, namelen); @@ -61,7 +62,7 @@ static bool AccessCommand(const char *name, char path[hasatleast PATH_MAX], return false; } -static bool SearchPath(const char *name, char path[hasatleast PATH_MAX], +static bool SearchPath(const char *name, char *path, size_t pathsz, size_t namelen, int *err, const char *suffix) { char sep; size_t i; @@ -70,11 +71,11 @@ static bool SearchPath(const char *name, char path[hasatleast PATH_MAX], sep = IsWindows() && strchr(p, ';') ? ';' : ':'; for (;;) { for (i = 0; p[i] && p[i] != sep; ++i) { - if (i < PATH_MAX) { + if (i < pathsz) { path[i] = p[i]; } } - if (AccessCommand(name, path, namelen, err, suffix, i)) { + if (AccessCommand(name, path, pathsz, namelen, err, suffix, i)) { return true; } if (p[i] == sep) { @@ -86,36 +87,36 @@ static bool SearchPath(const char *name, char path[hasatleast PATH_MAX], return false; } -static bool FindCommand(const char *name, char pathbuf[hasatleast PATH_MAX], - size_t namelen, bool priorityonly, const char *suffix, - int *err) { - if (priorityonly && - (memchr(name, '/', namelen) || memchr(name, '\\', namelen))) { - pathbuf[0] = 0; - return AccessCommand(name, pathbuf, namelen, err, suffix, 0); +static bool FindCommand(const char *name, char *pb, size_t pbsz, size_t namelen, + bool pri, const char *suffix, int *err) { + if (pri && (memchr(name, '/', namelen) || memchr(name, '\\', namelen))) { + pb[0] = 0; + return AccessCommand(name, pb, pbsz, namelen, err, suffix, 0); } - if (IsWindows() && priorityonly) { - return AccessCommand(name, pathbuf, namelen, err, suffix, - stpcpy(pathbuf, kNtSystemDirectory) - pathbuf) || - AccessCommand(name, pathbuf, namelen, err, suffix, - stpcpy(pathbuf, kNtWindowsDirectory) - pathbuf); + if (IsWindows() && pri && + pbsz > max(strlen(kNtSystemDirectory), strlen(kNtWindowsDirectory))) { + return AccessCommand(name, pb, pbsz, namelen, err, suffix, + stpcpy(pb, kNtSystemDirectory) - pb) || + AccessCommand(name, pb, pbsz, namelen, err, suffix, + stpcpy(pb, kNtWindowsDirectory) - pb); } - return (IsWindows() && AccessCommand(name, pathbuf, namelen, err, suffix, - stpcpy(pathbuf, ".") - pathbuf)) || - SearchPath(name, pathbuf, namelen, err, suffix); + return (IsWindows() && + (pbsz > 1 && AccessCommand(name, pb, pbsz, namelen, err, suffix, + stpcpy(pb, ".") - pb))) || + SearchPath(name, pb, pbsz, namelen, err, suffix); } -static bool FindVerbatim(const char *name, char pathbuf[hasatleast PATH_MAX], - size_t namelen, bool priorityonly, int *err) { - return FindCommand(name, pathbuf, namelen, priorityonly, "", err); +static bool FindVerbatim(const char *name, char *pb, size_t pbsz, + size_t namelen, bool pri, int *err) { + return FindCommand(name, pb, pbsz, namelen, pri, "", err); } -static bool FindSuffixed(const char *name, char pathbuf[hasatleast PATH_MAX], - size_t namelen, bool priorityonly, int *err) { +static bool FindSuffixed(const char *name, char *pb, size_t pbsz, + size_t namelen, bool pri, int *err) { return !IsExePath(name, namelen) && !IsComPath(name, namelen) && !IsComDbgPath(name, namelen) && - (FindCommand(name, pathbuf, namelen, priorityonly, ".com", err) || - FindCommand(name, pathbuf, namelen, priorityonly, ".exe", err)); + (FindCommand(name, pb, pbsz, namelen, pri, ".com", err) || + FindCommand(name, pb, pbsz, namelen, pri, ".exe", err)); } /** @@ -127,7 +128,7 @@ static bool FindSuffixed(const char *name, char pathbuf[hasatleast PATH_MAX], * @asyncsignalsafe * @vforksafe */ -char *commandv(const char *name, char pathbuf[hasatleast PATH_MAX]) { +char *commandv(const char *name, char *pathbuf, size_t pathbufsz) { int e, f; char *res; size_t namelen; @@ -136,25 +137,27 @@ char *commandv(const char *name, char pathbuf[hasatleast PATH_MAX]) { efault(); } else if (!(namelen = strlen(name))) { enoent(); - } else if (namelen + 1 > PATH_MAX) { + } else if (namelen + 1 > pathbufsz) { enametoolong(); } else { e = errno; f = ENOENT; - if ((IsWindows() && (FindSuffixed(name, pathbuf, namelen, true, &f) || - FindVerbatim(name, pathbuf, namelen, true, &f) || - FindSuffixed(name, pathbuf, namelen, false, &f) || - FindVerbatim(name, pathbuf, namelen, false, &f))) || - (!IsWindows() && (FindVerbatim(name, pathbuf, namelen, true, &f) || - FindSuffixed(name, pathbuf, namelen, true, &f) || - FindVerbatim(name, pathbuf, namelen, false, &f) || - FindSuffixed(name, pathbuf, namelen, false, &f)))) { + if ((IsWindows() && + (FindSuffixed(name, pathbuf, pathbufsz, namelen, true, &f) || + FindVerbatim(name, pathbuf, pathbufsz, namelen, true, &f) || + FindSuffixed(name, pathbuf, pathbufsz, namelen, false, &f) || + FindVerbatim(name, pathbuf, pathbufsz, namelen, false, &f))) || + (!IsWindows() && + (FindVerbatim(name, pathbuf, pathbufsz, namelen, true, &f) || + FindSuffixed(name, pathbuf, pathbufsz, namelen, true, &f) || + FindVerbatim(name, pathbuf, pathbufsz, namelen, false, &f) || + FindSuffixed(name, pathbuf, pathbufsz, namelen, false, &f)))) { errno = e; res = pathbuf; } else { errno = f; } } - STRACE("commandv(%#s, %p) → %#s% m", name, pathbuf, res); + STRACE("commandv(%#s, %p, %'zu) → %#s% m", name, pathbuf, pathbufsz, res); return res; } diff --git a/libc/calls/copyfile.c b/libc/calls/copyfile.c index c9e9cc93a..1e36a0f80 100644 --- a/libc/calls/copyfile.c +++ b/libc/calls/copyfile.c @@ -37,7 +37,7 @@ static textwindows int sys_copyfile_nt(const char *src, const char *dst, int flags) { int64_t fhsrc, fhdst; struct NtFileTime accessed, modified; - char16_t src16[PATH_MAX], dst16[PATH_MAX]; + char16_t src16[PATH_MAX + 1], dst16[PATH_MAX + 1]; if (__mkntpath(src, src16) == -1) return -1; if (__mkntpath(dst, dst16) == -1) return -1; if (CopyFile(src16, dst16, !!(flags & COPYFILE_NOCLOBBER))) { diff --git a/libc/calls/execlp.c b/libc/calls/execlp.c index 3444fe589..bc59b49d7 100644 --- a/libc/calls/execlp.c +++ b/libc/calls/execlp.c @@ -39,8 +39,8 @@ int execlp(const char *prog, const char *arg, ... /*, NULL*/) { char *exe; char **argv; va_list va, vb; - char pathbuf[PATH_MAX]; - if (!(exe = commandv(prog, pathbuf))) return -1; + char pathbuf[PATH_MAX + 1]; + if (!(exe = commandv(prog, pathbuf, sizeof(pathbuf)))) return -1; va_copy(vb, va); va_start(va, arg); for (i = 0; va_arg(va, const char *); ++i) donothing; diff --git a/libc/calls/execve-sysv.c b/libc/calls/execve-sysv.c index 28b842734..4c8eccfd3 100644 --- a/libc/calls/execve-sysv.c +++ b/libc/calls/execve-sysv.c @@ -33,7 +33,8 @@ int sys_execve(const char *prog, char *const argv[], char *const envp[]) { shargs = alloca((i + 2) * sizeof(char *)); memcpy(shargs + 2, argv + 1, i * sizeof(char *)); if (IsFreebsd() || IsNetbsd()) { - shargs[0] = firstnonnull(commandv("bash", alloca(PATH_MAX)), _PATH_BSHELL); + shargs[0] = firstnonnull( + commandv("bash", alloca(PATH_MAX + 1), PATH_MAX + 1), _PATH_BSHELL); } else { shargs[0] = _PATH_BSHELL; } diff --git a/libc/calls/execve.c b/libc/calls/execve.c index f7a278ecb..9df027a14 100644 --- a/libc/calls/execve.c +++ b/libc/calls/execve.c @@ -60,7 +60,7 @@ int execve(const char *prog, char *const argv[], char *const envp[]) { if (i) kprintf(", "); kprintf("%#s", envp[i]); } - kprintf("})%n"); + kprintf("})\n"); } #endif for (i = 3; i < g_fds.n; ++i) { diff --git a/libc/calls/execvpe.c b/libc/calls/execvpe.c index 8227f2eb1..ed1fd82a9 100644 --- a/libc/calls/execvpe.c +++ b/libc/calls/execvpe.c @@ -36,8 +36,8 @@ */ int execvpe(const char *prog, char *const argv[], char *const *envp) { char *exe; - char pathbuf[PATH_MAX]; + char pathbuf[PATH_MAX + 1]; if (IsAsan() && !__asan_is_valid(prog, 1)) return efault(); - if (!(exe = commandv(prog, pathbuf))) return -1; + if (!(exe = commandv(prog, pathbuf, sizeof(pathbuf)))) return -1; return execve(exe, argv, envp); } diff --git a/libc/calls/faccessat-nt.c b/libc/calls/faccessat-nt.c index c1c66d4da..f8298dcb7 100644 --- a/libc/calls/faccessat-nt.c +++ b/libc/calls/faccessat-nt.c @@ -22,7 +22,7 @@ #include "libc/sysv/errfuns.h" int sys_faccessat_nt(int dirfd, const char *path, int mode, uint32_t flags) { - char16_t path16[PATH_MAX]; + char16_t path16[PATH_MAX + 1]; if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1; return __fix_enotdir(ntaccesscheck(path16, mode), path16); } diff --git a/libc/calls/fchdir-nt.c b/libc/calls/fchdir-nt.c index ab8db742c..3c089a653 100644 --- a/libc/calls/fchdir-nt.c +++ b/libc/calls/fchdir-nt.c @@ -24,7 +24,7 @@ textwindows int sys_fchdir_nt(int dirfd) { uint32_t len; - char16_t dir[PATH_MAX]; + char16_t dir[PATH_MAX + 1]; if (!__isfdkind(dirfd, kFdFile)) return ebadf(); len = GetFinalPathNameByHandle(g_fds.p[dirfd].handle, dir, ARRAYLEN(dir), kNtFileNameNormalized | kNtVolumeNameDos); diff --git a/libc/calls/fchmodat-nt.c b/libc/calls/fchmodat-nt.c index 7b0ba5f7c..d8698aa2e 100644 --- a/libc/calls/fchmodat-nt.c +++ b/libc/calls/fchmodat-nt.c @@ -23,7 +23,7 @@ textwindows int sys_fchmodat_nt(int dirfd, const char *path, uint32_t mode, int flags) { uint32_t attr; - uint16_t path16[PATH_MAX]; + uint16_t path16[PATH_MAX + 1]; if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1; if ((attr = GetFileAttributes(path16)) != -1u) { if (mode & 0200) { diff --git a/libc/calls/fileexists.c b/libc/calls/fileexists.c index 876a1cac9..64aa86cac 100644 --- a/libc/calls/fileexists.c +++ b/libc/calls/fileexists.c @@ -48,7 +48,7 @@ bool fileexists(const char *path) { bool res; union metastat st; struct ZiposUri zipname; - uint16_t path16[PATH_MAX]; + uint16_t path16[PATH_MAX + 1]; e = errno; if (IsAsan() && !__asan_is_valid(path, 1)) { efault(); diff --git a/libc/calls/fstatat-nt.c b/libc/calls/fstatat-nt.c index 468752e70..b17d6d324 100644 --- a/libc/calls/fstatat-nt.c +++ b/libc/calls/fstatat-nt.c @@ -29,7 +29,7 @@ textwindows int sys_fstatat_nt(int dirfd, const char *path, struct stat *st, int flags) { int rc; int64_t fh; - uint16_t path16[PATH_MAX]; + uint16_t path16[PATH_MAX + 1]; if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1; if ((fh = CreateFile( path16, kNtFileReadAttributes, 0, 0, kNtOpenExisting, diff --git a/libc/calls/getcwd-nt.c b/libc/calls/getcwd-nt.c index b2236e276..0847b8653 100644 --- a/libc/calls/getcwd-nt.c +++ b/libc/calls/getcwd-nt.c @@ -28,15 +28,22 @@ textwindows char *sys_getcwd_nt(char *buf, size_t size) { uint64_t w; wint_t x, y; uint32_t n, i, j; - char16_t name16[PATH_MAX + 1]; - if ((n = GetCurrentDirectory(ARRAYLEN(name16), name16))) { - if (n <= PATH_MAX) { - tprecode16to8(buf, size, name16); - for (j = i = 0; i < n;) { - x = name16[i++] & 0xffff; + char16_t p[PATH_MAX + 1]; + if ((n = GetCurrentDirectory(ARRAYLEN(p), p))) { + if (4 + n + 1 <= size && 4 + n + 1 <= ARRAYLEN(p)) { + tprecode16to8(buf, size, p); + j = 0; + if (n >= 3 && isalpha(p[0]) && p[1] == ':' && p[2] == '\\') { + buf[j++] = '/'; + buf[j++] = '/'; + buf[j++] = '?'; + buf[j++] = '/'; + } + for (i = 0; i < n;) { + x = p[i++] & 0xffff; if (!IsUcs2(x)) { if (i < n) { - y = name16[i++] & 0xffff; + y = p[i++] & 0xffff; x = MergeUtf16(x, y); } else { x = 0xfffd; diff --git a/libc/calls/getegid.c b/libc/calls/getegid.c index a6c4709bb..929baeb10 100644 --- a/libc/calls/getegid.c +++ b/libc/calls/getegid.c @@ -19,13 +19,19 @@ #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/dce.h" /** * Returns effective group ID of calling process. + * @return group id */ uint32_t getegid(void) { int rc; - rc = sys_getegid(); + if (!IsWindows()) { + rc = sys_getegid(); + } else { + rc = getgid(); + } STRACE("%s() → %d% m", "getegid", rc); return rc; } diff --git a/libc/calls/geteuid.c b/libc/calls/geteuid.c index f1990087c..260c4b6ed 100644 --- a/libc/calls/geteuid.c +++ b/libc/calls/geteuid.c @@ -22,10 +22,15 @@ /** * Returns effective user ID of calling process. + * @return user id */ uint32_t geteuid(void) { int rc; - rc = sys_geteuid(); + if (!IsWindows()) { + rc = sys_geteuid(); + } else { + rc = getuid(); + } STRACE("%s() → %d% m", "geteuid", rc); return rc; } diff --git a/libc/calls/getpgrp.c b/libc/calls/getpgrp.c index e6edd52f4..9a15d1c6e 100644 --- a/libc/calls/getpgrp.c +++ b/libc/calls/getpgrp.c @@ -24,7 +24,7 @@ /** * Returns process group id of calling process. */ -uint32_t getpgrp(void) { +int getpgrp(void) { int rc; if (!IsWindows()) { rc = sys_getpgrp(); diff --git a/libc/calls/getrusage-nt.c b/libc/calls/getrusage-nt.c index 341736356..35bb44e50 100644 --- a/libc/calls/getrusage-nt.c +++ b/libc/calls/getrusage-nt.c @@ -18,11 +18,14 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/sig.internal.h" #include "libc/calls/struct/rusage.h" #include "libc/fmt/conv.h" +#include "libc/intrin/spinlock.h" #include "libc/nt/accounting.h" #include "libc/nt/process.h" #include "libc/nt/runtime.h" +#include "libc/nt/struct/iocounters.h" #include "libc/nt/struct/processmemorycounters.h" #include "libc/nt/thread.h" #include "libc/str/str.h" @@ -30,22 +33,32 @@ #include "libc/sysv/errfuns.h" textwindows int sys_getrusage_nt(int who, struct rusage *usage) { + int64_t me, nsignals; + struct NtIoCounters iocount; struct NtProcessMemoryCountersEx memcount; struct NtFileTime ftExit, ftUser, ftKernel, ftCreation; if (!usage) return efault(); if (who == 99) return enosys(); // @see libc/sysv/consts.sh if (!usage) return 0; + me = GetCurrentProcess(); if (!(who == RUSAGE_SELF ? GetProcessTimes : GetThreadTimes)( (who == RUSAGE_SELF ? GetCurrentProcess : GetCurrentThread)(), &ftCreation, &ftExit, &ftKernel, &ftUser) || - !GetProcessMemoryInfo(GetCurrentProcess(), &memcount, sizeof(memcount))) { + !GetProcessMemoryInfo(me, &memcount, sizeof(memcount)) || + !GetProcessIoCounters(me, &iocount)) { return __winerr(); } + _spinlock(&__sig_lock); + nsignals = __sig_count; + _spunlock(&__sig_lock); *usage = (struct rusage){ .ru_utime = WindowsDurationToTimeVal(ReadFileTime(ftUser)), .ru_stime = WindowsDurationToTimeVal(ReadFileTime(ftKernel)), .ru_maxrss = memcount.PeakWorkingSetSize / 1024, .ru_majflt = memcount.PageFaultCount, + .ru_inblock = iocount.ReadOperationCount, + .ru_oublock = iocount.WriteOperationCount, + .ru_nsignals = __sig_count, }; return 0; } diff --git a/libc/calls/getsid.c b/libc/calls/getsid.c index 4c29139f2..1d199e2b0 100644 --- a/libc/calls/getsid.c +++ b/libc/calls/getsid.c @@ -23,7 +23,7 @@ /** * Creates session and sets the process group id. */ -uint32_t getsid(int pid) { +int getsid(int pid) { int rc; rc = sys_getsid(pid); STRACE("%s(%d) → %d% m", "getsid", pid, rc); diff --git a/libc/calls/internal.h b/libc/calls/internal.h index 713383060..283b6f9fb 100644 --- a/libc/calls/internal.h +++ b/libc/calls/internal.h @@ -88,7 +88,7 @@ int __reservefd(int) hidden; void __releasefd(int) hidden; int __ensurefds(int) hidden; int64_t __getfdhandleactual(int) hidden; -void __printfds(void); +void __printfds(void) hidden; forceinline bool __isfdopen(int fd) { return 0 <= fd && fd < g_fds.n && g_fds.p[fd].kind != kFdEmpty; @@ -165,6 +165,7 @@ i32 sys_getppid(void) hidden; i32 sys_getpriority(i32, u32) hidden; i32 sys_getrlimit(i32, struct rlimit *) hidden; i32 sys_getrusage(i32, struct rusage *) hidden; +i32 sys_getsid(int) hidden; i32 sys_ioctl(i32, u64, ...) hidden; i32 sys_kill(i32, i32, i32) hidden; i32 sys_linkat(i32, const char *, i32, const char *, i32) hidden; @@ -191,8 +192,10 @@ i32 sys_setgid(i32) hidden; i32 sys_setitimer(i32, const struct itimerval *, struct itimerval *) hidden; i32 sys_setpgid(i32, i32) hidden; i32 sys_setpriority(i32, u32, i32) hidden; +i32 sys_setregid(u32, u32) hidden; i32 sys_setresgid(uint32_t, uint32_t, uint32_t) hidden; i32 sys_setresuid(uint32_t, uint32_t, uint32_t) hidden; +i32 sys_setreuid(u32, u32) hidden; i32 sys_setrlimit(i32, const struct rlimit *) hidden; i32 sys_setsid(void) hidden; i32 sys_setuid(i32) hidden; @@ -230,7 +233,6 @@ i64 sys_write(i32, const void *, u64) hidden; u32 sys_getegid(void) hidden; u32 sys_geteuid(void) hidden; u32 sys_getgid(void) hidden; -u32 sys_getsid(int) hidden; u32 sys_gettid(void) hidden; u32 sys_getuid(void) hidden; u32 sys_umask(u32) hidden; @@ -337,9 +339,10 @@ bool isregularfile_nt(const char *) hidden; bool issymlink_nt(const char *) hidden; bool32 ntsetprivilege(i64, const char16_t *, u32) hidden; char16_t *CreatePipeName(char16_t *) hidden; -int __mkntpath(const char *, char16_t[hasatleast PATH_MAX - 16]) hidden; -int __mkntpath2(const char *, char16_t[hasatleast PATH_MAX - 16], int) hidden; -int __mkntpathat(int, const char *, int, char16_t[PATH_MAX]) hidden; +int __mkntpath(const char *, char16_t[hasatleast PATH_MAX + 1]) hidden; +int __mkntpath2(const char *, char16_t[hasatleast PATH_MAX + 1], int) hidden; +int __mkntpathat(int, const char *, int, + char16_t[hasatleast PATH_MAX + 1]) hidden; int sys_clock_gettime_nt(int, struct timespec *) hidden; int ntaccesscheck(const char16_t *, u32) paramsnonnull() hidden; int sys_getsetpriority_nt(int, int, int, int (*)(int)); diff --git a/libc/calls/ioctl_tcgets-nt.c b/libc/calls/ioctl_tcgets-nt.c index c3b0110ac..ac04c84f5 100644 --- a/libc/calls/ioctl_tcgets-nt.c +++ b/libc/calls/ioctl_tcgets-nt.c @@ -19,6 +19,7 @@ #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/struct/termios.h" +#include "libc/calls/ttydefaults.h" #include "libc/nt/console.h" #include "libc/nt/enum/consolemodeflags.h" #include "libc/nt/struct/consolescreenbufferinfoex.h" @@ -35,6 +36,23 @@ textwindows int ioctl_tcgets_nt(int ignored, struct termios *tio) { outok = GetConsoleMode((out = __getfdhandleactual(1)), &outmode); if (inok | outok) { bzero(tio, sizeof(*tio)); + + tio->c_cflag |= CS8; + + tio->c_cc[VINTR] = CTRL('C'); + tio->c_cc[VQUIT] = CTRL('\\'); + tio->c_cc[VERASE] = CTRL('?'); + tio->c_cc[VKILL] = CTRL('U'); + tio->c_cc[VEOF] = CTRL('D'); + tio->c_cc[VMIN] = CTRL('A'); + tio->c_cc[VSTART] = CTRL('Q'); + tio->c_cc[VSTOP] = CTRL('S'); + tio->c_cc[VSUSP] = CTRL('Z'); + tio->c_cc[VREPRINT] = CTRL('R'); + tio->c_cc[VDISCARD] = CTRL('O'); + tio->c_cc[VWERASE] = CTRL('W'); + tio->c_cc[VLNEXT] = CTRL('V'); + if (inok) { if (inmode & kNtEnableLineInput) { tio->c_lflag |= ICANON; @@ -44,16 +62,21 @@ textwindows int ioctl_tcgets_nt(int ignored, struct termios *tio) { } if (inmode & kNtEnableProcessedInput) { tio->c_lflag |= IEXTEN | ISIG; + if (tio->c_lflag | ECHO) { + tio->c_lflag |= ECHOE; + } } } + if (outok) { if (outmode & kNtEnableProcessedOutput) { tio->c_oflag |= OPOST; } if (!(outmode & kNtDisableNewlineAutoReturn)) { - tio->c_oflag |= ONLCR; + tio->c_oflag |= OPOST | ONLCR; } } + return 0; } else { return enotty(); diff --git a/libc/calls/ioctl_tcsets-nt.c b/libc/calls/ioctl_tcsets-nt.c index a457a1d4e..9eb023cad 100644 --- a/libc/calls/ioctl_tcsets-nt.c +++ b/libc/calls/ioctl_tcsets-nt.c @@ -17,8 +17,10 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +#include "libc/calls/strace.internal.h" #include "libc/calls/struct/metatermios.internal.h" #include "libc/calls/termios.internal.h" +#include "libc/intrin/describeflags.internal.h" #include "libc/nt/console.h" #include "libc/nt/enum/consolemodeflags.h" #include "libc/nt/enum/version.h" @@ -29,35 +31,54 @@ textwindows int ioctl_tcsets_nt(int ignored, uint64_t request, const struct termios *tio) { int64_t in, out; - bool32 inok, outok; + bool32 ok, inok, outok; uint32_t inmode, outmode; inok = GetConsoleMode((in = __getfdhandleactual(0)), &inmode); outok = GetConsoleMode((out = __getfdhandleactual(1)), &outmode); if (inok | outok) { + if (inok) { if (request == TCSETSF) { FlushConsoleInputBuffer(in); } 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 (tio->c_lflag & ICANON) { + inmode |= kNtEnableLineInput; + } + if (tio->c_lflag & ECHO) { + /* + * kNtEnableEchoInput can be used only if the ENABLE_LINE_INPUT mode + * is also enabled. --Quoth MSDN + */ + inmode |= kNtEnableEchoInput | kNtEnableLineInput; + } + if (tio->c_lflag & (IEXTEN | ISIG)) { + inmode |= kNtEnableProcessedInput; + } if (NtGetVersion() >= kNtVersionWindows10) { inmode |= kNtEnableVirtualTerminalInput; } - SetConsoleMode(in, inmode); + ok = SetConsoleMode(in, inmode); + NTTRACE("SetConsoleMode(%p, %s) → %hhhd", in, + DescribeNtConsoleModeInputFlags(inmode), ok); } + if (outok) { - outmode |= kNtEnableWrapAtEolOutput; - if (tio->c_oflag & OPOST) outmode |= kNtEnableProcessedOutput; - if (!(tio->c_oflag & ONLCR)) outmode |= kNtDisableNewlineAutoReturn; + outmode &= ~(kNtDisableNewlineAutoReturn); + outmode |= kNtEnableProcessedOutput; + if (!(tio->c_oflag & ONLCR)) { + outmode |= kNtDisableNewlineAutoReturn; + } if (NtGetVersion() >= kNtVersionWindows10) { outmode |= kNtEnableVirtualTerminalProcessing; } - SetConsoleMode(out, outmode); + ok = SetConsoleMode(out, outmode); + NTTRACE("SetConsoleMode(%p, %s) → %hhhd", out, + DescribeNtConsoleModeOutputFlags(outmode), ok); } + return 0; } else { return enotty(); diff --git a/libc/calls/ioctl_tcsets.c b/libc/calls/ioctl_tcsets.c index b7f8a4155..34e2592fa 100644 --- a/libc/calls/ioctl_tcsets.c +++ b/libc/calls/ioctl_tcsets.c @@ -83,11 +83,6 @@ int ioctl_tcsets(int fd, uint64_t request, ...) { } else { rc = einval(); } - if (rc != -1) { - if (__nomultics == 0 || __nomultics == 1) { - __nomultics = !(tio->c_oflag & OPOST); - } - } STRACE("ioctl_tcsets(%d, %p, %p) → %d% m", fd, request, tio, rc); return rc; } diff --git a/libc/calls/isdirectory-nt.c b/libc/calls/isdirectory-nt.c index d0579a967..36e1dd9db 100644 --- a/libc/calls/isdirectory-nt.c +++ b/libc/calls/isdirectory-nt.c @@ -28,7 +28,7 @@ bool isdirectory_nt(const char *path) { int e; uint32_t x; - char16_t path16[PATH_MAX]; + char16_t path16[PATH_MAX + 1]; e = errno; if (__mkntpath(path, path16) == -1) return -1; if ((x = GetFileAttributes(path16)) != -1u) { diff --git a/libc/calls/isregularfile-nt.c b/libc/calls/isregularfile-nt.c index 45614ef64..91ffab2fd 100644 --- a/libc/calls/isregularfile-nt.c +++ b/libc/calls/isregularfile-nt.c @@ -28,7 +28,7 @@ bool isregularfile_nt(const char *path) { int e; uint32_t x; - char16_t path16[PATH_MAX]; + char16_t path16[PATH_MAX + 1]; e = errno; if (__mkntpath(path, path16) == -1) return -1; if ((x = GetFileAttributes(path16)) != -1u) { diff --git a/libc/calls/issymlink-nt.c b/libc/calls/issymlink-nt.c index 4c074cadb..ef9c3170a 100644 --- a/libc/calls/issymlink-nt.c +++ b/libc/calls/issymlink-nt.c @@ -28,7 +28,7 @@ bool issymlink_nt(const char *path) { int e; uint32_t x; - char16_t path16[PATH_MAX]; + char16_t path16[PATH_MAX + 1]; e = errno; if (__mkntpath(path, path16) == -1) return -1; if ((x = GetFileAttributes(path16)) != -1u) { diff --git a/libc/calls/linkat-nt.c b/libc/calls/linkat-nt.c index 072c203f6..dea46cee9 100644 --- a/libc/calls/linkat-nt.c +++ b/libc/calls/linkat-nt.c @@ -23,8 +23,8 @@ textwindows int sys_linkat_nt(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) { - char16_t newpath16[PATH_MAX]; - char16_t oldpath16[PATH_MAX]; + char16_t newpath16[PATH_MAX + 1]; + char16_t oldpath16[PATH_MAX + 1]; if (__mkntpathat(olddirfd, oldpath, 0, oldpath16) != -1 && __mkntpathat(newdirfd, newpath, 0, newpath16) != -1) { if (CreateHardLink(newpath16, oldpath16, NULL)) { diff --git a/libc/calls/mkdir.c b/libc/calls/mkdir.c index cda89a91c..d41c67814 100644 --- a/libc/calls/mkdir.c +++ b/libc/calls/mkdir.c @@ -36,7 +36,7 @@ * @param path is a UTF-8 string, preferably relative w/ forward slashes * @param mode can be, for example, 0755 * @return 0 on success or -1 w/ errno - * @error EEXIST, ENOTDIR, ENAMETOOLONG, EACCES + * @error ENAMETOOLONG if >246 characters on NT * @asyncsignalsafe * @see makedirs() */ diff --git a/libc/calls/mkdirat-nt.c b/libc/calls/mkdirat-nt.c index b167b9b5b..f15b90326 100644 --- a/libc/calls/mkdirat-nt.c +++ b/libc/calls/mkdirat-nt.c @@ -18,10 +18,13 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" #include "libc/nt/files.h" +#include "libc/str/str.h" +#include "libc/sysv/errfuns.h" textwindows int sys_mkdirat_nt(int dirfd, const char *path, uint32_t mode) { int e; - char16_t *p, path16[PATH_MAX]; + char16_t *p, path16[PATH_MAX + 1]; + /* if (strlen(path) > 248) return enametoolong(); */ if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1; if (CreateDirectory(path16, 0)) return 0; return __fix_enotdir(-1, path16); diff --git a/libc/calls/mkntpath.c b/libc/calls/mkntpath.c index 85248baad..ab5bdd6a8 100644 --- a/libc/calls/mkntpath.c +++ b/libc/calls/mkntpath.c @@ -19,6 +19,7 @@ #include "libc/calls/internal.h" #include "libc/calls/ntmagicpaths.internal.h" #include "libc/calls/strace.internal.h" +#include "libc/macros.internal.h" #include "libc/nt/systeminfo.h" #include "libc/str/oldutf16.internal.h" #include "libc/str/str.h" @@ -49,7 +50,7 @@ textwindows static const char *FixNtMagicPath(const char *path, } textwindows int __mkntpath(const char *path, - char16_t path16[hasatleast PATH_MAX - 16]) { + char16_t path16[hasatleast PATH_MAX + 1]) { return __mkntpath2(path, path16, -1); } @@ -67,14 +68,14 @@ textwindows int __mkntpath(const char *path, * @error ENAMETOOLONG */ textwindows int __mkntpath2(const char *path, - char16_t path16[hasatleast PATH_MAX - 16], + char16_t path16[hasatleast PATH_MAX + 1], int flags) { /* - * 1. Reserve +1 for NUL-terminator - * 2. Reserve +1 for UTF-16 overflow - * 3. Reserve ≥2 for SetCurrentDirectory trailing slash requirement - * 4. Reserve ≥10 for CreateNamedPipe "\\.\pipe\" prefix requirement - * 5. Reserve ≥13 for mkdir() i.e. 1+8+3+1, e.g. "\\ffffffff.xxx\0" + * 1. Need +1 for NUL-terminator + * 2. Need +1 for UTF-16 overflow + * 3. Need ≥2 for SetCurrentDirectory trailing slash requirement + * 5. Need ≥13 for mkdir() i.e. 1+8+3+1, e.g. "\\ffffffff.xxx\0" + * which is an "8.3 filename" from the DOS days */ char16_t *p; const char *q; @@ -83,7 +84,12 @@ textwindows int __mkntpath2(const char *path, path = FixNtMagicPath(path, flags); p = path16; q = path; - z = PATH_MAX - 16; + if (IsSlash(path[0]) && IsSlash(path[1]) && path[2] == '?' && + IsSlash(path[3])) { + z = MIN(32767, PATH_MAX); + } else { + z = MIN(260, PATH_MAX); + } if (IsSlash(q[0]) && q[1] == 't' && q[2] == 'm' && q[3] == 'p' && (IsSlash(q[4]) || !q[4])) { m = GetTempPath(z, p); diff --git a/libc/calls/mkntpathat.c b/libc/calls/mkntpathat.c index 0bd1e4e12..f4e6fd592 100644 --- a/libc/calls/mkntpathat.c +++ b/libc/calls/mkntpathat.c @@ -25,9 +25,9 @@ #include "libc/sysv/errfuns.h" int __mkntpathat(int dirfd, const char *path, int flags, - char16_t file[PATH_MAX]) { - char16_t dir[PATH_MAX]; + char16_t file[hasatleast PATH_MAX + 1]) { uint32_t dirlen, filelen; + char16_t dir[PATH_MAX + 1]; if ((filelen = __mkntpath2(path, file, flags)) == -1) return -1; if (!filelen) return enoent(); if (file[0] != u'\\' && dirfd != AT_FDCWD) { /* ProTip: \\?\C:\foo */ diff --git a/libc/calls/nanosleep-nt.c b/libc/calls/nanosleep-nt.c index c7b7d7125..275bf3086 100644 --- a/libc/calls/nanosleep-nt.c +++ b/libc/calls/nanosleep-nt.c @@ -20,6 +20,7 @@ #include "libc/calls/sig.internal.h" #include "libc/calls/strace.internal.h" #include "libc/errno.h" +#include "libc/limits.h" #include "libc/macros.internal.h" #include "libc/nt/errors.h" #include "libc/nt/nt/time.h" @@ -34,7 +35,7 @@ textwindows noinstrument int sys_nanosleep_nt(const struct timespec *req, int64_t ms, sec, nsec; if (__builtin_mul_overflow(req->tv_sec, 1000, &ms) || __builtin_add_overflow(ms, req->tv_nsec / 1000000, &ms)) { - ms = -1; + ms = INT64_MAX; } if (!ms && (req->tv_sec || req->tv_nsec)) { ms = 1; diff --git a/libc/calls/ntspawn.c b/libc/calls/ntspawn.c index d5f5055bb..584c42a2c 100644 --- a/libc/calls/ntspawn.c +++ b/libc/calls/ntspawn.c @@ -70,7 +70,7 @@ textwindows int ntspawn( int64_t handle; size_t blocksize; struct SpawnBlock *block; - char16_t prog16[PATH_MAX]; + char16_t prog16[PATH_MAX + 1]; rc = -1; block = NULL; if (__mkntpath(prog, prog16) == -1) return -1; diff --git a/libc/calls/open-nt.c b/libc/calls/open-nt.c index 1955a5d18..75c23d7dd 100644 --- a/libc/calls/open-nt.c +++ b/libc/calls/open-nt.c @@ -38,7 +38,7 @@ static textwindows int64_t sys_open_nt_impl(int dirfd, const char *path, uint32_t flags, int32_t mode) { - char16_t path16[PATH_MAX]; + char16_t path16[PATH_MAX + 1]; uint32_t perm, share, disp, attr; if (__mkntpathat(dirfd, path, flags, path16) == -1) return -1; if (GetNtOpenFlags(flags, mode, &perm, &share, &disp, &attr) == -1) return -1; diff --git a/libc/calls/poll.c b/libc/calls/poll.c index acada2c86..f58228e32 100644 --- a/libc/calls/poll.c +++ b/libc/calls/poll.c @@ -95,7 +95,7 @@ int poll(struct pollfd *fds, size_t nfds, int timeout_ms) { DescribePollFlags(flagbuf[0], sizeof(flagbuf[0]), fds[i].events), DescribePollFlags(flagbuf[1], sizeof(flagbuf[1]), fds[i].revents)); } - kprintf("%s}, %'zu, %'d) → %d% lm%n", i == 5 ? "..." : "", nfds, + kprintf("%s}, %'zu, %'d) → %d% lm\n", i == 5 ? "..." : "", nfds, timeout_ms, rc); } } diff --git a/libc/calls/preadv.c b/libc/calls/preadv.c index b4faa2411..94f83a1b8 100644 --- a/libc/calls/preadv.c +++ b/libc/calls/preadv.c @@ -118,7 +118,7 @@ ssize_t preadv(int fd, struct iovec *iov, int iovlen, int64_t off) { } else { kprintf(STRACE_PROLOGUE "preadv(%d, [", fd); __strace_iov(iov, iovlen, rc != -1 ? rc : 0); - kprintf("], %d, %'ld) → %'ld% m%n", iovlen, off, rc); + kprintf("], %d, %'ld) → %'ld% m\n", iovlen, off, rc); } } #endif diff --git a/libc/calls/printfds.c b/libc/calls/printfds.c index 8b5d93ffd..26ad273b2 100644 --- a/libc/calls/printfds.c +++ b/libc/calls/printfds.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" #include "libc/intrin/kprintf.h" +#include "libc/intrin/spinlock.h" static const char *__fdkind2str(int x) { switch (x) { @@ -44,6 +45,7 @@ static const char *__fdkind2str(int x) { void __printfds(void) { int i; + _spinlock(&__fds_lock); for (i = 0; i < g_fds.n; ++i) { if (!g_fds.p[i].kind) continue; kprintf("%3d %s", i, __fdkind2str(g_fds.p[i].kind)); @@ -53,6 +55,7 @@ void __printfds(void) { if (g_fds.p[i].handle) kprintf(" handle=%ld", g_fds.p[i].handle); if (g_fds.p[i].extra) kprintf(" extra=%ld", g_fds.p[i].extra); if (g_fds.p[i].worker) kprintf(" worker=%p", g_fds.p[i].worker); - kprintf("%n", g_fds.p[i].zombie); + kprintf("\n"); } + _spunlock(&__fds_lock); } diff --git a/libc/calls/program_executable_name.c b/libc/calls/program_executable_name.c index 0ef78b22f..316147b28 100644 --- a/libc/calls/program_executable_name.c +++ b/libc/calls/program_executable_name.c @@ -52,14 +52,21 @@ static textwindows bool GetNtExePath(char exe[SIZE]) { uint64_t w; wint_t x, y; uint32_t i, j; - char16_t path16[PATH_MAX + 1]; - path16[0] = 0; - rc = GetModuleFileName(0, path16, ARRAYLEN(path16)); - NTTRACE("GetModuleFileName(0, [%#hs]) → %hhhd", path16, rc); + char16_t p[PATH_MAX + 1]; + p[0] = 0; + rc = GetModuleFileName(0, p, ARRAYLEN(p)); + NTTRACE("GetModuleFileName(0, [%#hs]) → %hhhd", p, rc); if (!rc) return false; - for (i = j = 0; (x = path16[i++] & 0xffff);) { + j = 0; + if (p[0] != '\\' || p[1] != '\\' || p[2] != '?' || p[3] != '\\') { + exe[j++] = '/'; + exe[j++] = '/'; + exe[j++] = '?'; + exe[j++] = '/'; + } + for (i = 0; (x = p[i++] & 0xffff);) { if (!IsUcs2(x)) { - y = path16[i++] & 0xffff; + y = p[i++] & 0xffff; x = MergeUtf16(x, y); } if (x == '\\') x = '/'; diff --git a/libc/calls/pwritev.c b/libc/calls/pwritev.c index e6e08df84..a480338bc 100644 --- a/libc/calls/pwritev.c +++ b/libc/calls/pwritev.c @@ -123,7 +123,7 @@ ssize_t pwritev(int fd, const struct iovec *iov, int iovlen, int64_t off) { } else { kprintf(STRACE_PROLOGUE "pwritev(%d, ", fd); __strace_iov(iov, iovlen, rc != -1 ? rc : 0); - kprintf(", %d, %'ld) → %'ld% m%n", iovlen, off, rc); + kprintf(", %d, %'ld) → %'ld% m\n", iovlen, off, rc); } } #endif diff --git a/libc/calls/readlinkat-nt.c b/libc/calls/readlinkat-nt.c index bc9ac7048..e18549177 100644 --- a/libc/calls/readlinkat-nt.c +++ b/libc/calls/readlinkat-nt.c @@ -27,6 +27,7 @@ #include "libc/nt/files.h" #include "libc/nt/runtime.h" #include "libc/nt/struct/reparsedatabuffer.h" +#include "libc/str/str.h" #include "libc/str/tpenc.h" #include "libc/str/utf16.h" #include "libc/sysv/errfuns.h" @@ -39,7 +40,7 @@ textwindows ssize_t sys_readlinkat_nt(int dirfd, const char *path, char *buf, wint_t x, y; volatile char *memory; uint32_t i, j, n, mem; - char16_t path16[PATH_MAX], *p; + char16_t path16[PATH_MAX + 1], *p; struct NtReparseDataBuffer *rdb; if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1; mem = 16384; @@ -56,6 +57,12 @@ textwindows ssize_t sys_readlinkat_nt(int dirfd, const char *path, char *buf, n = rdb->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(char16_t); p = (char16_t *)((char *)rdb->SymbolicLinkReparseBuffer.PathBuffer + rdb->SymbolicLinkReparseBuffer.PrintNameOffset); + if (n >= 3 && isalpha(p[0]) && p[1] == ':' && p[2] == '\\') { + buf[j++] = '/'; + buf[j++] = '/'; + buf[j++] = '?'; + buf[j++] = '/'; + } while (i < n) { x = p[i++] & 0xffff; if (!IsUcs2(x)) { diff --git a/libc/calls/readv.c b/libc/calls/readv.c index 287641fd9..490f02c1c 100644 --- a/libc/calls/readv.c +++ b/libc/calls/readv.c @@ -72,7 +72,7 @@ ssize_t readv(int fd, const struct iovec *iov, int iovlen) { } else { kprintf(STRACE_PROLOGUE "readv(%d, [", fd); __strace_iov(iov, iovlen, rc != -1 ? rc : 0); - kprintf("], %d) → %'ld% m%n", iovlen, rc); + kprintf("], %d) → %'ld% m\n", iovlen, rc); } } #endif diff --git a/libc/calls/renameat-nt.c b/libc/calls/renameat-nt.c index 949d05d84..9b34e84f9 100644 --- a/libc/calls/renameat-nt.c +++ b/libc/calls/renameat-nt.c @@ -22,8 +22,8 @@ textwindows int sys_renameat_nt(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) { - char16_t oldpath16[PATH_MAX]; - char16_t newpath16[PATH_MAX]; + char16_t oldpath16[PATH_MAX + 1]; + char16_t newpath16[PATH_MAX + 1]; if (__mkntpathat(olddirfd, oldpath, 0, oldpath16) == -1 || __mkntpathat(newdirfd, newpath, 0, newpath16) == -1) { return -1; diff --git a/libc/calls/setegid.c b/libc/calls/setegid.c index 315ea4cbd..6a5c89087 100644 --- a/libc/calls/setegid.c +++ b/libc/calls/setegid.c @@ -22,9 +22,6 @@ /** * Sets effective group ID. */ -int setegid(unsigned egid) { - int rc; - rc = setregid(-1, egid); - STRACE("%s(%u) → %d% m", "setegid", egid, rc); - return rc; +int setegid(uint32_t egid) { + return setregid(-1, egid); } diff --git a/libc/calls/seteuid.c b/libc/calls/seteuid.c index 996ea7d98..2361627e1 100644 --- a/libc/calls/seteuid.c +++ b/libc/calls/seteuid.c @@ -22,9 +22,6 @@ /** * Sets effective user ID. */ -int seteuid(unsigned euid) { - int rc; - rc = setreuid(-1, euid); - STRACE("%s(%u) → %d% m", "seteuid", euid, rc); - return rc; +int seteuid(uint32_t euid) { + return setregid(euid, -1); } diff --git a/libc/calls/setgid.c b/libc/calls/setgid.c index 76a5d2989..93431f3a4 100644 --- a/libc/calls/setgid.c +++ b/libc/calls/setgid.c @@ -21,12 +21,16 @@ #include "libc/calls/strace.internal.h" /** - * Sets effective group id of current process. + * Sets group id of current process. * @return 0 on success or -1 w/ errno */ int setgid(int gid) { int rc; - rc = sys_setgid(gid); - STRACE("%s(%d) → %d% m", "setgid", gid); + if (IsWindows() && gid == getgid()) { + rc = 0; + } else { + rc = sys_setgid(gid); + } + STRACE("setgid(%d) → %d% m", gid, rc); return rc; } diff --git a/libc/calls/setregid.c b/libc/calls/setregid.c new file mode 100644 index 000000000..e2ee4dc94 --- /dev/null +++ b/libc/calls/setregid.c @@ -0,0 +1,35 @@ +/*-*- 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 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/calls/strace.internal.h" + +/** + * Sets real and/or effective group ids. + * + * @param rgid is real group id or -1 to leave it unchanged + * @param egid is effective group id or -1 to leave it unchanged + * @return 0 on success or -1 w/ errno + */ +int setregid(uint32_t rgid, uint32_t egid) { + int rc; + rc = sys_setregid(rgid, egid); + STRACE("setregid(%d, %d) → %d% m", rgid, egid, rc); + return rc; +} diff --git a/libc/calls/setresgid.c b/libc/calls/setresgid.c index b1af4e79d..846df7a93 100644 --- a/libc/calls/setresgid.c +++ b/libc/calls/setresgid.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/strace.internal.h" /** * Sets real, effective, and "saved" group ids. @@ -25,9 +26,17 @@ * @param real sets real group id or -1 to do nothing * @param effective sets effective group id or -1 to do nothing * @param saved sets saved group id or -1 to do nothing - * @see setregid(), getauxval(AT_SECURE) + * @see setresuid(), getauxval(AT_SECURE) + * @raise ENOSYS on Windows NT */ int setresgid(uint32_t real, uint32_t effective, uint32_t saved) { - if (saved == -1) return setregid(real, effective); - return sys_setresgid(real, effective, saved); + int rc; + if (saved != -1) { + rc = sys_setresgid(real, effective, saved); + } else { + // polyfill xnu and netbsd + rc = sys_setregid(real, effective); + } + STRACE("setresgid(%d, %d, %d) → %d% m", real, effective, saved, rc); + return rc; } diff --git a/libc/calls/setresuid.c b/libc/calls/setresuid.c index c4bc134de..a585dc91c 100644 --- a/libc/calls/setresuid.c +++ b/libc/calls/setresuid.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/strace.internal.h" /** * Sets real, effective, and "saved" user ids. @@ -25,9 +26,17 @@ * @param real sets real user id or -1 to do nothing * @param effective sets effective user id or -1 to do nothing * @param saved sets saved user id or -1 to do nothing - * @see setreuid(), getauxval(AT_SECURE) + * @see setresgid(), getauxval(AT_SECURE) + * @raise ENOSYS on Windows NT */ int setresuid(uint32_t real, uint32_t effective, uint32_t saved) { - if (saved == -1) return setreuid(real, effective); - return sys_setresuid(real, effective, saved); + int rc; + if (saved != -1) { + rc = sys_setresuid(real, effective, saved); + } else { + // polyfill xnu and netbsd + rc = sys_setreuid(real, effective); + } + STRACE("setresuid(%d, %d, %d) → %d% m", real, effective, saved, rc); + return rc; } diff --git a/libc/calls/setreuid.c b/libc/calls/setreuid.c new file mode 100644 index 000000000..e3ff17143 --- /dev/null +++ b/libc/calls/setreuid.c @@ -0,0 +1,35 @@ +/*-*- 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 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/calls/strace.internal.h" + +/** + * Sets real and/or effective user ids. + * + * @param ruid is real user id or -1 to leave it unchanged + * @param euid is effective user id or -1 to leave it unchanged + * @return 0 on success or -1 w/ errno + */ +int setreuid(uint32_t ruid, uint32_t euid) { + int rc; + rc = sys_setreuid(ruid, euid); + STRACE("setreuid(%d, %d) → %d% m", ruid, euid, rc); + return rc; +} diff --git a/libc/calls/setuid.c b/libc/calls/setuid.c index edf316f04..f46735559 100644 --- a/libc/calls/setuid.c +++ b/libc/calls/setuid.c @@ -21,12 +21,16 @@ #include "libc/calls/strace.internal.h" /** - * Sets effective group id of current process. + * Sets user id of current process. * @return 0 on success or -1 w/ errno */ int setuid(int uid) { int rc; - rc = sys_setuid(uid); - STRACE("%s(%d) → %d% m", "setuid", uid); + if (IsWindows() && uid == getuid()) { + rc = 0; + } else { + rc = sys_setuid(uid); + } + STRACE("setuid(%d) → %d% m", uid, rc); return rc; } diff --git a/libc/calls/sig.internal.h b/libc/calls/sig.internal.h index 40d3fabd9..2f58b1d6f 100644 --- a/libc/calls/sig.internal.h +++ b/libc/calls/sig.internal.h @@ -24,6 +24,7 @@ struct Signals { }; extern struct Signals __sig; // TODO(jart): Need TLS +extern long __sig_count; bool __sig_check(bool) hidden; bool __sig_handle(bool, int, int, ucontext_t *) hidden; diff --git a/libc/calls/sig2.c b/libc/calls/sig2.c index e48b5fc29..45626eff3 100644 --- a/libc/calls/sig2.c +++ b/libc/calls/sig2.c @@ -222,6 +222,7 @@ textwindows int __sig_add(int sig, int si_code) { if (1 <= sig && sig <= NSIG) { STRACE("enqueuing %G", sig); _spinlock(&__sig_lock); + ++__sig_count; if ((mem = __sig_alloc())) { mem->sig = sig; mem->si_code = si_code; diff --git a/libc/calls/sigcount.c b/libc/calls/sigcount.c new file mode 100644 index 000000000..74986db40 --- /dev/null +++ b/libc/calls/sigcount.c @@ -0,0 +1,21 @@ +/*-*- 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 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/sig.internal.h" + +long __sig_count; diff --git a/libc/calls/strace.internal.h b/libc/calls/strace.internal.h index aea83829a..17a56ca44 100644 --- a/libc/calls/strace.internal.h +++ b/libc/calls/strace.internal.h @@ -5,8 +5,8 @@ #include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/stat.h" -#define _KERNTRACE 1 /* not configurable w/ flag yet */ -#define _POLLTRACE 1 /* not configurable w/ flag yet */ +#define _KERNTRACE 0 /* not configurable w/ flag yet */ +#define _POLLTRACE 0 /* not configurable w/ flag yet */ #define _DATATRACE 1 /* not configurable w/ flag yet */ #define _NTTRACE 1 /* not configurable w/ flag yet */ @@ -19,7 +19,7 @@ COSMOPOLITAN_C_START_ #define STRACE(FMT, ...) \ do { \ if (__strace > 0) { \ - __stracef(STRACE_PROLOGUE FMT "%n", ##__VA_ARGS__); \ + __stracef(STRACE_PROLOGUE FMT "\n", ##__VA_ARGS__); \ } \ } while (0) #else diff --git a/libc/calls/symlinkat-nt.c b/libc/calls/symlinkat-nt.c index 1cbcc3907..89e7bf7c1 100644 --- a/libc/calls/symlinkat-nt.c +++ b/libc/calls/symlinkat-nt.c @@ -53,8 +53,8 @@ textwindows int sys_symlinkat_nt(const char *target, int newdirfd, const char *linkpath) { int targetlen; uint32_t attrs, flags; - char16_t target16[PATH_MAX]; - char16_t linkpath16[PATH_MAX]; + char16_t target16[PATH_MAX + 1]; + char16_t linkpath16[PATH_MAX + 1]; // convert the paths if (__mkntpathat(newdirfd, linkpath, 0, linkpath16) == -1) return -1; diff --git a/libc/calls/truncate-nt.c b/libc/calls/truncate-nt.c index 29c0819b5..df64631a9 100644 --- a/libc/calls/truncate-nt.c +++ b/libc/calls/truncate-nt.c @@ -28,7 +28,7 @@ textwindows int sys_truncate_nt(const char *path, uint64_t length) { int rc; bool32 ok; int64_t fh; - uint16_t path16[PATH_MAX]; + uint16_t path16[PATH_MAX + 1]; if (__mkntpath(path, path16) == -1) return -1; if ((fh = CreateFile(path16, kNtGenericWrite, kNtFileShareRead, NULL, kNtOpenExisting, kNtFileAttributeNormal, 0)) != -1) { diff --git a/libc/calls/ttyname.c b/libc/calls/ttyname.c index 94e064feb..084c0383d 100644 --- a/libc/calls/ttyname.c +++ b/libc/calls/ttyname.c @@ -19,7 +19,7 @@ #include "libc/calls/calls.h" #include "libc/log/log.h" -static char ttyname_buf[PATH_MAX]; +static char ttyname_buf[PATH_MAX + 1]; /** * Returns name of terminal. diff --git a/libc/calls/ttyname_r.c b/libc/calls/ttyname_r.c index fcef19b89..99d67d102 100644 --- a/libc/calls/ttyname_r.c +++ b/libc/calls/ttyname_r.c @@ -63,7 +63,7 @@ static int ttyname_freebsd(int fd, char *buf, size_t size) { static int ttyname_linux(int fd, char *buf, size_t size) { struct stat st1, st2; if (!isatty(fd)) return errno; - char name[PATH_MAX]; + char name[PATH_MAX + 1]; FormatInt32(stpcpy(name, "/proc/self/fd/"), fd); ssize_t got; got = readlink(name, buf, size); diff --git a/libc/calls/unlinkat-nt.c b/libc/calls/unlinkat-nt.c index 3b8555f03..9c08462f5 100644 --- a/libc/calls/unlinkat-nt.c +++ b/libc/calls/unlinkat-nt.c @@ -39,7 +39,8 @@ * from failing for no reason at all. For example a unit test that * repeatedly opens and unlinks the same filename. */ -static textwindows int SyncDirectory(int df, char16_t path[PATH_MAX], int n) { +static textwindows int SyncDirectory(int df, char16_t path[PATH_MAX + 1], + int n) { int rc; int64_t fh; char16_t *p; @@ -128,7 +129,7 @@ static textwindows int sys_unlink_nt(const char16_t *path) { textwindows int sys_unlinkat_nt(int dirfd, const char *path, int flags) { int n, rc; - char16_t path16[PATH_MAX]; + char16_t path16[PATH_MAX + 1]; if ((n = __mkntpathat(dirfd, path, 0, path16)) == -1) { rc = -1; } else if (flags & AT_REMOVEDIR) { diff --git a/libc/calls/utimensat-nt.c b/libc/calls/utimensat-nt.c index 0a5972cde..2694bf0cd 100644 --- a/libc/calls/utimensat-nt.c +++ b/libc/calls/utimensat-nt.c @@ -35,7 +35,7 @@ textwindows int sys_utimensat_nt(int dirfd, const char *path, const struct timespec ts[2], int flags) { int i, rc; int64_t fh; - uint16_t path16[PATH_MAX]; + uint16_t path16[PATH_MAX + 1]; struct NtFileTime ft[2], *ftp[2]; if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1; if ((fh = CreateFile(path16, kNtFileWriteAttributes, kNtFileShareRead, NULL, diff --git a/libc/calls/wait4-nt.c b/libc/calls/wait4-nt.c index c411018eb..14e00a01b 100644 --- a/libc/calls/wait4-nt.c +++ b/libc/calls/wait4-nt.c @@ -95,7 +95,11 @@ static textwindows int sys_wait4_nt_impl(int pid, int *opt_out_wstatus, return 0; } } else { - i = WaitForMultipleObjects(count, handles, false, -1); + i = WaitForMultipleObjects(count, handles, false, + __SIG_POLLING_INTERVAL_MS); + if (i == kNtWaitTimeout) { + continue; + } } if (i == kNtWaitFailed) { STRACE("%s failed %u", "WaitForMultipleObjects", GetLastError()); diff --git a/libc/calls/writev.c b/libc/calls/writev.c index 296fb5353..08bc6df0c 100644 --- a/libc/calls/writev.c +++ b/libc/calls/writev.c @@ -79,7 +79,7 @@ ssize_t writev(int fd, const struct iovec *iov, int iovlen) { } else { kprintf(STRACE_PROLOGUE "writev(%d, ", fd); __strace_iov(iov, iovlen, rc != -1 ? rc : 0); - kprintf(", %d) → %'ld% m%n", iovlen, rc); + kprintf(", %d) → %'ld% m\n", iovlen, rc); } } #endif diff --git a/libc/dns/gethoststxt.c b/libc/dns/gethoststxt.c index 9a8b8a012..360114cdf 100644 --- a/libc/dns/gethoststxt.c +++ b/libc/dns/gethoststxt.c @@ -37,7 +37,7 @@ static struct HostsTxtInitialStaticMemory { } g_hoststxt_init; static textwindows dontinline char *GetNtHostsTxtPath(char *pathbuf, - uint32_t size) { + uint32_t size) { const char *const kWinHostsPath = "\\drivers\\etc\\hosts"; uint32_t len = GetSystemDirectoryA(&pathbuf[0], size); if (len && len + strlen(kWinHostsPath) + 1 < size) { @@ -57,7 +57,7 @@ static textwindows dontinline char *GetNtHostsTxtPath(char *pathbuf, const struct HostsTxt *GetHostsTxt(void) { FILE *f; const char *path; - char pathbuf[PATH_MAX]; + char pathbuf[PATH_MAX + 1]; struct HostsTxtInitialStaticMemory *init; init = &g_hoststxt_init; if (!g_hoststxt) { diff --git a/libc/dns/lookupprotobyname.c b/libc/dns/lookupprotobyname.c index 921cf371d..598f7cb3d 100644 --- a/libc/dns/lookupprotobyname.c +++ b/libc/dns/lookupprotobyname.c @@ -48,10 +48,10 @@ int LookupProtoByName(const char *protoname, char *buf, size_t bufsize, const char *filepath) { FILE *f; char *line; - char pathbuf[PATH_MAX]; const char *path; size_t linesize; int found, result; + char pathbuf[PATH_MAX + 1]; char *name, *number, *alias, *comment, *tok; if (!(path = filepath)) { path = "/etc/protocols"; diff --git a/libc/dns/lookupprotobynumber.c b/libc/dns/lookupprotobynumber.c index 6d455d4e8..935a79cfe 100644 --- a/libc/dns/lookupprotobynumber.c +++ b/libc/dns/lookupprotobynumber.c @@ -52,10 +52,10 @@ int LookupProtoByNumber(const int protonum, char *buf, size_t bufsize, const char *filepath) { FILE *f; char *line; - char pathbuf[PATH_MAX]; - const char *path; - size_t linesize; int found; + size_t linesize; + const char *path; + char pathbuf[PATH_MAX + 1]; char *name, *number, *comment, *tok; if (!(path = filepath)) { path = "/etc/protocols"; diff --git a/libc/dns/lookupservicesbyname.c b/libc/dns/lookupservicesbyname.c index ffefbf45c..bf1bb132c 100644 --- a/libc/dns/lookupservicesbyname.c +++ b/libc/dns/lookupservicesbyname.c @@ -53,10 +53,10 @@ int LookupServicesByName(const char *servname, char *servproto, const char *filepath) { FILE *f; char *line; - char pathbuf[PATH_MAX]; const char *path; size_t linesize; int found, result; + char pathbuf[PATH_MAX + 1]; char *name, *port, *proto, *alias, *comment, *tok; if (!(path = filepath)) { path = "/etc/services"; diff --git a/libc/dns/lookupservicesbyport.c b/libc/dns/lookupservicesbyport.c index 4fa9a364c..301d5d5cf 100644 --- a/libc/dns/lookupservicesbyport.c +++ b/libc/dns/lookupservicesbyport.c @@ -59,7 +59,7 @@ int LookupServicesByPort(const int servport, char *servproto, const char *filepath) { FILE *f; char *line; - char pathbuf[PATH_MAX]; + char pathbuf[PATH_MAX + 1]; const char *path; size_t linesize; int found; diff --git a/libc/fmt/abs.c b/libc/fmt/abs.c index ab4b6b6a8..1baff0057 100644 --- a/libc/fmt/abs.c +++ b/libc/fmt/abs.c @@ -18,6 +18,11 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/fmt/conv.h" -int(abs)(int x) { +/** + * Returns absolute value of 32-bit integer. + * @note `labs(LONG_MIN)` returns `LONG_MIN` unless `-ftrapv` + * @note consider ABS() to avoid narrowing + */ +int abs(int x) { return 0 < x ? x : -x; } diff --git a/libc/fmt/fmt.c b/libc/fmt/fmt.c index 488a10cfb..fec8e214f 100644 --- a/libc/fmt/fmt.c +++ b/libc/fmt/fmt.c @@ -368,9 +368,6 @@ hidden int __fmt(void *fn, void *arg, const char *format, va_list va) { } break; case 'n': - // nonstandard %n specifier - // used to print newlines that work in raw terminal modes - if (__nomultics) __FMT_PUT('\r'); __FMT_PUT('\n'); break; case 'F': diff --git a/libc/fmt/fmt.h b/libc/fmt/fmt.h index 3bc5b89c2..199b004a6 100644 --- a/libc/fmt/fmt.h +++ b/libc/fmt/fmt.h @@ -26,10 +26,6 @@ int sscanf(const char *, const char *, ...) scanfesque(2); int vsscanf(const char *, const char *, va_list); int vcscanf(int (*)(void *), int (*)(int, void *), void *, const char *, va_list); -int strerror_r(int, char *, size_t) dontthrow nocallback; -int strerror_wr(int, uint32_t, char *, size_t) dontthrow nocallback; -const char *strerror_short(int) nosideeffect; -const char *strerror_long(int) nosideeffect; int __fmt(void *, void *, const char *, va_list) hidden; char *itoa(int, char *, int) compatfn; char *fcvt(double, int, int *, int *); diff --git a/libc/fmt/kerrornameslong.S b/libc/fmt/kerrnodocs.S similarity index 97% rename from libc/fmt/kerrornameslong.S rename to libc/fmt/kerrnodocs.S index 127638a41..cd7e86c9e 100644 --- a/libc/fmt/kerrornameslong.S +++ b/libc/fmt/kerrnodocs.S @@ -19,8 +19,8 @@ #include "libc/macros.internal.h" .macro .e e s - .long \e - kErrorNamesLong - .long 1f - kErrorNamesLong + .long \e - kErrnoDocs + .long 1f - kErrnoDocs .rodata.str1.1 1: .asciz "\s" .previous @@ -29,7 +29,7 @@ .section .rodata .align 4 .underrun -kErrorNamesLong: +kErrnoDocs: .e EINVAL,"Invalid argument" .e ENOSYS,"Function not implemented" .e EPERM,"Operation not permitted" @@ -115,6 +115,6 @@ kErrorNamesLong: .e ENOTRECOVERABLE,"State not recoverable" .e ENONET,"Machine is not on the network" .e ERESTART,"Interrupted system call should be restarted" - .long 0 - .endobj kErrorNamesLong,globl,hidden + .long -123 + .endobj kErrnoDocs,globl,hidden .overrun diff --git a/libc/fmt/kerrornames.S b/libc/fmt/kerrnonames.S similarity index 96% rename from libc/fmt/kerrornames.S rename to libc/fmt/kerrnonames.S index 6755891c0..789339e01 100644 --- a/libc/fmt/kerrornames.S +++ b/libc/fmt/kerrnonames.S @@ -19,8 +19,8 @@ #include "libc/macros.internal.h" .macro .e e - .long \e - kErrorNames - .long 1f - kErrorNames + .long \e - kErrnoNames + .long 1f - kErrnoNames .rodata.str1.1 1: .string "\e" .previous @@ -29,7 +29,7 @@ .section .rodata .align 4 .underrun -kErrorNames: +kErrnoNames: .e EINVAL .e ENOSYS .e EPERM @@ -116,6 +116,6 @@ kErrorNames: .e ENONET .e ERESTART .e ENODATA - .long 0 - .endobj kErrorNames,globl,hidden + .long -123 + .endobj kErrnoNames,globl,hidden .overrun diff --git a/libc/fmt/kerrornames.internal.h b/libc/fmt/kerrornames.internal.h deleted file mode 100644 index b8a2636c1..000000000 --- a/libc/fmt/kerrornames.internal.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_FMT_KERRORNAMES_INTERNAL_H_ -#define COSMOPOLITAN_LIBC_FMT_KERRORNAMES_INTERNAL_H_ -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -struct ErrorName { - int x, s; -}; - -extern const struct ErrorName kStrSignal[]; -extern const struct ErrorName kErrorNames[]; -extern const struct ErrorName kErrorNamesLong[]; - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_LIBC_FMT_KERRORNAMES_INTERNAL_H_ */ diff --git a/libc/fmt/labs.c b/libc/fmt/labs.c index e3a1d3916..c47c0a361 100644 --- a/libc/fmt/labs.c +++ b/libc/fmt/labs.c @@ -17,8 +17,12 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/fmt/conv.h" -#include "libc/macros.internal.h" -long(labs)(long x) { - return ABS(x); +/** + * Returns absolute value of long integer. + * @note `labs(LONG_MIN)` returns `LONG_MIN` unless `-ftrapv` + * @note consider ABS() to avoid narrowing + */ +long labs(long x) { + return 0 < x ? x : -x; } diff --git a/libc/fmt/llabs.c b/libc/fmt/llabs.c index 5ded9754c..fa1670b13 100644 --- a/libc/fmt/llabs.c +++ b/libc/fmt/llabs.c @@ -17,8 +17,12 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/fmt/conv.h" -#include "libc/macros.internal.h" -long long(llabs)(long long x) { - return ABS(x); +/** + * Returns absolute value of long long integer. + * @note `llabs(LONG_LONG_MIN)` returns `LONG_LONG_MIN` unless `-ftrapv` + * @note consider ABS() to avoid narrowing + */ +long long llabs(long long x) { + return 0 < x ? x : -x; } diff --git a/libc/fmt/magnumstrs.internal.h b/libc/fmt/magnumstrs.internal.h new file mode 100644 index 000000000..e23dfb2ad --- /dev/null +++ b/libc/fmt/magnumstrs.internal.h @@ -0,0 +1,23 @@ +#ifndef COSMOPOLITAN_LIBC_FMT_MAGNUMSTRS_H_ +#define COSMOPOLITAN_LIBC_FMT_MAGNUMSTRS_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +struct MagnumStr { + int x, s; +}; + +extern const struct MagnumStr kErrnoDocs[]; +extern const struct MagnumStr kErrnoNames[]; +extern const struct MagnumStr kIpOptnames[]; +extern const struct MagnumStr kSignalNames[]; +extern const struct MagnumStr kSockOptnames[]; +extern const struct MagnumStr kTcpOptnames[]; + +const char *DescribeSockLevel(int); +const char *DescribeSockOptname(int, int); +const char *GetMagnumStr(const struct MagnumStr *, int); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_FMT_MAGNUMSTRS_H_ */ diff --git a/libc/fmt/strerror_long.greg.c b/libc/fmt/strerdoc.greg.c similarity index 82% rename from libc/fmt/strerror_long.greg.c rename to libc/fmt/strerdoc.greg.c index c5081970a..abd68b616 100644 --- a/libc/fmt/strerror_long.greg.c +++ b/libc/fmt/strerdoc.greg.c @@ -17,23 +17,16 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/fmt/fmt.h" -#include "libc/fmt/kerrornames.internal.h" +#include "libc/fmt/magnumstrs.internal.h" /** * Converts errno value to descriptive sentence. * @return non-null rodata string or null if not found */ -privileged const char *strerror_long(int x) { - /* kprintf() weakly depends on this function */ - int i; +const char *strerdoc(int x) { if (x) { - for (i = 0; kErrorNamesLong[i].x; ++i) { - if (x == - *(const long *)((uintptr_t)kErrorNamesLong + kErrorNamesLong[i].x)) { - return (const char *)((uintptr_t)kErrorNamesLong + - kErrorNamesLong[i].s); - } - } + return GetMagnumStr(kErrnoDocs, x); + } else { + return 0; } - return 0; } diff --git a/libc/fmt/strerror_short.greg.c b/libc/fmt/strerrno.greg.c similarity index 83% rename from libc/fmt/strerror_short.greg.c rename to libc/fmt/strerrno.greg.c index 5075af526..042ae1942 100644 --- a/libc/fmt/strerror_short.greg.c +++ b/libc/fmt/strerrno.greg.c @@ -16,22 +16,17 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/fmt/fmt.h" -#include "libc/fmt/kerrornames.internal.h" +#include "libc/fmt/magnumstrs.internal.h" +#include "libc/str/str.h" /** * Converts errno value to symbolic name. * @return non-null rodata string or null if not found */ -privileged const char *strerror_short(int x) { - /* kprintf() weakly depends on this function */ - int i; +const char *strerrno(int x) { if (x) { - for (i = 0; kErrorNames[i].x; ++i) { - if (x == *(const int *)((uintptr_t)kErrorNames + kErrorNames[i].x)) { - return (const char *)((uintptr_t)kErrorNames + kErrorNames[i].s); - } - } + return GetMagnumStr(kErrnoNames, x); + } else { + return 0; } - return 0; } diff --git a/libc/fmt/strerror.c b/libc/fmt/strerror.c index 29f9f23fe..8cfefe61b 100644 --- a/libc/fmt/strerror.c +++ b/libc/fmt/strerror.c @@ -23,9 +23,9 @@ * Converts errno value to string non-reentrantly. * @see strerror_r() */ -noasan char *strerror(int err) { +char *strerror(int err) { if (IsTiny()) { - return firstnonnull(strerror_short(err), "EUNKNOWN"); + return firstnonnull(strerrno(err), "EUNKNOWN"); } else { _Alignas(1) static char buf[512]; strerror_r(err, buf, sizeof(buf)); diff --git a/libc/fmt/strerror_r.greg.c b/libc/fmt/strerror_r.greg.c index 22365785b..bec848b21 100644 --- a/libc/fmt/strerror_r.greg.c +++ b/libc/fmt/strerror_r.greg.c @@ -25,6 +25,6 @@ * @param err is error number or zero if unknown * @return 0 on success, or error code */ -privileged int strerror_r(int err, char *buf, size_t size) { +int strerror_r(int err, char *buf, size_t size) { return strerror_wr(err, GetLastError(), buf, size); } diff --git a/libc/fmt/strerror_wr.greg.c b/libc/fmt/strerror_wr.greg.c index b4c795038..dc3d2fc77 100644 --- a/libc/fmt/strerror_wr.greg.c +++ b/libc/fmt/strerror_wr.greg.c @@ -32,13 +32,13 @@ * @param err is error number or zero if unknown * @return 0 on success, or error code */ -privileged int strerror_wr(int err, uint32_t winerr, char *buf, size_t size) { +int strerror_wr(int err, uint32_t winerr, char *buf, size_t size) { /* kprintf() weakly depends on this function */ int c, n; char16_t winmsg[256]; const char *sym, *msg; - sym = firstnonnull(strerror_short(err), "EUNKNOWN"); - msg = firstnonnull(strerror_long(err), "No error information"); + sym = firstnonnull(strerrno(err), "EUNKNOWN"); + msg = firstnonnull(strerdoc(err), "No error information"); if (IsTiny()) { if (!sym) sym = "EUNKNOWN"; for (; (c = *sym++); --size) diff --git a/libc/integral/normalize.inc b/libc/integral/normalize.inc index 489d5a7f4..335a24b9b 100644 --- a/libc/integral/normalize.inc +++ b/libc/integral/normalize.inc @@ -71,7 +71,7 @@ #define CACHELINE 0x40 /* nexgen32e */ #define CHAR_BIT 8 /* b/c von neumann */ #define ARG_MAX 0x8000 /* b/c windows */ -#define PATH_MAX 248 /* b/c win32 apis limit ~248..260 */ +#define PATH_MAX 512 /* b/c bloat */ #define NAME_MAX 63 /* b/c dns */ #define CHILD_MAX 25 /* only if malloc isn't linked */ #define OPEN_MAX 16 /* only if malloc isn't linked */ diff --git a/libc/intrin/asan.c b/libc/intrin/asan.c index aa5a86468..582a59dee 100644 --- a/libc/intrin/asan.c +++ b/libc/intrin/asan.c @@ -59,7 +59,7 @@ STATIC_YOINK("_init_asan"); #define ASAN_MORGUE_ITEMS 512 #define ASAN_MORGUE_THRESHOLD 65536 // morgue memory O(ITEMS*THRESHOLD) -#define ASAN_TRACE_ITEMS 16 // backtrace limit on malloc origin +#define ASAN_TRACE_ITEMS 16 // backtrace limit on malloc origin /** * @fileoverview Cosmopolitan Address Sanitizer Runtime. @@ -102,7 +102,7 @@ STATIC_YOINK("_init_asan"); #define REQUIRE(FUNC) \ do { \ if (!weaken(FUNC)) { \ - kprintf("error: asan needs %s%n", #FUNC); \ + kprintf("error: asan needs %s\n", #FUNC); \ __asan_die()(); \ __asan_unreachable(); \ } \ @@ -179,8 +179,7 @@ static uint64_t __asan_roundup2pow(uint64_t x) { static char *__asan_utf8cpy(char *p, unsigned c) { uint64_t z; z = tpenc(c); - do - *p++ = z; + do *p++ = z; while ((z >>= 8)); return p; } @@ -322,9 +321,9 @@ static char *__asan_hexcpy(char *p, uint64_t x, uint8_t k) { } static void __asan_exit(void) { - kprintf("your asan runtime needs%n" - "\tSTATIC_YOINK(\"__die\");%n" - "in order to show you backtraces%n"); + kprintf("your asan runtime needs\n" + "\tSTATIC_YOINK(\"__die\");\n" + "in order to show you backtraces\n"); __restorewintty(); _Exit(99); } @@ -373,6 +372,7 @@ void __asan_unpoison(long p, long n) { } static bool __asan_is_mapped(int x) { + // xxx: we can't lock because no reentrant locks yet int i; struct MemoryIntervals *m; m = weaken(_mmi); @@ -609,7 +609,7 @@ const char *__asan_describe_access_poison(signed char kind) { dontdiscard static __asan_die_f *__asan_report_invalid_pointer( const void *addr) { - kprintf("%n\e[J\e[1;31masan error\e[0m: this corruption at %p shadow %p%n", + kprintf("\n\e[J\e[1;31masan error\e[0m: this corruption at %p shadow %p\n", addr, SHADOW(addr)); return __asan_die(); } @@ -629,7 +629,6 @@ static char *__asan_format_section(char *p, const void *p1, const void *p2, if (a <= (intptr_t)addr && (intptr_t)addr <= b) { p = __stpcpy(p, " ←address"); } - if (__nomultics) *p++ = '\r'; *p++ = '\n'; } return p; @@ -638,7 +637,7 @@ static char *__asan_format_section(char *p, const void *p1, const void *p2, static void __asan_report_memory_origin_image(intptr_t a, int z) { unsigned l, m, r, n, k; struct SymbolTable *st; - kprintf("%nthe memory belongs to image symbols%n"); + kprintf("\nthe memory belongs to image symbols\n"); if (weaken(GetSymbolTable)) { if ((st = weaken(GetSymbolTable)())) { l = 0; @@ -656,7 +655,7 @@ static void __asan_report_memory_origin_image(intptr_t a, int z) { if ((st->symbols[l].x <= k && k <= st->symbols[l].y) || (st->symbols[l].x <= k + z && k + z <= st->symbols[l].y) || (k < st->symbols[l].x && st->symbols[l].y < k + z)) { - kprintf("\t%s [%#x,%#x] size %'d%n", st->name_base + st->names[l], + kprintf("\t%s [%#x,%#x] size %'d\n", st->name_base + st->names[l], st->addr_base + st->symbols[l].x, st->addr_base + st->symbols[l].y, st->symbols[l].y - st->symbols[l].x + 1); @@ -665,10 +664,10 @@ static void __asan_report_memory_origin_image(intptr_t a, int z) { } } } else { - kprintf("\tunknown please supply .com.dbg symbols or set COMDBG%n"); + kprintf("\tunknown please supply .com.dbg symbols or set COMDBG\n"); } } else { - kprintf("\tunknown please STATIC_YOINK(\"GetSymbolTable\");%n"); + kprintf("\tunknown please STATIC_YOINK(\"GetSymbolTable\");\n"); } } @@ -686,13 +685,13 @@ static noasan void OnMemory(void *x, void *y, size_t n, void *a) { static void __asan_report_memory_origin_heap(const unsigned char *a, int z) { struct ReportOriginHeap t; - kprintf("%nthe memory was allocated by%n"); + kprintf("\nthe memory was allocated by\n"); if (weaken(malloc_inspect_all)) { t.a = a; t.z = z; weaken(malloc_inspect_all)(OnMemory, &t); } else { - kprintf("\tunknown please STATIC_YOINK(\"malloc_inspect_all\");%n"); + kprintf("\tunknown please STATIC_YOINK(\"malloc_inspect_all\");\n"); } } @@ -737,7 +736,7 @@ dontdiscard static __asan_die_f *__asan_report(const void *addr, int size, struct MemoryIntervals *m; ++g_ftrace; p = __fatalbuf; - kprintf("%n\e[J\e[1;31masan error\e[0m: %s %d-byte %s at %p shadow %p%n%s%n", + kprintf("\n\e[J\e[1;31masan error\e[0m: %s %d-byte %s at %p shadow %p\n%s\n", __asan_describe_access_poison(kind), size, message, addr, SHADOW(addr), __argv[0]); if (0 < size && size < 80) { @@ -753,7 +752,6 @@ dontdiscard static __asan_die_f *__asan_report(const void *addr, int size, *p++ = ' '; } } - if (__nomultics) *p++ = '\r'; *p++ = '\n'; for (c = i = 0; i < 80; ++i) { if (!(t = __asan_check(base + i, 1).kind)) { @@ -771,7 +769,6 @@ dontdiscard static __asan_die_f *__asan_report(const void *addr, int size, } } p = __stpcpy(p, "\e[39m"); - if (__nomultics) *p++ = '\r'; *p++ = '\n'; for (i = 0; (intptr_t)(base + i) & 7; ++i) *p++ = ' '; for (; i + 8 <= 80; i += 8) { @@ -788,19 +785,18 @@ dontdiscard static __asan_die_f *__asan_report(const void *addr, int size, } } for (; i < 80; ++i) *p++ = ' '; - if (__nomultics) *p++ = '\r'; *p++ = '\n'; for (i = 0; i < 80; ++i) { p = __asan_utf8cpy(p, __asan_exists(base + i) ? kCp437[((unsigned char *)base)[i]] : L'⋅'); } - if (__nomultics) *p++ = '\r'; *p++ = '\n'; } p = __asan_format_section(p, _base, _etext, ".text", addr); p = __asan_format_section(p, _etext, _edata, ".data", addr); p = __asan_format_section(p, _end, _edata, ".bss", addr); + // xxx: we can't lock because no reentrant locks yet for (m = weaken(_mmi), i = 0; i < m->i; ++i) { x = m->p[i].x; y = m->p[i].y; @@ -809,13 +805,12 @@ dontdiscard static __asan_die_f *__asan_report(const void *addr, int size, if (x <= z && z <= y) p = __stpcpy(p, " ←address"); z = (((intptr_t)addr >> 3) + 0x7fff8000) >> 16; if (x <= z && z <= y) p = __stpcpy(p, " ←shadow"); - if (__nomultics) *p++ = '\r'; *p++ = '\n'; } *p = 0; kprintf("%s", __fatalbuf); __asan_report_memory_origin(addr, size, kind); - kprintf("%nthe crash was caused by%n"); + kprintf("\nthe crash was caused by\n"); --g_ftrace; return __asan_die(); } @@ -924,8 +919,7 @@ static void __asan_trace(struct AsanTrace *bt, const struct StackFrame *bp) { if (!__asan_checka(SHADOW(bp), sizeof(*bp) >> 3).kind) { addr = bp->addr; if (addr == weakaddr("__gc") && weakaddr("__gc")) { - do - --gi; + do --gi; while ((addr = garbage->p[gi].ret) == weakaddr("__gc")); } bt->p[i] = addr; @@ -1018,12 +1012,12 @@ int __asan_print_trace(void *p) { kprintf(" bad cookie"); return -1; } - kprintf("%n%p %,lu bytes [asan]", (char *)p, n); + kprintf("\n%p %,lu bytes [asan]", (char *)p, n); if (!__asan_is_mapped((((intptr_t)p >> 3) + 0x7fff8000) >> 16)) { kprintf(" (shadow not mapped?!)"); } for (i = 0; i < ARRAYLEN(e->bt.p) && e->bt.p[i]; ++i) { - kprintf("%n%*lx %s", 12, e->bt.p[i], + kprintf("\n%*lx %s", 12, e->bt.p[i], weaken(__get_symbol_by_addr) ? weaken(__get_symbol_by_addr)(e->bt.p[i]) : "please STATIC_YOINK(\"__get_symbol_by_addr\")"); @@ -1203,7 +1197,7 @@ void __asan_evil(uint8_t *addr, int size, const char *s1, const char *s2) { struct AsanTrace tr; __asan_rawtrace(&tr, __builtin_frame_address(0)); kprintf( - "WARNING: ASAN error during %s bad %d byte %s at %x bt %x %x %x %x %x%n", + "WARNING: ASAN error during %s bad %d byte %s at %x bt %x %x %x %x %x\n", s1, size, s2, addr, tr.p[0], tr.p[1], tr.p[2], tr.p[3], tr.p[4], tr.p[5]); } @@ -1281,6 +1275,7 @@ void __asan_install_malloc_hooks(void) { } void __asan_map_shadow(uintptr_t p, size_t n) { + // assume _mmi.lock is held void *addr; int i, a, b; size_t size; @@ -1311,7 +1306,7 @@ void __asan_map_shadow(uintptr_t p, size_t n) { m, a, a + i - 1, sm.maphandle, PROT_READ | PROT_WRITE, MAP_PRIVATE | *weaken(MAP_ANONYMOUS) | MAP_FIXED, false, false, 0, size) == -1) { - kprintf("error: could not map asan shadow memory%n"); + kprintf("error: could not map asan shadow memory\n"); __asan_die()(); __asan_unreachable(); } diff --git a/libc/intrin/assertfail.c b/libc/intrin/assertfail.c index ba38f3dc5..1be6fe260 100644 --- a/libc/intrin/assertfail.c +++ b/libc/intrin/assertfail.c @@ -35,12 +35,12 @@ relegated wontreturn void __assert_fail(const char *expr, const char *file, static bool noreentry; __strace = 0; g_ftrace = 0; - kprintf("%s:%d: assert(%s) failed%n", file, line, expr); + kprintf("%s:%d: assert(%s) failed\n", file, line, expr); if (_lockcmpxchg(&noreentry, false, true)) { if (weaken(__die)) { weaken(__die)(); } else { - kprintf("can't backtrace b/c `__die` not linked%n"); + kprintf("can't backtrace b/c `__die` not linked\n"); } rc = 23; } else { diff --git a/libc/intrin/bzero.c b/libc/intrin/bzero.c index c321dc59b..40becb89b 100644 --- a/libc/intrin/bzero.c +++ b/libc/intrin/bzero.c @@ -26,7 +26,7 @@ typedef char xmm_t __attribute__((__vector_size__(16), __aligned__(1))); typedef long long xmm_a __attribute__((__vector_size__(16), __aligned__(16))); -noasan static dontinline antiquity void bzero_sse(char *p, size_t n) { +static dontinline antiquity void bzero_sse(char *p, size_t n) { xmm_t v = {0}; if (IsAsan()) __asan_verify(p, n); if (n <= 32) { @@ -43,7 +43,7 @@ noasan static dontinline antiquity void bzero_sse(char *p, size_t n) { } } -noasan microarchitecture("avx") static void bzero_avx(char *p, size_t n) { +microarchitecture("avx") static void bzero_avx(char *p, size_t n) { xmm_t v = {0}; if (IsAsan()) __asan_verify(p, n); if (n <= 32) { diff --git a/libc/intrin/getexitcodeprocess.greg.c b/libc/intrin/getexitcodeprocess.greg.c new file mode 100644 index 000000000..9fd02ea1e --- /dev/null +++ b/libc/intrin/getexitcodeprocess.greg.c @@ -0,0 +1,36 @@ +/*-*- 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 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/internal.h" +#include "libc/calls/strace.internal.h" +#include "libc/nt/accounting.h" +#include "libc/nt/thunk/msabi.h" + +__msabi extern typeof(GetExitCodeProcess) *const __imp_GetExitCodeProcess; + +/** + * Obtains exit code for process. + * @note this wrapper takes care of ABI, STRACE(), and __winerr() + */ +textwindows int32_t GetExitCodeProcess(int64_t hProcess, uint32_t *lpExitCode) { + int32_t rc; + rc = __imp_GetExitCodeProcess(hProcess, lpExitCode); + if (!rc) __winerr(); + NTTRACE("GetExitCodeProcess(%ld, [%u]) → %u% m", hProcess, *lpExitCode, rc); + return rc; +} diff --git a/libc/intrin/getmagnumstr.greg.c b/libc/intrin/getmagnumstr.greg.c new file mode 100644 index 000000000..4be124a29 --- /dev/null +++ b/libc/intrin/getmagnumstr.greg.c @@ -0,0 +1,29 @@ +/*-*- 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 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/magnumstrs.internal.h" + +const char *GetMagnumStr(const struct MagnumStr *ms, int x) { + int i; + for (i = 0; ms[i].x != -123; ++i) { + if (x == *(const int *)((uintptr_t)ms + ms[i].x)) { + return (const char *)((uintptr_t)ms + ms[i].s); + } + } + return 0; +} diff --git a/libc/intrin/intrin.mk b/libc/intrin/intrin.mk index 77acd0597..02b18b99b 100644 --- a/libc/intrin/intrin.mk +++ b/libc/intrin/intrin.mk @@ -89,9 +89,12 @@ o/$(MODE)/libc/intrin/flushfilebuffers.greg.o \ o/$(MODE)/libc/intrin/terminateprocess.greg.o \ o/$(MODE)/libc/intrin/describemapflags.greg.o \ o/$(MODE)/libc/intrin/getfileattributes.greg.o \ +o/$(MODE)/libc/intrin/getexitcodeprocess.greg.o \ +o/$(MODE)/libc/intrin/waitforsingleobject.greg.o \ o/$(MODE)/libc/intrin/setcurrentdirectory.greg.o \ o/$(MODE)/libc/intrin/mapviewoffileexnuma.greg.o \ o/$(MODE)/libc/intrin/createfilemappingnuma.greg.o \ +o/$(MODE)/libc/intrin/waitformultipleobjects.greg.o \ o/$(MODE)/libc/intrin/generateconsolectrlevent.greg.o \ o/$(MODE)/libc/intrin/kstarttsc.o \ o/$(MODE)/libc/intrin/nomultics.o \ diff --git a/libc/intrin/kdos2errno.S b/libc/intrin/kdos2errno.S index 180d796c6..f09390e7f 100644 --- a/libc/intrin/kdos2errno.S +++ b/libc/intrin/kdos2errno.S @@ -114,7 +114,7 @@ kDos2Errno: .e kNtErrorCrc,EACCES .e kNtErrorDirNotEmpty,ENOTEMPTY .e kNtErrorDupName,EADDRINUSE - .e kNtErrorFilenameExcedRange,ENOENT + .e kNtErrorFilenameExcedRange,ENAMETOOLONG .e kNtErrorGenFailure,EACCES .e kNtErrorGracefulDisconnect,EPIPE .e kNtErrorHostDown,EHOSTUNREACH diff --git a/libc/intrin/kprintf.greg.c b/libc/intrin/kprintf.greg.c index 39d604a92..7ecf44b1b 100644 --- a/libc/intrin/kprintf.greg.c +++ b/libc/intrin/kprintf.greg.c @@ -32,6 +32,7 @@ #include "libc/intrin/nomultics.internal.h" #include "libc/intrin/spinlock.h" #include "libc/limits.h" +#include "libc/log/internal.h" #include "libc/macros.internal.h" #include "libc/nexgen32e/rdtsc.h" #include "libc/nexgen32e/uart.internal.h" @@ -174,6 +175,7 @@ privileged static inline bool kismemtrackhosed(void) { } privileged static bool kismapped(int x) { + // xxx: we can't lock because no reentrant locks yet size_t m, r, l = 0; if (!weaken(_mmi)) return true; if (kismemtrackhosed()) return false; @@ -450,8 +452,7 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt, va_list va, i = 0; m = (1 << base) - 1; if (hash && x) sign = hash; - do - z[i++ & 127] = abet[x & m]; + do z[i++ & 127] = abet[x & m]; while ((x >>= base) || (pdot && i < prec)); goto EmitNumber; @@ -556,11 +557,6 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt, va_list va, case 'n': // nonstandard %n specifier - // used to print newlines that work in raw terminal modes - if (__nomultics) { - if (p < e) *p = '\r'; - ++p; - } if (p < e) *p = '\n'; ++p; break; @@ -569,7 +565,7 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt, va_list va, // undocumented %r specifier // used for good carriage return // helps integrate loggers with repls - if (!__replmode) { + if (!__replmode || __nocolor) { break; } else { s = "\r\033[K"; @@ -845,12 +841,12 @@ privileged size_t kvsnprintf(char *b, size_t n, const char *fmt, va_list v) { */ privileged void kvprintf(const char *fmt, va_list v) { size_t n; - char b[2048]; + char b[4000]; struct Timestamps t; if (!v) return; t = kenter(); n = kformat(b, sizeof(b), fmt, v, t); - klog(b, MIN(n, sizeof(b))); + klog(b, MIN(n, sizeof(b) - 1)); kleave(t); } diff --git a/libc/intrin/memset.c b/libc/intrin/memset.c index 30d8e8f72..b13c61325 100644 --- a/libc/intrin/memset.c +++ b/libc/intrin/memset.c @@ -26,7 +26,7 @@ typedef char xmm_t __attribute__((__vector_size__(16), __aligned__(1))); typedef long long xmm_a __attribute__((__vector_size__(16), __aligned__(16))); -noasan static dontinline antiquity void *memset_sse(char *p, char c, size_t n) { +static dontinline antiquity void *memset_sse(char *p, char c, size_t n) { xmm_t v = {c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c}; if (IsAsan()) __asan_verify(p, n); if (n <= 32) { @@ -44,8 +44,7 @@ noasan static dontinline antiquity void *memset_sse(char *p, char c, size_t n) { return p; } -noasan microarchitecture("avx") static void *memset_avx(char *p, char c, - size_t n) { +microarchitecture("avx") static void *memset_avx(char *p, char c, size_t n) { char *t; xmm_t v = {c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c}; if (IsAsan()) __asan_verify(p, n); diff --git a/libc/intrin/nomultics.c b/libc/intrin/nomultics.c index 797465a4b..a02f288c1 100644 --- a/libc/intrin/nomultics.c +++ b/libc/intrin/nomultics.c @@ -17,17 +17,6 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -/** - * Controls disablement of MULTICS newlines. - * - * Normally we use `\n` for newlines. If this is `true` then we'll try - * our best to use `\r\n`. This is toggled automatically on Windows or - * when `ioctl(TCSETS)` disables `OPOST`. - * - * @see kprintf() - */ -char __nomultics; - /** * Controls ANSI prefix for log emissions. * diff --git a/libc/intrin/nomultics.internal.h b/libc/intrin/nomultics.internal.h index 73298e7d2..50cf641ff 100644 --- a/libc/intrin/nomultics.internal.h +++ b/libc/intrin/nomultics.internal.h @@ -3,7 +3,6 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -extern bool __nomultics; extern bool __replmode; COSMOPOLITAN_C_END_ diff --git a/libc/intrin/onarithmeticoverflow.S b/libc/intrin/onarithmeticoverflow.S index c74d4446e..4e3d1efc2 100644 --- a/libc/intrin/onarithmeticoverflow.S +++ b/libc/intrin/onarithmeticoverflow.S @@ -25,5 +25,6 @@ __on_arithmetic_overflow: push %rbp mov %rsp,%rbp int3 - call abort +0: ud2 + jmp 0b .endfn __on_arithmetic_overflow,weak diff --git a/libc/intrin/ubsan.c b/libc/intrin/ubsan.c index 20c015296..ec357de0f 100644 --- a/libc/intrin/ubsan.c +++ b/libc/intrin/ubsan.c @@ -197,9 +197,9 @@ static wontreturn void __ubsan_unreachable(void) { } static void __ubsan_exit(void) { - kprintf("your ubsan runtime needs%n" - "\tSTATIC_YOINK(\"__die\");%n" - "in order to show you backtraces%n"); + kprintf("your ubsan runtime needs\n" + "\tSTATIC_YOINK(\"__die\");\n" + "in order to show you backtraces\n"); __restorewintty(); _Exit(99); } @@ -214,13 +214,13 @@ dontdiscard static __ubsan_die_f *__ubsan_die(void) { static void __ubsan_warning(const struct UbsanSourceLocation *loc, const char *description) { - kprintf("%s:%d: %subsan warning: %s is undefined behavior%s%n", loc->file, + kprintf("%s:%d: %subsan warning: %s is undefined behavior%s\n", loc->file, loc->line, SUBTLE, description, RESET); } dontdiscard __ubsan_die_f *__ubsan_abort(const struct UbsanSourceLocation *loc, const char *description) { - kprintf("%n%s:%d: %subsan error%s: %s%n", loc->file, loc->line, RED2, RESET, + kprintf("\n%s:%d: %subsan error%s: %s\n", loc->file, loc->line, RED2, RESET, description); return __ubsan_die(); } diff --git a/libc/intrin/waitformultipleobjects.greg.c b/libc/intrin/waitformultipleobjects.greg.c new file mode 100644 index 000000000..2b6aa0be0 --- /dev/null +++ b/libc/intrin/waitformultipleobjects.greg.c @@ -0,0 +1,39 @@ +/*-*- 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 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/internal.h" +#include "libc/calls/strace.internal.h" +#include "libc/nt/synchronization.h" +#include "libc/nt/thunk/msabi.h" + +__msabi extern typeof(WaitForMultipleObjects) *const + __imp_WaitForMultipleObjects; + +/** + * Waits for handles to change status. + * @note this wrapper takes care of ABI, STRACE(), and __winerr() + */ +uint32_t WaitForMultipleObjects(uint32_t nCount, const int64_t *lpHandles, + bool32 bWaitAll, uint32_t dwMilliseconds) { + uint32_t x; + x = __imp_WaitForMultipleObjects(nCount, lpHandles, bWaitAll, dwMilliseconds); + if (x == -1u) __winerr(); + POLLTRACE("WaitForMultipleObjects(%ld, %p, %hhhd, %'d) → %d% m", nCount, + lpHandles, bWaitAll, dwMilliseconds, x); + return x; +} diff --git a/libc/intrin/waitforsingleobject.greg.c b/libc/intrin/waitforsingleobject.greg.c new file mode 100644 index 000000000..d345ef17b --- /dev/null +++ b/libc/intrin/waitforsingleobject.greg.c @@ -0,0 +1,37 @@ +/*-*- 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 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/internal.h" +#include "libc/calls/strace.internal.h" +#include "libc/nt/synchronization.h" +#include "libc/nt/thunk/msabi.h" + +__msabi extern typeof(WaitForSingleObject) *const __imp_WaitForSingleObject; + +/** + * Waits for handle to change status. + * @note this wrapper takes care of ABI, STRACE(), and __winerr() + */ +uint32_t WaitForSingleObject(int64_t hHandle, uint32_t dwMilliseconds) { + uint32_t rc; + rc = __imp_WaitForSingleObject(hHandle, dwMilliseconds); + if (rc == -1u) __winerr(); + POLLTRACE("WaitForSingleObject(%ld, %'d) → %d% m", hHandle, dwMilliseconds, + rc); + return rc; +} diff --git a/libc/log/backtrace2.greg.c b/libc/log/backtrace2.greg.c index 642625fe1..359a7580a 100644 --- a/libc/log/backtrace2.greg.c +++ b/libc/log/backtrace2.greg.c @@ -50,7 +50,7 @@ #define kBacktraceBufSize ((kBacktraceMaxFrames - 1) * (18 + 1)) static void ShowHint(const char *s) { - kprintf("%snote: %s%s%n", SUBTLE, s, RESET); + kprintf("%snote: %s%s\n", SUBTLE, s, RESET); } static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) { @@ -63,8 +63,6 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) { char *debugbin, *p1, *p2, *p3, *addr2line; char buf[kBacktraceBufSize], *argv[kBacktraceMaxFrames]; - return -1; - if (!(debugbin = FindDebugBinary())) { return -1; } @@ -176,8 +174,8 @@ void ShowBacktrace(int fd, const struct StackFrame *bp) { __strace = st; g_ftrace = ft; #else - kprintf("ShowBacktrace() needs these flags to show C backtrace:%n" - "\t-D__FNO_OMIT_FRAME_POINTER__%n" - "\t-fno-omit-frame-pointer%n"); + kprintf("ShowBacktrace() needs these flags to show C backtrace:\n" + "\t-D__FNO_OMIT_FRAME_POINTER__\n" + "\t-fno-omit-frame-pointer\n"); #endif } diff --git a/libc/log/backtrace3.greg.c b/libc/log/backtrace3.greg.c index 24f8d15d1..e1857751e 100644 --- a/libc/log/backtrace3.greg.c +++ b/libc/log/backtrace3.greg.c @@ -56,11 +56,11 @@ noinstrument noasan int PrintBacktraceUsingSymbols(int fd, gi = garbage ? garbage->i : 0; for (i = 0, frame = bp; frame; frame = frame->next) { if (!IsValidStackFramePointer(frame)) { - kprintf("%p corrupt frame pointer%n", frame); + kprintf("%p corrupt frame pointer\n", frame); break; } if (++i == LIMIT) { - kprintf("%n"); + kprintf("\n"); break; } addr = frame->addr; @@ -84,8 +84,8 @@ noinstrument noasan int PrintBacktraceUsingSymbols(int fd, } else { addend = 0; } - kprintf("%012lx %012lx %s%+d\r%n", frame, addr, - __get_symbol_name(st, symbol), addend); + kprintf("%012lx %012lx %s%+d\n", frame, addr, __get_symbol_name(st, symbol), + addend); } return 0; } diff --git a/libc/log/checkfail.c b/libc/log/checkfail.c index 4944b53e9..d98055e7e 100644 --- a/libc/log/checkfail.c +++ b/libc/log/checkfail.c @@ -51,22 +51,22 @@ relegated void __check_fail(const char *suffix, const char *opstr, __start_fatal(file, line); __stpcpy(hostname, "unknown"); gethostname(hostname, sizeof(hostname)); - kprintf("check failed on %s pid %d%n", hostname, getpid()); - kprintf("\tCHECK_%^s(%s, %s);%n", suffix, wantstr, gotstr); - kprintf("\t\t → %p (%s)%n", want, wantstr); - kprintf("\t\t%s %p (%s)%n", opstr, got, gotstr); + kprintf("check failed on %s pid %d\n", hostname, getpid()); + kprintf("\tCHECK_%^s(%s, %s);\n", suffix, wantstr, gotstr); + kprintf("\t\t → %p (%s)\n", want, wantstr); + kprintf("\t\t%s %p (%s)\n", opstr, got, gotstr); if (!isempty(fmt)) { kprintf("\t"); va_start(va, fmt); kvprintf(fmt, va); va_end(va); - kprintf("%n"); + kprintf("\n"); } - kprintf("\t%m%n\t%s%s", SUBTLE, program_invocation_name); + kprintf("\t%m\n\t%s%s", SUBTLE, program_invocation_name); for (i = 1; i < __argc; ++i) { kprintf(" %s", __argv[i]); } - kprintf("%s%n", RESET); + kprintf("%s\n", RESET); if (!IsTiny() && e == ENOMEM) { PrintMemoryIntervals(2, &_mmi); } diff --git a/libc/log/checkfail_ndebug.c b/libc/log/checkfail_ndebug.c index ff313a715..a5a09318d 100644 --- a/libc/log/checkfail_ndebug.c +++ b/libc/log/checkfail_ndebug.c @@ -36,7 +36,7 @@ relegated void ___check_fail_ndebug(uint64_t want, uint64_t got, const char *opchar) { __restore_tty(); - kprintf("%n%serror: %s: check failed: 0x%x %s 0x%x (%s)%n", + kprintf("\n%serror: %s: check failed: 0x%x %s 0x%x (%s)\n", !__nocolor ? "\e[J" : "", program_invocation_name, want, opchar, got, strerror(errno)); __restorewintty(); diff --git a/libc/log/commandvenv.c b/libc/log/commandvenv.c index 5b1f10385..e8fd06130 100644 --- a/libc/log/commandvenv.c +++ b/libc/log/commandvenv.c @@ -47,7 +47,7 @@ */ const char *commandvenv(const char *var, const char *cmd) { const char *exepath; - static char pathbuf[PATH_MAX]; + static char pathbuf[PATH_MAX + 1]; if (*cmd == '/' || *cmd == '\\') return cmd; if ((exepath = getenv(var))) { if (isempty(exepath)) return NULL; @@ -57,5 +57,5 @@ const char *commandvenv(const char *var, const char *cmd) { return NULL; } } - return commandv(cmd, pathbuf); + return commandv(cmd, pathbuf, sizeof(pathbuf)); } diff --git a/libc/log/countexpr_report.c b/libc/log/countexpr_report.c index baba2caec..f3ffbec0b 100644 --- a/libc/log/countexpr_report.c +++ b/libc/log/countexpr_report.c @@ -55,7 +55,7 @@ static void PrintHistogram(const long *h, size_t n, long t) { for (j = 0; j < p / 100; ++j) s[j] = '#'; s[j] = 0; logos = i ? 1ul << (i - 1) : 0; - kprintf("%'12lu %'16ld %3d.%02d%% %s%n", logos, h[i], p / 100, p % 100, + kprintf("%'12lu %'16ld %3d.%02d%% %s\n", logos, h[i], p / 100, p % 100, s); } } diff --git a/libc/log/oncrash.c b/libc/log/oncrash.c index 3bdfc1fd7..b46452bd5 100644 --- a/libc/log/oncrash.c +++ b/libc/log/oncrash.c @@ -63,7 +63,7 @@ relegated static void ShowFunctionCalls(ucontext_t *ctx) { struct StackFrame *bp; struct StackFrame goodframe; if (!ctx->uc_mcontext.rip) { - kprintf("%s is NULL can't show backtrace%n", "RIP"); + kprintf("%s is NULL can't show backtrace\n", "RIP"); } else { goodframe.next = (struct StackFrame *)ctx->uc_mcontext.rbp; goodframe.addr = ctx->uc_mcontext.rip; @@ -114,7 +114,7 @@ relegated static void ShowGeneralRegisters(ucontext_t *ctx) { long double st; char *p, buf[128]; p = buf; - kprintf("%n"); + kprintf("\n"); for (i = 0, j = 0, k = 0; i < ARRAYLEN(kGregNames); ++i) { if (j > 0) *p++ = ' '; if (!(s = kGregNames[(unsigned)kGregOrder[i]])[2]) *p++ = ' '; @@ -135,7 +135,7 @@ relegated static void ShowGeneralRegisters(ucontext_t *ctx) { p = __uintcpy(p, x / 1000), *p++ = '.'; p = __uintcpy(p, x % 1000); *p = 0; - kprintf("%s%n", buf); + kprintf("%s\n", buf); p = buf; } } @@ -143,14 +143,14 @@ relegated static void ShowGeneralRegisters(ucontext_t *ctx) { p, ctx->uc_mcontext.eflags, ctx->uc_mcontext.fpregs ? ctx->uc_mcontext.fpregs->swd : 0, ctx->uc_mcontext.fpregs ? ctx->uc_mcontext.fpregs->mxcsr : 0); - kprintf("%s%n", buf); + kprintf("%s\n", buf); } relegated static void ShowSseRegisters(ucontext_t *ctx) { size_t i; char *p, buf[128]; if (ctx->uc_mcontext.fpregs) { - kprintf("%n"); + kprintf("\n"); for (i = 0; i < 8; ++i) { p = buf; if (i >= 10) { @@ -175,7 +175,7 @@ relegated static void ShowSseRegisters(ucontext_t *ctx) { p = __fixcpy(p, ctx->uc_mcontext.fpregs->xmm[i + 8].u64[1], 64); p = __fixcpy(p, ctx->uc_mcontext.fpregs->xmm[i + 8].u64[0], 64); *p = 0; - kprintf("XMM%s%n", buf); + kprintf("XMM%s\n", buf); } } } @@ -201,10 +201,10 @@ relegated void ShowCrashReport(int err, int sig, struct siginfo *si, uname(&names); p = buf; errno = err; - kprintf("%n%serror%s: Uncaught %G (%s) on %s pid %d%n" - " %s%n" - " %m%n" - " %s %s %s %s%n", + kprintf("\n%serror%s: Uncaught %G (%s) on %s pid %d\n" + " %s\n" + " %m\n" + " %s %s %s %s\n", !__nocolor ? "\e[30;101m" : "", !__nocolor ? "\e[0m" : "", sig, (ctx && (ctx->uc_mcontext.rsp >= GetStaticStackAddr(0) && ctx->uc_mcontext.rsp <= GetStaticStackAddr(0) + PAGESIZE)) @@ -213,12 +213,12 @@ relegated void ShowCrashReport(int err, int sig, struct siginfo *si, host, getpid(), program_invocation_name, names.sysname, names.version, names.nodename, names.release); if (ctx) { - kprintf("%n"); + kprintf("\n"); ShowFunctionCalls(ctx); ShowGeneralRegisters(ctx); ShowSseRegisters(ctx); } - kprintf("%n"); + kprintf("\n"); PrintMemoryIntervals(2, &_mmi); /* PrintSystemMappings(2); */ if (__argv) { @@ -228,7 +228,7 @@ relegated void ShowCrashReport(int err, int sig, struct siginfo *si, kprintf("%s ", __argv[i]); } } - kprintf("%n"); + kprintf("\n"); } relegated static void RestoreDefaultCrashSignalHandlers(void) { @@ -245,14 +245,14 @@ static wontreturn relegated noinstrument void __minicrash(int sig, struct siginfo *si, ucontext_t *ctx, const char *kind) { - kprintf("%n" - "%n" - "CRASHED %s WITH %G%n" - "%s%n" - "RIP %x%n" - "RSP %x%n" - "RBP %x%n" - "%n", + kprintf("\n" + "\n" + "CRASHED %s WITH %G\n" + "%s\n" + "RIP %x\n" + "RSP %x\n" + "RBP %x\n" + "\n", kind, sig, __argv[0], ctx ? ctx->uc_mcontext.rip : 0, ctx ? ctx->uc_mcontext.rsp : 0, ctx ? ctx->uc_mcontext.rbp : 0); __restorewintty(); diff --git a/libc/log/printgarbage.c b/libc/log/printgarbage.c index ac0afdb00..69f0bc574 100644 --- a/libc/log/printgarbage.c +++ b/libc/log/printgarbage.c @@ -30,10 +30,10 @@ void PrintGarbage(void) { size_t i; char name[19]; const char *symbol; - kprintf("%n"); - kprintf(" SHADOW STACK @ %p%n", __builtin_frame_address(0)); - kprintf("garbage ent. parent frame original ret callback arg %n"); - kprintf("------------ ------------ ------------------ ------------------ ------------------%n"); + kprintf("\n"); + kprintf(" SHADOW STACK @ %p\n", __builtin_frame_address(0)); + kprintf("garbage ent. parent frame original ret callback arg \n"); + kprintf("------------ ------------ ------------------ ------------------ ------------------\n"); if (__garbage.i) { for (i = __garbage.i; i--;) { symbol = __get_symbol_by_addr(__garbage.p[i].ret); @@ -42,7 +42,7 @@ void PrintGarbage(void) { } else { ksnprintf(name, sizeof(name), "%#014lx", __garbage.p[i].ret); } - kprintf("%12lx %12lx %18s %18s %#18lx%n", + kprintf("%12lx %12lx %18s %18s %#18lx\n", __garbage.p + i, __garbage.p[i].frame, name, @@ -50,7 +50,7 @@ void PrintGarbage(void) { __garbage.p[i].arg); } } else { - kprintf("%12s %12s %18s %18s %18s%n","empty","-","-","-","-"); + kprintf("%12s %12s %18s %18s %18s\n","empty","-","-","-","-"); } - kprintf("%n"); + kprintf("\n"); } diff --git a/libc/log/vflogf.c b/libc/log/vflogf.c index 64a83772d..ffedd9fac 100644 --- a/libc/log/vflogf.c +++ b/libc/log/vflogf.c @@ -55,7 +55,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\n", strerror(err)); } } @@ -105,7 +105,7 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f, vflogf_onfail(f); } (vfprintf)(f, fmt, va); - fprintf(f, "%n"); + fprintf(f, "\n"); if (bufmode == _IOLBF) { f->bufmode = _IOLBF; fflush(f); @@ -114,7 +114,7 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f, __start_fatal(file, line); strcpy(buf32, "unknown"); gethostname(buf32, sizeof(buf32)); - (dprintf)(STDERR_FILENO, "fatality %s pid %d%n", buf32, getpid()); + (dprintf)(STDERR_FILENO, "fatality %s pid %d\n", buf32, getpid()); __die(); unreachable; } diff --git a/libc/mem/vasprintf.c b/libc/mem/vasprintf.c index f74ca4251..26ad2fe6c 100644 --- a/libc/mem/vasprintf.c +++ b/libc/mem/vasprintf.c @@ -22,33 +22,36 @@ /** * Formats string w/ dynamic memory allocation. - * - * @param *strp is output-only and must be free'd, even on error; since - * that's the behavior that'll make your code most portable - * @return complete bytes written (excluding NUL) or -1 w/ errno * @see xasprintf() for a better API */ int(vasprintf)(char **strp, const char *fmt, va_list va) { - char *p; - size_t size; va_list vb; + size_t size; + char *p, *p2; int wrote, rc = -1; - if ((*strp = malloc((size = 512)))) { + if ((p = malloc((size = 512)))) { va_copy(vb, va); - wrote = (vsnprintf)(*strp, size, fmt, va); + wrote = (vsnprintf)(p, size, fmt, va); if (wrote < size) { - if ((p = realloc(*strp, wrote + 1))) *strp = p; - rc = wrote; + if ((p2 = realloc(p, wrote + 1))) { + p = p2; + rc = wrote; + } } else { size = wrote + 1; - if ((p = realloc(*strp, size))) { - *strp = p; - wrote = (vsnprintf)(*strp, size, fmt, vb); + if ((p2 = realloc(p, size))) { + p = p2; + wrote = (vsnprintf)(p, size, fmt, vb); assert(wrote == size - 1); rc = wrote; } } va_end(vb); } - return rc; + if (rc != -1) { + *strp = p; + return rc; + } else { + return -1; + } } diff --git a/libc/nt/errors.h b/libc/nt/errors.h index 6a0301d39..da05e1af2 100644 --- a/libc/nt/errors.h +++ b/libc/nt/errors.h @@ -164,7 +164,7 @@ #define kNtErrorInfloopInRelocChain 202 #define kNtErrorEnvvarNotFound 203 #define kNtErrorNoSignalSent 205 -#define kNtErrorFilenameExcedRange 206 +#define kNtErrorFilenameExcedRange 206 /* ENAMETOOLONG */ #define kNtErrorRing2StackInUse 207 #define kNtErrorMetaExpansionTooLong 208 #define kNtErrorInvalidSignalNumber 209 diff --git a/libc/nt/kernel32/GetExitCodeProcess.s b/libc/nt/kernel32/GetExitCodeProcess.s index 78b43b59a..bd7cdc55f 100644 --- a/libc/nt/kernel32/GetExitCodeProcess.s +++ b/libc/nt/kernel32/GetExitCodeProcess.s @@ -2,11 +2,11 @@ .imp kernel32,__imp_GetExitCodeProcess,GetExitCodeProcess,0 .text.windows -GetExitCodeProcess: +__GetExitCodeProcess: push %rbp mov %rsp,%rbp .profilable mov __imp_GetExitCodeProcess(%rip),%rax jmp __sysv2nt - .endfn GetExitCodeProcess,globl + .endfn __GetExitCodeProcess,globl .previous diff --git a/libc/nt/kernel32/WaitForMultipleObjects.s b/libc/nt/kernel32/WaitForMultipleObjects.s index d5bd67864..ec06eccd6 100644 --- a/libc/nt/kernel32/WaitForMultipleObjects.s +++ b/libc/nt/kernel32/WaitForMultipleObjects.s @@ -2,11 +2,11 @@ .imp kernel32,__imp_WaitForMultipleObjects,WaitForMultipleObjects,0 .text.windows -WaitForMultipleObjects: +__WaitForMultipleObjects: push %rbp mov %rsp,%rbp .profilable mov __imp_WaitForMultipleObjects(%rip),%rax jmp __sysv2nt - .endfn WaitForMultipleObjects,globl + .endfn __WaitForMultipleObjects,globl .previous diff --git a/libc/nt/kernel32/WaitForSingleObject.s b/libc/nt/kernel32/WaitForSingleObject.s index 90adca673..61363d463 100644 --- a/libc/nt/kernel32/WaitForSingleObject.s +++ b/libc/nt/kernel32/WaitForSingleObject.s @@ -2,11 +2,11 @@ .imp kernel32,__imp_WaitForSingleObject,WaitForSingleObject,0 .text.windows -WaitForSingleObject: +__WaitForSingleObject: push %rbp mov %rsp,%rbp .profilable mov __imp_WaitForSingleObject(%rip),%rax jmp __sysv2nt - .endfn WaitForSingleObject,globl + .endfn __WaitForSingleObject,globl .previous diff --git a/libc/nt/master.sh b/libc/nt/master.sh index 562cb3c3b..50db7cd24 100755 --- a/libc/nt/master.sh +++ b/libc/nt/master.sh @@ -500,7 +500,6 @@ imp 'GetEnvironmentStringsA' GetEnvironmentStringsA kernel32 0 1 imp 'GetEnvironmentVariable' GetEnvironmentVariableW kernel32 0 3 imp 'GetEnvironmentVariableA' GetEnvironmentVariableA kernel32 0 3 imp 'GetErrorMode' GetErrorMode kernel32 0 -imp 'GetExitCodeProcess' GetExitCodeProcess kernel32 0 2 imp 'GetExitCodeThread' GetExitCodeThread kernel32 0 2 imp 'GetExpandedName' GetExpandedNameW kernel32 579 imp 'GetExpandedNameA' GetExpandedNameA kernel32 578 @@ -1275,9 +1274,7 @@ imp 'VirtualUnlock' VirtualUnlock kernel32 0 imp 'WTSGetActiveConsoleSessionId' WTSGetActiveConsoleSessionId kernel32 1497 imp 'WaitCommEvent' WaitCommEvent kernel32 0 imp 'WaitForDebugEvent' WaitForDebugEvent kernel32 0 -imp 'WaitForMultipleObjects' WaitForMultipleObjects kernel32 0 4 imp 'WaitForMultipleObjectsEx' WaitForMultipleObjectsEx kernel32 0 5 -imp 'WaitForSingleObject' WaitForSingleObject kernel32 0 2 imp 'WaitForSingleObjectEx' WaitForSingleObjectEx kernel32 0 3 imp 'WaitNamedPipe' WaitNamedPipeW kernel32 0 imp 'WaitNamedPipeA' WaitNamedPipeA kernel32 1509 2 @@ -1357,6 +1354,7 @@ imp '__FindNextFile' FindNextFileW kernel32 0 2 imp '__FlushFileBuffers' FlushFileBuffers kernel32 0 1 imp '__FlushViewOfFile' FlushViewOfFile kernel32 0 2 imp '__GenerateConsoleCtrlEvent' GenerateConsoleCtrlEvent kernel32 0 2 +imp '__GetExitCodeProcess' GetExitCodeProcess kernel32 0 2 imp '__GetFileAttributes' GetFileAttributesW kernel32 0 1 imp '__MapViewOfFileEx' MapViewOfFileEx kernel32 0 6 imp '__MapViewOfFileExNuma' MapViewOfFileExNuma kernel32 0 7 @@ -1372,6 +1370,8 @@ imp '__TlsGetValue' TlsGetValue kernel32 0 1 imp '__TlsSetValue' TlsSetValue kernel32 0 2 imp '__UnmapViewOfFile' UnmapViewOfFile kernel32 0 1 imp '__VirtualProtect' VirtualProtect kernel32 0 4 +imp '__WaitForMultipleObjects' WaitForMultipleObjects kernel32 0 4 +imp '__WaitForSingleObject' WaitForSingleObject kernel32 0 2 # ADVAPI32.DLL # diff --git a/libc/nt/thunk/accounting.inc b/libc/nt/thunk/accounting.inc index 860f1f14b..5c1bcbf64 100644 --- a/libc/nt/thunk/accounting.inc +++ b/libc/nt/thunk/accounting.inc @@ -6,6 +6,3 @@ extern typeof(GetThreadTimes) *const __imp_GetThreadTimes __msabi; #define GetUserName(...) __imp_GetUserNameW(__VA_ARGS__) extern typeof(GetUserName) *const __imp_GetUserNameW __msabi; - -#define GetExitCodeProcess(...) __imp_GetExitCodeProcess(__VA_ARGS__) -extern typeof(GetExitCodeProcess) *const __imp_GetExitCodeProcess __msabi; diff --git a/libc/runtime/findcombinary.c b/libc/runtime/findcombinary.c index f780ade4d..ecf7dfc5e 100644 --- a/libc/runtime/findcombinary.c +++ b/libc/runtime/findcombinary.c @@ -24,7 +24,7 @@ struct FindComBinary { bool once; const char *res; - char buf[PATH_MAX]; + char buf[PATH_MAX + 1]; }; static struct FindComBinary g_findcombinary; diff --git a/libc/runtime/internal.h b/libc/runtime/internal.h index 468d0d401..135130d48 100644 --- a/libc/runtime/internal.h +++ b/libc/runtime/internal.h @@ -30,6 +30,7 @@ int GetDosArgv(const char16_t *, char *, size_t, char **, size_t); Elf64_Ehdr *MapElfRead(const char *, struct MappedFile *) hidden; int GetDosEnviron(const char16_t *, char *, size_t, char **, size_t); bool __intercept_flag(int *, char *[], const char *); +int sys_mprotect_nt(void *, size_t, int) hidden; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/runtime/ismemtracked.greg.c b/libc/runtime/ismemtracked.greg.c index ba0b7b2ee..becbd54aa 100644 --- a/libc/runtime/ismemtracked.greg.c +++ b/libc/runtime/ismemtracked.greg.c @@ -18,7 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/runtime/memtrack.internal.h" -bool IsMemtracked(int x, int y) { +static inline bool IsMemtrackedImpl(int x, int y) { unsigned i; i = FindMemoryInterval(&_mmi, x); if (i == _mmi.i) return false; @@ -29,3 +29,9 @@ bool IsMemtracked(int x, int y) { if (_mmi.p[i].x != _mmi.p[i - 1].y + 1) return false; } } + +bool IsMemtracked(int x, int y) { + bool res; + res = IsMemtrackedImpl(x, y); + return res; +} diff --git a/libc/runtime/memtrack.internal.h b/libc/runtime/memtrack.internal.h index b6ee29e36..ce551b24b 100644 --- a/libc/runtime/memtrack.internal.h +++ b/libc/runtime/memtrack.internal.h @@ -46,6 +46,7 @@ struct MemoryIntervals { size_t i, n; struct MemoryInterval *p; struct MemoryInterval s[OPEN_MAX]; + _Alignas(64) char lock; }; extern hidden struct MemoryIntervals _mmi; diff --git a/libc/runtime/mmap.c b/libc/runtime/mmap.c index 4e5895277..c929999a8 100644 --- a/libc/runtime/mmap.c +++ b/libc/runtime/mmap.c @@ -27,6 +27,7 @@ #include "libc/intrin/asan.internal.h" #include "libc/intrin/describeflags.internal.h" #include "libc/intrin/kprintf.h" +#include "libc/intrin/spinlock.h" #include "libc/limits.h" #include "libc/log/backtrace.internal.h" #include "libc/log/libfatal.internal.h" @@ -198,6 +199,133 @@ static textwindows dontinline noasan void *MapMemories(char *addr, size_t size, return addr; } +static noasan inline void *Mmap(void *addr, size_t size, int prot, int flags, + int fd, int64_t off) { +#if defined(SYSDEBUG) && (_KERNTRACE || _NTTRACE) + if (IsWindows()) { + STRACE("mmap(%p, %'zu, %s, %s, %d, %'ld) → ...", addr, size, + DescribeProtFlags(prot), DescribeMapFlags(flags), fd, off); + } +#endif + char *p = addr; + struct DirectMap dm; + size_t virtualused, virtualneed; + int a, b, i, f, m, n, x; + + if (UNLIKELY(!size)) { + STRACE("size=0"); + return VIP(einval()); + } + + if (UNLIKELY(!IsLegalSize(size))) { + STRACE("size isn't 48-bit"); + return VIP(einval()); + } + + if (UNLIKELY(!IsLegalPointer(p))) { + STRACE("p isn't 48-bit"); + return VIP(einval()); + } + + if (UNLIKELY(!ALIGNED(p))) { + STRACE("p isn't 64kb aligned"); + return VIP(einval()); + } + + if (UNLIKELY(fd < -1)) { + STRACE("mmap(%.12p, %'zu, fd=%d) EBADF", p, size, fd); + return VIP(ebadf()); + } + + if (UNLIKELY(!((fd != -1) ^ !!(flags & MAP_ANONYMOUS)))) { + STRACE("fd anonymous mismatch"); + return VIP(einval()); + } + + if (UNLIKELY(!(!!(flags & MAP_PRIVATE) ^ !!(flags & MAP_SHARED)))) { + STRACE("MAP_SHARED ^ MAP_PRIVATE"); + return VIP(einval()); + } + + if (UNLIKELY(off < 0)) { + STRACE("neg off"); + return VIP(einval()); + } + + if (UNLIKELY(INT64_MAX - size < off)) { + STRACE("too large"); + return VIP(einval()); + } + + if (UNLIKELY(!ALIGNED(off))) { + STRACE("p isn't 64kb aligned"); + return VIP(einval()); + } + + if ((flags & MAP_FIXED_NOREPLACE) && IsMapped(p, size)) { +#ifdef SYSDEBUG + if (OverlapsImageSpace(p, size)) { + STRACE("overlaps image"); + } else { + STRACE("overlaps existing"); + } +#endif + return VIP(efault()); + } + + if (__isfdkind(fd, kFdZip)) { + STRACE("fd is zipos handle"); + return VIP(einval()); + } + + if (__virtualmax < LONG_MAX && + (__builtin_add_overflow((virtualused = GetMemtrackSize(&_mmi)), size, + &virtualneed) || + virtualneed > __virtualmax)) { + STRACE("%'zu size + %'zu inuse exceeds virtual memory limit %'zu", size, + virtualused, __virtualmax); + return VIP(enomem()); + } + + if (fd == -1) { + size = ROUNDUP(size, FRAMESIZE); + if (IsWindows()) { + prot |= PROT_WRITE; /* kludge */ + } + } + + n = (int)(size >> 16) + !!(size & (FRAMESIZE - 1)); + assert(n > 0); + f = (flags & ~MAP_FIXED_NOREPLACE) | MAP_FIXED; + if (flags & MAP_FIXED) { + x = FRAME(p); + if (IsWindows()) { + if (UntrackMemoryIntervals(p, size)) { + OnUnrecoverableMmapError("FIXED UNTRACK FAILED"); + } + } + } else if (!NeedAutomap(p, size)) { + x = FRAME(p); + } else if (!Automap(n, &x)) { + STRACE("AUTOMAP OUT OF MEMORY D:"); + return VIP(enomem()); + } + + p = (char *)ADDR(x); + if (IsOpenbsd() && (f & MAP_GROWSDOWN)) { /* openbsd:dubstack */ + dm = sys_mmap(p, size, prot, f & ~MAP_GROWSDOWN, fd, off); + if (dm.addr == MAP_FAILED) { + return MAP_FAILED; + } + } + + if (!IsWindows()) { + return MapMemory(p, size, prot, flags, fd, off, f, x, n); + } else { + return MapMemories(p, size, prot, flags, fd, off, f, x, n); + } +} + /** * Beseeches system for page-table entries, e.g. * @@ -229,100 +357,10 @@ static textwindows dontinline noasan void *MapMemories(char *addr, size_t size, */ noasan void *mmap(void *addr, size_t size, int prot, int flags, int fd, int64_t off) { -#if defined(SYSDEBUG) && (_KERNTRACE || _NTTRACE) - if (IsWindows()) { - STRACE("mmap(%p, %'zu, %s, %s, %d, %'ld) → ...", addr, size, - DescribeProtFlags(prot), DescribeMapFlags(flags), fd, off); - } -#endif void *res; - char *p = addr; - struct DirectMap dm; - size_t virtualused, virtualneed; - int a, b, i, f, m, n, x; - if (UNLIKELY(!size)) { - STRACE("size=0"); - res = VIP(einval()); - } else if (UNLIKELY(!IsLegalSize(size))) { - STRACE("size isn't 48-bit"); - res = VIP(einval()); - } else if (UNLIKELY(!IsLegalPointer(p))) { - STRACE("p isn't 48-bit"); - res = VIP(einval()); - } else if (UNLIKELY(!ALIGNED(p))) { - STRACE("p isn't 64kb aligned"); - res = VIP(einval()); - } else if (UNLIKELY(fd < -1)) { - STRACE("mmap(%.12p, %'zu, fd=%d) EBADF", p, size, fd); - res = VIP(ebadf()); - } else if (UNLIKELY(!((fd != -1) ^ !!(flags & MAP_ANONYMOUS)))) { - STRACE("fd anonymous mismatch"); - res = VIP(einval()); - } else if (UNLIKELY(!(!!(flags & MAP_PRIVATE) ^ !!(flags & MAP_SHARED)))) { - STRACE("MAP_SHARED ^ MAP_PRIVATE"); - res = VIP(einval()); - } else if (UNLIKELY(off < 0)) { - STRACE("neg off"); - res = VIP(einval()); - } else if (UNLIKELY(INT64_MAX - size < off)) { - STRACE("too large"); - res = VIP(einval()); - } else if (UNLIKELY(!ALIGNED(off))) { - STRACE("p isn't 64kb aligned"); - res = VIP(einval()); - } else if ((flags & MAP_FIXED_NOREPLACE) && IsMapped(p, size)) { -#ifdef SYSDEBUG - if (OverlapsImageSpace(p, size)) { - STRACE("overlaps image"); - } else { - STRACE("overlaps existing"); - } -#endif - res = VIP(efault()); - } else if (__isfdkind(fd, kFdZip)) { - STRACE("fd is zipos handle"); - res = VIP(einval()); - } else if (__virtualmax < LONG_MAX && - (__builtin_add_overflow((virtualused = GetMemtrackSize(&_mmi)), - size, &virtualneed) || - virtualneed > __virtualmax)) { - STRACE("%'zu size + %'zu inuse exceeds virtual memory limit %'zu", size, - virtualused, __virtualmax); - res = VIP(enomem()); - } else { - if (fd == -1) { - size = ROUNDUP(size, FRAMESIZE); - if (IsWindows()) { - prot |= PROT_WRITE; /* kludge */ - } - } - n = (int)(size >> 16) + !!(size & (FRAMESIZE - 1)); - assert(n > 0); - f = (flags & ~MAP_FIXED_NOREPLACE) | MAP_FIXED; - if (flags & MAP_FIXED) { - x = FRAME(p); - if (IsWindows()) { - if (UntrackMemoryIntervals(p, size)) { - OnUnrecoverableMmapError("FIXED UNTRACK FAILED"); - } - } - } else if (!NeedAutomap(p, size)) { - x = FRAME(p); - } else if (!Automap(n, &x)) { - STRACE("AUTOMAP OUT OF MEMORY D:"); - return VIP(enomem()); - } - p = (char *)ADDR(x); - if (IsOpenbsd() && (f & MAP_GROWSDOWN)) { /* openbsd:dubstack */ - dm = sys_mmap(p, size, prot, f & ~MAP_GROWSDOWN, fd, off); - if (dm.addr == MAP_FAILED) res = MAP_FAILED; - } - if (!IsWindows()) { - res = MapMemory(p, size, prot, flags, fd, off, f, x, n); - } else { - res = MapMemories(p, size, prot, flags, fd, off, f, x, n); - } - } + _spinlock(&_mmi.lock); + res = Mmap(addr, size, prot, flags, fd, off); + _spunlock(&_mmi.lock); STRACE("mmap(%p, %'zu, %s, %s, %d, %'ld) → %p% m", addr, size, DescribeProtFlags(prot), DescribeMapFlags(flags), fd, off, res); return res; diff --git a/libc/runtime/mprotect-nt.greg.c b/libc/runtime/mprotect-nt.greg.c new file mode 100644 index 000000000..6c4c58673 --- /dev/null +++ b/libc/runtime/mprotect-nt.greg.c @@ -0,0 +1,63 @@ +/*-*- 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 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/intrin/spinlock.h" +#include "libc/nt/memory.h" +#include "libc/runtime/directmap.internal.h" +#include "libc/runtime/internal.h" +#include "libc/runtime/memtrack.internal.h" + +#define ADDR(x) ((char *)((int64_t)((uint64_t)(x) << 32) >> 16)) + +privileged int sys_mprotect_nt(void *addr, size_t size, int prot) { + int rc = 0; + unsigned i; + uint32_t op; + char *a, *b, *x, *y, *p; + _spinlock(&_mmi.lock); + p = addr; + i = FindMemoryInterval(&_mmi, (intptr_t)p >> 16); + if (i == _mmi.i || (!i && p + size <= ADDR(_mmi.p[0].x))) { + // memory isn't in memtrack + // let's just trust the user then + // it's probably part of the executable + if (!VirtualProtect(addr, size, __prot2nt(prot, false), &op)) { + rc = -1; + } + } else { + // memory is in memtrack, so use memtrack, to do dimensioning + // we unfortunately must do something similar to this for cow + for (; i < _mmi.i; ++i) { + x = ADDR(_mmi.p[i].x); + y = x + _mmi.p[i].size; + if ((x <= p && p < y) || (x < p + size && p + size <= y) || + (p < x && y < p + size)) { + a = MIN(MAX(p, x), y); + b = MAX(MIN(p + size, y), x); + if (!VirtualProtect(a, b - a, __prot2nt(prot, _mmi.p[i].iscow), &op)) { + rc = -1; + break; + } + } else { + break; + } + } + } + _spunlock(&_mmi.lock); + return rc; +} diff --git a/libc/runtime/mprotect.greg.c b/libc/runtime/mprotect.greg.c index 0215270ad..14b27b7db 100644 --- a/libc/runtime/mprotect.greg.c +++ b/libc/runtime/mprotect.greg.c @@ -16,26 +16,16 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/asmflag.h" #include "libc/bits/likely.h" #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/dce.h" -#include "libc/errno.h" #include "libc/intrin/describeflags.internal.h" -#include "libc/macros.internal.h" -#include "libc/nt/memory.h" -#include "libc/nt/thunk/msabi.h" -#include "libc/runtime/directmap.internal.h" -#include "libc/runtime/memtrack.internal.h" -#include "libc/sysv/consts/nr.h" +#include "libc/runtime/internal.h" +#include "libc/runtime/runtime.h" #include "libc/sysv/consts/prot.h" #include "libc/sysv/errfuns.h" -__msabi extern typeof(VirtualProtect) *const __imp_VirtualProtect; - -#define ADDR(x) ((char *)((int64_t)((uint64_t)(x) << 32) >> 16)) - /** * Modifies restrictions on virtual memory address range. * @@ -44,12 +34,8 @@ __msabi extern typeof(VirtualProtect) *const __imp_VirtualProtect; * @return 0 on success, or -1 w/ errno * @see mmap() */ -noasan noubsan privileged int mprotect(void *addr, size_t size, int prot) { - bool cf; +privileged int mprotect(void *addr, size_t size, int prot) { int64_t rc; - unsigned i; - uint32_t op; - char *a, *b, *x, *y, *p; if (SupportsWindows() && (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC | PROT_GROWSDOWN | PROT_GROWSUP))) { rc = einval(); // unix checks prot before checking size @@ -58,48 +44,9 @@ noasan noubsan privileged int mprotect(void *addr, size_t size, int prot) { } else if (UNLIKELY((intptr_t)addr & 4095)) { rc = einval(); } else if (!IsWindows()) { - asm volatile(CFLAG_ASM("clc\n\tsyscall") - : CFLAG_CONSTRAINT(cf), "=a"(rc) - : "1"(__NR_mprotect), "D"(addr), "S"(size), "d"(prot) - : "rcx", "r11", "memory", "cc"); - if (cf) { - errno = rc; - rc = -1; - } else if (rc > -4096ul) { - errno = -rc; - rc = -1; - } + rc = sys_mprotect(addr, size, prot); } else { - rc = 0; - p = addr; - i = FindMemoryInterval(&_mmi, (intptr_t)p >> 16); - if (i == _mmi.i || (!i && p + size <= ADDR(_mmi.p[0].x))) { - // memory isn't in memtrack - // let's just trust the user then - // it's probably part of the executable - if (!VirtualProtect(addr, size, __prot2nt(prot, false), &op)) { - rc = -1; - } - } else { - // memory is in memtrack, so use memtrack, to do dimensioning - // we unfortunately must do something similar to this for cow - for (; i < _mmi.i; ++i) { - x = ADDR(_mmi.p[i].x); - y = x + _mmi.p[i].size; - if ((x <= p && p < y) || (x < p + size && p + size <= y) || - (p < x && y < p + size)) { - a = MIN(MAX(p, x), y); - b = MAX(MIN(p + size, y), x); - if (!VirtualProtect(a, b - a, __prot2nt(prot, _mmi.p[i].iscow), - &op)) { - rc = -1; - break; - } - } else { - break; - } - } - } + rc = sys_mprotect_nt(addr, size, prot); } STRACE("mprotect(%p, %'zu, %s) → %d% m", addr, size, DescribeProtFlags(prot), rc); diff --git a/libc/runtime/msync-nt.c b/libc/runtime/msync-nt.c index 55fe5895f..22e680587 100644 --- a/libc/runtime/msync-nt.c +++ b/libc/runtime/msync-nt.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +#include "libc/intrin/spinlock.h" #include "libc/macros.internal.h" #include "libc/nt/files.h" #include "libc/nt/memory.h" @@ -31,6 +32,7 @@ noasan textwindows int sys_msync_nt(char *addr, size_t size, int flags) { int i, rc = 0; char *a, *b, *x, *y; + _spinlock(&_mmi.lock); for (i = FindMemoryInterval(&_mmi, (intptr_t)addr >> 16); i < _mmi.i; ++i) { x = ADDR(_mmi.p[i].x); y = x + _mmi.p[i].size; @@ -47,30 +49,6 @@ noasan textwindows int sys_msync_nt(char *addr, size_t size, int flags) { break; } } + _spunlock(&_mmi.lock); return rc; } - -#if 0 -noasan textwindows int sys_msync_nt(char *addr, size_t size, int flags) { - char *a, *b; - int rc, x, y, l, r, i; - rc = 0; - for (i = FindMemoryInterval(&_mmi, (intptr_t)addr >> 16); i < _mmi.i; ++i) { - if ((ADDR(_mmi.p[i].x) <= addr && addr < ADDR(_mmi.p[i].y + 1)) || - (ADDR(_mmi.p[i].x) < addr + size && - addr + size <= ADDR(_mmi.p[i].y + 1)) || - (addr < ADDR(_mmi.p[i].x) && ADDR(_mmi.p[i].y + 1) < addr + size)) { - a = MIN(MAX(addr, ADDR(_mmi.p[i].x)), ADDR(_mmi.p[i].y + 1)); - b = MAX(MIN(addr + size, ADDR(_mmi.p[i].y + 1)), ADDR(_mmi.p[i].x)); - if (!FlushViewOfFile(a, b - a)) { - rc = -1; - break; - } - // TODO(jart): FlushFileBuffers too on g_fds handle if MS_SYNC? - } else { - break; - } - } - return rc; -} -#endif diff --git a/libc/runtime/munmap.c b/libc/runtime/munmap.c index 47e2a51fe..19146c221 100644 --- a/libc/runtime/munmap.c +++ b/libc/runtime/munmap.c @@ -22,6 +22,7 @@ #include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/asan.internal.h" +#include "libc/intrin/spinlock.h" #include "libc/log/libfatal.internal.h" #include "libc/macros.internal.h" #include "libc/runtime/directmap.internal.h" @@ -35,6 +36,76 @@ #define ADDR(x) ((int64_t)((uint64_t)(x) << 32) >> 16) #define FRAME(x) ((int)((intptr_t)(x) >> 16)) +static noasan int Munmap(void *v, size_t n) { + char poison, *p = v; + intptr_t a, b, x, y; + assert(!__vforked); + + if (UNLIKELY(!n)) { + STRACE("munmap(%.12p, %'zu) %s (n=0)", p, n); + return einval(); + } + + if (UNLIKELY(!IsLegalSize(n))) { + STRACE("munmap(%.12p, %'zu) EINVAL (n isn't 48-bit)", p, n); + return einval(); + } + + if (UNLIKELY(!IsLegalPointer(p))) { + STRACE("munmap(%.12p, %'zu) EINVAL (p isn't 48-bit)", p, n); + return einval(); + } + + if (UNLIKELY(!IsLegalPointer(p + (n - 1)))) { + STRACE("munmap(%.12p, %'zu) EINVAL (p+(n-1) isn't 48-bit)", p, n); + return einval(); + } + + if (UNLIKELY(!ALIGNED(p))) { + STRACE("munmap(%.12p, %'zu) EINVAL (p isn't 64kb aligned)", p, n); + return einval(); + } + + if (!IsMemtracked(FRAME(p), FRAME(p + (n - 1)))) { + STRACE("munmap(%.12p, %'zu) EFAULT (interval not tracked)", p, n); + return efault(); + } + + if (UntrackMemoryIntervals(p, n) == -1) { + return -1; + } + + if (IsWindows()) { + return 0; // UntrackMemoryIntervals does it for NT + } + + if (sys_munmap(p, n) == -1) { + return -1; // ouch + } + + if (IsAsan() && !OverlapsShadowSpace(p, n)) { + a = ((intptr_t)p >> 3) + 0x7fff8000; + b = a + (n >> 3); + if (IsMemtracked(FRAME(a), FRAME(b - 1))) { + x = ROUNDUP(a, FRAMESIZE); + y = ROUNDDOWN(b, FRAMESIZE); + if (x < y) { + // delete shadowspace if unmapping ≥512kb + __repstosb((void *)a, kAsanUnmapped, x - a); + Munmap((void *)x, y - x); + __repstosb((void *)y, kAsanUnmapped, b - y); + } else { + // otherwise just poison and assume reuse + __repstosb((void *)a, kAsanUnmapped, b - a); + } + } else { + STRACE("unshadow(%.12p, %p) EFAULT", a, b - a); + } + } + + return 0; +} + /** * Releases memory pages. * @@ -49,66 +120,11 @@ * and for files size needs to be perfect to the byte bc openbsd * @return 0 on success, or -1 w/ errno */ -noasan int munmap(void *v, size_t n) { - /* asan runtime depends on this function */ +noasan int munmap(void *p, size_t n) { int rc; - char poison, *p = v; - intptr_t a, b, x, y; - assert(!__vforked); - if (UNLIKELY(!n)) { - STRACE("munmap(%.12p, %'zu) %s (n=0)", p, n); - return einval(); - } - if (UNLIKELY(!IsLegalSize(n))) { - STRACE("munmap(%.12p, %'zu) EINVAL (n isn't 48-bit)", p, n); - return einval(); - } - if (UNLIKELY(!IsLegalPointer(p))) { - STRACE("munmap(%.12p, %'zu) EINVAL (p isn't 48-bit)", p, n); - return einval(); - } - if (UNLIKELY(!IsLegalPointer(p + (n - 1)))) { - STRACE("munmap(%.12p, %'zu) EINVAL (p+(n-1) isn't 48-bit)", p, n); - return einval(); - } - if (UNLIKELY(!ALIGNED(p))) { - STRACE("munmap(%.12p, %'zu) EINVAL (p isn't 64kb aligned)", p, n); - return einval(); - } - if (!IsMemtracked(FRAME(p), FRAME(p + (n - 1)))) { - STRACE("munmap(%.12p, %'zu) EFAULT (interval not tracked)", p, n); - return efault(); - } - if (UntrackMemoryIntervals(p, n) != -1) { - if (!IsWindows()) { - rc = sys_munmap(p, n); - if (rc != -1) { - if (IsAsan() && !OverlapsShadowSpace(p, n)) { - a = ((intptr_t)p >> 3) + 0x7fff8000; - b = a + (n >> 3); - if (IsMemtracked(FRAME(a), FRAME(b - 1))) { - x = ROUNDUP(a, FRAMESIZE); - y = ROUNDDOWN(b, FRAMESIZE); - if (x < y) { - /* delete shadowspace if unmapping ≥512kb */ - __repstosb((void *)a, kAsanUnmapped, x - a); - munmap((void *)x, y - x); - __repstosb((void *)y, kAsanUnmapped, b - y); - } else { - /* otherwise just poison and assume reuse */ - __repstosb((void *)a, kAsanUnmapped, b - a); - } - } else { - STRACE("unshadow(%.12p, %p) EFAULT", a, b - a); - } - } - } - } else { - rc = 0; /* UntrackMemoryIntervals does it for NT */ - } - } else { - rc = -1; - } + _spinlock(&_mmi.lock); + rc = Munmap(p, n); + _spunlock(&_mmi.lock); STRACE("munmap(%.12p, %'zu) → %d% m", p, n, rc); return rc; } diff --git a/libc/runtime/paginate.c b/libc/runtime/paginate.c new file mode 100644 index 000000000..51002f2f5 --- /dev/null +++ b/libc/runtime/paginate.c @@ -0,0 +1,57 @@ +/*-*- 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 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/safemacros.internal.h" +#include "libc/calls/calls.h" +#include "libc/fmt/fmt.h" +#include "libc/runtime/runtime.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/o.h" +#include "libc/x/x.h" + +/** + * Displays wall of text in terminal with pagination. + */ +void __paginate(int fd, const char *s) { + int tfd, pid; + char *args[3] = {0}; + char tmppath[PATH_MAX + 1]; + char progpath[PATH_MAX + 1]; + if (strcmp(nulltoempty(getenv("TERM")), "dumb") && isatty(0) && isatty(1) && + ((args[0] = commandv("less", progpath, sizeof(progpath))) || + (args[0] = commandv("more", progpath, sizeof(progpath))))) { + snprintf(tmppath, sizeof(tmppath), "%s%s-%s-%d.txt", kTmpPath, + program_invocation_short_name, "paginate", getpid()); + if ((tfd = open(tmppath, O_WRONLY | O_CREAT | O_TRUNC, 0644)) != -1) { + write(tfd, s, strlen(s)); + close(tfd); + args[1] = tmppath; + if ((pid = vfork()) != -1) { + if (!pid) { + execv(args[0], args); + _Exit(127); + } + waitpid(pid, 0, 0); + unlink(tmppath); + return; + } + unlink(tmppath); + } + } + write(fd, s, strlen(s)); +} diff --git a/libc/runtime/printargs.greg.c b/libc/runtime/printargs.greg.c index 10e438f7a..322de9e2f 100644 --- a/libc/runtime/printargs.greg.c +++ b/libc/runtime/printargs.greg.c @@ -44,6 +44,7 @@ #include "libc/runtime/stack.h" #include "libc/sock/internal.h" #include "libc/sock/sock.h" +#include "libc/str/str.h" #include "libc/sysv/consts/auxv.h" #include "libc/sysv/consts/f.h" #include "libc/sysv/consts/poll.h" @@ -52,12 +53,13 @@ #include "tool/decode/lib/idname.h" #include "tool/decode/lib/x86idnames.h" +STATIC_YOINK("strerror"); // for kprintf() STATIC_YOINK("strsignal"); // for kprintf() #define PRINT(FMT, ...) \ do { \ kprintf(prologue); \ - kprintf(FMT "%n", ##__VA_ARGS__); \ + kprintf(FMT "\n", ##__VA_ARGS__); \ } while (0) static const struct AuxiliaryValue { @@ -137,8 +139,8 @@ textstartup void __printargs(const char *prologue) { unsigned i, n; uintptr_t *auxp; struct utsname uts; - char path[PATH_MAX]; struct termios termios; + char path[PATH_MAX + 1]; int e, x, st, ft, flags; struct pollfd pfds[128]; struct AuxiliaryValue *auxinfo; @@ -157,7 +159,7 @@ textstartup void __printargs(const char *prologue) { kprintf(" %s", uts.release); } } - kprintf("%n"); + kprintf("\n"); } else { PRINT(" uname() failed %m"); } @@ -177,7 +179,7 @@ textstartup void __printargs(const char *prologue) { FindNameById(kX86GradeNames, getx86processormodel(kX86ProcessorModelKey)->grade)); } - kprintf("%n"); + kprintf("\n"); if ((x = KCPUIDS(16H, EAX) & 0x7fff)) { kprintf(prologue); kprintf(" %dmhz %s", x, "freq"); @@ -187,7 +189,7 @@ textstartup void __printargs(const char *prologue) { if ((x = KCPUIDS(16H, ECX) & 0x7fff)) { kprintf(" / %dmhz %s", x, "bus"); } - kprintf("%n"); + kprintf("\n"); } if (X86_HAVE(HYPERVISOR)) { unsigned eax, ebx, ecx, edx; @@ -203,8 +205,9 @@ textstartup void __printargs(const char *prologue) { PRINT(" L%d%s%s %u-way %,u byte cache w/%s " "%,u sets of %,u byte lines shared across %u threads%s", CPUID4_CACHE_LEVEL, - CPUID4_CACHE_TYPE == 1 ? " data" - : CPUID4_CACHE_TYPE == 2 ? " code" : "", + CPUID4_CACHE_TYPE == 1 ? " data" + : CPUID4_CACHE_TYPE == 2 ? " code" + : "", CPUID4_IS_FULLY_ASSOCIATIVE ? " fully-associative" : "", CPUID4_WAYS_OF_ASSOCIATIVITY, CPUID4_CACHE_SIZE_IN_BYTES, CPUID4_PHYSICAL_LINE_PARTITIONS > 1 ? " physically partitioned" : "", @@ -233,7 +236,7 @@ textstartup void __printargs(const char *prologue) { if (X86_HAVE(RDPID)) kprintf(" RDPID"); if (X86_HAVE(LA57)) kprintf(" LA57"); if (X86_HAVE(FSGSBASE)) kprintf(" FSGSBASE"); - kprintf("%n"); + kprintf("\n"); PRINT(""); PRINT("FILE DESCRIPTORS"); @@ -355,7 +358,7 @@ textstartup void __printargs(const char *prologue) { if (termios.c_iflag & IMAXBEL) kprintf(" IMAXBEL"); if (termios.c_iflag & IUTF8) kprintf(" IUTF8"); if (termios.c_iflag & IUCLC) kprintf(" IUCLC"); - kprintf("%n"); + kprintf("\n"); kprintf(prologue); kprintf(" c_oflag ="); if (termios.c_oflag & OPOST) kprintf(" OPOST"); @@ -408,15 +411,15 @@ textstartup void __printargs(const char *prologue) { } else if ((termios.c_oflag & FFDLY) == FF1) { kprintf(" FF1"); } - kprintf("%n"); + kprintf("\n"); kprintf(prologue); kprintf(" c_cflag ="); - if (termios.c_cflag & ISIG) kprintf(" ISIG"); - if (termios.c_cflag & CSTOPB) kprintf(" CSTOPB"); - if (termios.c_cflag & CREAD) kprintf(" CREAD"); if (termios.c_cflag & PARENB) kprintf(" PARENB"); if (termios.c_cflag & PARODD) kprintf(" PARODD"); + if (termios.c_cflag & CSTOPB) kprintf(" CSTOPB"); + if (termios.c_cflag & PARODD) kprintf(" PARODD"); if (termios.c_cflag & HUPCL) kprintf(" HUPCL"); + if (termios.c_cflag & CREAD) kprintf(" CREAD"); if (termios.c_cflag & CLOCAL) kprintf(" CLOCAL"); if ((termios.c_cflag & CSIZE) == CS5) { kprintf(" CS5"); @@ -427,7 +430,7 @@ textstartup void __printargs(const char *prologue) { } else if ((termios.c_cflag & CSIZE) == CS8) { kprintf(" CS8"); } - kprintf("%n"); + kprintf("\n"); kprintf(prologue); kprintf(" c_lflag ="); if (termios.c_lflag & ISIG) kprintf(" ISIG"); @@ -445,7 +448,7 @@ textstartup void __printargs(const char *prologue) { if (termios.c_lflag & FLUSHO) kprintf(" FLUSHO"); if (termios.c_lflag & PENDIN) kprintf(" PENDIN"); if (termios.c_lflag & XCASE) kprintf(" XCASE"); - kprintf("%n"); + kprintf("\n"); PRINT(" c_ispeed = %u", termios.c_ispeed); PRINT(" c_ospeed = %u", termios.c_ospeed); PRINT(" c_cc[VINTR] = CTRL-%c", CTRL(termios.c_cc[VINTR])); diff --git a/libc/runtime/printmemoryintervals.c b/libc/runtime/printmemoryintervals.c index d3b5ae1db..2fdfebce4 100644 --- a/libc/runtime/printmemoryintervals.c +++ b/libc/runtime/printmemoryintervals.c @@ -54,7 +54,7 @@ void PrintMemoryIntervals(int fd, const struct MemoryIntervals *mm) { if (mm->p[i].h != -1) { kprintf(" h=%ld", mm->p[i].h); } - kprintf("%n"); + kprintf("\n"); } - kprintf("# %ld frames mapped w/ %'ld frames gapped%n", maptally, gaptally); + kprintf("# %ld frames mapped w/ %'ld frames gapped\n", maptally, gaptally); } diff --git a/libc/runtime/runtime.h b/libc/runtime/runtime.h index 0604d98dd..2da6ea692 100644 --- a/libc/runtime/runtime.h +++ b/libc/runtime/runtime.h @@ -104,6 +104,7 @@ long GetMaxFd(void); char *GetProgramExecutableName(void); char *GetInterpreterExecutableName(char *, size_t); void __printargs(const char *); +void __paginate(int, const char *); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/runtime/runtime.mk b/libc/runtime/runtime.mk index db205c38b..1892b2408 100644 --- a/libc/runtime/runtime.mk +++ b/libc/runtime/runtime.mk @@ -69,10 +69,12 @@ o/$(MODE)/libc/runtime/ezmap.o \ o/$(MODE)/libc/runtime/getdosargv.o \ o/$(MODE)/libc/runtime/getdosenviron.o \ o/$(MODE)/libc/runtime/hook.greg.o \ +o/$(MODE)/libc/runtime/mprotect.greg.o \ +o/$(MODE)/libc/runtime/mprotect-nt.greg.o \ +o/$(MODE)/libc/runtime/ismemtracked.greg.o \ o/$(MODE)/libc/runtime/isheap.o \ o/$(MODE)/libc/runtime/memtracknt.o \ o/$(MODE)/libc/runtime/memtrack.greg.o \ -o/$(MODE)/libc/runtime/ismemtracked.greg.o \ o/$(MODE)/libc/runtime/metalprintf.greg.o \ o/$(MODE)/libc/runtime/printargs.greg.o \ o/$(MODE)/libc/runtime/mman.greg.o \ diff --git a/libc/runtime/vfork.S b/libc/runtime/vfork.S index c10080021..b09a0ff36 100644 --- a/libc/runtime/vfork.S +++ b/libc/runtime/vfork.S @@ -94,7 +94,7 @@ vfork.bsd: #ifdef SYSDEBUG .rodata.str1.1 .Llog: .ascii STRACE_PROLOGUE - .asciz "vfork()%n" + .asciz "vfork()\n" .previous #endif /* DEBUGSYS */ diff --git a/libc/runtime/winmain.greg.c b/libc/runtime/winmain.greg.c index 6208d6d69..0ad345803 100644 --- a/libc/runtime/winmain.greg.c +++ b/libc/runtime/winmain.greg.c @@ -55,6 +55,7 @@ #include "libc/runtime/memtrack.internal.h" #include "libc/runtime/runtime.h" #include "libc/sock/internal.h" +#include "libc/str/str.h" #include "libc/str/tpenc.h" #include "libc/str/utf16.h" @@ -120,6 +121,15 @@ forceinline void MakeLongDoubleLongAgain(void) { asm volatile("fldcw\t%0" : /* no outputs */ : "m"(x87cw)); } +static inline size_t StrLen16(const char16_t *s) { + size_t n; + for (n = 0;; ++n) { + if (!s[n]) { + return n; + } + } +} + __msabi static textwindows int OnEarlyWinCrash(struct NtExceptionPointers *ep) { uint32_t wrote; char buf[64], *p = buf; @@ -209,6 +219,9 @@ __msabi static textwindows wontreturn void WinMainNew(const char16_t *cmdline) { } } env16 = GetEnvironmentStrings(); + for (char16_t *e = env16; *e; e += StrLen16(e) + 1) { + NTTRACE("GetEnvironmentStrings() → %!#hs", e); + } NTTRACE("WinMainNew() loading environment"); GetDosEnviron(env16, wa->envblock, ARRAYLEN(wa->envblock) - 8, wa->envp, ARRAYLEN(wa->envp) - 1); @@ -257,7 +270,6 @@ __msabi textwindows int64_t WinMain(int64_t hInstance, int64_t hPrevInstance, extern uint64_t ts asm("kStartTsc"); os = WINDOWS; /* madness https://news.ycombinator.com/item?id=21019722 */ ts = rdtsc(); - __nomultics = true; __pid = GetCurrentProcessId(); __wincrashearly = AddVectoredExceptionHandler(1, (void *)OnEarlyWinCrash); cmdline = GetCommandLine(); diff --git a/libc/sock/describesocklevel.greg.c b/libc/sock/describesocklevel.greg.c new file mode 100644 index 000000000..337d6e822 --- /dev/null +++ b/libc/sock/describesocklevel.greg.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 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/itoa.h" +#include "libc/sysv/consts/sol.h" + +/** + * Describes setsockopt() level arguments. + */ +const char *DescribeSockLevel(int x) { + static char buf[12]; + if (x == SOL_IP) return "SOL_IP"; + if (x == SOL_TCP) return "SOL_TCP"; + if (x == SOL_UDP) return "SOL_UDP"; + if (x == SOL_SOCKET) return "SOL_SOCKET"; + FormatInt32(buf, x); + return buf; +} diff --git a/libc/sock/describesockoptname.greg.c b/libc/sock/describesockoptname.greg.c new file mode 100644 index 000000000..d3f1388fa --- /dev/null +++ b/libc/sock/describesockoptname.greg.c @@ -0,0 +1,45 @@ +/*-*- 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 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/itoa.h" +#include "libc/fmt/magnumstrs.internal.h" +#include "libc/sysv/consts/sol.h" + +/** + * Describes setsockopt() optname arguments. + */ +const char *DescribeSockOptname(int l, int x) { + int i; + static char buf[12], *s; + const struct MagnumStr *ms = 0; + if (x) { + if (l == SOL_SOCKET) { + ms = kSockOptnames; + } else if (l == SOL_TCP) { + ms = kTcpOptnames; + } else if (l == SOL_IP) { + ms = kIpOptnames; + } + } + if (ms && (s = GetMagnumStr(ms, x))) { + return s; + } else { + FormatInt32(buf, x); + return buf; + } +} diff --git a/libc/sock/getsockopt-nt.c b/libc/sock/getsockopt-nt.c index 49ccc4ee7..0ab21dd1f 100644 --- a/libc/sock/getsockopt-nt.c +++ b/libc/sock/getsockopt-nt.c @@ -17,20 +17,54 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" +#include "libc/bits/bits.h" #include "libc/calls/internal.h" +#include "libc/calls/struct/timeval.h" #include "libc/nt/winsock.h" #include "libc/sock/internal.h" #include "libc/sock/yoink.inc" +#include "libc/str/str.h" +#include "libc/sysv/consts/so.h" +#include "libc/sysv/consts/sol.h" #include "libc/sysv/errfuns.h" textwindows int sys_getsockopt_nt(struct Fd *fd, int level, int optname, - void *out_opt_optval, uint32_t *out_optlen) { - /* TODO(jart): Use WSAIoctl? */ + void *out_opt_optval, + uint32_t *inout_optlen) { + uint64_t ms; + uint32_t in_optlen; assert(fd->kind == kFdSocket); - if (__sys_getsockopt_nt(fd->handle, level, optname, out_opt_optval, - out_optlen) != -1) { - return 0; + + if (out_opt_optval && inout_optlen) { + in_optlen = *inout_optlen; } else { + in_optlen = 0; + } + + // TODO(jart): Use WSAIoctl? + if (__sys_getsockopt_nt(fd->handle, level, optname, out_opt_optval, + inout_optlen) == -1) { return __winsockerr(); } + + if (level == SOL_SOCKET) { + if ((optname == SO_RCVTIMEO || optname == SO_SNDTIMEO) && + in_optlen == sizeof(struct timeval) && + *inout_optlen == sizeof(uint32_t)) { + ms = *(uint32_t *)out_opt_optval; + ((struct timeval *)out_opt_optval)->tv_sec = ms / 1000; + ((struct timeval *)out_opt_optval)->tv_usec = ms % 1000 * 1000; + *inout_optlen = sizeof(struct timeval); + } + } + + if (in_optlen == 4 && *inout_optlen == 1) { + // handle cases like this + // getsockopt(8, SOL_TCP, TCP_FASTOPEN, [u"☺"], [1]) → 0 + int32_t wut = *(signed char *)out_opt_optval; + memcpy(out_opt_optval, &wut, 4); + *inout_optlen = 4; + } + + return 0; } diff --git a/libc/sock/getsockopt.c b/libc/sock/getsockopt.c index 067c05267..d60d8d601 100644 --- a/libc/sock/getsockopt.c +++ b/libc/sock/getsockopt.c @@ -19,6 +19,8 @@ #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/dce.h" +#include "libc/fmt/magnumstrs.internal.h" +#include "libc/intrin/asan.internal.h" #include "libc/sock/internal.h" #include "libc/sock/sock.h" #include "libc/sysv/errfuns.h" @@ -36,10 +38,15 @@ int getsockopt(int fd, int level, int optname, void *out_opt_optval, uint32_t *out_optlen) { int rc; + if (!level || !optname) { rc = enoprotoopt(); /* our sysvconsts definition */ } else if (optname == -1) { rc = 0; /* our sysvconsts definition */ + } else if (IsAsan() && (out_opt_optval && out_optlen && + (!__asan_is_valid(out_optlen, sizeof(uint32_t)) || + !__asan_is_valid(out_opt_optval, *out_optlen)))) { + rc = efault(); } else if (!IsWindows()) { rc = sys_getsockopt(fd, level, optname, out_opt_optval, out_optlen); } else if (__isfdkind(fd, kFdSocket)) { @@ -48,7 +55,18 @@ int getsockopt(int fd, int level, int optname, void *out_opt_optval, } else { rc = ebadf(); } - STRACE("getsockopt(%d, %#x, %#x, %p, %p) → %d% lm", fd, level, optname, - out_opt_optval, out_optlen, rc); + +#ifdef SYSDEBUG + if (out_opt_optval && out_optlen && rc != -1) { + STRACE("getsockopt(%d, %s, %s, [%#.*hhs], [%d]) → %d% lm", fd, + DescribeSockLevel(level), DescribeSockOptname(level, optname), + *out_optlen, out_opt_optval, *out_optlen, rc); + } else { + STRACE("getsockopt(%d, %s, %s, %p, %p) → %d% lm", fd, + DescribeSockLevel(level), DescribeSockOptname(level, optname), + out_opt_optval, out_optlen, rc); + } +#endif + return rc; } diff --git a/libc/sock/kipoptnames.S b/libc/sock/kipoptnames.S new file mode 100644 index 000000000..4d3c8dae4 --- /dev/null +++ b/libc/sock/kipoptnames.S @@ -0,0 +1,39 @@ +/*-*- 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 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/macros.internal.h" + + .macro .e e + .long \e - kIpOptnames + .long 1f - kIpOptnames + .rodata.str1.1 +1: .string "\e" + .previous + .endm + + .section .rodata + .align 4 + .underrun +kIpOptnames: + .e IP_TOS # int + .e IP_MTU # int + .e IP_TTL # int + .e IP_HDRINCL # bool32 + .long -123 + .endobj kIpOptnames,globl,hidden + .overrun diff --git a/libc/sock/ksockoptnames.S b/libc/sock/ksockoptnames.S new file mode 100644 index 000000000..b56acbffe --- /dev/null +++ b/libc/sock/ksockoptnames.S @@ -0,0 +1,49 @@ +/*-*- 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 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/macros.internal.h" + + .macro .e e + .long \e - kSockOptnames + .long 1f - kSockOptnames + .rodata.str1.1 +1: .string "\e" + .previous + .endm + + .section .rodata + .align 4 + .underrun +kSockOptnames: + .e SO_DEBUG # bool32 + .e SO_BROADCAST # bool32 + .e SO_REUSEADDR # bool32 + .e SO_REUSEPORT # bool32 + .e SO_KEEPALIVE # bool32 + .e SO_DONTROUTE # bool32 + .e SO_RCVTIMEO # timeval + .e SO_SNDTIMEO # timeval + .e SO_LINGER # linger + .e SO_SNDBUF # int + .e SO_RCVBUF # int + .e SO_RCVLOWAT # int + .e SO_SNDLOWAT # int + .e SO_ERROR # int + .long -123 + .endobj kSockOptnames,globl,hidden + .overrun diff --git a/libc/sock/ktcpoptnames.S b/libc/sock/ktcpoptnames.S new file mode 100644 index 000000000..f5d837ae8 --- /dev/null +++ b/libc/sock/ktcpoptnames.S @@ -0,0 +1,48 @@ +/*-*- 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 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/macros.internal.h" + + .macro .e e + .long \e - kTcpOptnames + .long 1f - kTcpOptnames + .rodata.str1.1 +1: .string "\e" + .previous + .endm + + .section .rodata + .align 4 + .underrun +kTcpOptnames: + .e TCP_NODELAY # bool32 + .e TCP_CORK # bool32 + .e TCP_QUICKACK # bool32 + .e TCP_FASTOPEN_CONNECT # bool32 + .e TCP_DEFER_ACCEPT # bool32 + .e TCP_KEEPIDLE # int (seconds) + .e TCP_KEEPINTVL # int (seconds) + .e TCP_FASTOPEN # int + .e TCP_KEEPCNT # int + .e TCP_MAXSEG # int + .e TCP_SYNCNT # int + .e TCP_NOTSENT_LOWAT # int + .e TCP_WINDOW_CLAMP # int + .long -123 + .endobj kTcpOptnames,globl,hidden + .overrun diff --git a/libc/sock/recvmsg.c b/libc/sock/recvmsg.c index 137b6e74f..c688f808f 100644 --- a/libc/sock/recvmsg.c +++ b/libc/sock/recvmsg.c @@ -88,7 +88,7 @@ ssize_t recvmsg(int fd, struct msghdr *msg, int flags) { if (msg->msg_flags) kprintf(".flags=%#x, ", msg->msg_flags); kprintf(".iov=", fd); __strace_iov(msg->msg_iov, msg->msg_iovlen, rc != -1 ? rc : 0); - kprintf("}], %#x) → %'ld% m%n", flags, rc); + kprintf("}], %#x) → %'ld% m\n", flags, rc); } } #endif diff --git a/libc/sock/sendmsg.c b/libc/sock/sendmsg.c index d338ded3e..3c8545229 100644 --- a/libc/sock/sendmsg.c +++ b/libc/sock/sendmsg.c @@ -90,7 +90,7 @@ ssize_t sendmsg(int fd, const struct msghdr *msg, int flags) { if (msg->msg_flags) kprintf(".flags=%#x, ", msg->msg_flags); kprintf(".iov=", fd); __strace_iov(msg->msg_iov, msg->msg_iovlen, rc != -1 ? rc : 0); - kprintf("}, %#x) → %'ld% m%n", flags, rc); + kprintf("}, %#x) → %'ld% m\n", flags, rc); } } #endif diff --git a/libc/sock/setsockopt-nt.c b/libc/sock/setsockopt-nt.c index ed31ffda5..24c753517 100644 --- a/libc/sock/setsockopt-nt.c +++ b/libc/sock/setsockopt-nt.c @@ -17,9 +17,11 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/struct/timeval.h" +#include "libc/limits.h" #include "libc/macros.internal.h" #include "libc/sock/internal.h" #include "libc/sysv/consts/so.h" +#include "libc/sysv/consts/sol.h" #include "libc/sysv/errfuns.h" struct linger_nt { /* Linux+XNU+BSD ABI */ @@ -29,27 +31,34 @@ struct linger_nt { /* Linux+XNU+BSD ABI */ textwindows int sys_setsockopt_nt(struct Fd *fd, int level, int optname, const void *optval, uint32_t optlen) { + int64_t ms; struct timeval *tv; struct linger *linger; union { uint32_t millis; struct linger_nt linger; - } nt; + } u; - if (optname == SO_LINGER && optval && optlen == sizeof(struct linger)) { - linger = optval; - nt.linger.l_onoff = linger->l_onoff; - nt.linger.l_linger = MIN(0xFFFF, MAX(0, linger->l_linger)); - optval = &nt.linger; - optlen = sizeof(nt.linger); - } - - if ((optname == SO_RCVTIMEO || optname == SO_SNDTIMEO) && optval && - optlen == sizeof(struct timeval)) { - tv = optval; - nt.millis = MIN(0xFFFFFFFF, MAX(0, tv->tv_sec * 1000 + tv->tv_usec / 1000)); - optval = &nt.millis; - optlen = sizeof(nt.millis); + if (level == SOL_SOCKET) { + if (optname == SO_LINGER && optval && optlen == sizeof(struct linger)) { + linger = optval; + u.linger.l_onoff = linger->l_onoff; + u.linger.l_linger = MIN(0xFFFF, MAX(0, linger->l_linger)); + optval = &u.linger; + optlen = sizeof(u.linger); + } else if ((optname == SO_RCVTIMEO || optname == SO_SNDTIMEO) && optval && + optlen == sizeof(struct timeval)) { + tv = optval; + if (__builtin_mul_overflow(tv->tv_sec, 1000, &ms) || + __builtin_add_overflow(ms, tv->tv_usec / 1000, &ms) || + (ms < 0 || ms > 0xffffffff)) { + u.millis = 0xffffffff; + } else { + u.millis = ms; + } + optval = &u.millis; + optlen = sizeof(u.millis); + } } if (__sys_setsockopt_nt(fd->handle, level, optname, optval, optlen) != -1) { diff --git a/libc/sock/setsockopt.c b/libc/sock/setsockopt.c index a9e831b48..c66b078eb 100644 --- a/libc/sock/setsockopt.c +++ b/libc/sock/setsockopt.c @@ -20,6 +20,8 @@ #include "libc/calls/strace.internal.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/fmt/magnumstrs.internal.h" +#include "libc/intrin/asan.internal.h" #include "libc/nt/winsock.h" #include "libc/sock/internal.h" #include "libc/sock/sock.h" @@ -52,12 +54,12 @@ static bool setsockopt_polyfill(int *optname) { int setsockopt(int fd, int level, int optname, const void *optval, uint32_t optlen) { int e, rc; - if (!optval) { + + if (!optname) { + rc = enosys(); /* see libc/sysv/consts.sh */ + } else if ((!optval && optlen) || + (IsAsan() && !__asan_is_valid(optval, optlen))) { rc = efault(); - } else if (!level || !optname) { - rc = enoprotoopt(); /* our sysvconsts definition */ - } else if (optname == -1) { - rc = 0; /* our sysvconsts definition */ } else if (!IsWindows()) { rc = -1; e = errno; @@ -73,7 +75,18 @@ int setsockopt(int fd, int level, int optname, const void *optval, } else { rc = ebadf(); } - STRACE("setsockopt(%d, %#x, %#x, %p, %'u) → %d% lm", fd, level, optname, - optval, optlen, rc); + +#ifdef SYSDEBUG + if (!(rc == -1 && errno == EFAULT)) { + STRACE("setsockopt(%d, %s, %s, %#.*hhs, %'u) → %d% lm", fd, + DescribeSockLevel(level), DescribeSockOptname(level, optname), + optlen, optval, optlen, rc); + } else { + STRACE("setsockopt(%d, %s, %s, %p, %'u) → %d% lm", fd, + DescribeSockLevel(level), DescribeSockOptname(level, optname), + optval, optlen, rc); + } +#endif + return rc; } diff --git a/libc/sock/sock.mk b/libc/sock/sock.mk index e8d40fa58..4ffe02852 100644 --- a/libc/sock/sock.mk +++ b/libc/sock/sock.mk @@ -9,10 +9,16 @@ LIBC_SOCK_A = o/$(MODE)/libc/sock/sock.a LIBC_SOCK_A_FILES := $(wildcard libc/sock/*) LIBC_SOCK_A_HDRS = $(filter %.h,$(LIBC_SOCK_A_FILES)) LIBC_SOCK_A_INCS = $(filter %.inc,$(LIBC_SOCK_A_FILES)) -LIBC_SOCK_A_SRCS = $(filter %.c,$(LIBC_SOCK_A_FILES)) +LIBC_SOCK_A_SRCS_C = $(filter %.c,$(LIBC_SOCK_A_FILES)) +LIBC_SOCK_A_SRCS_S = $(filter %.S,$(LIBC_SOCK_A_FILES)) + +LIBC_SOCK_A_SRCS = \ + $(LIBC_SOCK_A_SRCS_C) \ + $(LIBC_SOCK_A_SRCS_S) LIBC_SOCK_A_OBJS = \ - $(LIBC_SOCK_A_SRCS:%.c=o/$(MODE)/%.o) + $(LIBC_SOCK_A_SRCS_C:%.c=o/$(MODE)/%.o) \ + $(LIBC_SOCK_A_SRCS_S:%.S=o/$(MODE)/%.o) LIBC_SOCK_A_CHECKS = \ $(LIBC_SOCK_A).pkg \ diff --git a/libc/sock/sockdebug.c b/libc/sock/sockdebug.c index aa9351f53..ff52cf07b 100644 --- a/libc/sock/sockdebug.c +++ b/libc/sock/sockdebug.c @@ -48,6 +48,10 @@ const char *__describe_socket_type(int type) { p = stpcpy(p, "SOCK_DGRAM"); } else if (x == SOCK_RAW) { p = stpcpy(p, "SOCK_RAW"); + } else if (x == SOCK_RDM) { + p = stpcpy(p, "SOCK_RDM"); + } else if (x == SOCK_SEQPACKET) { + p = stpcpy(p, "SOCK_SEQPACKET"); } else { p = FormatInt32(p, x); } diff --git a/libc/stdio/printf.c b/libc/stdio/printf.c index 54e3a2cf3..5dcffa13a 100644 --- a/libc/stdio/printf.c +++ b/libc/stdio/printf.c @@ -32,10 +32,6 @@ * - `%m` inserts strerror(errno) into the formatted output. This is * consistent with glibc, musl, and uclibc. * - * - `%n` inserts "\n" on non-Windows and "\r\n" on Windows. This is the - * same behavior as Java. It's incompatible with glibc's behavior, - * since that's problematic according to Android's security team. - * * - `%hs` converts UTF-16/UCS-2 → UTF-8, which can be helpful on Windows. * Formatting (e.g. %-10hs) will use monospace display width rather * than string length or codepoint count. diff --git a/libc/stdio/spawn.c b/libc/stdio/spawn.c index 2ec52e244..13ae5af10 100644 --- a/libc/stdio/spawn.c +++ b/libc/stdio/spawn.c @@ -40,7 +40,7 @@ int posix_spawn(int *pid, const char *path, unsigned mode; sigset_t allsigs; struct sigaction dfl; - char *p, *q, opath[PATH_MAX]; + char *p, *q, opath[PATH_MAX + 1]; int s, fd, newfd, oflag, tempfd; if (!(*pid = vfork())) { if (attrp) { @@ -77,7 +77,7 @@ int posix_spawn(int *pid, const char *path, if (sscanf(p + 5, "%d,", &fd) != 1) _Exit(127); p = strchr(p, ',') + 1; q = strchr(p, '*'); - if (!q || q - p + 1 > PATH_MAX) _Exit(127); + if (!q || q - p > PATH_MAX) _Exit(127); strncpy(opath, p, q - p); opath[q - p] = '\0'; if (sscanf(q + 1, "%o,%o)", &oflag, &mode) != 2) _Exit(127); diff --git a/libc/stdio/spawnp.c b/libc/stdio/spawnp.c index 2191b3c99..d6b62614e 100644 --- a/libc/stdio/spawnp.c +++ b/libc/stdio/spawnp.c @@ -31,7 +31,7 @@ int posix_spawnp(int *pid, const char *path, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[]) { - char pathbuf[PATH_MAX]; - if (!(path = commandv(path, pathbuf))) return errno; + char pathbuf[PATH_MAX + 1]; + if (!(path = commandv(path, pathbuf, sizeof(pathbuf)))) return errno; return posix_spawn(pid, path, file_actions, attrp, argv, envp); } diff --git a/libc/stdio/tmpfile.c b/libc/stdio/tmpfile.c index 67e54721a..d4c886338 100644 --- a/libc/stdio/tmpfile.c +++ b/libc/stdio/tmpfile.c @@ -32,11 +32,11 @@ */ FILE *tmpfile(void) { int fd; - char *tmp, *sep, tpl[PATH_MAX]; + char *tmp, *sep, tpl[PATH_MAX + 1]; tmp = firstnonnull(getenv("TMPDIR"), kTmpPath); sep = !isempty(tmp) && !endswith(tmp, "/") ? "/" : ""; - if ((snprintf)(tpl, PATH_MAX, "%s%stmp.%s.XXXXXX", tmp, sep, - program_invocation_short_name) < PATH_MAX) { + if ((snprintf)(tpl, PATH_MAX + 1, "%s%stmp.%s.XXXXXX", tmp, sep, + program_invocation_short_name) <= PATH_MAX) { if ((fd = mkostemps(tpl, 0, 0)) != -1) { return fdopen(fd, "w+"); } diff --git a/libc/str/kstrsignal.S b/libc/str/ksignalnames.S similarity index 95% rename from libc/str/kstrsignal.S rename to libc/str/ksignalnames.S index 838354752..5939eb3e9 100644 --- a/libc/str/kstrsignal.S +++ b/libc/str/ksignalnames.S @@ -19,8 +19,8 @@ #include "libc/macros.internal.h" .macro .e e s - .long \e - kStrSignal - .long 1f - kStrSignal + .long \e - kSignalNames + .long 1f - kSignalNames .rodata.str1.1 1: .string "\s" .previous @@ -29,7 +29,7 @@ .section .rodata .align 4 .underrun -kStrSignal: +kSignalNames: .e SIGHUP,"HUP" .e SIGINT,"INT" .e SIGQUIT,"QUIT" @@ -65,6 +65,6 @@ kStrSignal: .e SIGRTMIN,"RTMIN" .e SIGEMT,"EMT" .e SIGPWR,"PWR" - .long 0 - .endobj kStrSignal,globl,hidden + .long -123 + .endobj kSignalNames,globl,hidden .overrun diff --git a/libc/str/qsort.c b/libc/str/qsort.c index d280a394b..d0f112e07 100644 --- a/libc/str/qsort.c +++ b/libc/str/qsort.c @@ -53,7 +53,7 @@ static inline int pntz(size_t p[2]) { return 0; } -/* smoothsort_shl() and smoothsort_shr() need n > 0 */ +// smoothsort_shl() and smoothsort_shr() need n > 0 static inline void smoothsort_shl(size_t p[2], int n) { if (n >= CHAR_BIT * sizeof(size_t)) { n -= CHAR_BIT * sizeof(size_t); @@ -163,7 +163,7 @@ static void smoothsort(struct SmoothSort *s, void *base, size_t nel, if (!size) return; head = base; high = head + size - width; - /* Precompute Leonardo numbers, scaled by element width */ + // precompute Leonardo numbers, scaled by element width for (s->lp[0] = s->lp[1] = width, i = 2; (s->lp[i] = s->lp[i - 2] + s->lp[i - 1] + width) < size; i++) { } diff --git a/libc/str/str.h b/libc/str/str.h index 3b06b430b..6ec97a339 100644 --- a/libc/str/str.h +++ b/libc/str/str.h @@ -192,7 +192,6 @@ wchar_t *wmempcpy(wchar_t *, const wchar_t *, size_t) memcpyesque; wchar_t *wmemmove(wchar_t *, const wchar_t *, size_t) memcpyesque; void *tinymemccpy(void *, const void *, int, size_t) memcpyesque; void *memmem(const void *, size_t, const void *, size_t) libcesque nosideeffect; -char *strerror(int) returnsnonnull dontthrow nocallback; long a64l(const char *); char *l64a(long); @@ -262,14 +261,12 @@ wint_t towctrans(wint_t, wctrans_t); ╚────────────────────────────────────────────────────────────────────────────│*/ char *strsignal(int) returnsnonnull libcesque; +char *strerror(int) returnsnonnull dontthrow nocallback; +const char *strerrno(int) nosideeffect libcesque; +const char *strerdoc(int) nosideeffect libcesque; +int strerror_r(int, char *, size_t) dontthrow nocallback; +int strerror_wr(int, uint32_t, char *, size_t) dontthrow nocallback; -#if defined(__GNUC__) && !defined(__STRICT_ANSI__) -/* gcc rewrites to memset otherwise :'( */ -void __bzero(void *, size_t) asm("bzero") memcpyesque; -#define bzero(DEST, SIZE) \ - ((void)((__builtin_constant_p(SIZE)) ? memset(DEST, 0, SIZE) \ - : __bzero(DEST, SIZE))) -#endif /* __GNUC__ && !__STRICT_ANSI__ */ COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_STR_STR_H_ */ diff --git a/libc/str/strsignal.c b/libc/str/strsignal.c index 7b4283462..ea6b0868f 100644 --- a/libc/str/strsignal.c +++ b/libc/str/strsignal.c @@ -17,7 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/bits.h" -#include "libc/fmt/kerrornames.internal.h" +#include "libc/fmt/magnumstrs.internal.h" #include "libc/macros.internal.h" #include "libc/str/str.h" @@ -35,15 +35,12 @@ static char g_strsignal[12]; * @see sigaction() */ char *strsignal(int sig) { - int i; + const char *s; strcpy(g_strsignal, "SIG"); if (sig) { - for (i = 0; kStrSignal[i].x; ++i) { - if (sig == *(const int *)((uintptr_t)kStrSignal + kStrSignal[i].x)) { - strcpy(g_strsignal + 3, - (const char *)((uintptr_t)kStrSignal + kStrSignal[i].s)); - return g_strsignal; - } + if ((s = GetMagnumStr(kSignalNames, sig))) { + strcpy(g_strsignal + 3, s); + return g_strsignal; } } if (!sig) { diff --git a/libc/sysv/calls/setregid.s b/libc/sysv/calls/setregid.s deleted file mode 100644 index 7daa7a16a..000000000 --- a/libc/sysv/calls/setregid.s +++ /dev/null @@ -1,2 +0,0 @@ -.include "o/libc/sysv/macros.internal.inc" -.scall setregid,0x07f07f07f207f072,globl diff --git a/libc/sysv/calls/setreuid.s b/libc/sysv/calls/setreuid.s deleted file mode 100644 index e0b88ab32..000000000 --- a/libc/sysv/calls/setreuid.s +++ /dev/null @@ -1,2 +0,0 @@ -.include "o/libc/sysv/macros.internal.inc" -.scall setreuid,0x07e07e07e207e071,globl diff --git a/libc/sysv/calls/sys_setregid.s b/libc/sysv/calls/sys_setregid.s new file mode 100644 index 000000000..8b42b154a --- /dev/null +++ b/libc/sysv/calls/sys_setregid.s @@ -0,0 +1,2 @@ +.include "o/libc/sysv/macros.internal.inc" +.scall sys_setregid,0x07f07f07f207f072,globl,hidden diff --git a/libc/sysv/calls/sys_setreuid.s b/libc/sysv/calls/sys_setreuid.s new file mode 100644 index 000000000..a3f3fc5b3 --- /dev/null +++ b/libc/sysv/calls/sys_setreuid.s @@ -0,0 +1,2 @@ +.include "o/libc/sysv/macros.internal.inc" +.scall sys_setreuid,0x07e07e07e207e071,globl,hidden diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index ddace70e8..48fec094f 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -685,7 +685,6 @@ syscon so SO_RCVBUF 8 0x1002 0x1002 0x1002 0x1002 0x1002 # bsd co syscon so SO_RCVTIMEO 20 0x1006 0x1006 0x1006 0x100c 0x1006 # recv timeout; takes struct timeval (overrides SA_RESTART restoring EINTR behavior on recv/send/connect/accept/etc.; bsd consensus) syscon so SO_SNDTIMEO 21 0x1005 0x1005 0x1005 0x100b 0x1005 # send timeout; takes struct timeval; bsd consensus syscon so SO_RCVLOWAT 18 0x1004 0x1004 0x1004 0x1004 0x1004 # bsd consensus -syscon so SO_EXCLUSIVEADDRUSE 0 0 0 0 0 0xfffffffb # hoo boy syscon so SO_SNDLOWAT 19 0x1003 0x1003 0x1003 0x1003 0x1003 # bsd consensus syscon so SO_TYPE 3 0x1008 0x1008 0x1008 0x1008 0x1008 # bsd consensus syscon so SO_TIMESTAMP 29 0x0400 0x0400 0x0800 0x2000 0 @@ -693,6 +692,7 @@ syscon so SO_SETFIB 0 0 0x1014 0 0 0 syscon so SO_DOMAIN 39 0 0x1019 0x1024 0 0 syscon so SO_MAX_PACING_RATE 47 0 0x1018 0 0 0 syscon so SO_PEERCRED 17 0 0 0x1022 0 0 +syscon so SO_EXCLUSIVEADDRUSE 0 0 0 0 0 0xfffffffb # hoo boy syscon so LOCAL_PEERCRED 0 1 1 0 0 0 syscon so SO_PROTOCOL 38 0 0x1016 0x1025 0 0 syscon so SO_ATTACH_BPF 50 0 0 0 0 0 @@ -730,9 +730,10 @@ syscon so SO_TIMESTAMPNS 35 0 0 0 0 0 syscon so SO_WIFI_STATUS 41 0 0 0 0 0 syscon sol SOL_IP 0 0 0 0 0 0 # consensus -syscon sol SOL_SOCKET 1 0xffff 0xffff 0xffff 0xffff 0xffff # bsd+nt consensus (todo: what's up with ipproto_icmp overlap) +syscon sol SOL_SOCKET 1 0xffff 0xffff 0xffff 0xffff 0xffff # yes it's actually 0xffff; bsd+nt consensus (todo: what's up with ipproto_icmp overlap) syscon sol SOL_TCP 6 6 6 6 6 6 # consensus syscon sol SOL_UDP 17 17 17 17 17 17 # consensus +syscon sol SOL_RAW 255 0 0 0 0 0 syscon sol SOL_IPV6 41 41 41 41 41 41 syscon sol SOL_ICMPV6 58 58 58 58 58 0 syscon sol SOL_AAL 265 0 0 0 0 0 @@ -752,7 +753,6 @@ syscon sol SOL_NFC 280 0 0 0 0 0 syscon sol SOL_PACKET 263 0 0 0 0 0 syscon sol SOL_PNPIPE 275 0 0 0 0 0 syscon sol SOL_PPPOL2TP 273 0 0 0 0 0 -syscon sol SOL_RAW 255 0 0 0 0 0 syscon sol SOL_RDS 276 0 0 0 0 0 syscon sol SOL_RXRPC 272 0 0 0 0 0 syscon sol SOL_TIPC 271 0 0 0 0 0 @@ -808,13 +808,14 @@ syscon tcp TCP_REPAIR_QUEUE 20 0 0 0 0 0 # what is it syscon tcp TCP_THIN_LINEAR_TIMEOUTS 16 0 0 0 0 0 # what is it # group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary +syscon ip IP_TOS 1 3 3 3 3 8 # bsd consensus +syscon ip IP_TTL 2 4 4 4 4 7 # bsd consensus +syscon ip IP_HDRINCL 3 2 2 2 2 2 # bsd consensus syscon ip IP_DEFAULT_MULTICAST_LOOP 1 1 1 1 1 1 # consensus syscon ip IP_DEFAULT_MULTICAST_TTL 1 1 1 1 1 1 # consensus syscon ip IP_PMTUDISC_DONT 0 0 0 0 0 0 # consensus -syscon ip IP_HDRINCL 3 2 2 2 2 2 # bsd consensus syscon ip IP_MAX_MEMBERSHIPS 20 0x0fff 0x0fff 0x0fff 0x0fff 20 # bsd consensus syscon ip IP_OPTIONS 4 1 1 1 1 1 # bsd consensus -syscon ip IP_TOS 1 3 3 3 3 8 # bsd consensus syscon ip IP_RECVTTL 12 24 65 31 23 21 syscon ip IP_ADD_MEMBERSHIP 35 12 12 12 12 5 # bsd consensus syscon ip IP_DROP_MEMBERSHIP 36 13 13 13 13 6 # bsd consensus @@ -825,7 +826,6 @@ syscon ip IP_RECVOPTS 6 5 5 5 5 0 # bsd consensus syscon ip IP_RECVRETOPTS 7 6 6 6 6 0 # bsd consensus syscon ip IP_RECVDSTADDR 0 7 7 7 7 0 # bsd consensus syscon ip IP_RETOPTS 7 8 8 8 8 0 # bsd consensus -syscon ip IP_TTL 2 4 4 4 4 7 # bsd consensus syscon ip IP_ADD_SOURCE_MEMBERSHIP 39 70 70 0 0 15 syscon ip IP_BLOCK_SOURCE 38 72 72 0 0 17 syscon ip IP_DROP_SOURCE_MEMBERSHIP 40 71 71 0 0 0x10 @@ -1367,6 +1367,25 @@ syscon termios ENDRUNDISC 0 0 0 0x9 0x9 0 # boop syscon termios TIOCPTMASTER 0 0 0x2000741c 0 0 0 # boop syscon termios NETGRAPHDISC 0 0 0x6 0 0 0 # boop syscon termios H4DISC 0 0 0x7 0 0 0 # boop + +# Teletypewriter Control Modes +# +# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary +syscon termios CS5 0b0000000000000000 0b000000000000000000 0b000000000000000000 0b0000000000000000 0b0000000000000000 0b0000000000000000 # termios.c_cflag; consensus +syscon termios CS6 0b0000000000010000 0b000000000100000000 0b000000000100000000 0b0000000100000000 0b0000000100000000 0b0000000000010000 # termios.c_cflag; 6-bit characters +syscon termios CS7 0b0000000000100000 0b000000001000000000 0b000000001000000000 0b0000001000000000 0b0000001000000000 0b0000000000100000 # termios.c_cflag; 7-bit characters +syscon termios CS8 0b0000000000110000 0b000000001100000000 0b000000001100000000 0b0000001100000000 0b0000001100000000 0b0000000000110000 # termios.c_cflag; 8-bit characters +syscon termios CSIZE 0b0000000000110000 0b000000001100000000 0b000000001100000000 0b0000001100000000 0b0000001100000000 0b0000000000110000 # termios.c_cflag; mask for CS𝑥 flags +syscon termios CSTOPB 0b0000000001000000 0b000000010000000000 0b000000010000000000 0b0000010000000000 0b0000010000000000 0b0000000001000000 # termios.c_cflag; bsd consensus +syscon termios CREAD 0b0000000010000000 0b000000100000000000 0b000000100000000000 0b0000100000000000 0b0000100000000000 0b0000000010000000 # termios.c_cflag; bsd consensus +syscon termios PARENB 0b0000000100000000 0b000001000000000000 0b000001000000000000 0b0001000000000000 0b0001000000000000 0b0000000100000000 # termios.c_cflag +syscon termios PARODD 0b0000001000000000 0b000010000000000000 0b000010000000000000 0b0010000000000000 0b0010000000000000 0b0000001000000000 # termios.c_cflag +syscon termios HUPCL 0b0000010000000000 0b000100000000000000 0b000100000000000000 0b0100000000000000 0b0100000000000000 0b0000010000000000 # termios.c_cflag; bsd consensus +syscon termios CLOCAL 0b0000100000000000 0b1000000000000000 0b1000000000000000 0b1000000000000000 0b1000000000000000 0b0000100000000000 # termios.c_cflag; consensus + +# Teletypewriter Local Modes +# +# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary syscon termios ISIG 0b0000000000000001 0b0000000010000000 0b0000000010000000 0b0000000010000000 0b0000000010000000 0b0000000000000001 # termios.c_lflag|=ISIG makes Ctrl-C, Ctrl-\, etc. generate signals syscon termios ICANON 0b0000000000000010 0b0000000100000000 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 0 0b0000000000000100 # termios.c_lflag @@ -1383,6 +1402,10 @@ syscon termios FLUSHO 0b0001000000000000 8388608 8388608 8388608 83886 syscon termios PENDIN 0b0100000000000000 536870912 536870912 536870912 536870912 0b0100000000000000 # termios.c_lflag syscon termios IEXTEN 0b1000000000000000 0b0000010000000000 0b0000010000000000 0b0000010000000000 0b0000010000000000 0b1000000000000000 # termios.c_lflag&=~IEXTEN disables platform input processing magic syscon termios EXTPROC 65536 0b0000100000000000 0b0000100000000000 0b0000100000000000 0b0000100000000000 65536 # termios.c_lflag + +# Teletypewriter Input Modes +# +# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary syscon termios IGNBRK 0b0000000000000001 0b0000000000000001 0b0000000000000001 0b0000000000000001 0b0000000000000001 0b0000000000000001 # termios.c_iflag it's complicated, uart only? UNIXCONSENSUS syscon termios BRKINT 0b0000000000000010 0b0000000000000010 0b0000000000000010 0b0000000000000010 0b0000000000000010 0b0000000000000010 # termios.c_iflag it's complicated, uart only? UNIXCONSENSUS syscon termios IGNPAR 0b0000000000000100 0b0000000000000100 0b0000000000000100 0b0000000000000100 0b0000000000000100 0b0000000000000100 # termios.c_iflag|=IGNPAR ignores parity and framing errors; see PARMRK UNIXCONSENSUS @@ -1398,9 +1421,13 @@ syscon termios IXANY 0b0000100000000000 0b0000100000000000 0b000010000000000 syscon termios IXOFF 0b0001000000000000 0b0000010000000000 0b0000010000000000 0b0000010000000000 0b0000010000000000 0b0001000000000000 # termios.c_iflag|=IXOFF disables annoying display freeze keys syscon termios IMAXBEL 0b0010000000000000 0b0010000000000000 0b0010000000000000 0b0010000000000000 0b0010000000000000 0b0010000000000000 # termios.c_iflag|=IMAXBEL rings when queue full UNIXCONSENSUS syscon termios IUTF8 0b0100000000000000 0b0100000000000000 0 0 0 0b0100000000000000 # termios.c_iflag|=IUTF8 helps w/ rubout on UTF-8 input + +# Teletypewriter Output Modes +# +# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary syscon termios OPOST 0b0000000000000001 0b000000000000000001 0b000000000000000001 0b0000000000000001 0b0000000000000001 0b0000000000000001 # termios.c_oflag&=~OPOST disables output processing magic, e.g. MULTICS newlines syscon termios OLCUC 0b0000000000000010 0 0 0b0000000000100000 0 0b0000000000000010 # termios.c_oflag|=OLCUC maps a-z → A-Z output -syscon termios ONLCR 0b0000000000000100 0b000000000000000010 0b000000000000000010 0b0000000000000010 0b0000000000000010 0b0000000000000100 # termios.c_oflag|=ONLCR claims to map \n → \r\n output +syscon termios ONLCR 0b0000000000000100 0b000000000000000010 0b000000000000000010 0b0000000000000010 0b0000000000000010 0b0000000000000100 # termios.c_oflag|=ONLCR map \n → \r\n output (MULTICS newline) and requires OPOST syscon termios OCRNL 0b0000000000001000 0b000000000000010000 0b000000000000010000 0b0000000000010000 0b0000000000010000 0b0000000000001000 # termios.c_oflag|=OCRNL maps \r → \n output syscon termios ONOCR 0b0000000000010000 0b000000000000100000 0b000000000000100000 0b0000000001000000 0b0000000001000000 0b0000000000010000 # termios.c_oflag|=ONOCR maps \r → ∅ output iff column 0 syscon termios ONLRET 0b0000000000100000 0b000000000001000000 0b000000000001000000 0b0000000010000000 0b0000000010000000 0b0000000000100000 # termios.c_oflag|=ONLRET maps \r → ∅ output @@ -1431,11 +1458,10 @@ syscon termios VT1 0b0100000000000000 0b010000000000000000 0b0100000000000 syscon termios FFDLY 0b1000000000000000 0b000100000000000000 0b000100000000000000 0 0 0b1000000000000000 # termios.c_oflag syscon termios FF0 0b0000000000000000 0b000000000000000000 0b000000000000000000 0 0 0b0000000000000000 # termios.c_oflag syscon termios FF1 0b1000000000000000 0b000100000000000000 0b000100000000000000 0 0 0b1000000000000000 # termios.c_oflag -syscon termios CS5 0 0 0 0 0 0 # consensus -syscon termios CS6 0b0000000000010000 0b0000000100000000 0b0000000100000000 0b0000000100000000 0b0000000100000000 0b0000000000010000 # termios.c_cflag flag for 6-bit characters -syscon termios CS7 0b0000000000100000 0b0000001000000000 0b0000001000000000 0b0000001000000000 0b0000001000000000 0b0000000000100000 # termios.c_cflag flag for 7-bit characters -syscon termios CS8 0b0000000000110000 0b0000001100000000 0b0000001100000000 0b0000001100000000 0b0000001100000000 0b0000000000110000 # termios.c_cflag flag for 8-bit characters -syscon termios CSIZE 0b0000000000110000 0b0000001100000000 0b0000001100000000 0b0000001100000000 0b0000001100000000 0b0000000000110000 # mask for CS𝑥 flags + +# Teletypewriter Special Control Character Assignments +# +# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary syscon termios NCCS 20 20 20 20 20 20 # ARRAYLEN(termios.c_cc); we schlep c_line into c_cc on linux syscon termios VINTR 0+1 8 8 8 8 0 # termios.c_cc[VINTR]=𝑥 syscon termios VQUIT 1+1 9 9 9 9 1 # termios.c_cc[VQUIT]=𝑥 @@ -1454,15 +1480,13 @@ syscon termios VDISCARD 13+1 15 15 15 15 13 # termios.c_cc[VDISCA syscon termios VWERASE 14+1 4 4 4 4 14 # termios.c_cc[VWERASE]=𝑥 syscon termios VLNEXT 15+1 14 14 14 14 15 # termios.c_cc[VLNEXT]=𝑥 syscon termios VEOL2 16+1 2 2 2 2 16 # termios.c_cc[VEOL2]=𝑥 + syscon termios TIOCSERGETLSR 0x5459 0 0 0 0 0 # syscon termios TIOCSERGETMULTI 0x545a 0 0 0 0 0 # syscon termios TIOCSERSETMULTI 0x545b 0 0 0 0 0 # syscon termios TIOCSER_TEMT 1 0 0 0 0 0 # syscon termios VERIFY 47 0 0 0 0 0 -syscon termios PARENB 0x0100 0x1000 0x1000 0x1000 0x1000 0 # -syscon termios PARODD 0x0200 0x2000 0x2000 0x2000 0x2000 0 # syscon termios CIBAUD 0x100f0000 0 0 0 0 0 -syscon termios CLOCAL 0x0800 0x8000 0x8000 0x8000 0x8000 0 # syscon termios CMSPAR 0x40000000 0 0 0 0 0 syscon termios BUSY 4 0 0 0 0 0 syscon termios CANBSIZ 255 0 0 0 0 0 @@ -1488,9 +1512,6 @@ syscon termios TCOON 1 2 2 2 2 1 # see tcflow; bsd consensus syscon termios TCIOFF 2 3 3 3 3 2 # see tcflow; bsd consensus syscon termios TCION 3 4 4 4 4 3 # see tcflow; bsd consensus -syscon termios CREAD 0x80 0x0800 0x0800 0x0800 0x0800 0 # bsd consensus -syscon termios CSTOPB 0x40 0x0400 0x0400 0x0400 0x0400 0 # bsd consensus -syscon termios HUPCL 0x0400 0x4000 0x4000 0x4000 0x4000 0 # bsd consensus syscon termios CSTART 17 17 17 17 17 0 # unix consensus syscon termios CSTOP 19 19 19 19 19 0 # unix consensus diff --git a/libc/sysv/consts/CLOCAL.S b/libc/sysv/consts/CLOCAL.S index 73bdc0a62..d9bcd3c33 100644 --- a/libc/sysv/consts/CLOCAL.S +++ b/libc/sysv/consts/CLOCAL.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon termios,CLOCAL,0x0800,0x8000,0x8000,0x8000,0x8000,0 +.syscon termios,CLOCAL,0b0000100000000000,0b1000000000000000,0b1000000000000000,0b1000000000000000,0b1000000000000000,0b0000100000000000 diff --git a/libc/sysv/consts/CREAD.S b/libc/sysv/consts/CREAD.S index 1eac14cd8..e18ada92d 100644 --- a/libc/sysv/consts/CREAD.S +++ b/libc/sysv/consts/CREAD.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon termios,CREAD,0x80,0x0800,0x0800,0x0800,0x0800,0 +.syscon termios,CREAD,0b0000000010000000,0b000000100000000000,0b000000100000000000,0b0000100000000000,0b0000100000000000,0b0000000010000000 diff --git a/libc/sysv/consts/CS5.S b/libc/sysv/consts/CS5.S index 01a9b5895..ac9704d4c 100644 --- a/libc/sysv/consts/CS5.S +++ b/libc/sysv/consts/CS5.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon termios,CS5,0,0,0,0,0,0 +.syscon termios,CS5,0b0000000000000000,0b000000000000000000,0b000000000000000000,0b0000000000000000,0b0000000000000000,0b0000000000000000 diff --git a/libc/sysv/consts/CS6.S b/libc/sysv/consts/CS6.S index f5031010e..4b5329830 100644 --- a/libc/sysv/consts/CS6.S +++ b/libc/sysv/consts/CS6.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon termios,CS6,0b0000000000010000,0b0000000100000000,0b0000000100000000,0b0000000100000000,0b0000000100000000,0b0000000000010000 +.syscon termios,CS6,0b0000000000010000,0b000000000100000000,0b000000000100000000,0b0000000100000000,0b0000000100000000,0b0000000000010000 diff --git a/libc/sysv/consts/CS7.S b/libc/sysv/consts/CS7.S index d4fb534a7..923e5982c 100644 --- a/libc/sysv/consts/CS7.S +++ b/libc/sysv/consts/CS7.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon termios,CS7,0b0000000000100000,0b0000001000000000,0b0000001000000000,0b0000001000000000,0b0000001000000000,0b0000000000100000 +.syscon termios,CS7,0b0000000000100000,0b000000001000000000,0b000000001000000000,0b0000001000000000,0b0000001000000000,0b0000000000100000 diff --git a/libc/sysv/consts/CS8.S b/libc/sysv/consts/CS8.S index 08ad827a8..21784475b 100644 --- a/libc/sysv/consts/CS8.S +++ b/libc/sysv/consts/CS8.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon termios,CS8,0b0000000000110000,0b0000001100000000,0b0000001100000000,0b0000001100000000,0b0000001100000000,0b0000000000110000 +.syscon termios,CS8,0b0000000000110000,0b000000001100000000,0b000000001100000000,0b0000001100000000,0b0000001100000000,0b0000000000110000 diff --git a/libc/sysv/consts/CSIZE.S b/libc/sysv/consts/CSIZE.S index 90372e69c..59abbeb63 100644 --- a/libc/sysv/consts/CSIZE.S +++ b/libc/sysv/consts/CSIZE.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon termios,CSIZE,0b0000000000110000,0b0000001100000000,0b0000001100000000,0b0000001100000000,0b0000001100000000,0b0000000000110000 +.syscon termios,CSIZE,0b0000000000110000,0b000000001100000000,0b000000001100000000,0b0000001100000000,0b0000001100000000,0b0000000000110000 diff --git a/libc/sysv/consts/CSTOPB.S b/libc/sysv/consts/CSTOPB.S index 0e8700592..5374da0e4 100644 --- a/libc/sysv/consts/CSTOPB.S +++ b/libc/sysv/consts/CSTOPB.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon termios,CSTOPB,0x40,0x0400,0x0400,0x0400,0x0400,0 +.syscon termios,CSTOPB,0b0000000001000000,0b000000010000000000,0b000000010000000000,0b0000010000000000,0b0000010000000000,0b0000000001000000 diff --git a/libc/sysv/consts/HUPCL.S b/libc/sysv/consts/HUPCL.S index dd03f7ab0..6d27a2fb8 100644 --- a/libc/sysv/consts/HUPCL.S +++ b/libc/sysv/consts/HUPCL.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon termios,HUPCL,0x0400,0x4000,0x4000,0x4000,0x4000,0 +.syscon termios,HUPCL,0b0000010000000000,0b000100000000000000,0b000100000000000000,0b0100000000000000,0b0100000000000000,0b0000010000000000 diff --git a/libc/sysv/consts/PARENB.S b/libc/sysv/consts/PARENB.S index 2a40e7dc4..9900cf43b 100644 --- a/libc/sysv/consts/PARENB.S +++ b/libc/sysv/consts/PARENB.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon termios,PARENB,0x0100,0x1000,0x1000,0x1000,0x1000,0 +.syscon termios,PARENB,0b0000000100000000,0b000001000000000000,0b000001000000000000,0b0001000000000000,0b0001000000000000,0b0000000100000000 diff --git a/libc/sysv/consts/PARODD.S b/libc/sysv/consts/PARODD.S index 0ef8fff7a..1cfe04b15 100644 --- a/libc/sysv/consts/PARODD.S +++ b/libc/sysv/consts/PARODD.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon termios,PARODD,0x0200,0x2000,0x2000,0x2000,0x2000,0 +.syscon termios,PARODD,0b0000001000000000,0b000010000000000000,0b000010000000000000,0b0010000000000000,0b0010000000000000,0b0000001000000000 diff --git a/libc/sysv/consts/so.h b/libc/sysv/consts/so.h index e3ec79d74..3c61cf7a0 100644 --- a/libc/sysv/consts/so.h +++ b/libc/sysv/consts/so.h @@ -1,69 +1,6 @@ #ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS_SO_H_ #define COSMOPOLITAN_LIBC_SYSV_CONSTS_SO_H_ #include "libc/runtime/symbolic.h" - -#define LOCAL_PEERCRED SYMBOLIC(LOCAL_PEERCRED) -#define SO_ACCEPTCONN SYMBOLIC(SO_ACCEPTCONN) -#define SO_ATTACH_BPF SYMBOLIC(SO_ATTACH_BPF) -#define SO_ATTACH_FILTER SYMBOLIC(SO_ATTACH_FILTER) -#define SO_ATTACH_REUSEPORT_CBPF SYMBOLIC(SO_ATTACH_REUSEPORT_CBPF) -#define SO_ATTACH_REUSEPORT_EBPF SYMBOLIC(SO_ATTACH_REUSEPORT_EBPF) -#define SO_BINDTODEVICE SYMBOLIC(SO_BINDTODEVICE) -#define SO_BPF_EXTENSIONS SYMBOLIC(SO_BPF_EXTENSIONS) -#define SO_BROADCAST SYMBOLIC(SO_BROADCAST) -#define SO_BSDCOMPAT SYMBOLIC(SO_BSDCOMPAT) -#define SO_BUSY_POLL SYMBOLIC(SO_BUSY_POLL) -#define SO_CNX_ADVICE SYMBOLIC(SO_CNX_ADVICE) -#define SO_DEBUG SYMBOLIC(SO_DEBUG) -#define SO_DETACH_BPF SYMBOLIC(SO_DETACH_BPF) -#define SO_DETACH_FILTER SYMBOLIC(SO_DETACH_FILTER) -#define SO_DOMAIN SYMBOLIC(SO_DOMAIN) -#define SO_DONTROUTE SYMBOLIC(SO_DONTROUTE) -#define SO_ERROR SYMBOLIC(SO_ERROR) -#define SO_EXCLUSIVEADDRUSE SYMBOLIC(SO_EXCLUSIVEADDRUSE) -#define SO_GET_FILTER SYMBOLIC(SO_GET_FILTER) -#define SO_INCOMING_CPU SYMBOLIC(SO_INCOMING_CPU) -#define SO_KEEPALIVE SYMBOLIC(SO_KEEPALIVE) -#define SO_LINGER SYMBOLIC(SO_LINGER) -#define SO_LOCK_FILTER SYMBOLIC(SO_LOCK_FILTER) -#define SO_MARK SYMBOLIC(SO_MARK) -#define SO_MAX_PACING_RATE SYMBOLIC(SO_MAX_PACING_RATE) -#define SO_NOFCS SYMBOLIC(SO_NOFCS) -#define SO_NO_CHECK SYMBOLIC(SO_NO_CHECK) -#define SO_OOBINLINE SYMBOLIC(SO_OOBINLINE) -#define SO_PASSCRED SYMBOLIC(SO_PASSCRED) -#define SO_PASSSEC SYMBOLIC(SO_PASSSEC) -#define SO_PEEK_OFF SYMBOLIC(SO_PEEK_OFF) -#define SO_PEERCRED SYMBOLIC(SO_PEERCRED) -#define SO_PEERNAME SYMBOLIC(SO_PEERNAME) -#define SO_PEERSEC SYMBOLIC(SO_PEERSEC) -#define SO_PRIORITY SYMBOLIC(SO_PRIORITY) -#define SO_PROTOCOL SYMBOLIC(SO_PROTOCOL) -#define SO_RCVBUF SYMBOLIC(SO_RCVBUF) -#define SO_RCVBUFFORCE SYMBOLIC(SO_RCVBUFFORCE) -#define SO_RCVLOWAT SYMBOLIC(SO_RCVLOWAT) -#define SO_RCVTIMEO SYMBOLIC(SO_RCVTIMEO) -#define SO_REUSEADDR SYMBOLIC(SO_REUSEADDR) -#define SO_REUSEPORT SYMBOLIC(SO_REUSEPORT) -#define SO_RXQ_OVFL SYMBOLIC(SO_RXQ_OVFL) -#define SO_SELECT_ERR_QUEUE SYMBOLIC(SO_SELECT_ERR_QUEUE) -#define SO_SETFIB SYMBOLIC(SO_SETFIB) -#define SO_SNDBUF SYMBOLIC(SO_SNDBUF) -#define SO_SNDBUFFORCE SYMBOLIC(SO_SNDBUFFORCE) -#define SO_SNDLOWAT SYMBOLIC(SO_SNDLOWAT) -#define SO_SNDTIMEO SYMBOLIC(SO_SNDTIMEO) -#define SO_TIMESTAMP SYMBOLIC(SO_TIMESTAMP) -#define SO_TIMESTAMPING SYMBOLIC(SO_TIMESTAMPING) -#define SO_TIMESTAMPNS SYMBOLIC(SO_TIMESTAMPNS) -#define SO_TYPE SYMBOLIC(SO_TYPE) -#define SO_USELOOPBACK SYMBOLIC(SO_USELOOPBACK) -#define SO_WIFI_STATUS SYMBOLIC(SO_WIFI_STATUS) - -#define SO_SECURITY_AUTHENTICATION SYMBOLIC(SO_SECURITY_AUTHENTICATION) -#define SO_SECURITY_ENCRYPTION_NETWORK SYMBOLIC(SO_SECURITY_ENCRYPTION_NETWORK) -#define SO_SECURITY_ENCRYPTION_TRANSPORT \ - SYMBOLIC(SO_SECURITY_ENCRYPTION_TRANSPORT) - #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ @@ -129,4 +66,68 @@ extern const long SO_WIFI_STATUS; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ + +#define SO_DEBUG LITERALLY(1) + +#define LOCAL_PEERCRED SYMBOLIC(LOCAL_PEERCRED) +#define SO_ACCEPTCONN SYMBOLIC(SO_ACCEPTCONN) +#define SO_ATTACH_BPF SYMBOLIC(SO_ATTACH_BPF) +#define SO_ATTACH_FILTER SYMBOLIC(SO_ATTACH_FILTER) +#define SO_ATTACH_REUSEPORT_CBPF SYMBOLIC(SO_ATTACH_REUSEPORT_CBPF) +#define SO_ATTACH_REUSEPORT_EBPF SYMBOLIC(SO_ATTACH_REUSEPORT_EBPF) +#define SO_BINDTODEVICE SYMBOLIC(SO_BINDTODEVICE) +#define SO_BPF_EXTENSIONS SYMBOLIC(SO_BPF_EXTENSIONS) +#define SO_BROADCAST SYMBOLIC(SO_BROADCAST) +#define SO_BSDCOMPAT SYMBOLIC(SO_BSDCOMPAT) +#define SO_BUSY_POLL SYMBOLIC(SO_BUSY_POLL) +#define SO_CNX_ADVICE SYMBOLIC(SO_CNX_ADVICE) +#define SO_DETACH_BPF SYMBOLIC(SO_DETACH_BPF) +#define SO_DETACH_FILTER SYMBOLIC(SO_DETACH_FILTER) +#define SO_DOMAIN SYMBOLIC(SO_DOMAIN) +#define SO_DONTROUTE SYMBOLIC(SO_DONTROUTE) +#define SO_ERROR SYMBOLIC(SO_ERROR) +#define SO_EXCLUSIVEADDRUSE SYMBOLIC(SO_EXCLUSIVEADDRUSE) +#define SO_GET_FILTER SYMBOLIC(SO_GET_FILTER) +#define SO_INCOMING_CPU SYMBOLIC(SO_INCOMING_CPU) +#define SO_KEEPALIVE SYMBOLIC(SO_KEEPALIVE) +#define SO_LINGER SYMBOLIC(SO_LINGER) +#define SO_LOCK_FILTER SYMBOLIC(SO_LOCK_FILTER) +#define SO_MARK SYMBOLIC(SO_MARK) +#define SO_MAX_PACING_RATE SYMBOLIC(SO_MAX_PACING_RATE) +#define SO_NOFCS SYMBOLIC(SO_NOFCS) +#define SO_NO_CHECK SYMBOLIC(SO_NO_CHECK) +#define SO_OOBINLINE SYMBOLIC(SO_OOBINLINE) +#define SO_PASSCRED SYMBOLIC(SO_PASSCRED) +#define SO_PASSSEC SYMBOLIC(SO_PASSSEC) +#define SO_PEEK_OFF SYMBOLIC(SO_PEEK_OFF) +#define SO_PEERCRED SYMBOLIC(SO_PEERCRED) +#define SO_PEERNAME SYMBOLIC(SO_PEERNAME) +#define SO_PEERSEC SYMBOLIC(SO_PEERSEC) +#define SO_PRIORITY SYMBOLIC(SO_PRIORITY) +#define SO_PROTOCOL SYMBOLIC(SO_PROTOCOL) +#define SO_RCVBUF SYMBOLIC(SO_RCVBUF) +#define SO_RCVBUFFORCE SYMBOLIC(SO_RCVBUFFORCE) +#define SO_RCVLOWAT SYMBOLIC(SO_RCVLOWAT) +#define SO_RCVTIMEO SYMBOLIC(SO_RCVTIMEO) +#define SO_REUSEADDR SYMBOLIC(SO_REUSEADDR) +#define SO_REUSEPORT SYMBOLIC(SO_REUSEPORT) +#define SO_RXQ_OVFL SYMBOLIC(SO_RXQ_OVFL) +#define SO_SELECT_ERR_QUEUE SYMBOLIC(SO_SELECT_ERR_QUEUE) +#define SO_SETFIB SYMBOLIC(SO_SETFIB) +#define SO_SNDBUF SYMBOLIC(SO_SNDBUF) +#define SO_SNDBUFFORCE SYMBOLIC(SO_SNDBUFFORCE) +#define SO_SNDLOWAT SYMBOLIC(SO_SNDLOWAT) +#define SO_SNDTIMEO SYMBOLIC(SO_SNDTIMEO) +#define SO_TIMESTAMP SYMBOLIC(SO_TIMESTAMP) +#define SO_TIMESTAMPING SYMBOLIC(SO_TIMESTAMPING) +#define SO_TIMESTAMPNS SYMBOLIC(SO_TIMESTAMPNS) +#define SO_TYPE SYMBOLIC(SO_TYPE) +#define SO_USELOOPBACK SYMBOLIC(SO_USELOOPBACK) +#define SO_WIFI_STATUS SYMBOLIC(SO_WIFI_STATUS) + +#define SO_SECURITY_AUTHENTICATION SYMBOLIC(SO_SECURITY_AUTHENTICATION) +#define SO_SECURITY_ENCRYPTION_NETWORK SYMBOLIC(SO_SECURITY_ENCRYPTION_NETWORK) +#define SO_SECURITY_ENCRYPTION_TRANSPORT \ + SYMBOLIC(SO_SECURITY_ENCRYPTION_TRANSPORT) + #endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_SO_H_ */ diff --git a/libc/sysv/consts/sol.h b/libc/sysv/consts/sol.h index a24cb776b..f3a1d6f06 100644 --- a/libc/sysv/consts/sol.h +++ b/libc/sysv/consts/sol.h @@ -1,36 +1,6 @@ #ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS_SOL_H_ #define COSMOPOLITAN_LIBC_SYSV_CONSTS_SOL_H_ #include "libc/runtime/symbolic.h" - -#define SOL_AAL SYMBOLIC(SOL_AAL) -#define SOL_ALG SYMBOLIC(SOL_ALG) -#define SOL_ATM SYMBOLIC(SOL_ATM) -#define SOL_BLUETOOTH SYMBOLIC(SOL_BLUETOOTH) -#define SOL_CAIF SYMBOLIC(SOL_CAIF) -#define SOL_DCCP SYMBOLIC(SOL_DCCP) -#define SOL_DECNET SYMBOLIC(SOL_DECNET) -#define SOL_ICMPV6 SYMBOLIC(SOL_ICMPV6) -#define SOL_IP SYMBOLIC(SOL_IP) -#define SOL_IPV6 SYMBOLIC(SOL_IPV6) -#define SOL_IRDA SYMBOLIC(SOL_IRDA) -#define SOL_IUCV SYMBOLIC(SOL_IUCV) -#define SOL_KCM SYMBOLIC(SOL_KCM) -#define SOL_LLC SYMBOLIC(SOL_LLC) -#define SOL_NETBEUI SYMBOLIC(SOL_NETBEUI) -#define SOL_NETLINK SYMBOLIC(SOL_NETLINK) -#define SOL_NFC SYMBOLIC(SOL_NFC) -#define SOL_PACKET SYMBOLIC(SOL_PACKET) -#define SOL_PNPIPE SYMBOLIC(SOL_PNPIPE) -#define SOL_PPPOL2TP SYMBOLIC(SOL_PPPOL2TP) -#define SOL_RAW SYMBOLIC(SOL_RAW) -#define SOL_RDS SYMBOLIC(SOL_RDS) -#define SOL_RXRPC SYMBOLIC(SOL_RXRPC) -#define SOL_SOCKET SYMBOLIC(SOL_SOCKET) -#define SOL_TCP SYMBOLIC(SOL_TCP) -#define SOL_TIPC SYMBOLIC(SOL_TIPC) -#define SOL_UDP SYMBOLIC(SOL_UDP) -#define SOL_X25 SYMBOLIC(SOL_X25) - #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ @@ -65,4 +35,35 @@ extern const long SOL_X25; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ + +#define SOL_IP LITERALLY(0) +#define SOL_TCP LITERALLY(6) +#define SOL_UDP LITERALLY(17) + +#define SOL_AAL SYMBOLIC(SOL_AAL) +#define SOL_ALG SYMBOLIC(SOL_ALG) +#define SOL_ATM SYMBOLIC(SOL_ATM) +#define SOL_BLUETOOTH SYMBOLIC(SOL_BLUETOOTH) +#define SOL_CAIF SYMBOLIC(SOL_CAIF) +#define SOL_DCCP SYMBOLIC(SOL_DCCP) +#define SOL_DECNET SYMBOLIC(SOL_DECNET) +#define SOL_ICMPV6 SYMBOLIC(SOL_ICMPV6) +#define SOL_IPV6 SYMBOLIC(SOL_IPV6) +#define SOL_IRDA SYMBOLIC(SOL_IRDA) +#define SOL_IUCV SYMBOLIC(SOL_IUCV) +#define SOL_KCM SYMBOLIC(SOL_KCM) +#define SOL_LLC SYMBOLIC(SOL_LLC) +#define SOL_NETBEUI SYMBOLIC(SOL_NETBEUI) +#define SOL_NETLINK SYMBOLIC(SOL_NETLINK) +#define SOL_NFC SYMBOLIC(SOL_NFC) +#define SOL_PACKET SYMBOLIC(SOL_PACKET) +#define SOL_PNPIPE SYMBOLIC(SOL_PNPIPE) +#define SOL_PPPOL2TP SYMBOLIC(SOL_PPPOL2TP) +#define SOL_RAW SYMBOLIC(SOL_RAW) +#define SOL_RDS SYMBOLIC(SOL_RDS) +#define SOL_RXRPC SYMBOLIC(SOL_RXRPC) +#define SOL_SOCKET SYMBOLIC(SOL_SOCKET) +#define SOL_TIPC SYMBOLIC(SOL_TIPC) +#define SOL_X25 SYMBOLIC(SOL_X25) + #endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_SOL_H_ */ diff --git a/libc/sysv/syscalls.sh b/libc/sysv/syscalls.sh index 20d6d2da4..92e5bc1a1 100755 --- a/libc/sysv/syscalls.sh +++ b/libc/sysv/syscalls.sh @@ -154,8 +154,8 @@ scall sys_geteuid 0x019019019201906b globl hidden scall sys_getegid 0x02b02b02b202b06c globl hidden scall getgroups 0x04f04f04f204f073 globl scall setgroups 0x0500500502050074 globl -scall setreuid 0x07e07e07e207e071 globl -scall setregid 0x07f07f07f207f072 globl +scall sys_setreuid 0x07e07e07e207e071 globl hidden +scall sys_setregid 0x07f07f07f207f072 globl hidden scall sys_setuid 0x0170170172017069 globl hidden scall sys_setgid 0x0b50b50b520b506a globl hidden scall sys_setresuid 0xfff11a137ffff075 globl hidden # polyfilled for xnu diff --git a/libc/sysv/systemfive.S b/libc/sysv/systemfive.S index 9540717f0..fd37a06f4 100644 --- a/libc/sysv/systemfive.S +++ b/libc/sysv/systemfive.S @@ -553,7 +553,7 @@ syscon_windows:/* .rodata.str1.1 .Llog: .ascii STRACE_PROLOGUE .ascii "bell system five system call support" - .asciz " %'u magnums loaded on %s%n" + .asciz " %'u magnums loaded on %s\n" .previous #endif /* DEBUGSYS */ diff --git a/libc/testlib/testlib.h b/libc/testlib/testlib.h index a1e266856..1ad271559 100644 --- a/libc/testlib/testlib.h +++ b/libc/testlib/testlib.h @@ -114,11 +114,12 @@ void TearDownOnce(void); #define ASSERT_SYS(ERRNO, WANT, GOT, ...) \ do { \ - errno = 0; \ + int e = errno; \ __TEST_EQ(assert, __FILE__, __LINE__, __FUNCTION__, #WANT, #GOT, WANT, \ GOT, __VA_ARGS__); \ __TEST_EQ(assert, __FILE__, __LINE__, __FUNCTION__, #ERRNO, \ strerror(errno), ERRNO, errno, __VA_ARGS__); \ + errno = e; \ } while (0) #define ASSERT_BETWEEN(BEG, END, GOT) \ @@ -314,8 +315,8 @@ struct TestFixture { }; extern char g_fixturename[256]; -extern char g_testlib_olddir[PATH_MAX]; -extern char g_testlib_tmpdir[PATH_MAX]; +extern char g_testlib_olddir[PATH_MAX + 1]; +extern char g_testlib_tmpdir[PATH_MAX + 1]; extern bool g_testlib_shoulddebugbreak; /* set by testmain */ extern unsigned g_testlib_ran; /* set by wrappers */ extern unsigned g_testlib_failed; /* set by wrappers */ diff --git a/libc/testlib/testrunner.c b/libc/testlib/testrunner.c index d1ce3f6fb..0b076645c 100644 --- a/libc/testlib/testrunner.c +++ b/libc/testlib/testrunner.c @@ -46,9 +46,9 @@ #include "libc/x/x.h" static int x; -char g_testlib_olddir[PATH_MAX]; -char g_testlib_tmpdir[PATH_MAX]; struct sigaction wanthandlers[31]; +char g_testlib_olddir[PATH_MAX + 1]; +char g_testlib_tmpdir[PATH_MAX + 1]; void testlib_finish(void) { if (g_testlib_failed) { @@ -142,7 +142,7 @@ static void CheckForFileDescriptors(void) { for (i = 0; i < ARRAYLEN(pfds); ++i) { if (pfds[i].revents & POLLNVAL) continue; ++g_testlib_failed; - fprintf(stderr, "error: test failed to close() fd %d%n", pfds[i].fd); + fprintf(stderr, "error: test failed to close() fd %d\n", pfds[i].fd); } } #endif @@ -161,7 +161,7 @@ static void CheckForZombies(void) { break; } else { ++g_testlib_failed; - fprintf(stderr, "error: test failed to reap zombies %d%n", pid); + fprintf(stderr, "error: test failed to reap zombies %d\n", pid); } } #endif diff --git a/libc/time/localtime.c b/libc/time/localtime.c index 74bad0461..a79d0330c 100644 --- a/libc/time/localtime.c +++ b/libc/time/localtime.c @@ -399,7 +399,7 @@ tzload( 2 * sizeof *sp + 4 * TZ_MAX_TIMES]; } * up; - char fullname[PATH_MAX]; + char fullname[PATH_MAX+1]; up = calloc(1, sizeof *up); if (up == NULL) diff --git a/libc/zip.h b/libc/zip.h index 038cac34c..35477adf5 100644 --- a/libc/zip.h +++ b/libc/zip.h @@ -44,11 +44,10 @@ #define kZipCompressionNone 0 #define kZipCompressionDeflate 8 -#define kZipCdirHdrMagic 0x06054b50 /* PK♣♠ "PK\5\6" */ -#define kZipCdirHdrMinSize 22 -#define kZipCdirAlign kZipAlign -#define kZipCdirHdrLinkableSize \ - ROUNDUP(kZipCfileHdrMinSize + PATH_MAX, kZipCdirAlign) +#define kZipCdirHdrMagic 0x06054b50 /* PK♣♠ "PK\5\6" */ +#define kZipCdirHdrMinSize 22 +#define kZipCdirAlign kZipAlign +#define kZipCdirHdrLinkableSize 294 #define kZipCdir64HdrMagic 0x06064b50 /* PK♣♠ "PK\6\6" */ #define kZipCdir64HdrMinSize 56 diff --git a/net/http/decodelatin1.c b/net/http/decodelatin1.c index a52ce8b95..04ec84bd7 100644 --- a/net/http/decodelatin1.c +++ b/net/http/decodelatin1.c @@ -39,7 +39,7 @@ char *DecodeLatin1(const char *p, size_t n, size_t *z) { if (n == -1) n = p ? strlen(p) : 0; if ((q = r = malloc(n * 2 + 1))) { for (i = 0; i < n;) { - memset(vz, 0, 16); /* 3x speedup for ASCII */ + bzero(vz, 16); /* 3x speedup for ASCII */ while (i + 16 < n) { memcpy(v1, p + i, 16); pcmpgtb(v2, v1, vz); diff --git a/net/http/encodelatin1.c b/net/http/encodelatin1.c index 924188c4e..abb15c433 100644 --- a/net/http/encodelatin1.c +++ b/net/http/encodelatin1.c @@ -40,7 +40,7 @@ char *EncodeLatin1(const char *p, size_t n, size_t *z, int f) { size_t i; char t[256]; char *r, *q; - memset(t, 0, sizeof(t)); + bzero(t, sizeof(t)); if (f & kControlC0) memset(t + 0x00, 1, 0x20 - 0x00), t[0x7F] = 1; if (f & kControlC1) memset(t + 0x80, 1, 0xA0 - 0x80); t['\t'] = t['\r'] = t['\n'] = t['\v'] = !!(f & kControlWs); diff --git a/net/http/foldheader.c b/net/http/foldheader.c index f840527dc..ac930abf9 100644 --- a/net/http/foldheader.c +++ b/net/http/foldheader.c @@ -21,6 +21,9 @@ #include "libc/str/str.h" #include "net/http/http.h" +/** + * Collapses repeating headers onto a single line. + */ char *FoldHeader(struct HttpMessage *msg, char *b, int h, size_t *z) { char *p; size_t i, n, m; diff --git a/net/http/hascontrolcodes.c b/net/http/hascontrolcodes.c index c7fba8f22..dd6c4e30d 100644 --- a/net/http/hascontrolcodes.c +++ b/net/http/hascontrolcodes.c @@ -34,7 +34,7 @@ ssize_t HasControlCodes(const char *p, size_t n, int f) { char t[256]; wint_t x, a, b; size_t i, j, m, g; - memset(t, 0, sizeof(t)); + bzero(t, sizeof(t)); if (f & kControlC0) memset(t + 0x00, 1, 0x20 - 0x00), t[0x7F] = 1; if (f & kControlC1) memset(t + 0x80, 1, 0xA0 - 0x80); t['\t'] = t['\r'] = t['\n'] = t['\v'] = !!(f & kControlWs); diff --git a/net/http/parsehttpmessage.c b/net/http/parsehttpmessage.c index f9babfebb..77afdf730 100644 --- a/net/http/parsehttpmessage.c +++ b/net/http/parsehttpmessage.c @@ -36,7 +36,7 @@ */ void InitHttpMessage(struct HttpMessage *r, int type) { assert(type == kHttpRequest || type == kHttpResponse); - memset(r, 0, sizeof(*r)); + bzero(r, sizeof(*r)); r->type = type; } diff --git a/net/http/parseurl.c b/net/http/parseurl.c index cf5a8c0a6..8bdc6ac1b 100644 --- a/net/http/parseurl.c +++ b/net/http/parseurl.c @@ -257,7 +257,7 @@ static char *ParseUrlImpl(const char *s, size_t n, struct Url *h, bool latin1) { u.isform = false; u.isopaque = false; u.islatin1 = latin1; - memset(h, 0, sizeof(*h)); + bzero(h, sizeof(*h)); if ((m = malloc(latin1 ? u.n * 2 : u.n))) { u.q = u.p = m; if (ParseScheme(&u, h)) ParseAuthority(&u, h); diff --git a/net/http/underlong.c b/net/http/underlong.c index 0d924dbfb..e6e1c959b 100644 --- a/net/http/underlong.c +++ b/net/http/underlong.c @@ -46,7 +46,7 @@ char *Underlong(const char *p, size_t n, size_t *z) { if (n == -1) n = p ? strlen(p) : 0; if ((q = r = malloc(n + 1))) { for (i = 0; i < n;) { - memset(vz, 0, 16); /* 50x speedup for ASCII */ + bzero(vz, 16); /* 50x speedup for ASCII */ while (i + 16 < n) { memcpy(v1, p + i, 16); pcmpgtb(v2, v1, vz); diff --git a/test/libc/calls/commandv_test.c b/test/libc/calls/commandv_test.c index e4f6da7a7..e30ae0cda 100644 --- a/test/libc/calls/commandv_test.c +++ b/test/libc/calls/commandv_test.c @@ -35,8 +35,8 @@ uint64_t i; char *oldpath; -char tmp[PATH_MAX]; -char pathbuf[PATH_MAX]; +char tmp[PATH_MAX + 1]; +char pathbuf[PATH_MAX + 1]; char testlib_enable_tmp_setup_teardown; void SetUp(void) { @@ -54,34 +54,34 @@ void TearDown(void) { TEST(commandv, testPathSearch) { EXPECT_NE(-1, touch("bin/sh", 0755)); - EXPECT_STREQ("bin/sh", commandv("sh", pathbuf)); + EXPECT_STREQ("bin/sh", commandv("sh", pathbuf, sizeof(pathbuf))); } TEST(commandv, testPathSearch_appendsComExtension) { EXPECT_NE(-1, touch("bin/sh.com", 0755)); - EXPECT_STREQ("bin/sh.com", commandv("sh", pathbuf)); + EXPECT_STREQ("bin/sh.com", commandv("sh", pathbuf, sizeof(pathbuf))); } TEST(commandv, testSlashes_wontSearchPath_butChecksAccess) { EXPECT_NE(-1, touch("home/sh", 0755)); i = g_syscount; - EXPECT_STREQ("home/sh", commandv("home/sh", pathbuf)); + EXPECT_STREQ("home/sh", commandv("home/sh", pathbuf, sizeof(pathbuf))); if (!IsWindows()) EXPECT_EQ(i + 1, g_syscount); } TEST(commandv, testSlashes_wontSearchPath_butStillAppendsComExtension) { EXPECT_NE(-1, touch("home/sh.com", 0755)); i = g_syscount; - EXPECT_STREQ("home/sh.com", commandv("home/sh", pathbuf)); + EXPECT_STREQ("home/sh.com", commandv("home/sh", pathbuf, sizeof(pathbuf))); if (!IsWindows()) EXPECT_EQ(i + 2, g_syscount); } TEST(commandv, testSameDir_doesntHappenByDefaultUnlessItsWindows) { EXPECT_NE(-1, touch("bog", 0755)); if (IsWindows()) { - EXPECT_STREQ("./bog", commandv("bog", pathbuf)); + EXPECT_STREQ("./bog", commandv("bog", pathbuf, sizeof(pathbuf))); } else { - EXPECT_EQ(NULL, commandv("bog", pathbuf)); + EXPECT_EQ(NULL, commandv("bog", pathbuf, sizeof(pathbuf))); } } @@ -89,9 +89,9 @@ TEST(commandv, testSameDir_willHappenWithColonBlank) { CHECK_NE(-1, setenv("PATH", "bin:", true)); EXPECT_NE(-1, touch("bog", 0755)); if (IsWindows()) { - EXPECT_STREQ("./bog", commandv("bog", pathbuf)); + EXPECT_STREQ("./bog", commandv("bog", pathbuf, sizeof(pathbuf))); } else { - EXPECT_STREQ("bog", commandv("bog", pathbuf)); + EXPECT_STREQ("bog", commandv("bog", pathbuf, sizeof(pathbuf))); } } @@ -99,8 +99,8 @@ TEST(commandv, testSameDir_willHappenWithColonBlank2) { CHECK_NE(-1, setenv("PATH", ":bin", true)); EXPECT_NE(-1, touch("bog", 0755)); if (IsWindows()) { - EXPECT_STREQ("./bog", commandv("bog", pathbuf)); + EXPECT_STREQ("./bog", commandv("bog", pathbuf, sizeof(pathbuf))); } else { - EXPECT_STREQ("bog", commandv("bog", pathbuf)); + EXPECT_STREQ("bog", commandv("bog", pathbuf, sizeof(pathbuf))); } } diff --git a/test/libc/calls/execve_test.c b/test/libc/calls/execve_test.c index ce11cd967..88cbd140f 100644 --- a/test/libc/calls/execve_test.c +++ b/test/libc/calls/execve_test.c @@ -29,6 +29,13 @@ void SetUp(void) { } else { exit(7); } + } else if (getenv("_WEIRDENV")) { + for (char **e = environ; *e; ++e) { + if (!strcmp(*e, "WEIRD")) { + exit(0); + } + } + exit(7); } } @@ -47,3 +54,20 @@ TEST(execve, testWeirdAnsiC89emptyArgv) { EXPECT_TRUE(WIFEXITED(ws)); EXPECT_EQ(0, WEXITSTATUS(ws)); } + +TEST(execve, testWeirdEnvironmentVariable) { + char *prog; + int pid, ws; + if (IsWindows()) return; + if (IsOpenbsd()) return; + prog = GetProgramExecutableName(); + ASSERT_NE(-1, (pid = fork())); + if (!pid) { + execve(prog, (char *const[]){prog, 0}, + (char *const[]){"_WEIRDENV=1", "WEIRD", 0}); + _Exit(127); + } + ASSERT_NE(-1, wait(&ws)); + EXPECT_TRUE(WIFEXITED(ws)); + EXPECT_EQ(0, WEXITSTATUS(ws)); +} diff --git a/test/libc/calls/getcwd_test.c b/test/libc/calls/getcwd_test.c index e6adae3bd..111d6d03f 100644 --- a/test/libc/calls/getcwd_test.c +++ b/test/libc/calls/getcwd_test.c @@ -40,3 +40,10 @@ TEST(getcwd, testNullBuf_allocatesResult) { EXPECT_NE(-1, chdir("subdir")); EXPECT_STREQ("subdir", basename(gc(getcwd(0, 0)))); } + +TEST(getcwd, testWindows_addsFunnyPrefix) { + if (!IsWindows()) return; + char path[PATH_MAX + 1]; + ASSERT_NE(0, getcwd(path, sizeof(path))); + EXPECT_STARTSWITH("//?/", path); +} diff --git a/test/libc/calls/mkdir_test.c b/test/libc/calls/mkdir_test.c index 8da031afd..e0037d558 100644 --- a/test/libc/calls/mkdir_test.c +++ b/test/libc/calls/mkdir_test.c @@ -17,9 +17,11 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/dce.h" #include "libc/errno.h" #include "libc/fmt/fmt.h" #include "libc/log/check.h" +#include "libc/mem/mem.h" #include "libc/runtime/runtime.h" #include "libc/sysv/consts/o.h" #include "libc/testlib/testlib.h" @@ -58,6 +60,15 @@ TEST(mkdir, testPathIsDirectory_EEXIST) { EXPECT_EQ(EEXIST, errno); } +TEST(mkdir, enametoolong) { + int i; + size_t n = 2048; + char *d, *s = gc(calloc(1, n)); + for (i = 0; i < n - 1; ++i) s[i] = 'x'; + s[i] = 0; + EXPECT_SYS(ENAMETOOLONG, -1, mkdir(s, 0644)); +} + TEST(makedirs, testEmptyString_EEXIST) { EXPECT_EQ(-1, mkdir("", 0755)); EXPECT_EQ(ENOENT, errno); @@ -72,3 +83,14 @@ TEST(mkdirat, testRelativePath_opensRelativeToDirFd) { EXPECT_EQ(-1, makedirs("", 0755)); EXPECT_NE(-1, close(dirfd)); } + +TEST(mkdir, longname) { + int i; + char *d, s[270] = {0}; + for (i = 0; i < sizeof(s) - 1; ++i) s[i] = 'x'; + s[i] = 0; + ASSERT_NE(NULL, (d = gc(getcwd(0, 0)))); + memcpy(s, d, strlen(d)); + s[strlen(d)] = '/'; + ASSERT_SYS(0, 0, mkdir(s, 0644)); +} diff --git a/test/libc/calls/read_test.c b/test/libc/calls/read_test.c index c2f156805..20db7ebb4 100644 --- a/test/libc/calls/read_test.c +++ b/test/libc/calls/read_test.c @@ -38,7 +38,13 @@ BENCH(read, bench) { char buf[16]; ASSERT_SYS(0, 3, open("/dev/zero", O_RDONLY)); EZBENCH2("read", donothing, read(3, buf, 5)); - EZBENCH2("readv", donothing, readv(3, &(struct iovec){buf, 5}, 1)); + EZBENCH2("pread", donothing, pread(3, buf, 5, 0)); + EZBENCH2("readv₁", donothing, readv(3, &(struct iovec){buf, 5}, 1)); + EZBENCH2("readv₂", donothing, + readv(3, (struct iovec[]){{buf, 1}, {buf + 1, 4}}, 2)); + EZBENCH2("preadv₁", donothing, preadv(3, &(struct iovec){buf, 5}, 1, 0)); + EZBENCH2("preadv₂", donothing, + preadv(3, (struct iovec[]){{buf, 1}, {buf + 1, 4}}, 2, 0)); EZBENCH2("sys_read", donothing, sys_read(3, buf, 5)); EZBENCH2("sys_readv", donothing, sys_readv(3, &(struct iovec){buf, 5}, 1)); EZBENCH2("Read", donothing, Read(3, buf, 5)); diff --git a/test/libc/calls/readlinkat_test.c b/test/libc/calls/readlinkat_test.c index add8e75aa..b178c07ee 100644 --- a/test/libc/calls/readlinkat_test.c +++ b/test/libc/calls/readlinkat_test.c @@ -83,10 +83,8 @@ TEST(readlinkat, frootloop) { ASSERT_SYS(0, 0, symlink("froot", "froot")); ASSERT_SYS(ELOOP, -1, readlink("froot/loop", buf, sizeof(buf))); if (O_NOFOLLOW) { - ASSERT_SYS(IsFreebsd() ? EMLINK - : IsNetbsd() ? EFTYPE - : ELOOP, - -1, open("froot", O_RDONLY | O_NOFOLLOW)); + ASSERT_SYS(IsFreebsd() ? EMLINK : IsNetbsd() ? EFTYPE : ELOOP, -1, + open("froot", O_RDONLY | O_NOFOLLOW)); if (0 && O_PATH) { /* need rhel5 test */ ASSERT_NE(-1, (fd = open("froot", O_RDONLY | O_NOFOLLOW | O_PATH))); ASSERT_NE(-1, close(fd)); @@ -101,3 +99,11 @@ TEST(readlinkat, statReadsNameLength) { EXPECT_TRUE(S_ISLNK(st.st_mode)); EXPECT_EQ(5, st.st_size); } + +TEST(readlinkat, realpathReturnsLongPath) { + if (!IsWindows()) return; + struct stat st; + char buf[PATH_MAX]; + ASSERT_SYS(0, 0, touch("froot", 0644)); + ASSERT_STARTSWITH("//?/", realpath("froot", buf)); +} diff --git a/test/libc/calls/seccomp_test.c b/test/libc/calls/seccomp_test.c index 7ea7d6329..6b576b9f0 100644 --- a/test/libc/calls/seccomp_test.c +++ b/test/libc/calls/seccomp_test.c @@ -22,7 +22,6 @@ #include "libc/calls/struct/iovec.h" #include "libc/calls/struct/seccomp.h" #include "libc/errno.h" -#include "libc/intrin/kprintf.h" #include "libc/runtime/runtime.h" #include "libc/sock/sock.h" #include "libc/sysv/consts/audit.h" diff --git a/test/libc/calls/sigaction_test.c b/test/libc/calls/sigaction_test.c index efbc18aab..fe4983c30 100644 --- a/test/libc/calls/sigaction_test.c +++ b/test/libc/calls/sigaction_test.c @@ -23,7 +23,6 @@ #include "libc/calls/ucontext.h" #include "libc/dce.h" #include "libc/errno.h" -#include "libc/intrin/kprintf.h" #include "libc/runtime/runtime.h" #include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/sig.h" diff --git a/test/libc/calls/sigprocmask_test.c b/test/libc/calls/sigprocmask_test.c index 5ccc94071..51836565e 100644 --- a/test/libc/calls/sigprocmask_test.c +++ b/test/libc/calls/sigprocmask_test.c @@ -23,7 +23,6 @@ #include "libc/calls/struct/sigset.h" #include "libc/calls/ucontext.h" #include "libc/dce.h" -#include "libc/intrin/kprintf.h" #include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/sig.h" #include "libc/testlib/testlib.h" diff --git a/test/libc/calls/sigsuspend_test.c b/test/libc/calls/sigsuspend_test.c index 7cd46385e..0772c8ef9 100644 --- a/test/libc/calls/sigsuspend_test.c +++ b/test/libc/calls/sigsuspend_test.c @@ -21,7 +21,6 @@ #include "libc/calls/struct/sigset.h" #include "libc/dce.h" #include "libc/errno.h" -#include "libc/intrin/kprintf.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" #include "libc/sysv/consts/sa.h" diff --git a/test/libc/fmt/atoi_test.c b/test/libc/fmt/atoi_test.c index a36ff54e3..1b0fc92c3 100644 --- a/test/libc/fmt/atoi_test.c +++ b/test/libc/fmt/atoi_test.c @@ -23,6 +23,13 @@ #include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" +void __on_arithmetic_overflow(void) { + // prevent -ftrapv crashes + // + // for some reason gcc generates trap code even when we're doing it + // manually with __builtin_mul_overflow() :'( +} + TEST(atoi, test) { EXPECT_EQ(0, atoi("")); EXPECT_EQ(0, atoi("-b")); diff --git a/test/libc/intrin/kprintf_test.c b/test/libc/intrin/kprintf_test.c index cf4f9ab09..071c15f5d 100644 --- a/test/libc/intrin/kprintf_test.c +++ b/test/libc/intrin/kprintf_test.c @@ -354,6 +354,33 @@ TEST(ksnprintf, badUtf16) { } } +TEST(ksnprintf, truncation) { + char buf[16] = {0}; + rngset(buf, sizeof(buf) - 1, lemur64, -1); + ksnprintf(0, 0, "%s", "xxxxx"); + rngset(buf, sizeof(buf) - 1, lemur64, -1); + ksnprintf(buf, 1, "%s", "xxxxx"); + EXPECT_STREQ("", buf); + rngset(buf, sizeof(buf) - 1, lemur64, -1); + ksnprintf(buf, 2, "%s", "xxxxx"); + EXPECT_STREQ(".", buf); + rngset(buf, sizeof(buf) - 1, lemur64, -1); + ksnprintf(buf, 3, "%s", "xxxxx"); + EXPECT_STREQ("..", buf); + rngset(buf, sizeof(buf) - 1, lemur64, -1); + ksnprintf(buf, 4, "%s", "xxxxx"); + EXPECT_STREQ("...", buf); + rngset(buf, sizeof(buf) - 1, lemur64, -1); + ksnprintf(buf, 5, "%s", "xxxxx"); + EXPECT_STREQ("x...", buf); + rngset(buf, sizeof(buf) - 1, lemur64, -1); + ksnprintf(buf, 6, "%s", "xxxxxxxxxxx"); + EXPECT_STREQ("xx...", buf); + rngset(buf, sizeof(buf) - 1, lemur64, -1); + ksnprintf(buf, 7, "%s", "xxxxxxxxx"); + EXPECT_STREQ("xxx...", buf); +} + BENCH(printf, bench) { char b[128]; int snprintf_(char *, size_t, const char *, ...) asm("snprintf"); diff --git a/test/libc/runtime/memtrack_test.c b/test/libc/runtime/memtrack_test.c index ff84e1727..200ec8a2e 100644 --- a/test/libc/runtime/memtrack_test.c +++ b/test/libc/runtime/memtrack_test.c @@ -63,7 +63,7 @@ static void CheckMemoryIntervalsAreOk(const struct MemoryIntervals *mm) { static void RunTrackMemoryIntervalTest(const struct MemoryIntervals t[2], int x, int y, long h) { struct MemoryIntervals *mm; - mm = memcpy(malloc(sizeof(*t)), t, sizeof(*t)); + mm = memcpy(memalign(64, sizeof(*t)), t, sizeof(*t)); CheckMemoryIntervalsAreOk(mm); CHECK_NE(-1, TrackMemoryInterval(mm, x, y, h, 0, 0, 0, 0, 0, 0)); CheckMemoryIntervalsAreOk(mm); @@ -75,7 +75,7 @@ static int RunReleaseMemoryIntervalsTest(const struct MemoryIntervals t[2], int x, int y) { int rc; struct MemoryIntervals *mm; - mm = memcpy(malloc(sizeof(*t)), t, sizeof(*t)); + mm = memcpy(memalign(64, sizeof(*t)), t, sizeof(*t)); CheckMemoryIntervalsAreOk(mm); if ((rc = ReleaseMemoryIntervals(mm, x, y, NULL)) != -1) { CheckMemoryIntervalsAreOk(mm); diff --git a/test/libc/runtime/mprotect_test.c b/test/libc/runtime/mprotect_test.c index 366bb2cbc..d9f64e1a4 100644 --- a/test/libc/runtime/mprotect_test.c +++ b/test/libc/runtime/mprotect_test.c @@ -57,7 +57,7 @@ void OnSigBus(int sig, struct siginfo *si, struct ucontext *ctx) { #if 0 kprintf("SIGBUS%n"); kprintf("si->si_signo = %G%n", si->si_signo); - kprintf("si->si_errno = %s (%d)%n", strerror_short(si->si_errno), + kprintf("si->si_errno = %s (%d)%n", strerrno(si->si_errno), si->si_errno); kprintf("si->si_code = %s (%d)%n", GetSiCodeName(sig, si->si_code), si->si_code); diff --git a/third_party/argon2/blake2b.c b/third_party/argon2/blake2b.c index b004e8ef5..5aad00f29 100644 --- a/third_party/argon2/blake2b.c +++ b/third_party/argon2/blake2b.c @@ -42,7 +42,7 @@ static const uint64_t blake2b_IV[8] = { UINT64_C(0x1f83d9abfb41bd6b), UINT64_C(0x5be0cd19137e2179), }; -static const unsigned int blake2b_sigma[12][16] = { +static const unsigned char blake2b_sigma[12][16] = { {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, {11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4}, @@ -69,7 +69,7 @@ static inline void blake2b_set_lastblock(blake2b_state *S) { } static inline void blake2b_increment_counter(blake2b_state *S, - uint64_t inc) { + uint64_t inc) { S->t[0] += inc; S->t[1] += (S->t[0] < inc); } diff --git a/third_party/linenoise/linenoise.c b/third_party/linenoise/linenoise.c index 7952909ec..6c7a727f3 100644 --- a/third_party/linenoise/linenoise.c +++ b/third_party/linenoise/linenoise.c @@ -125,6 +125,7 @@ │ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. │ │ │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/alg/alg.h" #include "libc/assert.h" #include "libc/bits/bits.h" #include "libc/calls/calls.h" @@ -136,6 +137,7 @@ #include "libc/calls/termios.h" #include "libc/calls/ttydefaults.h" #include "libc/calls/weirdtypes.h" +#include "libc/dce.h" #include "libc/errno.h" #include "libc/fmt/conv.h" #include "libc/intrin/asan.internal.h" @@ -147,6 +149,7 @@ #include "libc/mem/mem.h" #include "libc/nexgen32e/bsr.h" #include "libc/nexgen32e/rdtsc.h" +#include "libc/nt/version.h" #include "libc/runtime/runtime.h" #include "libc/sock/sock.h" #include "libc/stdio/append.internal.h" @@ -190,15 +193,21 @@ Copyright 2010-2013 Pieter Noordhuis \""); #define DEBUG(L, ...) (void)0 #endif -#define DUFF_ROUTINE_READ(STATE) \ - case STATE: \ - linenoiseRefreshLineForce(l); \ - rc = linenoiseRead(l->ifd, seq, sizeof(seq), l, block); \ - if (rc == -1 && errno == EAGAIN) { \ - l->state = STATE; \ - return -1; \ - } \ - l->state = 0 +#define DUFF_ROUTINE_LOOP 0 +#define DUFF_ROUTINE_START 5 + +#define DUFF_ROUTINE_LABEL(STATE) \ + case STATE: \ + linenoiseRefreshLineForce(l); \ + l->state = DUFF_ROUTINE_LOOP + +#define DUFF_ROUTINE_READ(STATE) \ + DUFF_ROUTINE_LABEL(STATE); \ + rc = linenoiseRead(l->ifd, seq, sizeof(seq), l, block); \ + if (rc == -1 && errno == EAGAIN) { \ + l->state = STATE; \ + return -1; \ + } #define BLOCKING_READ() rc = linenoiseRead(l->ifd, seq, sizeof(seq), l, false) @@ -399,7 +408,9 @@ static int linenoiseIsUnsupportedTerm(void) { char *term; static char once, res; if (!once) { - if ((term = getenv("TERM"))) { + if (IsWindows() && !IsAtLeastWindows10()) { + res = 1; + } else if ((term = getenv("TERM"))) { for (i = 0; i < sizeof(kUnsupported) / sizeof(*kUnsupported); i++) { if (!strcasecmp(term, kUnsupported[i])) { res = 1; @@ -656,7 +667,7 @@ int linenoiseEnableRawMode(int fd) { raw = orig_termios; raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); - raw.c_oflag &= ~OPOST; + raw.c_oflag |= OPOST | ONLCR; raw.c_iflag |= IUTF8; raw.c_cflag |= CS8; raw.c_cc[VMIN] = 1; @@ -810,10 +821,10 @@ struct winsize linenoiseGetTerminalSize(struct winsize ws, int ifd, int ofd) { } if (((!ws.ws_col || !ws.ws_row) && linenoiseRead(ifd, 0, 0, 0, 1) != -1 && linenoiseWriteStr( - ofd, "\0337" /* save position */ - "\033[9979;9979H" /* move cursor to bottom right corner */ - "\033[6n" /* report position */ - "\0338") != -1 && /* restore position */ + ofd, "\e7" /* save position */ + "\e[9979;9979H" /* move cursor to bottom right corner */ + "\e[6n" /* report position */ + "\e8") != -1 && /* restore position */ (n = linenoiseRead(ifd, b, sizeof(b), 0, 1)) != -1 && n && b[0] == 033 && b[1] == '[' && b[n - 1] == 'R')) { p = b + 2; @@ -827,8 +838,8 @@ struct winsize linenoiseGetTerminalSize(struct winsize ws, int ifd, int ofd) { /* Clear the screen. Used to handle ctrl+l */ void linenoiseClearScreen(int fd) { - linenoiseWriteStr(fd, "\033[H" /* move cursor to top left corner */ - "\033[2J"); /* erase display */ + linenoiseWriteStr(fd, "\e[H" /* move cursor to top left corner */ + "\e[2J"); /* erase display */ } static void linenoiseBeep(void) { @@ -962,7 +973,7 @@ static char *linenoiseMakeSearchPrompt(int fail, const char *s, int n) { if (fail) abAppends(&ab, "failed "); abAppends(&ab, "reverse-i-search `\e[4m"); abAppend(&ab, s, n); - abAppends(&ab, "\033[24m"); + abAppends(&ab, "\e[24m"); abAppends(&ab, s + n); abAppendw(&ab, READ32LE("') ")); return ab.b; @@ -1007,8 +1018,8 @@ static char *linenoiseRefreshHints(struct linenoiseState *l) { if (!hintsCallback) return 0; if (!(hint = hintsCallback(l->buf, &ansi1, &ansi2))) return 0; abInit(&ab); - ansi1 = "\033[90m"; - ansi2 = "\033[39m"; + ansi1 = "\e[90m"; + ansi2 = "\e[39m"; if (ansi1) abAppends(&ab, ansi1); abAppends(&ab, hint); if (ansi2) abAppends(&ab, ansi2); @@ -1174,7 +1185,7 @@ StartOver: abInit(&ab); abAppendw(&ab, '\r'); /* start of line */ if (l->rows - l->oldpos - 1 > 0) { - abAppends(&ab, "\033["); + abAppends(&ab, "\e["); abAppendu(&ab, l->rows - l->oldpos - 1); abAppendw(&ab, 'A'); /* cursor up clamped */ } @@ -1185,7 +1196,7 @@ StartOver: if (x && x + rune.n > xn) { if (cy >= 0) ++cy; if (x < xn) { - abAppends(&ab, "\033[K"); /* clear line forward */ + abAppends(&ab, "\e[K"); /* clear line forward */ } abAppends(&ab, "\r" /* start of line */ "\n"); /* cursor down unclamped */ @@ -1200,9 +1211,9 @@ StartOver: abAppendw(&ab, '*'); } else { flipit = hasflip && (i == flip[0] || i == flip[1]); - if (flipit) abAppendw(&ab, READ32LE("\033[1m")); + if (flipit) abAppendw(&ab, READ32LE("\e[1m")); abAppendw(&ab, tpenc(rune.c)); - if (flipit) abAppendw(&ab, READ64LE("\033[22m\0\0")); + if (flipit) abAppendw(&ab, READ64LE("\e[22m\0\0")); } t = wcwidth(rune.c); t = MAX(0, t); @@ -1217,7 +1228,7 @@ StartOver: } free(hint); } - abAppendw(&ab, READ32LE("\033[J")); /* erase display forwards */ + abAppendw(&ab, READ32LE("\e[J")); /* erase display forwards */ /* * if we are at the very end of the screen with our prompt, we need to @@ -1232,12 +1243,12 @@ StartOver: * move cursor to right position */ if (cy > 0) { - abAppendw(&ab, READ32LE("\033[\0")); + abAppendw(&ab, READ32LE("\e[\0")); abAppendu(&ab, cy); abAppendw(&ab, 'A'); /* cursor up */ } if (cx > 0) { - abAppendw(&ab, READ32LE("\r\033[")); + abAppendw(&ab, READ32LE("\r\e[")); abAppendu(&ab, cx); abAppendw(&ab, 'C'); /* cursor right */ } else if (!cx) { @@ -1509,7 +1520,7 @@ static void linenoiseEditYank(struct linenoiseState *l) { static void linenoiseEditRotate(struct linenoiseState *l) { if ((l->seq[1][0] == CTRL('Y') || - (l->seq[1][0] == 033 && l->seq[1][1] == 'y'))) { + (l->seq[1][0] == '\e' && l->seq[1][1] == 'y'))) { if (l->yi < l->len && l->yj <= l->len) { memmove(l->buf + l->yi, l->buf + l->yj, l->len - l->yj + 1); l->len -= l->yj - l->yi; @@ -1780,12 +1791,12 @@ struct linenoiseState *linenoiseBegin(const char *prompt, int ifd, int ofd) { free(l); return 0; } + l->state = DUFF_ROUTINE_START; l->buf[0] = 0; l->ifd = ifd; l->ofd = ofd; l->prompt = strdup(prompt ? prompt : ""); l->ws = linenoiseGetTerminalSize(l->ws, l->ifd, l->ofd); - linenoiseHistoryAdd(""); linenoiseWriteStr(l->ofd, l->prompt); abInit(&l->ab); return l; @@ -1814,6 +1825,10 @@ void linenoiseEnd(struct linenoiseState *l) { } } +static int CompareStrings(const void *a, const void *b) { + return strcasecmp(*(const char **)a, *(const char **)b); +} + /** * Runs linenoise engine. * @@ -1844,7 +1859,7 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf, switch (l->state) { for (;;) { - DUFF_ROUTINE_READ(0); + DUFF_ROUTINE_READ(DUFF_ROUTINE_LOOP); HandleRead: if (!rc && l->len) { rc = 1; @@ -1933,7 +1948,7 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf, // handle tab and tab-tab completion if (seq[0] == '\t' && completionCallback) { - size_t i, j, k, n, m, perline, itemlen; + size_t i, j, k, n, m, itemlen; // we know that the user pressed tab once rc = 0; linenoiseFreeCompletions(&l->lc); @@ -1956,6 +1971,7 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf, continue; } if (l->lc.len > 1) { + qsort(l->lc.cvec, l->lc.len, sizeof(*l->lc.cvec), CompareStrings); // if there's a multiline completions, then do nothing and wait and // see if the user presses tab again. if the user does this we then // print ALL the completions, to above the editing line @@ -1965,29 +1981,42 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf, free(s); } for (;;) { - DUFF_ROUTINE_READ(3); + DUFF_ROUTINE_READ(2); if (rc == 1 && seq[0] == '\t') { + const char **p; struct abuf ab; + int i, x, y, xn, yn, xy; itemlen = linenoiseMaxCompletionWidth(&l->lc) + 4; - perline = MAX(1, (l->ws.ws_col - 1) / itemlen); - abInit(&ab); - abAppends(&ab, "\r\n\033[K"); - for (size_t i = 0; i < l->lc.len;) { - for (size_t j = 0; i < l->lc.len && j < perline; ++j, ++i) { - n = GetMonospaceWidth(l->lc.cvec[i], strlen(l->lc.cvec[i]), - 0); - abAppends(&ab, l->lc.cvec[i]); + xn = MAX(1, (l->ws.ws_col - 1) / itemlen); + yn = (l->lc.len + (xn - 1)) / xn; + if (!__builtin_mul_overflow(xn, yn, &xy) && + (p = calloc(xy, sizeof(char *)))) { + // arrange in column major order + for (i = x = 0; x < xn; ++x) { + for (y = 0; y < yn; ++y) { + p[y * xn + x] = i < l->lc.len ? l->lc.cvec[i++] : ""; + } + } + abInit(&ab); + abAppends(&ab, "\r\n\e[K"); + for (x = i = 0; i < xy; ++i) { + n = GetMonospaceWidth(p[i], strlen(p[i]), 0); + abAppends(&ab, p[i]); for (k = n; k < itemlen; ++k) { abAppendw(&ab, ' '); } + if (++x == xn) { + abAppendw(&ab, READ16LE("\r\n")); + x = 0; + } } - abAppendw(&ab, READ16LE("\r\n")); + ab.len -= 2; + abAppends(&ab, "\n"); + linenoiseWriteStr(l->ofd, ab.b); + linenoiseRefreshLine(l); + abFree(&ab); + free(p); } - ab.len -= 2; - abAppends(&ab, "\n"); - linenoiseWriteStr(l->ofd, ab.b); - linenoiseRefreshLine(l); - abFree(&ab); } else { goto HandleRead; } @@ -1998,7 +2027,7 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf, // handle (1) emacs keyboard combos // (2) otherwise sigint exit if (seq[0] == CTRL('C')) { - DUFF_ROUTINE_READ(4); + DUFF_ROUTINE_READ(3); if (rc == 1) { switch (seq[0]) { CASE(CTRL('C'), linenoiseEditInterrupt(l)); @@ -2019,7 +2048,7 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf, if (ispaused) { linenoiseUnpause(l->ofd); } else { - DUFF_ROUTINE_READ(5); + DUFF_ROUTINE_READ(4); if (rc > 0) { char esc[sizeof(seq) * 4]; size_t m = linenoiseEscape(esc, seq, rc); @@ -2031,6 +2060,28 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf, continue; } + // handle enter key + if (seq[0] == '\r') { + char *p; + l->final = 1; + free(history[--historylen]); + history[historylen] = 0; + linenoiseEditEnd(l); + linenoiseRefreshLineForce(l); + p = strdup(l->buf); + linenoiseReset(l); + if (p) { + *obuf = p; + l->state = DUFF_ROUTINE_START; + return l->len; + } else { + return -1; + } + DUFF_ROUTINE_LABEL(DUFF_ROUTINE_START); + linenoiseHistoryAdd(""); + continue; + } + // handle keystrokes that don't need read() switch (seq[0]) { CASE(CTRL('P'), linenoiseEditUp(l)); @@ -2070,22 +2121,6 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf, } break; - case '\r': { - l->final = 1; - free(history[--historylen]); - history[historylen] = 0; - linenoiseEditEnd(l); - linenoiseRefreshLineForce(l); - char *p = strdup(l->buf); - linenoiseReset(l); - if (p) { - *obuf = p; - return l->len; - } else { - return -1; - } - } - case '\e': // handle ansi escape if (rc < 2) break; diff --git a/third_party/lua/README.cosmo b/third_party/lua/README.cosmo index 9cdfdeee5..a418cc10c 100644 --- a/third_party/lua/README.cosmo +++ b/third_party/lua/README.cosmo @@ -30,3 +30,5 @@ LOCAL MODIFICATIONS bold text, which can be encoded elegantly as `\e[1mHELLO\e[0m`. Added luaL_traceback2() for function parameters in traceback. + + Added Python-like printf modulus operator for strings. diff --git a/third_party/lua/cosmo.h b/third_party/lua/cosmo.h index d8df45356..10a0bbc04 100644 --- a/third_party/lua/cosmo.h +++ b/third_party/lua/cosmo.h @@ -12,11 +12,12 @@ int LuaEncodeJsonData(lua_State *, char **, int, char *); int LuaEncodeLuaData(lua_State *, char **, int, char *); int LuaEncodeUrl(lua_State *); int LuaParseUrl(lua_State *); -void EscapeLuaString(char *, size_t, char **); -void LuaPushUrlParams(lua_State *, struct UrlParams *); -int LuaPushHeaders(lua_State *, struct HttpMessage *, const char *); -void LuaPushLatin1(lua_State *, const char *, size_t); int LuaPushHeader(lua_State *, struct HttpMessage *, char *, int); +int LuaPushHeaders(lua_State *, struct HttpMessage *, const char *); +void EscapeLuaString(char *, size_t, char **); +void LuaPushLatin1(lua_State *, const char *, size_t); +void LuaPushUrlParams(lua_State *, struct UrlParams *); +void LuaPrintStack(lua_State *); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/third_party/lua/lapi.c b/third_party/lua/lapi.c index ae3212cb9..3723ef79b 100644 --- a/third_party/lua/lapi.c +++ b/third_party/lua/lapi.c @@ -1,12 +1,32 @@ -/* -** $Id: lapi.c $ -** Lua API -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lapi_c #define LUA_CORE - #include "third_party/lua/lapi.h" #include "third_party/lua/ldebug.h" #include "third_party/lua/ldo.h" @@ -22,8 +42,13 @@ #include "third_party/lua/lua.h" #include "third_party/lua/lundump.h" #include "third_party/lua/lvm.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ const char lua_ident[] = "$LuaVersion: " LUA_COPYRIGHT " $" diff --git a/third_party/lua/lapi.h b/third_party/lua/lapi.h index 48c1c203d..0c5d784d7 100644 --- a/third_party/lua/lapi.h +++ b/third_party/lua/lapi.h @@ -1,9 +1,3 @@ -/* -** $Id: lapi.h $ -** Auxiliary functions from Lua API -** See Copyright Notice in lua.h -*/ - #ifndef lapi_h #define lapi_h diff --git a/third_party/lua/lauxlib.c b/third_party/lua/lauxlib.c index 03e7a6b52..16d4db7f3 100644 --- a/third_party/lua/lauxlib.c +++ b/third_party/lua/lauxlib.c @@ -1,19 +1,43 @@ -/* -** $Id: lauxlib.c $ -** Auxiliary functions for building Lua libraries -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lauxlib_c #define LUA_LIB - #include "libc/calls/calls.h" #include "libc/errno.h" #include "third_party/lua/lauxlib.h" #include "third_party/lua/lprefix.h" #include "third_party/lua/lua.h" +// clang-format off -/* clang-format off */ +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); /** diff --git a/third_party/lua/lbaselib.c b/third_party/lua/lbaselib.c index 25f37e902..13d8013a9 100644 --- a/third_party/lua/lbaselib.c +++ b/third_party/lua/lbaselib.c @@ -1,19 +1,43 @@ -/* -** $Id: lbaselib.c $ -** Basic library -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lbaselib_c #define LUA_LIB - #include "third_party/lua/lauxlib.h" #include "third_party/lua/lprefix.h" #include "third_party/lua/lua.h" #include "third_party/lua/lualib.h" - /* clang-format off */ +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); + static int luaB_print (lua_State *L) { int n = lua_gettop(L); /* number of arguments */ diff --git a/third_party/lua/lcode.c b/third_party/lua/lcode.c index b448fd185..1e54b3f6a 100644 --- a/third_party/lua/lcode.c +++ b/third_party/lua/lcode.c @@ -1,12 +1,32 @@ -/* -** $Id: lcode.c $ -** Code generator for Lua -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lcode_c #define LUA_CORE - #include "libc/fmt/conv.h" #include "third_party/lua/lcode.h" #include "third_party/lua/ldebug.h" @@ -22,8 +42,13 @@ #include "third_party/lua/ltable.h" #include "third_party/lua/lua.h" #include "third_party/lua/lvm.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ /* Maximum number of registers in a Lua function (must fit in 8 bits) */ #define MAXREGS 255 diff --git a/third_party/lua/lcode.h b/third_party/lua/lcode.h index 5538136da..fe7da153a 100644 --- a/third_party/lua/lcode.h +++ b/third_party/lua/lcode.h @@ -1,9 +1,3 @@ -/* -** $Id: lcode.h $ -** Code generator for Lua -** See Copyright Notice in lua.h -*/ - #ifndef lcode_h #define lcode_h diff --git a/third_party/lua/lcorolib.c b/third_party/lua/lcorolib.c index 9c34df9ba..6cb615f10 100644 --- a/third_party/lua/lcorolib.c +++ b/third_party/lua/lcorolib.c @@ -1,18 +1,43 @@ -/* -** $Id: lcorolib.c $ -** Coroutine Library -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lcorolib_c #define LUA_LIB - #include "third_party/lua/lauxlib.h" #include "third_party/lua/lprefix.h" #include "third_party/lua/lua.h" #include "third_party/lua/lualib.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ static lua_State *getco (lua_State *L) { lua_State *co = lua_tothread(L, 1); diff --git a/third_party/lua/lctype.c b/third_party/lua/lctype.c deleted file mode 100644 index 373e56bc5..000000000 --- a/third_party/lua/lctype.c +++ /dev/null @@ -1,63 +0,0 @@ -/* -** $Id: lctype.c $ -** 'ctype' functions for Lua -** See Copyright Notice in lua.h -*/ - -#define lctype_c -#define LUA_CORE - -#include "third_party/lua/lctype.h" -#include "third_party/lua/lprefix.h" - -/* clang-format off */ - -#if !LUA_USE_CTYPE /* { */ - - - -#if defined (LUA_UCID) /* accept UniCode IDentifiers? */ -/* consider all non-ascii codepoints to be alphabetic */ -#define NONA 0x01 -#else -#define NONA 0x00 /* default */ -#endif - - -LUAI_DDEF const lu_byte luai_ctype_[UCHAR_MAX + 2] = { - 0x00, /* EOZ */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0. */ - 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1. */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, /* 2. */ - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, /* 3. */ - 0x16, 0x16, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 4. */ - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 5. */ - 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x05, - 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 6. */ - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 7. */ - 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x00, - NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* 8. */ - NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, - NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* 9. */ - NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, - NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* a. */ - NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, - NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* b. */ - NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, - 0x00, 0x00, NONA, NONA, NONA, NONA, NONA, NONA, /* c. */ - NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, - NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* d. */ - NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, - NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* e. */ - NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, - NONA, NONA, NONA, NONA, NONA, 0x00, 0x00, 0x00, /* f. */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -#endif /* } */ diff --git a/third_party/lua/lctype.h b/third_party/lua/lctype.h index 472a5fb73..b4988a444 100644 --- a/third_party/lua/lctype.h +++ b/third_party/lua/lctype.h @@ -1,100 +1,54 @@ -/* -** $Id: lctype.h $ -** 'ctype' functions for Lua -** See Copyright Notice in lua.h -*/ - #ifndef lctype_h #define lctype_h - #include "third_party/lua/lua.h" -/* clang-format off */ - -/* -** WARNING: the functions defined here do not necessarily correspond -** to the similar functions in the standard C ctype.h. They are -** optimized for the specific needs of Lua. -*/ - -#if !defined(LUA_USE_CTYPE) - -#if 'A' == 65 && '0' == 48 -/* ASCII case: can use its own tables; faster and fixed */ -#define LUA_USE_CTYPE 0 -#else -/* must use standard C ctype */ -#define LUA_USE_CTYPE 1 -#endif - -#endif - - -#if !LUA_USE_CTYPE /* { */ - - -#include "third_party/lua/llimits.h" - - -#define ALPHABIT 0 -#define DIGITBIT 1 -#define PRINTBIT 2 -#define SPACEBIT 3 -#define XDIGITBIT 4 - - -#define MASK(B) (1 << (B)) - - -/* -** add 1 to char to allow index -1 (EOZ) -*/ -#define testprop(c,p) (luai_ctype_[(c)+1] & (p)) - -/* -** 'lalpha' (Lua alphabetic) and 'lalnum' (Lua alphanumeric) both include '_' -*/ -#define lislalpha(c) testprop(c, MASK(ALPHABIT)) -#define lislalnum(c) testprop(c, (MASK(ALPHABIT) | MASK(DIGITBIT))) -#define lisdigit(c) testprop(c, MASK(DIGITBIT)) -#define lisspace(c) testprop(c, MASK(SPACEBIT)) -#define lisprint(c) testprop(c, MASK(PRINTBIT)) -#define lisxdigit(c) testprop(c, MASK(XDIGITBIT)) - - /* ** In ASCII, this 'ltolower' is correct for alphabetic characters and ** for '.'. That is enough for Lua needs. ('check_exp' ensures that ** the character either is an upper-case letter or is unchanged by ** the transformation, which holds for lower-case letters and '.'.) */ -#define ltolower(c) \ - check_exp(('A' <= (c) && (c) <= 'Z') || (c) == ((c) | ('A' ^ 'a')), \ +#define ltolower(c) \ + check_exp(('A' <= (c) && (c) <= 'Z') || (c) == ((c) | ('A' ^ 'a')), \ (c) | ('A' ^ 'a')) +#define lisdigit(C) \ + ({ \ + unsigned char c_ = (C); \ + '0' <= c_&& c_ <= '9'; \ + }) -/* one entry for each character and for -1 (EOZ) */ -LUAI_DDEC(const lu_byte luai_ctype_[UCHAR_MAX + 2];) +#define lislalpha(C) \ + ({ \ + unsigned char c_ = (C); \ + ('A' <= c_ && c_ <= 'Z') || ('a' <= c_ && c_ <= 'z') || c_ == '_'; \ + }) +#define lislalnum(C) \ + ({ \ + unsigned char c_ = (C); \ + (('0' <= c_ && c_ <= '9') || ('A' <= c_ && c_ <= 'Z') || \ + ('a' <= c_ && c_ <= 'z') || c_ == '_'); \ + }) -#else /* }{ */ +#define lisspace(C) \ + ({ \ + unsigned char c_ = (C); \ + (c_ == ' ' || c_ == '\t' || c_ == '\r' || c_ == '\n' || c_ == '\f' || \ + c_ == '\v'); \ + }) -/* -** use standard C ctypes -*/ +#define lisxdigit(C) \ + ({ \ + unsigned char c_ = (C); \ + (('0' <= c_ && c_ <= '9') || ('A' <= c_ && c_ <= 'F') || \ + ('a' <= c_ && c_ <= 'f')); \ + }) - - -#define lislalpha(c) (isalpha(c) || (c) == '_') -#define lislalnum(c) (isalnum(c) || (c) == '_') -#define lisdigit(c) (isdigit(c)) -#define lisspace(c) (isspace(c)) -#define lisprint(c) (isprint(c)) -#define lisxdigit(c) (isxdigit(c)) - -#define ltolower(c) (tolower(c)) - -#endif /* } */ +#define lisprint(C) \ + ({ \ + unsigned char c_ = (C); \ + 32 <= c_&& c_ <= 126; \ + }) #endif - diff --git a/third_party/lua/ldblib.c b/third_party/lua/ldblib.c index ed9c48d31..8cdde9f90 100644 --- a/third_party/lua/ldblib.c +++ b/third_party/lua/ldblib.c @@ -1,20 +1,42 @@ -/* -** $Id: ldblib.c $ -** Interface from Lua to its debug API -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define ldblib_c #define LUA_LIB - -#include "third_party/lua/lprefix.h" - - - -#include "third_party/lua/lua.h" - #include "third_party/lua/lauxlib.h" +#include "third_party/lua/lprefix.h" +#include "third_party/lua/lua.h" #include "third_party/lua/lualib.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); /* @@ -477,4 +499,3 @@ LUAMOD_API int luaopen_debug (lua_State *L) { luaL_newlib(L, dblib); return 1; } - diff --git a/third_party/lua/ldebug.c b/third_party/lua/ldebug.c index 1f2c0785c..7d710db75 100644 --- a/third_party/lua/ldebug.c +++ b/third_party/lua/ldebug.c @@ -1,18 +1,32 @@ -/* -** $Id: ldebug.c $ -** Debug Interface -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define ldebug_c #define LUA_CORE - -#include "third_party/lua/lprefix.h" - - - -#include "third_party/lua/lua.h" - #include "third_party/lua/lapi.h" #include "third_party/lua/lcode.h" #include "third_party/lua/ldebug.h" @@ -20,12 +34,19 @@ #include "third_party/lua/lfunc.h" #include "third_party/lua/lobject.h" #include "third_party/lua/lopcodes.h" +#include "third_party/lua/lprefix.h" #include "third_party/lua/lstate.h" #include "third_party/lua/lstring.h" #include "third_party/lua/ltable.h" #include "third_party/lua/ltm.h" +#include "third_party/lua/lua.h" #include "third_party/lua/lvm.h" +// clang-format off +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); #define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_VCCL) diff --git a/third_party/lua/ldebug.h b/third_party/lua/ldebug.h index e6a1a17ed..fb87123e7 100644 --- a/third_party/lua/ldebug.h +++ b/third_party/lua/ldebug.h @@ -1,9 +1,3 @@ -/* -** $Id: ldebug.h $ -** Auxiliary functions from Debug Interface module -** See Copyright Notice in lua.h -*/ - #ifndef ldebug_h #define ldebug_h diff --git a/third_party/lua/ldo.c b/third_party/lua/ldo.c index 0ebffb139..e873e6de8 100644 --- a/third_party/lua/ldo.c +++ b/third_party/lua/ldo.c @@ -1,12 +1,32 @@ -/* -** $Id: ldo.c $ -** Stack and Call structure of Lua -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define ldo_c #define LUA_CORE - #include "libc/runtime/gc.h" #include "third_party/lua/lapi.h" #include "third_party/lua/ldebug.h" @@ -26,8 +46,12 @@ #include "third_party/lua/lundump.h" #include "third_party/lua/lvm.h" #include "third_party/lua/lzio.h" +// clang-format off -/* clang-format off */ +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); #define errorstatus(s) ((s) > LUA_YIELD) diff --git a/third_party/lua/ldo.h b/third_party/lua/ldo.h index 209f7b60f..5a5bff6a8 100644 --- a/third_party/lua/ldo.h +++ b/third_party/lua/ldo.h @@ -1,9 +1,3 @@ -/* -** $Id: ldo.h $ -** Stack and Call structure of Lua -** See Copyright Notice in lua.h -*/ - #ifndef ldo_h #define ldo_h diff --git a/third_party/lua/ldump.c b/third_party/lua/ldump.c index 0fb11c043..05c2c1cab 100644 --- a/third_party/lua/ldump.c +++ b/third_party/lua/ldump.c @@ -1,19 +1,44 @@ -/* -** $Id: ldump.c $ -** save precompiled Lua chunks -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define ldump_c #define LUA_CORE - #include "third_party/lua/lobject.h" #include "third_party/lua/lprefix.h" #include "third_party/lua/lstate.h" #include "third_party/lua/lua.h" #include "third_party/lua/lundump.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ typedef struct { lua_State *L; diff --git a/third_party/lua/lfunc.c b/third_party/lua/lfunc.c index 0b5d66d59..cedce1480 100644 --- a/third_party/lua/lfunc.c +++ b/third_party/lua/lfunc.c @@ -1,12 +1,32 @@ -/* -** $Id: lfunc.c $ -** Auxiliary functions to manipulate prototypes and closures -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lfunc_c #define LUA_CORE - #include "third_party/lua/ldebug.h" #include "third_party/lua/ldo.h" #include "third_party/lua/lfunc.h" @@ -17,8 +37,13 @@ #include "third_party/lua/lstate.h" #include "third_party/lua/ltm.h" #include "third_party/lua/lua.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ CClosure *luaF_newCclosure (lua_State *L, int nupvals) { GCObject *o = luaC_newobj(L, LUA_VCCL, sizeCclosure(nupvals)); diff --git a/third_party/lua/lfunc.h b/third_party/lua/lfunc.h index ba8839496..4f15eb98b 100644 --- a/third_party/lua/lfunc.h +++ b/third_party/lua/lfunc.h @@ -1,9 +1,3 @@ -/* -** $Id: lfunc.h $ -** Auxiliary functions to manipulate prototypes and closures -** See Copyright Notice in lua.h -*/ - #ifndef lfunc_h #define lfunc_h diff --git a/third_party/lua/lgc.c b/third_party/lua/lgc.c index da7894b72..224f15ab5 100644 --- a/third_party/lua/lgc.c +++ b/third_party/lua/lgc.c @@ -1,12 +1,32 @@ -/* -** $Id: lgc.c $ -** Garbage Collector -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lgc_c #define LUA_CORE - #include "third_party/lua/ldebug.h" #include "third_party/lua/ldo.h" #include "third_party/lua/lfunc.h" @@ -19,8 +39,13 @@ #include "third_party/lua/ltable.h" #include "third_party/lua/ltm.h" #include "third_party/lua/lua.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ /* ** Maximum number of elements to sweep in each single step. diff --git a/third_party/lua/lgc.h b/third_party/lua/lgc.h index 2e1b59ecb..efe88f329 100644 --- a/third_party/lua/lgc.h +++ b/third_party/lua/lgc.h @@ -1,9 +1,3 @@ -/* -** $Id: lgc.h $ -** Garbage Collector -** See Copyright Notice in lua.h -*/ - #ifndef lgc_h #define lgc_h diff --git a/third_party/lua/linit.c b/third_party/lua/linit.c index 3cca5c479..f64dd1327 100644 --- a/third_party/lua/linit.c +++ b/third_party/lua/linit.c @@ -1,12 +1,32 @@ -/* -** $Id: linit.c $ -** Initialization of libraries for lua.c and other clients -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define linit_c #define LUA_LIB - /* ** If you embed Lua in your program and need to open the standard ** libraries, call luaL_openlibs in your program. If you need a @@ -22,13 +42,17 @@ ** lua_setfield(L, -2, modname); ** lua_pop(L, 1); // remove PRELOAD table */ - #include "third_party/lua/lauxlib.h" #include "third_party/lua/lprefix.h" #include "third_party/lua/lua.h" #include "third_party/lua/lualib.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ /* ** these libs are loaded by lua.c and are readily available to any Lua diff --git a/third_party/lua/liolib.c b/third_party/lua/liolib.c index 8974e2823..92027cb1b 100644 --- a/third_party/lua/liolib.c +++ b/third_party/lua/liolib.c @@ -1,12 +1,32 @@ -/* -** $Id: liolib.c $ -** Standard I/O (and system) library -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define liolib_c #define LUA_LIB - #include "libc/calls/calls.h" #include "libc/calls/weirdtypes.h" #include "libc/errno.h" @@ -16,8 +36,13 @@ #include "third_party/lua/lprefix.h" #include "third_party/lua/lua.h" #include "third_party/lua/lualib.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ /* ** Change this macro to accept other modes for 'fopen' besides diff --git a/third_party/lua/llex.c b/third_party/lua/llex.c index 535f4cfee..337adf666 100644 --- a/third_party/lua/llex.c +++ b/third_party/lua/llex.c @@ -1,12 +1,32 @@ -/* -** $Id: llex.c $ -** Lexical Analyzer -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define llex_c #define LUA_CORE - #include "third_party/lua/lctype.h" #include "third_party/lua/ldebug.h" #include "third_party/lua/ldo.h" @@ -20,8 +40,12 @@ #include "third_party/lua/ltable.h" #include "third_party/lua/lua.h" #include "third_party/lua/lzio.h" +// clang-format off -/* clang-format off */ +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); #define next(ls) (ls->current = zgetc(ls->z)) diff --git a/third_party/lua/llimits.h b/third_party/lua/llimits.h index babd88ae2..941ea9219 100644 --- a/third_party/lua/llimits.h +++ b/third_party/lua/llimits.h @@ -1,9 +1,3 @@ -/* -** $Id: llimits.h $ -** Limits, basic types, and some other 'installation-dependent' definitions -** See Copyright Notice in lua.h -*/ - #ifndef llimits_h #define llimits_h diff --git a/third_party/lua/lmathlib.c b/third_party/lua/lmathlib.c index ac8d6070c..9e0ee7305 100644 --- a/third_party/lua/lmathlib.c +++ b/third_party/lua/lmathlib.c @@ -1,12 +1,32 @@ -/* -** $Id: lmathlib.c $ -** Standard mathematical library -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lmathlib_c #define LUA_LIB - #include "libc/math.h" #include "libc/nt/struct/msg.h" #include "libc/time/time.h" @@ -14,8 +34,13 @@ #include "third_party/lua/lprefix.h" #include "third_party/lua/lua.h" #include "third_party/lua/lualib.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ #undef PI #define PI (l_mathop(3.141592653589793238462643383279502884)) diff --git a/third_party/lua/lmem.c b/third_party/lua/lmem.c index 58a98fbe9..c83b25f86 100644 --- a/third_party/lua/lmem.c +++ b/third_party/lua/lmem.c @@ -1,12 +1,32 @@ -/* -** $Id: lmem.c $ -** Interface to Memory Manager -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lmem_c #define LUA_CORE - #include "third_party/lua/ldebug.h" #include "third_party/lua/ldo.h" #include "third_party/lua/lgc.h" @@ -15,8 +35,13 @@ #include "third_party/lua/lprefix.h" #include "third_party/lua/lstate.h" #include "third_party/lua/lua.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ #if defined(EMERGENCYGCTESTS) /* diff --git a/third_party/lua/lmem.h b/third_party/lua/lmem.h index 3b4b66dd2..e3b4f4d01 100644 --- a/third_party/lua/lmem.h +++ b/third_party/lua/lmem.h @@ -1,9 +1,3 @@ -/* -** $Id: lmem.h $ -** Interface to Memory Manager -** See Copyright Notice in lua.h -*/ - #ifndef lmem_h #define lmem_h diff --git a/third_party/lua/loadlib.c b/third_party/lua/loadlib.c index ae7631f9a..10b69877c 100644 --- a/third_party/lua/loadlib.c +++ b/third_party/lua/loadlib.c @@ -1,24 +1,52 @@ +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#define loadlib_c +#define LUA_LIB +#include "third_party/lua/lauxlib.h" +#include "third_party/lua/lprefix.h" +#include "third_party/lua/lua.h" +#include "third_party/lua/lualib.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); + /* -** $Id: loadlib.c $ -** Dynamic library loader for Lua -** See Copyright Notice in lua.h -** ** This module contains an implementation of loadlib for Unix systems ** that have dlfcn, an implementation for Windows, and a stub for other ** systems. */ -#define loadlib_c -#define LUA_LIB - -#include "third_party/lua/lauxlib.h" -#include "third_party/lua/lprefix.h" -#include "third_party/lua/lua.h" -#include "third_party/lua/lualib.h" const char *g_lua_path_default = LUA_PATH_DEFAULT; -/* clang-format off */ /* ** LUA_IGMARK is a mark to ignore all before it when building the diff --git a/third_party/lua/lobject.c b/third_party/lua/lobject.c index ff67a677c..efadd9002 100644 --- a/third_party/lua/lobject.c +++ b/third_party/lua/lobject.c @@ -1,12 +1,32 @@ -/* -** $Id: lobject.c $ -** Some generic functions over Lua objects -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lobject_c #define LUA_CORE - #include "libc/intrin/kprintf.h" #include "third_party/lua/lctype.h" #include "third_party/lua/ldebug.h" @@ -18,8 +38,12 @@ #include "third_party/lua/lstring.h" #include "third_party/lua/lua.h" #include "third_party/lua/lvm.h" +// clang-format off -/* clang-format off */ +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); static lua_Integer intarith (lua_State *L, int op, lua_Integer v1, diff --git a/third_party/lua/lobject.h b/third_party/lua/lobject.h index 0514a80fe..2f784103d 100644 --- a/third_party/lua/lobject.h +++ b/third_party/lua/lobject.h @@ -1,9 +1,3 @@ -/* -** $Id: lobject.h $ -** Type definitions for Lua objects -** See Copyright Notice in lua.h -*/ - #ifndef lobject_h #define lobject_h diff --git a/third_party/lua/lopcodes.c b/third_party/lua/lopcodes.c index f9eca6ae5..9af17c352 100644 --- a/third_party/lua/lopcodes.c +++ b/third_party/lua/lopcodes.c @@ -1,16 +1,41 @@ -/* -** $Id: lopcodes.c $ -** Opcodes for Lua virtual machine -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lopcodes_c #define LUA_CORE - #include "third_party/lua/lopcodes.h" #include "third_party/lua/lprefix.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ /* ORDER OP */ diff --git a/third_party/lua/lopcodes.h b/third_party/lua/lopcodes.h index 2c5ef28db..7ae716b85 100644 --- a/third_party/lua/lopcodes.h +++ b/third_party/lua/lopcodes.h @@ -1,9 +1,3 @@ -/* -** $Id: lopcodes.h $ -** Opcodes for Lua virtual machine -** See Copyright Notice in lua.h -*/ - #ifndef lopcodes_h #define lopcodes_h diff --git a/third_party/lua/loslib.c b/third_party/lua/loslib.c index 1373637fe..314d0308d 100644 --- a/third_party/lua/loslib.c +++ b/third_party/lua/loslib.c @@ -1,12 +1,32 @@ -/* -** $Id: loslib.c $ -** Standard Operating System library -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define loslib_c #define LUA_LIB - #include "libc/calls/calls.h" #include "libc/calls/weirdtypes.h" #include "libc/errno.h" @@ -20,8 +40,13 @@ #include "third_party/lua/lprefix.h" #include "third_party/lua/lua.h" #include "third_party/lua/lualib.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ /* ** {================================================================== diff --git a/third_party/lua/lparser.c b/third_party/lua/lparser.c index 4cf90cca2..ca933328e 100644 --- a/third_party/lua/lparser.c +++ b/third_party/lua/lparser.c @@ -1,12 +1,32 @@ -/* -** $Id: lparser.c $ -** Lua Parser -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lparser_c #define LUA_CORE - #include "third_party/lua/lcode.h" #include "third_party/lua/ldebug.h" #include "third_party/lua/ldo.h" @@ -21,8 +41,12 @@ #include "third_party/lua/lstring.h" #include "third_party/lua/ltable.h" #include "third_party/lua/lua.h" +// clang-format off -/* clang-format off */ +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); /* maximum number of local variables per function (must be smaller diff --git a/third_party/lua/lparser.h b/third_party/lua/lparser.h index f6244474c..96ad3bc31 100644 --- a/third_party/lua/lparser.h +++ b/third_party/lua/lparser.h @@ -1,9 +1,3 @@ -/* -** $Id: lparser.h $ -** Lua Parser -** See Copyright Notice in lua.h -*/ - #ifndef lparser_h #define lparser_h diff --git a/third_party/lua/lprefix.h b/third_party/lua/lprefix.h index 484f2ad6f..373580fe4 100644 --- a/third_party/lua/lprefix.h +++ b/third_party/lua/lprefix.h @@ -1,45 +1,36 @@ -/* -** $Id: lprefix.h $ -** Definitions for Lua code that must come before any other header file -** See Copyright Notice in lua.h -*/ - #ifndef lprefix_h #define lprefix_h - /* ** Allows POSIX/XSI stuff */ -#if !defined(LUA_USE_C89) /* { */ +#if !defined(LUA_USE_C89) /* { */ #if !defined(_XOPEN_SOURCE) -#define _XOPEN_SOURCE 600 +#define _XOPEN_SOURCE 600 #elif _XOPEN_SOURCE == 0 -#undef _XOPEN_SOURCE /* use -D_XOPEN_SOURCE=0 to undefine it */ +#undef _XOPEN_SOURCE /* use -D_XOPEN_SOURCE=0 to undefine it */ #endif /* ** Allows manipulation of large files in gcc and some other compilers */ #if !defined(LUA_32BITS) && !defined(_FILE_OFFSET_BITS) -#define _LARGEFILE_SOURCE 1 -#define _FILE_OFFSET_BITS 64 +#define _LARGEFILE_SOURCE 1 +#define _FILE_OFFSET_BITS 64 #endif -#endif /* } */ - +#endif /* } */ /* ** Windows stuff */ -#if defined(_WIN32) /* { */ +#if defined(_WIN32) /* { */ #if !defined(_CRT_SECURE_NO_WARNINGS) -#define _CRT_SECURE_NO_WARNINGS /* avoid warnings about ISO C functions */ +#define _CRT_SECURE_NO_WARNINGS /* avoid warnings about ISO C functions */ #endif -#endif /* } */ +#endif /* } */ #endif - diff --git a/third_party/lua/lrepl.c b/third_party/lua/lrepl.c index 4b446c088..0fb0d1372 100644 --- a/third_party/lua/lrepl.c +++ b/third_party/lua/lrepl.c @@ -1,9 +1,36 @@ +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lua_c +#include "libc/alg/alg.h" #include "libc/calls/calls.h" #include "libc/calls/sigbits.h" #include "libc/intrin/kprintf.h" #include "libc/intrin/nomultics.internal.h" -#include "libc/intrin/spinlock.h" #include "libc/log/check.h" #include "libc/runtime/gc.h" #include "libc/runtime/runtime.h" @@ -12,13 +39,21 @@ #include "third_party/linenoise/linenoise.h" #include "third_party/lua/lauxlib.h" #include "third_party/lua/lprefix.h" +#include "third_party/lua/lrepl.h" #include "third_party/lua/lua.h" #include "third_party/lua/lualib.h" // clang-format off +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); + + bool lua_repl_blocking; bool lua_repl_isterminal; -_Alignas(64) char lualock; +linenoiseCompletionCallback *lua_repl_completions_callback; +_Alignas(64) char lua_repl_lock; struct linenoiseState *lua_repl_linenoise; static lua_State *globalL; static const char *g_progname; @@ -63,6 +98,7 @@ void lua_readline_completions(const char *p, linenoiseCompletions *c) { lua_pop(L, 1); } lua_pop(L, 1); + lua_repl_completions_callback(p, c); } char *lua_readline_hint(const char *p, const char **ansi1, const char **ansi2) { @@ -128,7 +164,7 @@ static ssize_t pushline (lua_State *L, int firstline) { if (lua_repl_isterminal) { prmt = strdup(get_prompt(L, firstline)); lua_pop(L, 1); /* remove prompt */ - _spunlock(&lualock); + LUA_REPL_UNLOCK; rc = linenoiseEdit(lua_repl_linenoise, prmt, &b, !firstline || lua_repl_blocking); free(prmt); if (rc != -1) { @@ -138,11 +174,11 @@ static ssize_t pushline (lua_State *L, int firstline) { linenoiseHistorySave(g_historypath); } } - _spinlock(&lualock); + LUA_REPL_LOCK; } else { - _spunlock(&lualock); + LUA_REPL_UNLOCK; b = linenoiseGetLine(stdin); - _spinlock(&lualock); + LUA_REPL_LOCK; rc = b ? 1 : -1; } if (rc == -1 || (!rc && !b)) { @@ -208,7 +244,7 @@ static int multiline (lua_State *L) { void lua_initrepl(lua_State *L, const char *progname) { const char *prompt; - _spinlock(&lualock); + LUA_REPL_LOCK; g_progname = progname; if ((lua_repl_isterminal = linenoiseIsTerminal())) { linenoiseSetCompletionCallback(lua_readline_completions); @@ -224,20 +260,18 @@ void lua_initrepl(lua_State *L, const char *progname) { } lua_repl_linenoise = linenoiseBegin(prompt, 0, 1); lua_pop(L, 1); /* remove prompt */ - __nomultics = 2; __replmode = true; } - _spunlock(&lualock); + LUA_REPL_UNLOCK; } void lua_freerepl(void) { - _spinlock(&lualock); - __nomultics = false; + LUA_REPL_LOCK; __replmode = false; linenoiseEnd(lua_repl_linenoise); free(g_historypath); - _spunlock(&lualock); + LUA_REPL_UNLOCK; } @@ -254,16 +288,16 @@ int lua_loadline (lua_State *L) { ssize_t rc; int status; lua_settop(L, 0); - _spinlock(&lualock); + LUA_REPL_LOCK; if ((rc = pushline(L, 1)) != 1) { - _spunlock(&lualock); + LUA_REPL_UNLOCK; return rc - 1; /* eof or error */ } if ((status = addreturn(L)) != LUA_OK) /* 'return ...' did not work? */ status = multiline(L); /* try as command, maybe with continuation lines */ lua_remove(L, 1); /* remove line from the stack */ lua_assert(lua_gettop(L) == 1); - _spunlock(&lualock); + LUA_REPL_UNLOCK; return status; } diff --git a/third_party/lua/lrepl.h b/third_party/lua/lrepl.h index f1d6ac11c..5de303a7a 100644 --- a/third_party/lua/lrepl.h +++ b/third_party/lua/lrepl.h @@ -1,14 +1,29 @@ #ifndef COSMOPOLITAN_THIRD_PARTY_LUA_LREPL_H_ #define COSMOPOLITAN_THIRD_PARTY_LUA_LREPL_H_ +#include "libc/dce.h" +#include "libc/intrin/spinlock.h" #include "third_party/linenoise/linenoise.h" #include "third_party/lua/lauxlib.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -extern char lualock; +#if !defined(STATIC) && SupportsWindows() +#define LUA_REPL_LOCK _spinlock(&lua_repl_lock) +#else +#define LUA_REPL_LOCK (void)0 +#endif + +#if !defined(STATIC) && SupportsWindows() +#define LUA_REPL_UNLOCK _spunlock(&lua_repl_lock) +#else +#define LUA_REPL_UNLOCK (void)0 +#endif + +extern char lua_repl_lock; extern bool lua_repl_blocking; extern bool lua_repl_isterminal; extern struct linenoiseState *lua_repl_linenoise; +extern linenoiseCompletionCallback *lua_repl_completions_callback; void lua_freerepl(void); int lua_loadline(lua_State *); diff --git a/third_party/lua/lstate.c b/third_party/lua/lstate.c index db3871d69..100766934 100644 --- a/third_party/lua/lstate.c +++ b/third_party/lua/lstate.c @@ -1,12 +1,32 @@ -/* -** $Id: lstate.c $ -** Global State -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lstate_c #define LUA_CORE - #include "libc/time/time.h" #include "third_party/lua/lapi.h" #include "third_party/lua/ldebug.h" @@ -21,8 +41,13 @@ #include "third_party/lua/ltable.h" #include "third_party/lua/ltm.h" #include "third_party/lua/lua.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ /* ** thread state + extra space diff --git a/third_party/lua/lstate.h b/third_party/lua/lstate.h index e5d5096ef..b0b7cd17f 100644 --- a/third_party/lua/lstate.h +++ b/third_party/lua/lstate.h @@ -1,9 +1,3 @@ -/* -** $Id: lstate.h $ -** Global State -** See Copyright Notice in lua.h -*/ - #ifndef lstate_h #define lstate_h diff --git a/third_party/lua/lstring.c b/third_party/lua/lstring.c index 3ea66a5f8..d5cfcf764 100644 --- a/third_party/lua/lstring.c +++ b/third_party/lua/lstring.c @@ -1,12 +1,32 @@ -/* -** $Id: lstring.c $ -** String table (keeps all strings handled by Lua) -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lstring_c #define LUA_CORE - #include "third_party/lua/ldebug.h" #include "third_party/lua/ldo.h" #include "third_party/lua/lmem.h" @@ -15,8 +35,13 @@ #include "third_party/lua/lstate.h" #include "third_party/lua/lstring.h" #include "third_party/lua/lua.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ /* ** Maximum size for string table. diff --git a/third_party/lua/lstring.h b/third_party/lua/lstring.h index 00d632865..d9cd11ada 100644 --- a/third_party/lua/lstring.h +++ b/third_party/lua/lstring.h @@ -1,9 +1,3 @@ -/* -** $Id: lstring.h $ -** String table (keep all strings handled by Lua) -** See Copyright Notice in lua.h -*/ - #ifndef lstring_h #define lstring_h diff --git a/third_party/lua/lstrlib.c b/third_party/lua/lstrlib.c index 0b503196b..e49444ea2 100644 --- a/third_party/lua/lstrlib.c +++ b/third_party/lua/lstrlib.c @@ -1,19 +1,44 @@ -/* -** $Id: lstrlib.c $ -** Standard library for string operations and pattern-matching -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lstrlib_c #define LUA_LIB - #include "libc/math.h" +#include "third_party/lua/cosmo.h" #include "third_party/lua/lauxlib.h" #include "third_party/lua/lprefix.h" #include "third_party/lua/lua.h" #include "third_party/lua/lualib.h" +// clang-format off -/* clang-format off */ +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); /* @@ -41,6 +66,8 @@ +static int str_format(lua_State *); + static int str_len (lua_State *L) { size_t l; @@ -297,7 +324,21 @@ static int arith_mul (lua_State *L) { } static int arith_mod (lua_State *L) { - return arith(L, LUA_OPMOD, "__mod"); + int i, n; + if (lua_istable(L, 2)) { // [jart] python printf operator + lua_len(L, 2); + n = lua_tointeger(L, -1); + lua_pop(L, 1); + lua_pushcfunction(L, str_format); + lua_pushvalue(L, 1); + for (i = 1; i <= n; ++i) { + lua_geti(L, 2, i); + } + lua_call(L, 1 + n, 1); + return 1; + } else { + return arith(L, LUA_OPMOD, "__mod"); + } } static int arith_pow (lua_State *L) { @@ -536,7 +577,7 @@ static const char *start_capture (MatchState *ms, const char *s, static const char *end_capture (MatchState *ms, const char *s, - const char *p) { + const char *p) { int l = capture_to_close(ms); const char *res; ms->capture[l].len = s - ms->capture[l].init; /* close capture */ diff --git a/third_party/lua/ltable.c b/third_party/lua/ltable.c index 4d021a141..a5bf84153 100644 --- a/third_party/lua/ltable.c +++ b/third_party/lua/ltable.c @@ -1,12 +1,32 @@ -/* -** $Id: ltable.c $ -** Lua tables (hash) -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define ltable_c #define LUA_CORE - #include "third_party/lua/ldebug.h" #include "third_party/lua/ldo.h" #include "third_party/lua/lgc.h" @@ -18,8 +38,13 @@ #include "third_party/lua/ltable.h" #include "third_party/lua/lua.h" #include "third_party/lua/lvm.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ /* ** Implementation of tables (aka arrays, objects, or hash tables). diff --git a/third_party/lua/ltable.h b/third_party/lua/ltable.h index f5d462601..7b4014e14 100644 --- a/third_party/lua/ltable.h +++ b/third_party/lua/ltable.h @@ -1,9 +1,3 @@ -/* -** $Id: ltable.h $ -** Lua tables (hash) -** See Copyright Notice in lua.h -*/ - #ifndef ltable_h #define ltable_h diff --git a/third_party/lua/ltablib.c b/third_party/lua/ltablib.c index 553f2a7e6..9edcc3f52 100644 --- a/third_party/lua/ltablib.c +++ b/third_party/lua/ltablib.c @@ -1,20 +1,45 @@ -/* -** $Id: ltablib.c $ -** Library for Table Manipulation -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define ltablib_c #define LUA_LIB - #include "libc/calls/weirdtypes.h" #include "libc/time/time.h" #include "third_party/lua/lauxlib.h" #include "third_party/lua/lprefix.h" #include "third_party/lua/lua.h" #include "third_party/lua/lualib.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ /* ** Operations that an object must define to mimic a table diff --git a/third_party/lua/ltests.c b/third_party/lua/ltests.c index 7658c8c40..6b1606902 100644 --- a/third_party/lua/ltests.c +++ b/third_party/lua/ltests.c @@ -1,12 +1,32 @@ -/* -** $Id: ltests.c $ -** Internal Module for Debugging of the Lua Implementation -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define ltests_c #define LUA_CORE - #include "third_party/lua/lapi.h" #include "third_party/lua/lauxlib.h" #include "third_party/lua/lcode.h" @@ -23,8 +43,13 @@ #include "third_party/lua/ltable.h" #include "third_party/lua/lua.h" #include "third_party/lua/lualib.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ /* ** The whole module only makes sense with LUA_DEBUG on diff --git a/third_party/lua/ltests.h b/third_party/lua/ltests.h index b8fab980a..20fe75070 100644 --- a/third_party/lua/ltests.h +++ b/third_party/lua/ltests.h @@ -1,12 +1,5 @@ -/* -** $Id: ltests.h $ -** Internal Header for Debugging of the Lua Implementation -** See Copyright Notice in lua.h -*/ - #ifndef ltests_h #define ltests_h - #include "third_party/lua/lua.h" /* clang-format off */ diff --git a/third_party/lua/ltm.c b/third_party/lua/ltm.c index 75f67c5f6..25becb214 100644 --- a/third_party/lua/ltm.c +++ b/third_party/lua/ltm.c @@ -1,12 +1,32 @@ -/* -** $Id: ltm.c $ -** Tag methods -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define ltm_c #define LUA_CORE - #include "third_party/lua/ldebug.h" #include "third_party/lua/ldo.h" #include "third_party/lua/lgc.h" @@ -18,8 +38,13 @@ #include "third_party/lua/ltm.h" #include "third_party/lua/lua.h" #include "third_party/lua/lvm.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ static const char udatatypename[] = "userdata"; diff --git a/third_party/lua/ltm.h b/third_party/lua/ltm.h index aa8c3785e..0bc15fddf 100644 --- a/third_party/lua/ltm.h +++ b/third_party/lua/ltm.h @@ -1,16 +1,8 @@ -/* -** $Id: ltm.h $ -** Tag methods -** See Copyright Notice in lua.h -*/ - #ifndef ltm_h #define ltm_h - #include "third_party/lua/lobject.h" #include "third_party/lua/luaconf.h" #include "third_party/lua/tms.h" - /* clang-format off */ /* diff --git a/third_party/lua/lua.main.c b/third_party/lua/lua.main.c index 876d78ec7..afbc7280b 100644 --- a/third_party/lua/lua.main.c +++ b/third_party/lua/lua.main.c @@ -1,11 +1,31 @@ -/* -** $Id: lua.c $ -** Lua stand-alone interpreter -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lua_c - #include "libc/calls/calls.h" #include "libc/calls/sigbits.h" #include "libc/calls/struct/sigaction.h" @@ -28,11 +48,15 @@ #include "third_party/lua/lrepl.h" #include "third_party/lua/lua.h" #include "third_party/lua/lualib.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); STATIC_STACK_SIZE(0x40000); -/* clang-format off */ - #if !defined(LUA_PROGNAME) #define LUA_PROGNAME "lua" #endif diff --git a/third_party/lua/luac.main.c b/third_party/lua/luac.main.c index 7b3c23470..83b11dcbc 100644 --- a/third_party/lua/luac.main.c +++ b/third_party/lua/luac.main.c @@ -1,12 +1,32 @@ -/* -** $Id: luac.c $ -** Lua compiler (saves bytecodes to files; also lists bytecodes) -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define luac_c #define LUA_CORE - #include "libc/calls/calls.h" #include "libc/calls/sigbits.h" #include "libc/calls/struct/sigaction.h" @@ -22,8 +42,13 @@ #include "third_party/lua/lua.h" #include "third_party/lua/lualib.h" #include "third_party/lua/lundump.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ static void PrintFunction(const Proto* f, int full); #define luaU_print PrintFunction diff --git a/third_party/lua/lualib.h b/third_party/lua/lualib.h index 7fa19122d..47dfc39e3 100644 --- a/third_party/lua/lualib.h +++ b/third_party/lua/lualib.h @@ -1,12 +1,5 @@ -/* -** $Id: lualib.h $ -** Lua standard libraries -** See Copyright Notice in lua.h -*/ - #ifndef lualib_h #define lualib_h - #include "third_party/lua/lua.h" /* clang-format off */ diff --git a/third_party/lua/luaprintstack.c b/third_party/lua/luaprintstack.c new file mode 100644 index 000000000..6d286fb4b --- /dev/null +++ b/third_party/lua/luaprintstack.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 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/stdio/stdio.h" +#include "third_party/lua/cosmo.h" +#include "third_party/lua/lua.h" + +/** + * Development tool for quickly viewing the Lua stack. + */ +void LuaPrintStack(lua_State *L) { + char *s = LuaFormatStack(L); + fputs(s, stderr); + fputc('\n', stderr); + free(s); +} diff --git a/third_party/lua/lundump.h b/third_party/lua/lundump.h index ee0dbe0ab..84fa58ab3 100644 --- a/third_party/lua/lundump.h +++ b/third_party/lua/lundump.h @@ -1,12 +1,5 @@ -/* -** $Id: lundump.h $ -** load precompiled Lua chunks -** See Copyright Notice in lua.h -*/ - #ifndef lundump_h #define lundump_h - #include "third_party/lua/llimits.h" #include "third_party/lua/lobject.h" #include "third_party/lua/lzio.h" diff --git a/third_party/lua/lvm.c b/third_party/lua/lvm.c index 7780b974a..f3bf1e711 100644 --- a/third_party/lua/lvm.c +++ b/third_party/lua/lvm.c @@ -1,12 +1,32 @@ -/* -** $Id: lvm.c $ -** Lua virtual machine -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lvm_c #define LUA_CORE - #include "third_party/lua/ldebug.h" #include "third_party/lua/ldo.h" #include "third_party/lua/lfunc.h" @@ -20,8 +40,13 @@ #include "third_party/lua/ltm.h" #include "third_party/lua/lua.h" #include "third_party/lua/lvm.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ /* ** By default, use jump tables in the main interpreter loop on gcc diff --git a/third_party/lua/lvm.h b/third_party/lua/lvm.h index 8fd25ed22..9abf879ce 100644 --- a/third_party/lua/lvm.h +++ b/third_party/lua/lvm.h @@ -1,12 +1,5 @@ -/* -** $Id: lvm.h $ -** Lua virtual machine -** See Copyright Notice in lua.h -*/ - #ifndef lvm_h #define lvm_h - #include "third_party/lua/ldo.h" #include "third_party/lua/lobject.h" #include "third_party/lua/ltm.h" diff --git a/third_party/lua/lzio.c b/third_party/lua/lzio.c index 622bf60b1..4857b0957 100644 --- a/third_party/lua/lzio.c +++ b/third_party/lua/lzio.c @@ -1,20 +1,45 @@ -/* -** $Id: lzio.c $ -** Buffered streams -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lzio_c #define LUA_CORE - #include "third_party/lua/llimits.h" #include "third_party/lua/lmem.h" #include "third_party/lua/lprefix.h" #include "third_party/lua/lstate.h" #include "third_party/lua/lua.h" #include "third_party/lua/lzio.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ int luaZ_fill (ZIO *z) { size_t size; diff --git a/third_party/python/Modules/termios.c b/third_party/python/Modules/termios.c index 63814c1b1..31a9a09ba 100644 --- a/third_party/python/Modules/termios.c +++ b/third_party/python/Modules/termios.c @@ -603,7 +603,7 @@ PyInit_termios(void) if (IXANY) PyModule_AddIntConstant(m, "IXANY", IXANY); if (IXOFF) PyModule_AddIntConstant(m, "IXOFF", IXOFF); if (IMAXBEL) PyModule_AddIntConstant(m, "IMAXBEL", IMAXBEL); - if (OPOST) PyModule_AddIntConstant(m, "OPOST", OPOST); + PyModule_AddIntConstant(m, "OPOST", OPOST); if (OLCUC) PyModule_AddIntConstant(m, "OLCUC", OLCUC); if (ONLCR) PyModule_AddIntConstant(m, "ONLCR", ONLCR); if (OCRNL) PyModule_AddIntConstant(m, "OCRNL", OCRNL); diff --git a/third_party/python/pyobj.c b/third_party/python/pyobj.c index 47de11a58..0429a2485 100644 --- a/third_party/python/pyobj.c +++ b/third_party/python/pyobj.c @@ -35,6 +35,7 @@ #include "libc/sysv/consts/o.h" #include "libc/time/time.h" #include "libc/x/x.h" +#include "libc/zip.h" #include "third_party/getopt/getopt.h" #include "third_party/python/Include/abstract.h" #include "third_party/python/Include/bytesobject.h" @@ -658,16 +659,19 @@ Objectify(void) if (ispkg) { elfwriter_zip(elf, zipdir, zipdir, strlen(zipdir), pydata, 0, 040755, timestamp, timestamp, - timestamp, nocompress, image_base); + timestamp, nocompress, image_base, + kZipCdirHdrLinkableSize); } if (!binonly) { elfwriter_zip(elf, gc(xstrcat("py:", modname)), zipfile, strlen(zipfile), pydata, pysize, st.st_mode, timestamp, - timestamp, timestamp, nocompress, image_base); + timestamp, timestamp, nocompress, image_base, + kZipCdirHdrLinkableSize); } elfwriter_zip(elf, gc(xstrcat("pyc:", modname)), gc(xstrcat(zipfile, 'c')), strlen(zipfile) + 1, pycdata, pycsize, st.st_mode, timestamp, - timestamp, timestamp, nocompress, image_base); + timestamp, timestamp, nocompress, image_base, + kZipCdirHdrLinkableSize); elfwriter_align(elf, 1, 0); elfwriter_startsection(elf, ".yoink", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR); diff --git a/third_party/python/repl.c b/third_party/python/repl.c index 45aae5174..addf235ca 100644 --- a/third_party/python/repl.c +++ b/third_party/python/repl.c @@ -87,7 +87,7 @@ CompleteModule(const char *s, const char *p, linenoiseCompletions *c) PyObject *m, *f, *g, *i, *v, *n; plen = strlen(p); for (it = PyImport_Inittab; it->name; ++it) { - if (startswith(it->name, p)) { + if (startswithi(it->name, p)) { AddCompletion(c, xasprintf("%s%s", s, it->name + plen)); } } @@ -98,7 +98,7 @@ CompleteModule(const char *s, const char *p, linenoiseCompletions *c) while ((v = PyIter_Next(i))) { if ((n = PyObject_GetAttrString(v, "name"))) { if (((name = PyUnicode_AsUTF8AndSize(n, &namelen)) && - namelen >= plen && !bcmp(name, p, plen))) { + namelen >= plen && !memcasecmp(name, p, plen))) { AddCompletion(c, xasprintf("%s%s", s, name + plen)); } Py_DECREF(n); @@ -125,7 +125,7 @@ CompleteDict(const char *b, const char *q, const char *p, for (i = 0; PyDict_Next(o, &i, &k, &v);) { if ((v != Py_None && PyUnicode_Check(k) && (s = PyUnicode_AsUTF8AndSize(k, &m)) && - m >= q - p && !bcmp(s, p, q - p))) { + m >= q - p && !memcasecmp(s, p, q - p))) { AddCompletion(c, xasprintf("%.*s%.*s", p - b, b, m, s)); } } @@ -142,7 +142,10 @@ CompleteDir(const char *b, const char *q, const char *p, if ((i = PyObject_GetIter(d))) { while ((k = PyIter_Next(i))) { if (((s = PyUnicode_AsUTF8AndSize(k, &m)) && - m >= q - p && !bcmp(s, p, q - p))) { + m >= q - p && !memcasecmp(s, p, q - p) && + !(q - p == 0 && m > 4 && + (s[0+0] == '_' && s[0+1] == '_' && + s[m-1] == '_' && s[m-2] == '_')))) { AddCompletion(c, xasprintf("%.*s%.*s", p - b, b, m, s)); } Py_DECREF(k); diff --git a/third_party/zip/zip.c b/third_party/zip/zip.c index 10a74456a..b7624f230 100644 --- a/third_party/zip/zip.c +++ b/third_party/zip/zip.c @@ -34,6 +34,7 @@ #include "libc/log/log.h" #include "libc/stdio/stdio.h" #include "libc/stdio/temp.h" +#include "libc/runtime/runtime.h" #include "third_party/bzip2/bzlib.h" #define MAXCOM 256 /* Maximum one-line comment size */ @@ -646,13 +647,6 @@ local void help() } } -static const char *GetPagerPath(char path[PATH_MAX]) { - const char *s; - if ((s = commandv("less", path))) return s; - if ((s = commandv("more", path))) return s; - return 0; -} - #ifdef VMSCLI void help_extended() #else @@ -660,10 +654,7 @@ local void help_extended() #endif /* Print extended help to stdout. */ { - extent i; /* counter for help array */ - - /* help array */ - const char *text = "\n\ + __paginate(1, "\n\ Extended Help for Zip\n\ \n\ See the Zip Manual for more detailed help\n\ @@ -986,27 +977,7 @@ More option highlights (see manual for additional options and details):\n\ -so show all available options on this system\n\ -X default=strip old extra fields, -X- keep old, -X strip most\n\ -ws wildcards don't span directory boundaries in paths\n\ -"; - - int pip[2]; - char *args[2] = {0}; - char pathbuf[PATH_MAX]; - if (isatty(0) && isatty(1) && (args[0] = GetPagerPath(pathbuf))) { - sigaction(SIGPIPE, &(struct sigaction){.sa_handler = SIG_IGN}, 0); - close(0); - pipe(pip); - if (!fork()) { - close(pip[1]); - execv(args[0], args); - _Exit(127); - } - close(0); - write(pip[1], text, strlen(text)); - close(pip[1]); - wait(0); - } else { - fputs(text, stdout); - } +"); exit(0); } diff --git a/tool/build/compile.c b/tool/build/compile.c index 747a57b9d..a30fde12f 100644 --- a/tool/build/compile.c +++ b/tool/build/compile.c @@ -173,7 +173,7 @@ char *shortened; char *cachedcmd; char *colorflag; char *originalcmd; -char ccpath[PATH_MAX]; +char ccpath[PATH_MAX + 1]; struct stat st; struct Strings env; @@ -811,7 +811,7 @@ int main(int argc, char *argv[]) { cmd = argv[optind]; if (!strchr(cmd, '/')) { - if (!(cmd = commandv(cmd, ccpath))) exit(127); + if (!(cmd = commandv(cmd, ccpath, sizeof(ccpath)))) exit(127); } s = basename(strdup(cmd)); diff --git a/tool/build/lib/demangle.c b/tool/build/lib/demangle.c index 5206651e3..3f702da00 100644 --- a/tool/build/lib/demangle.c +++ b/tool/build/lib/demangle.c @@ -42,9 +42,9 @@ void CloseCxxFilt(void) { void SpawnCxxFilt(void) { int pipefds[2][2]; const char *cxxfilt; - char path[PATH_MAX]; + char path[PATH_MAX + 1]; cxxfilt = firstnonnull(emptytonull(getenv("CXXFILT")), "c++filt"); - if (commandv(cxxfilt, path)) { + if (commandv(cxxfilt, path, sizeof(path))) { pipe2(pipefds[0], O_CLOEXEC); pipe2(pipefds[1], O_CLOEXEC); if (!(g_cxxfilt.pid = vfork())) { diff --git a/tool/build/lib/elfwriter.h b/tool/build/lib/elfwriter.h index 8a0d4583f..62b3277c1 100644 --- a/tool/build/lib/elfwriter.h +++ b/tool/build/lib/elfwriter.h @@ -73,7 +73,7 @@ void elfwriter_yoink(struct ElfWriter *, const char *, int); void elfwriter_setsection(struct ElfWriter *, struct ElfWriterSymRef, uint16_t); void elfwriter_zip(struct ElfWriter *, const char *, const char *, size_t, const void *, size_t, uint32_t, struct timespec, - struct timespec, struct timespec, bool, uint64_t); + struct timespec, struct timespec, bool, uint64_t, size_t); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/tool/build/lib/elfwriter_zip.c b/tool/build/lib/elfwriter_zip.c index 3501bb0d1..79e2e7971 100644 --- a/tool/build/lib/elfwriter_zip.c +++ b/tool/build/lib/elfwriter_zip.c @@ -136,7 +136,8 @@ static void EmitZipCdirHdr(unsigned char *p, const void *name, size_t namesize, void elfwriter_zip(struct ElfWriter *elf, const char *symbol, const char *name, size_t namesize, const void *data, size_t size, uint32_t mode, struct timespec mtim, struct timespec atim, - struct timespec ctim, bool nocompress, uint64_t imagebase) { + struct timespec ctim, bool nocompress, uint64_t imagebase, + size_t kZipCdirHdrLinkableSizeBootstrap) { z_stream zs; uint8_t era; uint32_t crc; @@ -159,7 +160,7 @@ void elfwriter_zip(struct ElfWriter *elf, const char *symbol, const char *name, if (S_ISREG(mode) && _istext(data, size)) { iattrs |= kZipIattrText; } - commentsize = kZipCdirHdrLinkableSize - (CFILE_HDR_SIZE + namesize); + commentsize = kZipCdirHdrLinkableSizeBootstrap - (CFILE_HDR_SIZE + namesize); dosmode = !(mode & 0200) ? kNtFileAttributeReadonly : 0; method = ShouldCompress(name, namesize, data, size, nocompress) ? kZipCompressionDeflate @@ -209,15 +210,15 @@ void elfwriter_zip(struct ElfWriter *elf, const char *symbol, const char *name, elfwriter_startsection(elf, gc(xasprintf("%s%s", ZIP_DIRECTORY_SECTION, name)), SHT_PROGBITS, SHF_ALLOC); - EmitZipCdirHdr((cfile = elfwriter_reserve(elf, kZipCdirHdrLinkableSize)), - name, namesize, crc, era, gflags, method, mtime, mdate, iattrs, - dosmode, mode, compsize, uncompsize, commentsize, mtim, atim, - ctim); + EmitZipCdirHdr( + (cfile = elfwriter_reserve(elf, kZipCdirHdrLinkableSizeBootstrap)), name, + namesize, crc, era, gflags, method, mtime, mdate, iattrs, dosmode, mode, + compsize, uncompsize, commentsize, mtim, atim, ctim); elfwriter_appendsym(elf, gc(xasprintf("%s%s", "zip+cdir:", name)), ELF64_ST_INFO(STB_LOCAL, STT_OBJECT), STV_DEFAULT, 0, - kZipCdirHdrLinkableSize); + kZipCdirHdrLinkableSizeBootstrap); elfwriter_appendrela(elf, kZipCfileOffsetOffset, lfilesym, R_X86_64_32, -imagebase); - elfwriter_commit(elf, kZipCdirHdrLinkableSize); + elfwriter_commit(elf, kZipCdirHdrLinkableSizeBootstrap); elfwriter_finishsection(elf); } diff --git a/tool/build/lib/ssemov.c b/tool/build/lib/ssemov.c index cff58591e..182e64fa3 100644 --- a/tool/build/lib/ssemov.c +++ b/tool/build/lib/ssemov.c @@ -121,11 +121,11 @@ static void MovqQqPq(struct Machine *m, uint32_t rde) { static void MovqVdqEqp(struct Machine *m, uint32_t rde) { memcpy(XmmRexrReg(m, rde), GetModrmRegisterWordPointerRead8(m, rde), 8); - memset(XmmRexrReg(m, rde) + 8, 0, 8); + bzero(XmmRexrReg(m, rde) + 8, 8); } static void MovdVdqEd(struct Machine *m, uint32_t rde) { - memset(XmmRexrReg(m, rde), 0, 16); + bzero(XmmRexrReg(m, rde), 16); memcpy(XmmRexrReg(m, rde), GetModrmRegisterWordPointerRead4(m, rde), 4); } @@ -135,7 +135,7 @@ static void MovqPqEqp(struct Machine *m, uint32_t rde) { static void MovdPqEd(struct Machine *m, uint32_t rde) { memcpy(MmReg(m, rde), GetModrmRegisterWordPointerRead4(m, rde), 4); - memset(MmReg(m, rde) + 4, 0, 4); + bzero(MmReg(m, rde) + 4, 4); } static void MovdEdVdq(struct Machine *m, uint32_t rde) { @@ -168,7 +168,7 @@ static void MovntqMqPq(struct Machine *m, uint32_t rde) { static void MovqVqWq(struct Machine *m, uint32_t rde) { memcpy(XmmRexrReg(m, rde), GetModrmRegisterXmmPointerRead8(m, rde), 8); - memset(XmmRexrReg(m, rde) + 8, 0, 8); + bzero(XmmRexrReg(m, rde) + 8, 8); } static void MovssVpsWps(struct Machine *m, uint32_t rde) { @@ -176,7 +176,7 @@ static void MovssVpsWps(struct Machine *m, uint32_t 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); + bzero(XmmRexrReg(m, rde) + 4, 12); } } @@ -189,7 +189,7 @@ static void MovsdVpsWps(struct Machine *m, uint32_t 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); + bzero(XmmRexrReg(m, rde) + 8, 8); } } @@ -267,7 +267,7 @@ static void MovhpdMqVq(struct Machine *m, uint32_t rde) { static void MovqWqVq(struct Machine *m, uint32_t rde) { if (IsModrmRegister(rde)) { memcpy(XmmRexbRm(m, rde), XmmRexrReg(m, rde), 8); - memset(XmmRexbRm(m, rde) + 8, 0, 8); + bzero(XmmRexbRm(m, rde) + 8, 8); } else { memcpy(ComputeReserveAddressWrite8(m, rde), XmmRexrReg(m, rde), 8); } @@ -275,7 +275,7 @@ static void MovqWqVq(struct Machine *m, uint32_t rde) { static void Movq2dqVdqNq(struct Machine *m, uint32_t rde) { memcpy(XmmRexrReg(m, rde), MmRm(m, rde), 8); - memset(XmmRexrReg(m, rde) + 8, 0, 8); + bzero(XmmRexrReg(m, rde) + 8, 8); } static void Movdq2qPqUq(struct Machine *m, uint32_t rde) { diff --git a/tool/build/runit.c b/tool/build/runit.c index b6a31568e..eef2446e2 100644 --- a/tool/build/runit.c +++ b/tool/build/runit.c @@ -113,10 +113,10 @@ char *g_prog; char *g_runitd; jmp_buf g_jmpbuf; uint16_t g_sshport; -char g_ssh[PATH_MAX]; char g_hostname[128]; uint16_t g_runitdport; volatile bool alarmed; +char g_ssh[PATH_MAX + 1]; int __sys_execve(const char *, char *const[], char *const[]) hidden; @@ -505,7 +505,8 @@ int main(int argc, char *argv[]) { } CheckExists((g_runitd = argv[1])); CheckExists((g_prog = argv[2])); - CHECK_NOTNULL(commandv(firstnonnull(getenv("SSH"), "ssh"), g_ssh)); + CHECK_NOTNULL( + commandv(firstnonnull(getenv("SSH"), "ssh"), g_ssh, sizeof(g_ssh))); if (argc == 3) { /* hosts list empty */ return 0; diff --git a/tool/build/strace.c b/tool/build/strace.c index c7a003eed..f5d59a59d 100644 --- a/tool/build/strace.c +++ b/tool/build/strace.c @@ -660,7 +660,7 @@ static void Flush(void) { static const char *GetErrnoName(int x) { const char *s; static char buf[16]; - if ((s = strerror_short(x))) return s; + if ((s = strerrno(x))) return s; FormatInt64(buf, x); return buf; } @@ -1195,6 +1195,5 @@ wontreturn void StraceMain(int argc, char *argv[]) { } int main(int argc, char *argv[]) { - __nomultics = true; StraceMain(argc, argv); } diff --git a/tool/build/zipobj.c b/tool/build/zipobj.c index cb28134e4..bdaa29025 100644 --- a/tool/build/zipobj.c +++ b/tool/build/zipobj.c @@ -34,6 +34,7 @@ #include "libc/sysv/consts/prot.h" #include "libc/time/time.h" #include "libc/x/x.h" +#include "libc/zip.h" #include "third_party/getopt/getopt.h" #include "tool/build/lib/elfwriter.h" #include "tool/build/lib/stripcomponents.h" @@ -48,6 +49,7 @@ int64_t image_base_; int strip_components_; const char *path_prefix_; struct timespec timestamp; +size_t kZipCdirHdrLinkableSizeBootstrap; wontreturn void PrintUsage(int rc, FILE *f) { fprintf(f, "%s%s%s\n", "Usage: ", program_invocation_name, @@ -60,7 +62,8 @@ void GetOpts(int *argc, char ***argv) { int opt; yoink_ = "__zip_start"; image_base_ = IMAGE_BASE_VIRTUAL; - while ((opt = getopt(*argc, *argv, "?0nhBN:C:P:o:s:y:b:")) != -1) { + kZipCdirHdrLinkableSizeBootstrap = kZipCdirHdrLinkableSize; + while ((opt = getopt(*argc, *argv, "?0nhBL:N:C:P:o:s:y:b:")) != -1) { switch (opt) { case 'o': outpath_ = optarg; @@ -91,6 +94,9 @@ void GetOpts(int *argc, char ***argv) { case '0': nocompress_ = true; break; + case 'L': + kZipCdirHdrLinkableSizeBootstrap = strtoul(optarg, NULL, 0); + break; case '?': case 'h': PrintUsage(EXIT_SUCCESS, stdout); @@ -135,7 +141,8 @@ void ProcessFile(struct ElfWriter *elf, const char *path) { } } elfwriter_zip(elf, name, name, strlen(name), map, st.st_size, st.st_mode, - timestamp, timestamp, timestamp, nocompress_, image_base_); + timestamp, timestamp, timestamp, nocompress_, image_base_, + kZipCdirHdrLinkableSizeBootstrap); if (st.st_size) CHECK_NE(-1, munmap(map, st.st_size)); close(fd); } @@ -149,7 +156,8 @@ void PullEndOfCentralDirectoryIntoLinkage(struct ElfWriter *elf) { } void CheckFilenameKosher(const char *path) { - CHECK_LE(strlen(path), PATH_MAX); + CHECK_LE(kZipCfileHdrMinSize + strlen(path), + kZipCdirHdrLinkableSizeBootstrap); CHECK(!startswith(path, "/")); CHECK(!strstr(path, "..")); } diff --git a/tool/decode/ent.c b/tool/decode/ent.c index b6bf0e39b..856989371 100644 --- a/tool/decode/ent.c +++ b/tool/decode/ent.c @@ -151,7 +151,7 @@ int main(int argc, char *argv[]) } samp = binary ? "bit" : "byte"; - memset(ccount, 0, sizeof ccount); + bzero(ccount, sizeof ccount); /* Initialise for calculations */ diff --git a/tool/decode/lib/bitabuilder.c b/tool/decode/lib/bitabuilder.c index 874d5e797..1174607c1 100644 --- a/tool/decode/lib/bitabuilder.c +++ b/tool/decode/lib/bitabuilder.c @@ -58,7 +58,7 @@ bool bitabuilder_setbit(struct BitaBuilder *bb, size_t bit) { if (i > bb->n) { n = i + (i >> 2); if ((p2 = realloc(bb->p, n))) { - memset((char *)p2 + bb->n, 0, n - bb->n); + bzero((char *)p2 + bb->n, n - bb->n); bb->n = n; bb->p = p2; } else { diff --git a/tool/decode/lib/disassemblehex.c b/tool/decode/lib/disassemblehex.c index 8d0c5def2..4a7b0a964 100644 --- a/tool/decode/lib/disassemblehex.c +++ b/tool/decode/lib/disassemblehex.c @@ -44,7 +44,7 @@ void disassemblehex(uint8_t *data, size_t size, FILE *f) { if (i == size) break; } fprintf(f, "\t.%s\t", "byte"); - memset(glyphs, 0, sizeof(glyphs)); + bzero(glyphs, sizeof(glyphs)); } /* TODO(jart): Fix Emacs */ glyphs[col] = kCp437[ch == '"' || ch == '\\' || ch == '#' ? '.' : ch]; diff --git a/tool/emacs/cosmo-stuff.el b/tool/emacs/cosmo-stuff.el index 2217aea8f..c47eb24ab 100644 --- a/tool/emacs/cosmo-stuff.el +++ b/tool/emacs/cosmo-stuff.el @@ -793,6 +793,7 @@ (message header)))) (progn + (define-key prog-mode-map (kbd "C-c C-h") 'cosmo-add-include) (define-key asm-mode-map (kbd "C-c C-h") 'cosmo-add-include) (define-key c-mode-base-map (kbd "C-c C-h") 'cosmo-add-include)) diff --git a/tool/net/demo/.init.lua b/tool/net/demo/.init.lua index acb360df4..304d0f89a 100644 --- a/tool/net/demo/.init.lua +++ b/tool/net/demo/.init.lua @@ -34,3 +34,7 @@ function OnHttpRequest() end SetHeader('Server', 'redbean!') end + +function Adder(x, y) + return x + y +end diff --git a/tool/net/demo/crashreport.lua b/tool/net/demo/crashreport.lua index 1cee29e69..3a46d1611 100644 --- a/tool/net/demo/crashreport.lua +++ b/tool/net/demo/crashreport.lua @@ -12,7 +12,7 @@ function dosomething(param1, x) SetHeader('Content-Type', 'text/plain; charset=utf-8') Write('preprae to crash... now\r\n') res = x / y - Write(string.format('42 / 0 is %d\r\n', res)) + Write('42 / 0 is %d\r\n' % {res}) end function start(param1) diff --git a/tool/net/demo/fetch.lua b/tool/net/demo/fetch.lua index 7833ab3e8..ff28742aa 100644 --- a/tool/net/demo/fetch.lua +++ b/tool/net/demo/fetch.lua @@ -2,7 +2,7 @@ local function WriteForm(url) Write('\r\n') - Write(string.format([[ + Write([[ redbean fetch demo \r\n') Write('
\r\n') -Write('
getuid()\r\n') -Write(string.format('
%d\r\n', unix.getuid())) -Write('
getgid()\r\n') -Write(string.format('
%d\r\n', unix.getgid())) -Write('
getpid()\r\n') -Write(string.format('
%d\r\n', unix.getpid())) -Write('
getppid()\r\n') -Write(string.format('
%d\r\n', unix.getppid())) -Write('
getpgrp()\r\n') -Write(string.format('
%d\r\n', unix.getpgrp())) +Write('
unix.getuid()\r\n') +Write('
%d\r\n' % {unix.getuid()}) +Write('
unix.getgid()\r\n') +Write('
%d\r\n' % {unix.getgid()}) +Write('
unix.getpid()\r\n') +Write('
%d\r\n' % {unix.getpid()}) +Write('
unix.getppid()\r\n') +Write('
%d\r\n' % {unix.getppid()}) +Write('
unix.getpgrp()\r\n') +Write('
%d\r\n' % {unix.getpgrp()}) +Write('
unix.umask()\r\n') +mask = unix.umask(027) +unix.umask(mask) +Write('
%.4o\r\n' % {mask}) -Write('
getsid(0)\r\n') +Write('
unix.getsid(0)\r\n') sid, errno = unix.getsid(0) if sid then - Write(string.format('
%d\r\n', sid)) + Write('
%d\r\n' % {sid}) else - Write(string.format('
%s\r\n', EscapeHtml(unix.strerrno(errno)))) + Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) end -Write('
gethostname()\r\n') -Write(string.format('
%s\r\n', EscapeHtml(unix.gethostname()))) -Write('
getcwd()\r\n') -Write(string.format('
%s\r\n', EscapeHtml(unix.getcwd()))) +Write('
unix.gethostname()\r\n') +Write('
%s\r\n' % {EscapeHtml(unix.gethostname())}) +Write('
unix.getcwd()\r\n') +Write('
%s\r\n' % {EscapeHtml(unix.getcwd())}) function PrintResourceLimit(name, id) soft, hard, errno = unix.getrlimit(id) - Write(string.format('
getrlimit(%s)\r\n', name)) + Write('
getrlimit(%s)\r\n' % {name}) if soft then Write('
') Write('soft ') if soft == -1 then Write('∞') else - Write(string.format('%d', soft)) + Write('%d' % {soft}) end Write('
\r\n') Write('hard ') if hard == -1 then Write('∞') else - Write(string.format('%d', hard)) + Write('%d' % {hard}) end Write('\r\n') else - Write(string.format('
%s\r\n', EscapeHtml(unix.strerrno(errno)))) + Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) end end PrintResourceLimit('RLIMIT_AS', unix.RLIMIT_AS) @@ -59,7 +64,7 @@ PrintResourceLimit('RLIMIT_FSIZE', unix.RLIMIT_FSIZE) PrintResourceLimit('RLIMIT_NPROC', unix.RLIMIT_NPROC) PrintResourceLimit('RLIMIT_NOFILE', unix.RLIMIT_NOFILE) -Write('
siocgifconf()\r\n') +Write('
unix.siocgifconf()\r\n') Write('
\r\n') ifs, errno = unix.siocgifconf() if ifs then @@ -69,12 +74,217 @@ if ifs then else cidr = 0 end - Write(string.format('%s %s/%d
\r\n', - EscapeHtml(ifs[i].name), - FormatIp(ifs[i].ip), - cidr)) + Write('%s %s/%d
\r\n' % {EscapeHtml(ifs[i].name), FormatIp(ifs[i].ip), cidr}) end else - Write(string.format('%s\r\n', EscapeHtml(unix.strerrno(errno)))) + Write('%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) end -Write('
\r\n') + +errno, enabled = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_DEBUG) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_DEBUG)\r\n') +if errno then + Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) +else + Write('
%s\r\n' % {enabled}) +end + +errno, enabled = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEADDR) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEADDR)\r\n') +if errno then + Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) +else + Write('
%s\r\n' % {enabled}) +end + +errno, enabled = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEPORT) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEPORT)\r\n') +if errno then + Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) +else + Write('
%s\r\n' % {enabled}) +end + +errno, enabled = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_KEEPALIVE) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_KEEPALIVE)\r\n') +if errno then + Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) +else + Write('
%s\r\n' % {enabled}) +end + +errno, enabled = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_NODELAY) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_NODELAY)\r\n') +if errno then + Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) +else + Write('
%s\r\n' % {enabled}) +end + +errno, secs, micros = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVTIMEO) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVTIMEO)\r\n') +if errno then + Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) +else + Write('
%d sec %d µs\r\n' % {secs, micros}) +end + +errno, secs, micros = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDTIMEO) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDTIMEO)\r\n') +if errno then + Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) +else + Write('
%d sec %d µs\r\n' % {secs, micros}) +end + +errno, enabled = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_DONTROUTE) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_DONTROUTE)\r\n') +if errno then + Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) +else + Write('
%s\r\n' % {enabled}) +end + +errno, bytes = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDBUF) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDBUF)\r\n') +if errno then + Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) +else + Write('
%d\r\n' % {bytes}) +end + +errno, bytes = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVBUF) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVBUF)\r\n') +if errno then + Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) +else + Write('
%d\r\n' % {bytes}) +end + +errno, bytes = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_FASTOPEN) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_FASTOPEN)\r\n') +if errno then + Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) +else + Write('
%d\r\n' % {bytes}) +end + +errno, enabled = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_BROADCAST) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_BROADCAST)\r\n') +if errno then + Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) +else + Write('
%s\r\n' % {enabled}) +end + +errno, enabled = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_CORK) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_CORK)\r\n') +if errno then + Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) +else + Write('
%s\r\n' % {enabled}) +end + +errno, enabled = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_QUICKACK) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_QUICKACK)\r\n') +if errno then + Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) +else + Write('
%s\r\n' % {enabled}) +end + +errno, enabled = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_DEFER_ACCEPT) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_DEFER_ACCEPT)\r\n') +if errno then + Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) +else + Write('
%s\r\n' % {enabled}) +end + +errno, enabled = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_FASTOPEN_CONNECT) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_FASTOPEN_CONNECT)\r\n') +if errno then + Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) +else + Write('
%s\r\n' % {enabled}) +end + +errno, bytes = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDLOWAT) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDLOWAT)\r\n') +if errno then + Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) +else + Write('
%d\r\n' % {bytes}) +end + +errno, bytes = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVLOWAT) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVLOWAT)\r\n') +if errno then + Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) +else + Write('
%d\r\n' % {bytes}) +end + +errno, bytes = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPCNT) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPCNT)\r\n') +if errno then + Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) +else + Write('
%d\r\n' % {bytes}) +end + +errno, bytes = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_MAXSEG) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_MAXSEG)\r\n') +if errno then + Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) +else + Write('
%d\r\n' % {bytes}) +end + +errno, bytes = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_SYNCNT) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_SYNCNT)\r\n') +if errno then + Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) +else + Write('
%d\r\n' % {bytes}) +end + +errno, bytes = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_NOTSENT_LOWAT) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_NOTSENT_LOWAT)\r\n') +if errno then + Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) +else + Write('
%d\r\n' % {bytes}) +end + +errno, bytes = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_WINDOW_CLAMP) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_WINDOW_CLAMP)\r\n') +if errno then + Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) +else + Write('
%d\r\n' % {bytes}) +end + +errno, bytes = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPIDLE) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPIDLE)\r\n') +if errno then + Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) +else + Write('
%d\r\n' % {bytes}) +end + +errno, bytes = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPINTVL) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPINTVL)\r\n') +if errno then + Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) +else + Write('
%d\r\n' % {bytes}) +end + +Write('
unix.environ()\r\n') +Write('
\r\n') +Write('
    \r\n') +env = unix.environ() +for i = 1,#env do + Write('
  • %s\r\n' % {EscapeHtml(env[i])}) +end +Write('
\r\n') diff --git a/tool/net/demo/unix-rawsocket.lua b/tool/net/demo/unix-rawsocket.lua index f0b3d5147..bae11971e 100644 --- a/tool/net/demo/unix-rawsocket.lua +++ b/tool/net/demo/unix-rawsocket.lua @@ -32,8 +32,9 @@ local function main() -- steal client from redbean fd = GetClientFd() - rc, errno = unix.fork() + -- this function returns twice + pid, errno = unix.fork() if errno then SetStatus(400) SetHeader('Content-Type', 'text/html; charset=utf-8') @@ -44,7 +45,7 @@ local function main() Write(EncodeBase64(LoadAsset('/redbean.png'))) Write('">\r\n') Write('redbean unix demo\r\n') - Write(string.format(' %s\r\n', unix.strerrno(errno))) + Write(' %s\r\n' % {unix.strerrno(errno)}) Write('\r\n') Write([[

@@ -57,11 +58,13 @@ local function main() return end - if rc ~= 0 then + -- the parent process gets the pid + if pid ~= 0 then unix.close(fd) return end + -- if pid is zero then we're the child -- turn into a daemon unix.umask(0) unix.setsid() @@ -119,7 +122,7 @@ local function main() else st, err = unix.stat(name) if st then - unix.write(fd, string.format(' (%d bytes)', st:size())) + unix.write(fd, ' (%d bytes)' % {st:size()}) end end unix.write(fd, '\r\n') @@ -127,7 +130,7 @@ local function main() unix.write(fd, '\r\n') else unix.write(fd, '

\r\n') - unix.write(fd, string.format('failed: %s\r\n', EscapeHtml(VisualizeControlCodes(unix:strerror(err))))) + unix.write(fd, 'failed: %s\r\n' % {EscapeHtml(VisualizeControlCodes(unix:strerror(err)))}) unix.write(fd, '

\r\n') end diff --git a/tool/net/demo/unix-subprocess.lua b/tool/net/demo/unix-subprocess.lua index 13e14980a..5e0fbe7b9 100644 --- a/tool/net/demo/unix-subprocess.lua +++ b/tool/net/demo/unix-subprocess.lua @@ -2,15 +2,20 @@ -- and pipe its output to the http user local unix = require "unix" function main() + if GetHostOs() == 'WINDOWS' then + cmd = 'dir' + else + cmd = 'ls' + end syscall = 'commandv' - ls, errno = unix.commandv("ls") + ls, errno = unix.commandv(cmd) if ls then syscall = 'pipe' reader, writer, errno = unix.pipe() if reader then - oldint = unix.sigaction(unix.SIGINT, unix.SIG_IGN) - oldquit = unix.sigaction(unix.SIGQUIT, unix.SIG_IGN) - oldmask = unix.sigprocmask(unix.SIG_BLOCK, unix.SIGCHLD) + -- oldint = unix.sigaction(unix.SIGINT, unix.SIG_IGN) + -- oldquit = unix.sigaction(unix.SIGQUIT, unix.SIG_IGN) + -- oldmask = unix.sigprocmask(unix.SIG_BLOCK, unix.SIGCHLD) syscall = 'fork' child, errno = unix.fork() if child then @@ -19,10 +24,10 @@ function main() unix.dup(writer) unix.close(writer) unix.close(reader) - unix.sigaction(unix.SIGINT, oldint) - unix.sigaction(unix.SIGQUIT, oldquit) - unix.sigprocmask(unix.SIG_SETMASK, oldmask) - unix.execve(ls, {ls, "-Shal"}) + -- unix.sigaction(unix.SIGINT, oldint) + -- unix.sigaction(unix.SIGQUIT, oldquit) + -- unix.sigprocmask(unix.SIG_SETMASK, oldmask) + unix.execve(ls, {ls, '-Shal'}) unix.exit(127) else unix.close(writer) @@ -31,21 +36,21 @@ function main() while true do data, errno = unix.read(reader) if data then - if data ~= "" then + if data ~= '' then Write(data) else break end elseif errno ~= unix.EINTR then - Log(kLogWarn, string.format('read() failed: %s', unix.strerror(errno))) + Log(kLogWarn, 'read() failed: %s' % {unix.strerror(errno)}) break end end unix.close(reader) unix.wait(-1) - unix.sigaction(unix.SIGINT, oldint) - unix.sigaction(unix.SIGQUIT, oldquit) - unix.sigprocmask(unix.SIG_SETMASK, oldmask) + -- unix.sigaction(unix.SIGINT, oldint) + -- unix.sigaction(unix.SIGQUIT, oldquit) + -- unix.sigprocmask(unix.SIG_SETMASK, oldmask) return end end @@ -53,6 +58,6 @@ function main() end SetStatus(200) SetHeader('Content-Type', 'text/plain') - Write(string.format('error %s calling %s()', unix.strerrno(errno), syscall)) + Write('error %s calling %s()' % {unix.strerrno(errno), syscall}) end main() diff --git a/tool/net/demo/unix-webserver.lua b/tool/net/demo/unix-webserver.lua index 3b399c37c..8c1bf6605 100644 --- a/tool/net/demo/unix-webserver.lua +++ b/tool/net/demo/unix-webserver.lua @@ -46,12 +46,10 @@ function main() unix.bind(server, ifs[i].ip) unix.listen(server) ip, port = unix.getsockname(server) - addr = string.format('%s:%d', FormatIp(ip), port) - url = string.format('http://%s', addr) - Log(kLogInfo, string.format('listening on %s', addr)) - unix.write(mainfd, string.format( - 'listening on %s
\r\n', - url, url)) + addr = '%s:%d' % {FormatIp(ip), port} + url = 'http://%s' % {addr} + Log(kLogInfo, 'listening on %s' % {addr}) + unix.write(mainfd, 'listening on %s
\r\n' % {url, url}) pollfds[server] = unix.POLLIN | unix.POLLHUP servers[server] = true addrs[server] = addr @@ -67,7 +65,7 @@ function main() if fd == mainfd then data, errno = unix.read(mainfd) if not data then - Log(kLogInfo, string.format('got %s from parent client', unix.strerrno(errno))) + Log(kLogInfo, 'got %s from parent client' % {unix.strerrno(errno)}) -- prevent redbean core from writing a response unix.exit(1) end @@ -80,20 +78,20 @@ function main() -- echo it back for fun unix.write(mainfd, data) elseif servers[fd] then - unix.write(mainfd, string.format('preparing to accept from %d
\r\n', fd)) + unix.write(mainfd, 'preparing to accept from %d
\r\n' % {fd}) client, clientip, clientport = unix.accept(fd) - unix.write(mainfd, string.format('preparing to accept from %d
\r\n', fd)) - addr = string.format('%s:%d', FormatIp(clientip), clientport) + unix.write(mainfd, 'preparing to accept from %d
\r\n' % {fd}) + addr = '%s:%d' % {FormatIp(clientip), clientport} addrs[client] = addr - unix.write(mainfd, string.format('got client %s
\r\n', addr)) + unix.write(mainfd, 'got client %s
\r\n' % {addr}) pollfds[client] = unix.POLLIN evs[server] = nil else - unix.write(mainfd, string.format('preparing to read from %d
\r\n', fd)) + unix.write(mainfd, 'preparing to read from %d
\r\n' % {fd}) data = unix.read(fd) - unix.write(mainfd, string.format('done reading from %d
\r\n', fd)) + unix.write(mainfd, 'done reading from %d
\r\n' % {fd}) if data and #data ~= 0 then - unix.write(mainfd, string.format('got %d bytes from %s
\r\n', #data, addrs[fd])) + unix.write(mainfd, 'got %d bytes from %s
\r\n' % {#data, addrs[fd]}) unix.write(fd, 'HTTP/1.0 200 OK\r\n' .. 'Date: '.. FormatHttpDateTime(GetDate()) ..'\r\n' .. 'Content-Type: text/html; charset=utf-8\r\n' .. diff --git a/tool/net/help.txt b/tool/net/help.txt index b48076d03..47da46e38 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -76,54 +76,56 @@ FLAGS --strace enables system call tracing --ftrace enables function call tracing + KEYBOARD - CTRL-D EXIT - CTRL-C CTRL-C EXIT - CTRL-E END - CTRL-A START - CTRL-B BACK - CTRL-F FORWARD - CTRL-L CLEAR - CTRL-H BACKSPACE - CTRL-D DELETE - CTRL-N NEXT HISTORY - CTRL-P PREVIOUS HISTORY - CTRL-R SEARCH HISTORY - CTRL-G CANCEL SEARCH - ALT-< BEGINNING OF HISTORY - ALT-> END OF HISTORY - ALT-F FORWARD WORD - ALT-B BACKWARD WORD - CTRL-K KILL LINE FORWARDS - CTRL-U KILL LINE BACKWARDS - ALT-H KILL WORD BACKWARDS - CTRL-W KILL WORD BACKWARDS - CTRL-ALT-H KILL WORD BACKWARDS - ALT-D KILL WORD FORWARDS - CTRL-Y YANK - ALT-Y ROTATE KILL RING AND YANK AGAIN - CTRL-T TRANSPOSE - ALT-T TRANSPOSE WORD - ALT-U UPPERCASE WORD - ALT-L LOWERCASE WORD - ALT-C CAPITALIZE WORD - CTRL-\ QUIT PROCESS - CTRL-S PAUSE OUTPUT - CTRL-Q UNPAUSE OUTPUT (IF PAUSED) - CTRL-Q ESCAPED INSERT - CTRL-ALT-F FORWARD EXPR - CTRL-ALT-B BACKWARD EXPR - ALT-RIGHT FORWARD EXPR - ALT-LEFT BACKWARD EXPR - ALT-SHIFT-B BARF EXPR - ALT-SHIFT-S SLURP EXPR - CTRL-SPACE SET MARK - CTRL-X CTRL-X GOTO MARK - CTRL-Z SUSPEND PROCESS - ALT-\ SQUEEZE ADJACENT WHITESPACE - PROTIP REMAP CAPS LOCK TO CTRL + CTRL-D EXIT + CTRL-C CTRL-C EXIT + CTRL-E END + CTRL-A START + CTRL-B BACK + CTRL-F FORWARD + CTRL-L CLEAR + CTRL-H BACKSPACE + CTRL-D DELETE + CTRL-N NEXT HISTORY + CTRL-P PREVIOUS HISTORY + CTRL-R SEARCH HISTORY + CTRL-G CANCEL SEARCH + ALT-< BEGINNING OF HISTORY + ALT-> END OF HISTORY + ALT-F FORWARD WORD + ALT-B BACKWARD WORD + CTRL-K KILL LINE FORWARDS + CTRL-U KILL LINE BACKWARDS + ALT-H KILL WORD BACKWARDS + CTRL-W KILL WORD BACKWARDS + CTRL-ALT-H KILL WORD BACKWARDS + ALT-D KILL WORD FORWARDS + CTRL-Y YANK + ALT-Y ROTATE KILL RING AND YANK AGAIN + CTRL-T TRANSPOSE + ALT-T TRANSPOSE WORD + ALT-U UPPERCASE WORD + ALT-L LOWERCASE WORD + ALT-C CAPITALIZE WORD + CTRL-\ QUIT PROCESS + CTRL-S PAUSE OUTPUT + CTRL-Q UNPAUSE OUTPUT (IF PAUSED) + CTRL-Q ESCAPED INSERT + CTRL-ALT-F FORWARD EXPR + CTRL-ALT-B BACKWARD EXPR + ALT-RIGHT FORWARD EXPR + ALT-LEFT BACKWARD EXPR + ALT-SHIFT-B BARF EXPR + ALT-SHIFT-S SLURP EXPR + CTRL-SPACE SET MARK + CTRL-X CTRL-X GOTO MARK + CTRL-Z SUSPEND PROCESS + ALT-\ SQUEEZE ADJACENT WHITESPACE + PROTIP REMAP CAPS LOCK TO CTRL + USAGE This executable is also a ZIP file that contains static assets. @@ -231,6 +233,24 @@ USAGE inside the binary. redbean also respects your privacy and won't phone home because your computer is its home. + +REPL + + Your redbean displays a REPL that lets you modify the state of the + main server process while your server is running. Any changes will + propagate into forked clients. + + Your REPL is displayed only when redbean is run as a non-daemon in a + UNIX terminal or the Windows 10 command prompt or powershell. Since + the REPL is a Lua REPL it's not included in a redbean-static builds. + + redbean uses the same keyboard shortcuts as GNU Readline and Emacs. + Some of its keyboard commands (listed in a previous section) were + inspired by Paredit. + + A history of your commands is saved to `~/.redbean_history`. + + SECURITY redbean uses a protocol polyglot for serving HTTP and HTTPS on @@ -246,1034 +266,1067 @@ SECURITY -VVV log ssl informational messages too -VVVV log ssl verbose details too - See https://redbean.dev for further details. + See http://redbean.dev for further details. + LUA SERVER PAGES - Any files with the extension .lua will be dynamically served by redbean. - Here's the simplest possible example: + Any files with the extension .lua will be dynamically served by redbean. + Here's the simplest possible example: - Write('Hello World') + Write('Hello World') - The Lua Server Page above should be able to perform at 700,000 responses - per second on a Core i9, without any sort of caching. If you want a Lua - handler that can do 1,000,000 responses per second, then try adding the - following global handler to your /.init.lua file: + The Lua Server Page above should be able to perform at 700,000 responses + per second on a Core i9, without any sort of caching. If you want a Lua + handler that can do 1,000,000 responses per second, then try adding the + following global handler to your /.init.lua file: - function OnHttpRequest() - Write('Hello World') - end + function OnHttpRequest() + Write('Hello World') + end - Here's an example of a more typical workflow for Lua Server Pages using - the redbean API: + Here's an example of a more typical workflow for Lua Server Pages using + the redbean API: - SetStatus(200) - SetHeader('Content-Type', 'text/plain; charset=utf-8') - Write('

Hello ') - Write(EscapeHtml(GetParam('name'))) + SetStatus(200) + SetHeader('Content-Type', 'text/plain; charset=utf-8') + Write('

Hello ') + Write(EscapeHtml(GetParam('name'))) - We didn't need the first two lines in the previous example, because - they're implied by redbean automatically if you don't set them. Responses - are also buffered until the script finishes executing. That enables - redbean to make HTTP as easy as possible. In the future, API capabilities - will be expanded to make possible things like websockets. + We didn't need the first two lines in the previous example, because + they're implied by redbean automatically if you don't set them. Responses + are also buffered until the script finishes executing. That enables + redbean to make HTTP as easy as possible. In the future, API capabilities + will be expanded to make possible things like websockets. - redbean embeds the Lua standard library. You can use packages such as io - to persist and share state across requests and connections, as well as the - StoreAsset function, and the lsqlite3 module. + redbean embeds the Lua standard library. You can use packages such as io + to persist and share state across requests and connections, as well as the + StoreAsset function, and the lsqlite3 module. - Your Lua interpreter begins its life in the main process at startup in the - .init.lua, which is likely where you'll want to perform all your expensive - one-time operations like importing modules. Then, as requests roll in, - isolated processes are cloned from the blueprint you created. + Your Lua interpreter begins its life in the main process at startup in the + .init.lua, which is likely where you'll want to perform all your expensive + one-time operations like importing modules. Then, as requests roll in, + isolated processes are cloned from the blueprint you created. + SPECIAL PATHS - / - redbean will generate a zip central directory listing for this - page, and this page only, but only if there isn't an /index.lua or - /index.html file defined. + / + redbean will generate a zip central directory listing for this + page, and this page only, but only if there isn't an /index.lua or + /index.html file defined. - /.init.lua - This script is run once in the main process at startup. This lets - you modify the state of the Lua interpreter before connection - processes are forked off. For example, it's a good idea to do - expensive one-time computations here. You can also use this file - to call the ProgramFOO() functions below. The init module load - happens after redbean's arguments and zip assets have been parsed, - but before calling functions like socket() and fork(). Note that - this path is a hidden file so that it can't be unintentionally run - by the network client. + /.init.lua + This script is run once in the main process at startup. This lets + you modify the state of the Lua interpreter before connection + processes are forked off. For example, it's a good idea to do + expensive one-time computations here. You can also use this file + to call the ProgramFOO() functions below. The init module load + happens after redbean's arguments and zip assets have been parsed, + but before calling functions like socket() and fork(). Note that + this path is a hidden file so that it can't be unintentionally run + by the network client. - /.reload.lua - This script is run from the main process when SIGHUP is received. - This only applies to redbean when running in daemon mode. Any - changes that are made to the Lua interpreter state will be - inherited by future forked connection processes. Note that this - path is a hidden file so that it can't be unintentionally run by - the network client. + /.reload.lua + This script is run from the main process when SIGHUP is received. + This only applies to redbean when running in daemon mode. Any + changes that are made to the Lua interpreter state will be + inherited by future forked connection processes. Note that this + path is a hidden file so that it can't be unintentionally run by + the network client. - /.lua/... - Your Lua modules go in this directory. The way it works is redbean - sets Lua's package.path to /zip/.lua/?.lua;/zip/.lua/?/init.lua by - default. Cosmopolitan Libc lets system calls like open read from - the ZIP structure, if the filename is prefixed with /zip/. So this - works like magic. + /.lua/... + Your Lua modules go in this directory. The way it works is redbean + sets Lua's package.path to /zip/.lua/?.lua;/zip/.lua/?/init.lua by + default. Cosmopolitan Libc lets system calls like open read from + the ZIP structure, if the filename is prefixed with /zip/. So this + works like magic. - /redbean.png - If it exists, it'll be used as the / listing page icon, embedded - as a base64 URI. + /redbean.png + If it exists, it'll be used as the / listing page icon, embedded + as a base64 URI. - /usr/share/ssl/root - This directory contains your root certificate authorities. It is - needed so the Fetch() HTTPS client API can verify that a remote - certificate was signed by a third party. You can add your own - certificate files to this directory within the ZIP executable. - If you enable HTTPS client verification then redbean will check - that HTTPS clients (a) have a certificate and (b) it was signed. + /usr/share/ssl/root + This directory contains your root certificate authorities. It is + needed so the Fetch() HTTPS client API can verify that a remote + certificate was signed by a third party. You can add your own + certificate files to this directory within the ZIP executable. + If you enable HTTPS client verification then redbean will check + that HTTPS clients (a) have a certificate and (b) it was signed. + GLOBALS - argv: array[str] - Array of command line arguments, excluding those parsed by - getopt() in the C code, which stops parsing at the first - non-hyphenated arg. In some cases you can use the magic -- - argument to delimit C from Lua arguments. + argv: array[str] + Array of command line arguments, excluding those parsed by + getopt() in the C code, which stops parsing at the first + non-hyphenated arg. In some cases you can use the magic -- + argument to delimit C from Lua arguments. + HOOKS - OnHttpRequest() - If this function is defined in the global scope by your /.init.lua - then redbean will call it at the ealiest possible moment to - hand over control for all messages (with the exception of OPTIONS - *). See functions like Route which asks redbean to do its default - thing from the handler. + OnHttpRequest() + If this function is defined in the global scope by your /.init.lua + then redbean will call it at the ealiest possible moment to + hand over control for all messages (with the exception of OPTIONS + *). See functions like Route which asks redbean to do its default + thing from the handler. - OnClientConnection(ip:int,port:int,serverip:int,serverport:int) → bool - If this function is defined it'll be called from the main process - each time redbean accepts a new client connection. If it returns - true then redbean will close the connection without calling fork. + OnClientConnection(ip:int,port:int,serverip:int,serverport:int) → bool + If this function is defined it'll be called from the main process + each time redbean accepts a new client connection. If it returns + true then redbean will close the connection without calling fork. - OnProcessCreate(pid:int,ip:int,port:int,serverip:int,serverport:int) - If this function is defined it'll be called from the main process - each time redbean forks a connection handler worker process. The - ip/port of the remote client is provided, along with the ip/port - of the listening interface that accepted the connection. This may - be used to create a server activity dashboard, in which case the - data provider handler should set SetHeader('Connection','Close'). - This won't be called in uniprocess mode. + OnProcessCreate(pid:int,ip:int,port:int,serverip:int,serverport:int) + If this function is defined it'll be called from the main process + each time redbean forks a connection handler worker process. The + ip/port of the remote client is provided, along with the ip/port + of the listening interface that accepted the connection. This may + be used to create a server activity dashboard, in which case the + data provider handler should set SetHeader('Connection','Close'). + This won't be called in uniprocess mode. - OnProcessDestroy(pid:int) - If this function is defined it'll be called from the main process - each time redbean reaps a child connection process using wait4(). - This won't be called in uniprocess mode. + OnProcessDestroy(pid:int) + If this function is defined it'll be called from the main process + each time redbean reaps a child connection process using wait4(). + This won't be called in uniprocess mode. - OnServerStart() - If this function is defined it'll be called from the main process - right before the main event loop starts. + OnServerStart() + If this function is defined it'll be called from the main process + right before the main event loop starts. - OnServerStop() - If this function is defined it'll be called from the main process - after all the connection processes have been reaped and exit() is - ready to be called. + OnServerStop() + If this function is defined it'll be called from the main process + after all the connection processes have been reaped and exit() is + ready to be called. - OnWorkerStart() - If this function is defined it'll be called from the child worker - process after it's been forked and before messages are handled. - This won't be called in uniprocess mode. + OnWorkerStart() + If this function is defined it'll be called from the child worker + process after it's been forked and before messages are handled. + This won't be called in uniprocess mode. - OnWorkerStop() - If this function is defined it'll be called from the child worker - process once _exit() is ready to be called. This won't be called - in uniprocess mode. + OnWorkerStop() + If this function is defined it'll be called from the child worker + process once _exit() is ready to be called. This won't be called + in uniprocess mode. + FUNCTIONS - Write(data:str) - Appends data to HTTP response payload buffer. This is buffered - independently of headers. - - SetStatus(code:int[,reason:str]) - Starts an HTTP response, specifying the parameters on its first - line. reason is optional since redbean can fill in the appropriate - text for well-known magic numbers, e.g. 200, 404, etc. This method - will reset the response and is therefore mutually exclusive with - ServeAsset and other Serve* functions. If this function isn't - called, then the default behavior is to send 200 OK. - - SetHeader(name:str,value:str) - Appends HTTP header to response header buffer. name is - case-insensitive and restricted to non-space ASCII. value is a - UTF-8 string that must be encodable as ISO-8859-1. Leading and - trailing whitespace is trimmed automatically. Overlong characters - are canonicalized. C0 and C1 control codes are forbidden, with the - exception of tab. This function automatically calls SetStatus(200, - "OK") if a status has not yet been set. As SetStatus and Serve* - functions reset the response, SetHeader needs to be called after - SetStatus and Serve* functions are called. The header buffer is - independent of the payload buffer. Neither is written to the wire - until the Lua Server Page has finished executing. This function - disallows the setting of certain headers such as Content-Range and - Date, which are abstracted by the transport layer. In such cases, - consider calling ServeAsset. - - SetCookie(name:str,value:str[,options:table]) - Appends Set-Cookie HTTP header to the response header buffer. - Several Set-Cookie headers can be added to the same response. - __Host- and __Secure- prefixes are supported and may set or - overwrite some of the options (for example, specifying __Host- - prefix sets the Secure option to true, sets the path to "/", and - removes the Domain option). The following options can be used (their - lowercase equivalents are supported as well): - - Expires: sets the maximum lifetime of the cookie as an HTTP-date - timestamp. Can be specified as a Date in the RFC1123 (string) - format or as a UNIX timestamp (number of seconds). - - MaxAge: sets number of seconds until the cookie expires. A zero - or negative number will expire the cookie immediately. If both - Expires and MaxAge are set, MaxAge has precedence. - - Domain: sets the host to which the cookie will be sent. - - Path: sets the path that must be present in the request URL, or - the client will not send the Cookie header. - - Secure: (bool) requests the cookie to be only send to the - server when a request is made with the https: scheme. - - HttpOnly: (bool) forbids JavaScript from accessing the cookie. - - SameSite: (Strict, Lax, or None) controls whether a cookie is - sent with cross-origin requests, providing some protection - against cross-site request forgery attacks. - - GetParam(name:str) → value:str - Returns first value associated with name. name is handled in a - case-sensitive manner. This function checks Request-URL parameters - first. Then it checks application/x-www-form-urlencoded from the - message body, if it exists, which is common for HTML forms sending - POST requests. If a parameter is supplied matching name that has - no value, e.g. foo in ?foo&bar=value, then the returned value will - be nil, whereas for ?foo=&bar=value it would be "". To - differentiate between no-equal and absent, use the HasParam - function. The returned value is decoded from ISO-8859-1 (only in - the case of Request-URL) and we assume that percent-encoded - characters were supplied by the client as UTF-8 sequences, which - are returned exactly as the client supplied them, and may - therefore may contain overlong sequences, control codes, NUL - characters, and even numbers which have been banned by the IETF. - It is the responsibility of the caller to impose further - restrictions on validity, if they're desired. - - EscapeHtml(str) → str - Escapes HTML entities: The set of entities is &><"' which become - &><"'. This function is charset agnostic and - will not canonicalize overlong encodings. It is assumed that a - UTF-8 string will be supplied. See escapehtml.c. - - LaunchBrowser([path:str]) - Launches web browser on local machine with URL to this redbean - server. This function may be called from your /.init.lua. - - CategorizeIp(ip:uint32) → str - Returns a string describing an IP address. This is currently Class - A granular. It can tell you if traffic originated from private - networks, ARIN, APNIC, DOD, etc. - - DecodeBase64(ascii:str) → binary:str - Turns ASCII into binary, in a permissive way that ignores - characters outside the base64 alphabet, such as whitespace. See - decodebase64.c. - - DecodeLatin1(iso-8859-1:str) → utf-8:str - Turns ISO-8859-1 string into UTF-8. - - EncodeBase64(binary:str) → ascii:str - Turns binary into ASCII. This can be used to create HTML data: - URIs that do things like embed a PNG file in a web page. See - encodebase64.c. - - EncodeJson(value[,options:table]) → json:str - Turns passed Lua value into a JSON string. Tables with non-zero - length (as reported by `#`) are encoded as arrays with non-array - elements ignored. Empty tables are encoded as empty arrays. All - other tables are encoded as objects with numerical keys - converted to strings (so `{[3]=1}` is encoded as `{"3":1}`). - The following options can be used: - - useoutput: (bool=false) encodes the result directly to the - output buffer and returns `nil` value. This option is - ignored if used outside of request handling code. - - numformat: (string="%.14g") sets numeric format to be used. - - maxdepth: (number=64) sets the max number of nested tables. - - EncodeLua(value[,options:table]) → json:str - Turns passed Lua value into a Lua string. The following options - can be used: - - useoutput: (bool=false) encodes the result directly to the - output buffer and returns `nil` value. This option is - ignored if used outside of request handling code. - - numformat: (string="%.14g") sets numeric format to be used. - - maxdepth: (number=64) sets the max number of nested tables. - - EncodeLatin1(utf-8:str[,flags:int]) → iso-8859-1:str - Turns UTF-8 into ISO-8859-1 string. - - EscapeFragment(str) → str - Escapes URL #fragment. The allowed characters are - -/?.~_@:!$&'()*+,;=0-9A-Za-z and everything else gets %XX encoded. - Please note that '& can still break HTML and that '() can still - break CSS URLs. This function is charset agnostic and will not - canonicalize overlong encodings. It is assumed that a UTF-8 string - will be supplied. See kescapefragment.c. - - EscapeHost(str) → str - Escapes URL host. See kescapeauthority.c - - EscapeLiteral(str) → str - Escapes JavaScript or JSON string literal content. The caller is - responsible for adding the surrounding quotation marks. This - implementation \uxxxx sequences for all non-ASCII sequences. HTML - entities are also encoded, so the output doesn't need EscapeHtml. - This function assumes UTF-8 input. Overlong encodings are - canonicalized. Invalid input sequences are assumed to be - ISO-8859-1. The output is UTF-16 since that's what JavaScript - uses. For example, some individual codepoints such as emoji - characters will encode as multiple \uxxxx sequences. Ints that are - impossible to encode as UTF-16 are substituted with the \xFFFD - replacement character. See escapejsstringliteral.c. - - EscapeParam(str) → str - Escapes URL parameter name or value. The allowed characters are - -.*_0-9A-Za-z and everything else gets %XX encoded. This function - is charset agnostic and will not canonicalize overlong encodings. - It is assumed that a UTF-8 string will be supplied. See - kescapeparam.c. - - EscapePass(str) → str - Escapes URL password. See kescapeauthority.c. - - EscapePath(str) → str - Escapes URL path. This is the same as EscapeSegment except slash - is allowed. The allowed characters are -.~_@:!$&'()*+,;=0-9A-Za-z/ - and everything else gets %XX encoded. Please note that '& can - still break HTML, so the output may need EscapeHtml too. Also note - that '() can still break CSS URLs. This function is charset - agnostic and will not canonicalize overlong encodings. It is - assumed that a UTF-8 string will be supplied. See kescapepath.c. - - EscapeSegment(str) → str - Escapes URL path segment. This is the same as EscapePath except - slash isn't allowed. The allowed characters are - -.~_@:!$&'()*+,;=0-9A-Za-z and everything else gets %XX encoded. - Please note that '& can still break HTML, so the output may need - EscapeHtml too. Also note that '() can still break CSS URLs. This - function is charset agnostic and will not canonicalize overlong - encodings. It is assumed that a UTF-8 string will be supplied. See - kescapesegment.c. - - EscapeUser(str) → str - Escapes URL username. See kescapeauthority.c. - - Fetch(url:str[,body:str|{method=value:str,body=value:str,...}]) - → status:int,{header:str=value:str,...},body:str - Sends an HTTP/HTTPS request to the specified URL. If only the URL is - provided, then a GET request is sent. If both URL and body parameters - are specified, then a POST request is sent. If any other method needs - to be specified (for example, PUT or DELETE), then passing a table as - the second value allows setting method and body values as well other - options: - - method (default = "GET"): sets the method to be used for the - request. The specified method is converted to uppercase. - - body (default = ""): sets the body value to be sent. - - followredirect (default = true): forces temporary and permanent - redirects to be followed. This behavior can be disabled by - passing `false`. - - maxredirects (default = 5): sets the number of allowed redirects - to minimize looping due to misconfigured servers. When the number - is exceeded, the result of the last redirect is returned. - When the redirect is being followed, the same method and body values - are being sent in all cases except when 303 status is returned. In - that case the method is set to GET and the body is removed before the - redirect is followed. Note that if these (method/body) values are - provided as table fields, they will be modified in place. - - FormatHttpDateTime(seconds:int) → rfc1123:str - Converts UNIX timestamp to an RFC1123 string that looks like this: - Mon, 29 Mar 2021 15:37:13 GMT. See formathttpdatetime.c. - - FormatIp(uint32) → str - Turns integer like 0x01020304 into a string like 1.2.3.4. See also - ParseIp for the inverse operation. - - GetAssetComment(path:str) → str - Returns comment text associated with asset in the ZIP central - directory. Also available as GetComment (deprecated). - - GetAssetMode(path:str) → int - Returns UNIX-style octal mode for ZIP asset (or local file if the - -D flag is used) - - GetAssetSize(path:str) → int - Returns byte size of uncompressed contents of ZIP asset (or local - file if the -D flag is used) - - GetBody() → str - Returns the request message body if present or an empty string. - Also available as GetPayload (deprecated). - - GetCookie(name:str) → str - Returns cookie value. - - GetCryptoHash(name:str,payload:str[,key:str]) → str - Returns value of the specified cryptographic hash function. If the - key is provided, then HMAC value of the same function is returned. - The name can be one of the following strings: MD5, SHA1, SHA224, - SHA256, SHA384, SHA512, and BLAKE2B256. - - GetRemoteAddr() → ip:uint32,port:uint16 - Returns client ip4 address and port, e.g. 0x01020304,31337 would - represent 1.2.3.4:31337. This is the same as GetClientAddr except - it will use the ip:port from the X-Forwarded-For header, only if - IsPrivateIp or IsLoopbackIp return true. When multiple addresses - are present in the header, the last/right-most address is used. - - GetClientAddr() → ip:uint32,port:uint16 - Returns client socket ip4 address and port, e.g. 0x01020304,31337 - would represent 1.2.3.4:31337. Please consider using GetRemoteAddr - instead, since the latter takes into consideration reverse proxy - scenarios. - - GetClientFd() → int - Returns file descriptor being used for client connection. - This is useful for scripts that want to use unix:fork(). - - IsClientUsingSsl() → bool - Returns true if client connection has begun being managed by - the MbedTLS security layer. This is an important thing to - consider if a script is taking control of GetClientFd() - - GetServerAddr() → ip:uint32,port:uint16 - Returns address to which listening server socket is bound, e.g. - 0x01020304,8080 would represent 1.2.3.4:8080. If -p 0 was supplied - as the listening port, then the port in this string will be - whatever number the operating system assigned. - - GetDate() → seconds:int - Returns date associated with request that's used to generate the - Date header, which is now, give or take a second. The returned - value is a UNIX timestamp. - - GetHeader(name:str) → value:str - Returns HTTP header. name is case-insensitive. The header value is - returned as a canonical UTF-8 string, with leading and trailing - whitespace trimmed, which was decoded from ISO-8859-1, which is - guaranteed to not have C0/C1 control sequences, with the exception - of the tab character. Leading and trailing whitespace is - automatically removed. In the event that the client suplies raw - UTF-8 in the HTTP message headers, the original UTF-8 sequence can - be losslessly restored by counter-intuitively recoding the - returned string back to Latin1. If the requested header is defined - by the RFCs as storing comma-separated values (e.g. Allow, - Accept-Encoding) and the field name occurs multiple times in the - message, then this function will fold those multiple entries into - a single string. - - GetHeaders() → {name:str=value:str,...} - Returns HTTP headers as dictionary mapping header key strings to - their UTF-8 decoded values. The ordering of headers from the - request message is not preserved. Whether or not the same key can - repeat depends on whether or not it's a standard header, and if - so, if it's one of the ones that the RFCs define as repeatable. - See khttprepeatable.c. Those headers will not be folded. Standard - headers which aren't on that list, will be overwritten with the - last-occurring one during parsing. Extended headers are always - passed through exactly as they're received. Please consider using - GetHeader API if possible since it does a better job abstracting - these issues. - - GetLogLevel() → int - Returns logger verbosity level. Likely return values are kLogDebug - > kLogVerbose > kLogInfo > kLogWarn > kLogError > kLogFatal. - - GetHost() → str - Returns host associated with request. This will be the Host - header, if it's supplied. Otherwise it's the bind address. - - GetHostOs() → str - Returns string that describes the host OS. - - GetMonospaceWidth(str|char) → int - Returns monospace display width of string. This is useful for - fixed-width formatting. For example, CJK characters typically take - up two cells. This function takes into consideration combining - characters, which are discounted, as well as control codes and - ANSI escape sequences. - - GetMethod() → str - Returns HTTP method. Normally this will be GET, HEAD, or POST in - which case redbean normalizes this value to its uppercase form. - Anything else that the RFC classifies as a "token" string is - accepted too, which might contain characters like &". - - GetParams() → {{name:str[,value:str]},...} - Returns name=value parameters from Request-URL and - application/x-www-form-urlencoded message body in the order they - were received. This may contain duplicates. The inner array will - have either one or two items, depending on whether or not the - equals sign was used. - - GetPath() → str - Returns the Request-URL path. This is guaranteed to begin with - "/". It is further guaranteed that no "//" or "/." exists in the - path. The returned value is returned as a UTF-8 string which was - decoded from ISO-8859-1. We assume that percent-encoded characters - were supplied by the client as UTF-8 sequences, which are returned - exactly as the client supplied them, and may therefore may contain - overlong sequences, control codes, NUL characters, and even - numbers which have been banned by the IETF. redbean takes those - things into consideration when performing path safety checks. It - is the responsibility of the caller to impose further restrictions - on validity, if they're desired. - - GetEffectivePath() → str - Returns path as it was resolved by the routing algorithms, which - might contain the virtual host prepended if used. - - GetScheme() → str - Returns scheme from Request-URL, if any. - - GetSslIdentity() → str - Returns certificate subject or PSK identity from the current SSL - session. `nil` is returned for regular (non-SSL) connections. - - GetStatus() → int - Returns current status (as set by an earlier SetStatus call) or - `nil` if the status hasn't been set yet. - - GetTime() → seconds:number - Returns current time as a UNIX timestamp with 0.0001s precision. - - GetUrl() → str - Returns the effective Request-URL as an ASCII string, where - illegal characters or UTF-8 is guaranteed to be percent encoded, - and has been normalized to include either the Host or - X-Forwarded-Host headers, if they exist, and possibly a scheme too - if redbean is being used as an HTTP proxy server. In the future - this API might change to return an object instead. - - GetHttpVersion() → int - Returns the request HTTP protocol version, which can be 9 for - HTTP/0.9, 10 for HTTP/1.0, or 11 for HTTP/1.1. Also available - as GetVersion (deprecated). - - GetRandomBytes([length:int]) → str - Returns string with the specified number of random bytes (1..256). - If no length is specified, then a string of length 16 is returned. - - GetRedbeanVersion() → int - Returns the Redbean version in the format 0xMMmmpp, with major (MM), - minor (mm), and patch (pp) versions encoded. The version value 1.4 - would be represented as 0x010400. - - GetZipPaths([prefix:str]) → {path:str,...} - Returns paths of all assets in the zip central directory, prefixed - by a slash. If prefix parameter is provided, then only paths that - start with the prefix (case sensitive) are returned. - - HasParam(name:str) → bool - Returns true if parameter with name was supplied in either the - Request-URL or an application/x-www-form-urlencoded message body. - - HidePath(prefix:str) - Programs redbean / listing page to not display any paths beginning - with prefix. This function should only be called from /.init.lua. - - IsPublicIp(uint32) → bool - Returns true if IP address is not a private network (10.0.0.0/8, - 172.16.0.0/12, 192.168.0.0/16) and is not localhost (127.0.0.0/8). - Note: we intentionally regard TEST-NET IPs as public. - - IsPrivateIp(uint32) → bool - Returns true if IP address is part of a private network - (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16). - - IsLoopbackClient() → bool - Returns true if the client IP address (returned by GetRemoteAddr) - is part of the localhost network (127.0.0.0/8). - - IsPrivateClient() → bool - Returns true if the client IP address (returned by GetRemoteAddr) - is part of the localhost network (127.0.0.0/8) or a private network - (10.0.0.0/8, etc.) - - IsLoopbackIp(uint32) → bool - Returns true if IP address is part of the localhost network - (127.0.0.0/8). - - IsCompressed(path:str) → bool - Returns true if ZIP artifact at path is stored on disk using - DEFLATE compression. - - IndentLines(str[,int]) → str - Adds spaces to beginnings of multiline string. If the int - parameter is not supplied then 1 space will be added. - - LoadAsset(path:str) → str - Returns contents of file as string. The asset may be sourced from - either the zip (decompressed) or the local filesystem if the -D - flag was used. If slurping large file into memory is a concern, - then consider using ServeAsset which can serve directly off disk. - - StoreAsset(path:str,data:str[,mode:int]) - Stores asset to executable's ZIP central directory. This currently - happens in an append-only fashion and is still largely in the - proof-of-concept stages. Currently only supported on Linux, XNU, - and FreeBSD. - - Log(level:int,message:str) - Emits message string to log, if level is less than or equal to - GetLogLevel. If redbean is running in interactive mode, then this - will log to the console. If redbean is running as a daemon or the - -L LOGFILE flag is passed, then this will log to the file. - Reasonable values for level are kLogDebug > kLogVerbose > kLogInfo - > kLogWarn > kLogError > kLogFatal. The logger emits timestamps in - the local timezone with microsecond precision. If log entries are - emitted more frequently than once per second, then the log entry - will display a delta timestamp, showing how much time has elapsed - since the previous log entry. This behavior is useful for quickly - measuring how long various portions of your code take to execute. - - ParseHttpDateTime(rfc1123:str) → seconds:int - Converts RFC1123 string that looks like this: Mon, 29 Mar 2021 - 15:37:13 GMT to a UNIX timestamp. See parsehttpdatetime.c. - - ParseUrl(str) → URL - Parses URL, returning object having the following fields: scheme, - user, pass, host, port, path, params, fragment. This parser is - charset agnostic. Percent encoded bytes are decoded for all - fields. Returned values might contain things like NUL characters, - spaces, control codes, and non-canonical encodings. Absent can be - discerned from empty by checking if the pointer is set. There's no - failure condition for this routine. This is a permissive parser. - This doesn't normalize path segments like `.` or `..` so use - IsAcceptablePath() to check for those. No restrictions are imposed - beyond that which is strictly necessary for parsing. All the data - that is provided will be consumed to the one of the fields. Strict - conformance is enforced on some fields more than others, like - scheme, since it's the most non-deterministically defined field of - them all. Please note this is a URL parser, not a URI parser. - Which means we support everything everything the URI spec says we - should do except for the things we won't do, like tokenizing path - segments into an array and then nesting another array beneath each - of those for storing semicolon parameters. So this parser won't - make SIP easy. What it can do is parse HTTP URLs and most URIs - like data:opaque, better in fact than most things which claim to - be URI parsers. - - EncodeUrl(URL) → str - This function is the inverse of ParseUrl. The output will always - be correctly formatted. The exception is if illegal characters are - supplied in the scheme field, since there's no way of escaping - those. Opaque parts are escaped as though they were paths, since - many URI parsers won't understand things like an unescaped - question mark in path. - - ParseIp(str) → int - Converts IPv4 address string to integer, e.g. "1.2.3.4" → - 0x01020304, or returns -1 for invalid inputs. See also FormatIp - for the inverse operation. - - ProgramAddr(str) - Configures the address on which to listen. Can be used multiple - times to set more than one address. - - ProgramBrand(str) - Changes HTTP Server header, as well as the

title on the / - listing page. The brand string needs to be a UTF-8 value that's - encodable as ISO-8859-1. If the brand is changed to something - other than redbean, then the promotional links will be removed - from the listing page too. This function should only be called - from /.init.lua. - - ProgramCache(seconds:int) - Configures Cache-Control and Expires header generation for static - asset serving. A negative value will disable the headers. Zero - means don't cache. Greater than zero asks public proxies and - browsers to cache for a given number of seconds. This should only - be called from /.init.lua. - - ProgramCertificate(pem:str) - Same as the -C flag if called from .init.lua, e.g. - ProgramCertificate(LoadAsset("/.sign.crt")) for zip loading or - ProgramCertificate(Slurp("/etc/letsencrypt.lol/fullchain.pem")) for - local file system only. - - ProgramHeader(name:str,value:str) - Appends HTTP header to the header buffer for all responses (whereas - SetHeader only appends a header to the current response buffer). - name is case-insensitive and restricted to non-space ASCII. value - is a UTF-8 string that must be encodable as ISO-8859-1. Leading and - trailing whitespace is trimmed automatically. Overlong characters - are canonicalized. C0 and C1 control codes are forbidden, with the - exception of tab. The header buffer is independent of the payload - buffer. This function disallows the setting of certain headers such - as Content-Range and Date, which are abstracted by the transport - layer. - - ProgramPort(uint16) - Hard-codes the port number on which to listen, which can be any - number in the range 1..65535, or alternatively 0 to ask the - operating system to choose a port, which may be revealed later on - by GetServerAddr or the -z flag to stdout. - - ProgramPrivateKey(pem:str) - Same as the -K flag if called from .init.lua, e.g. - ProgramPrivateKey(LoadAsset("/.sign.key")) for zip loading or - ProgramPrivateKey(Slurp("/etc/letsencrypt/fullchain.pem")) for - local file system only. - - ProgramRedirect(code:int,src:str,location:str) - Configures fallback routing for paths which would otherwise return - 404 Not Found. If code is 0 then the path is rewritten internally - as an accelerated redirect. If code is 301, 302, 307, or 308 then - a redirect response will be sent to the client. This should only - be called from /.init.lua. - - ProgramSslTicketLifetime(seconds:int) - Defaults to 86400 (24 hours). This may be set to ≤0 to disable - SSL tickets. It's a good idea to use these since it increases - handshake performance 10x and eliminates a network round trip. - - EvadeDragnetSurveillance(bool) - If this option is programmed then redbean will not transmit a - Server Name Indicator (SNI) when performing Fetch() requests. - - ProgramSslPresharedKey(key:str,identity:str) - This function can be used to enable the PSK ciphersuites - which simplify SSL and enhance its performance in controlled - environments. `key` may contain 1..32 bytes of random binary - data and identity is usually a short plaintext string. The - first time this function is called, the preshared key will - be added to both the client and the server SSL configs. If - it's called multiple times, then the remaining keys will be - added to the server, which is useful if you want to assign - separate keys to each client, each of which needs a separate - identity too. If this function is called multiple times with - the same identity string, then the latter call will overwrite - the prior. If a preshared key is supplied and no certificates - or key-signing-keys are programmed, then redbean won't bother - auto-generating any serving certificates and will instead use - only PSK ciphersuites. - - ProgramSslCiphersuite(name:str) - See https://redbean.dev/ for further details. - - IsDaemon() → bool - Returns true if -d flag was passed to redbean. - - ProgramUid(int) - Same as the -U flag if called from .init.lua for setuid() - - ProgramGid(int) - Same as the -G flag if called from .init.lua for setgid() - - ProgramDirectory(str) - Same as the -D flag if called from .init.lua for overlaying local - file system directories. This may be called multiple times. The - first directory programmed is preferred. These currently do not - show up in the index page listing. - - ProgramLogMessages(bool) - Same as the -m flag if called from .init.lua for logging message - headers only. - - ProgramLogBodies(bool) - Same as the -b flag if called from .init.lua for logging message - bodies as part of POST / PUT / etc. requests. - - ProgramLogPath(str) - Same as the -L flag if called from .init.lua for setting the log - file path on the local file system. It's created if it doesn't - exist. This is called before de-escalating the uesr / group id. - The file is opened in append only mode. If the disk runs out of - space then redbean will truncate the log file if has access to - change the log file after daemonizing. - - ProgramPidPath(str) - Same as the -P flag if called from .init.lua for setting the pid - file path on the local file system. It's useful for reloading - daemonized redbean using `kill -HUP $(cat /var/run/redbean.pid)` - or terminating redbean with `kill $(cat /var/run/redbean.pid)` - which will gracefully terminate all clients. Sending the TERM - signal twice will cause a forceful shutdown, which might make - someone with a slow internet connection who's downloading big - files unhappy. - - ProgramUniprocess([bool]) → bool - Same as the -u flag if called from .init.lua. Can be used to - configure the uniprocess mode. The current value is returned. - - Slurp(filename:str) → str - Reads file data from local file system. - - Sleep(seconds:number) - Sleeps the specified number of seconds (can be fractional). The - smallest interval is a microsecond. - - Route([host:str,[path:str]]) - Instructs redbean to follow the normal HTTP serving path. This - function is useful when writing an OnHttpRequest handler, since - that overrides the serving path entirely. So if the handler - decides it doesn't want to do anything, it can simply call this - function, to hand over control back to the redbean core. By - default, the host and path arguments are supplied from the - resolved GetUrl value. This handler always resolves, since it will - generate a 404 Not Found response if redbean couldn't find an - appropriate endpoint. - - RouteHost([host:str,[path:str]]) → bool - This is the same as Route, except it only implements the subset of - request routing needed for serving virtual-hosted assets, where - redbean tries to prefix the path with the hostname when looking up - a file. This function returns true if the request was resolved. If - it was resolved, then your OnHttpRequest request handler can still - set additional headers. - - RoutePath([path:str]) → bool - This is the same as Route, except it only implements the subset of - request routing needed for serving assets. This function returns - true if the request was resolved. If it was resolved, then your - OnHttpRequest request handler can still set additional headers. - Note that the asset needs to have "read other" permissions; - otherwise this function logs a warning and returns 403 Forbidden. - If this is undesirable, use GetAssetMode and ServeAsset to bypass - the check. - - ServeAsset(path:str) - Instructs redbean to serve static asset at path. This function - causes what would normally happen outside a dynamic handler to - happen. The asset can be sourced from either the zip or local - filesystem if -D is used. This function is mutually exclusive with - SetStatus and other Serve* functions. - - ServeError(code:int[,reason:str]) - Instructs redbean to serve a boilerplate error page. This takes - care of logging the error, setting the reason phrase, and adding a - payload. This function is mutually exclusive with SetStatus and - other Serve* functions. - - ServeRedirect(code:int,location:str) - Instructs redbean to return the specified redirect code along with - the Location header set. This function is mutually exclusive with - SetStatus and other Serve* functions. - - SetLogLevel(level:int) - Sets logger verbosity. Reasonable values for level are kLogDebug > - kLogVerbose > kLogInfo > kLogWarn > kLogError > kLogFatal. This is - reset at the end of the http request, so it can be used to disable - access log and message logging. - - VisualizeControlCodes(str) → str - Replaces C0 control codes with their UNICODE pictures - representation. This function also canonicalizes overlong - encodings. C1 control codes are replaced with a JavaScript-like - escape sequence. - - Underlong(str) → str - Canonicalizes overlong encodings. - - Crc32(initial:int,data:str) → int - Computes 32-bit CRC-32 used by zip/zlib/gzip/etc. - - Crc32c(initial:int,data:str) → int - Computes 32-bit Castagnoli Cyclic Redundancy Check. - - Md5(str) → str - Computes MD5 checksum, returning 16 bytes of binary. - - Sha1(str) → str - Computes SHA1 checksum, returning 20 bytes of binary. - - Sha224(str) → str - Computes SHA224 checksum, returning 28 bytes of binary. - - Sha256(str) → str - Computes SHA256 checksum, returning 32 bytes of binary. - - Sha384(str) → str - Computes SHA384 checksum, returning 48 bytes of binary. - - Sha512(str) → str - Computes SHA512 checksum, returning 64 bytes of binary. - - Bsf(x:int) → int - Returns position of first bit set. Passing 0 will raise an error. - Same as the Intel x86 instruction BSF. - - Bsr(x:int) → int - Returns binary logarithm of 𝑥. Passing 0 will raise an error. Same - as the Intel x86 instruction BSR. - - Popcnt(x:int) → int - Returns number of bits set in integer. - - Rdtsc() → int - Returns CPU timestamp counter. - - Lemur64() → int - Returns fastest pseudorandom non-cryptographic random number. This - linear congruential generator passes practrand and bigcrush. - - Rand64() → int - Returns nondeterministic pseudorandom non-cryptographic number. This - linear congruential generator passes practrand and bigcrush. This - generator is safe across fork(), threads, and signal handlers. - - Rdrand() → int - Returns 64-bit hardware random integer from RDRND instruction, with - automatic fallback to getrandom() if not available. - - Rdseed() → int - Returns 64-bit hardware random integer from RDSEED instruction, with - automatic fallback to RDRND and getrandom() if not available. + Write(data:str) + Appends data to HTTP response payload buffer. This is buffered + independently of headers. + + SetStatus(code:int[,reason:str]) + Starts an HTTP response, specifying the parameters on its first + line. reason is optional since redbean can fill in the appropriate + text for well-known magic numbers, e.g. 200, 404, etc. This method + will reset the response and is therefore mutually exclusive with + ServeAsset and other Serve* functions. If this function isn't + called, then the default behavior is to send 200 OK. + + SetHeader(name:str,value:str) + Appends HTTP header to response header buffer. name is + case-insensitive and restricted to non-space ASCII. value is a + UTF-8 string that must be encodable as ISO-8859-1. Leading and + trailing whitespace is trimmed automatically. Overlong characters + are canonicalized. C0 and C1 control codes are forbidden, with the + exception of tab. This function automatically calls SetStatus(200, + "OK") if a status has not yet been set. As SetStatus and Serve* + functions reset the response, SetHeader needs to be called after + SetStatus and Serve* functions are called. The header buffer is + independent of the payload buffer. Neither is written to the wire + until the Lua Server Page has finished executing. This function + disallows the setting of certain headers such as Content-Range and + Date, which are abstracted by the transport layer. In such cases, + consider calling ServeAsset. + + SetCookie(name:str,value:str[,options:table]) + Appends Set-Cookie HTTP header to the response header buffer. + Several Set-Cookie headers can be added to the same response. + __Host- and __Secure- prefixes are supported and may set or + overwrite some of the options (for example, specifying __Host- + prefix sets the Secure option to true, sets the path to "/", and + removes the Domain option). The following options can be used (their + lowercase equivalents are supported as well): + - Expires: sets the maximum lifetime of the cookie as an HTTP-date + timestamp. Can be specified as a Date in the RFC1123 (string) + format or as a UNIX timestamp (number of seconds). + - MaxAge: sets number of seconds until the cookie expires. A zero + or negative number will expire the cookie immediately. If both + Expires and MaxAge are set, MaxAge has precedence. + - Domain: sets the host to which the cookie will be sent. + - Path: sets the path that must be present in the request URL, or + the client will not send the Cookie header. + - Secure: (bool) requests the cookie to be only send to the + server when a request is made with the https: scheme. + - HttpOnly: (bool) forbids JavaScript from accessing the cookie. + - SameSite: (Strict, Lax, or None) controls whether a cookie is + sent with cross-origin requests, providing some protection + against cross-site request forgery attacks. + + GetParam(name:str) → value:str + Returns first value associated with name. name is handled in a + case-sensitive manner. This function checks Request-URL parameters + first. Then it checks application/x-www-form-urlencoded from the + message body, if it exists, which is common for HTML forms sending + POST requests. If a parameter is supplied matching name that has + no value, e.g. foo in ?foo&bar=value, then the returned value will + be nil, whereas for ?foo=&bar=value it would be "". To + differentiate between no-equal and absent, use the HasParam + function. The returned value is decoded from ISO-8859-1 (only in + the case of Request-URL) and we assume that percent-encoded + characters were supplied by the client as UTF-8 sequences, which + are returned exactly as the client supplied them, and may + therefore may contain overlong sequences, control codes, NUL + characters, and even numbers which have been banned by the IETF. + It is the responsibility of the caller to impose further + restrictions on validity, if they're desired. + + EscapeHtml(str) → str + Escapes HTML entities: The set of entities is &><"' which become + &><"'. This function is charset agnostic and + will not canonicalize overlong encodings. It is assumed that a + UTF-8 string will be supplied. See escapehtml.c. + + LaunchBrowser([path:str]) + Launches web browser on local machine with URL to this redbean + server. This function may be called from your /.init.lua. + + CategorizeIp(ip:uint32) → str + Returns a string describing an IP address. This is currently Class + A granular. It can tell you if traffic originated from private + networks, ARIN, APNIC, DOD, etc. + + DecodeBase64(ascii:str) → binary:str + Turns ASCII into binary, in a permissive way that ignores + characters outside the base64 alphabet, such as whitespace. See + decodebase64.c. + + DecodeLatin1(iso-8859-1:str) → utf-8:str + Turns ISO-8859-1 string into UTF-8. + + EncodeBase64(binary:str) → ascii:str + Turns binary into ASCII. This can be used to create HTML data: + URIs that do things like embed a PNG file in a web page. See + encodebase64.c. + + EncodeJson(value[,options:table]) → json:str + Turns passed Lua value into a JSON string. Tables with non-zero + length (as reported by `#`) are encoded as arrays with non-array + elements ignored. Empty tables are encoded as empty arrays. All + other tables are encoded as objects with numerical keys + converted to strings (so `{[3]=1}` is encoded as `{"3":1}`). + The following options can be used: + - useoutput: (bool=false) encodes the result directly to the + output buffer and returns `nil` value. This option is + ignored if used outside of request handling code. + - numformat: (string="%.14g") sets numeric format to be used. + - maxdepth: (number=64) sets the max number of nested tables. + + EncodeLua(value[,options:table]) → json:str + Turns passed Lua value into a Lua string. The following options + can be used: + - useoutput: (bool=false) encodes the result directly to the + output buffer and returns `nil` value. This option is + ignored if used outside of request handling code. + - numformat: (string="%.14g") sets numeric format to be used. + - maxdepth: (number=64) sets the max number of nested tables. + + EncodeLatin1(utf-8:str[,flags:int]) → iso-8859-1:str + Turns UTF-8 into ISO-8859-1 string. + + EscapeFragment(str) → str + Escapes URL #fragment. The allowed characters are + -/?.~_@:!$&'()*+,;=0-9A-Za-z and everything else gets %XX encoded. + Please note that '& can still break HTML and that '() can still + break CSS URLs. This function is charset agnostic and will not + canonicalize overlong encodings. It is assumed that a UTF-8 string + will be supplied. See kescapefragment.c. + + EscapeHost(str) → str + Escapes URL host. See kescapeauthority.c + + EscapeLiteral(str) → str + Escapes JavaScript or JSON string literal content. The caller is + responsible for adding the surrounding quotation marks. This + implementation \uxxxx sequences for all non-ASCII sequences. HTML + entities are also encoded, so the output doesn't need EscapeHtml. + This function assumes UTF-8 input. Overlong encodings are + canonicalized. Invalid input sequences are assumed to be + ISO-8859-1. The output is UTF-16 since that's what JavaScript + uses. For example, some individual codepoints such as emoji + characters will encode as multiple \uxxxx sequences. Ints that are + impossible to encode as UTF-16 are substituted with the \xFFFD + replacement character. See escapejsstringliteral.c. + + EscapeParam(str) → str + Escapes URL parameter name or value. The allowed characters are + -.*_0-9A-Za-z and everything else gets %XX encoded. This function + is charset agnostic and will not canonicalize overlong encodings. + It is assumed that a UTF-8 string will be supplied. See + kescapeparam.c. + + EscapePass(str) → str + Escapes URL password. See kescapeauthority.c. + + EscapePath(str) → str + Escapes URL path. This is the same as EscapeSegment except slash + is allowed. The allowed characters are -.~_@:!$&'()*+,;=0-9A-Za-z/ + and everything else gets %XX encoded. Please note that '& can + still break HTML, so the output may need EscapeHtml too. Also note + that '() can still break CSS URLs. This function is charset + agnostic and will not canonicalize overlong encodings. It is + assumed that a UTF-8 string will be supplied. See kescapepath.c. + + EscapeSegment(str) → str + Escapes URL path segment. This is the same as EscapePath except + slash isn't allowed. The allowed characters are + -.~_@:!$&'()*+,;=0-9A-Za-z and everything else gets %XX encoded. + Please note that '& can still break HTML, so the output may need + EscapeHtml too. Also note that '() can still break CSS URLs. This + function is charset agnostic and will not canonicalize overlong + encodings. It is assumed that a UTF-8 string will be supplied. See + kescapesegment.c. + + EscapeUser(str) → str + Escapes URL username. See kescapeauthority.c. + + Fetch(url:str[,body:str|{method=value:str,body=value:str,...}]) + → status:int,{header:str=value:str,...},body:str + Sends an HTTP/HTTPS request to the specified URL. If only the URL is + provided, then a GET request is sent. If both URL and body parameters + are specified, then a POST request is sent. If any other method needs + to be specified (for example, PUT or DELETE), then passing a table as + the second value allows setting method and body values as well other + options: + - method (default = "GET"): sets the method to be used for the + request. The specified method is converted to uppercase. + - body (default = ""): sets the body value to be sent. + - followredirect (default = true): forces temporary and permanent + redirects to be followed. This behavior can be disabled by + passing `false`. + - maxredirects (default = 5): sets the number of allowed redirects + to minimize looping due to misconfigured servers. When the number + is exceeded, the result of the last redirect is returned. + When the redirect is being followed, the same method and body values + are being sent in all cases except when 303 status is returned. In + that case the method is set to GET and the body is removed before the + redirect is followed. Note that if these (method/body) values are + provided as table fields, they will be modified in place. + + FormatHttpDateTime(seconds:int) → rfc1123:str + Converts UNIX timestamp to an RFC1123 string that looks like this: + Mon, 29 Mar 2021 15:37:13 GMT. See formathttpdatetime.c. + + FormatIp(uint32) → str + Turns integer like 0x01020304 into a string like 1.2.3.4. See also + ParseIp for the inverse operation. + + GetAssetComment(path:str) → str + Returns comment text associated with asset in the ZIP central + directory. Also available as GetComment (deprecated). + + GetAssetMode(path:str) → int + Returns UNIX-style octal mode for ZIP asset (or local file if the + -D flag is used) + + GetAssetSize(path:str) → int + Returns byte size of uncompressed contents of ZIP asset (or local + file if the -D flag is used) + + GetBody() → str + Returns the request message body if present or an empty string. + Also available as GetPayload (deprecated). + + GetCookie(name:str) → str + Returns cookie value. + + GetCryptoHash(name:str,payload:str[,key:str]) → str + Returns value of the specified cryptographic hash function. If the + key is provided, then HMAC value of the same function is returned. + The name can be one of the following strings: MD5, SHA1, SHA224, + SHA256, SHA384, SHA512, and BLAKE2B256. + + GetRemoteAddr() → ip:uint32,port:uint16 + Returns client ip4 address and port, e.g. 0x01020304,31337 would + represent 1.2.3.4:31337. This is the same as GetClientAddr except + it will use the ip:port from the X-Forwarded-For header, only if + IsPrivateIp or IsLoopbackIp return true. When multiple addresses + are present in the header, the last/right-most address is used. + + GetClientAddr() → ip:uint32,port:uint16 + Returns client socket ip4 address and port, e.g. 0x01020304,31337 + would represent 1.2.3.4:31337. Please consider using GetRemoteAddr + instead, since the latter takes into consideration reverse proxy + scenarios. + + GetClientFd() → int + Returns file descriptor being used for client connection. + This is useful for scripts that want to use unix:fork(). + + IsClientUsingSsl() → bool + Returns true if client connection has begun being managed by + the MbedTLS security layer. This is an important thing to + consider if a script is taking control of GetClientFd() + + GetServerAddr() → ip:uint32,port:uint16 + Returns address to which listening server socket is bound, e.g. + 0x01020304,8080 would represent 1.2.3.4:8080. If -p 0 was supplied + as the listening port, then the port in this string will be + whatever number the operating system assigned. + + GetDate() → seconds:int + Returns date associated with request that's used to generate the + Date header, which is now, give or take a second. The returned + value is a UNIX timestamp. + + GetHeader(name:str) → value:str + Returns HTTP header. name is case-insensitive. The header value is + returned as a canonical UTF-8 string, with leading and trailing + whitespace trimmed, which was decoded from ISO-8859-1, which is + guaranteed to not have C0/C1 control sequences, with the exception + of the tab character. Leading and trailing whitespace is + automatically removed. In the event that the client suplies raw + UTF-8 in the HTTP message headers, the original UTF-8 sequence can + be losslessly restored by counter-intuitively recoding the + returned string back to Latin1. If the requested header is defined + by the RFCs as storing comma-separated values (e.g. Allow, + Accept-Encoding) and the field name occurs multiple times in the + message, then this function will fold those multiple entries into + a single string. + + GetHeaders() → {name:str=value:str,...} + Returns HTTP headers as dictionary mapping header key strings to + their UTF-8 decoded values. The ordering of headers from the + request message is not preserved. Whether or not the same key can + repeat depends on whether or not it's a standard header, and if + so, if it's one of the ones that the RFCs define as repeatable. + See khttprepeatable.c. Those headers will not be folded. Standard + headers which aren't on that list, will be overwritten with the + last-occurring one during parsing. Extended headers are always + passed through exactly as they're received. Please consider using + GetHeader API if possible since it does a better job abstracting + these issues. + + GetLogLevel() → int + Returns logger verbosity level. Likely return values are kLogDebug + > kLogVerbose > kLogInfo > kLogWarn > kLogError > kLogFatal. + + GetHost() → str + Returns host associated with request. This will be the Host + header, if it's supplied. Otherwise it's the bind address. + + GetHostOs() → str + Returns string that describes the host OS. + + This can return: + + - `"LINUX"` + - `"METAL"` + - `"WINDOWS"` + - `"XNU"` + - `"NETBSD"` + - `"FREEBSD"` + - `"OPENBSD"` + + GetMonospaceWidth(str|char) → int + Returns monospace display width of string. This is useful for + fixed-width formatting. For example, CJK characters typically take + up two cells. This function takes into consideration combining + characters, which are discounted, as well as control codes and + ANSI escape sequences. + + GetMethod() → str + Returns HTTP method. Normally this will be GET, HEAD, or POST in + which case redbean normalizes this value to its uppercase form. + Anything else that the RFC classifies as a "token" string is + accepted too, which might contain characters like &". + + GetParams() → {{name:str[,value:str]},...} + Returns name=value parameters from Request-URL and + application/x-www-form-urlencoded message body in the order they + were received. This may contain duplicates. The inner array will + have either one or two items, depending on whether or not the + equals sign was used. + + GetPath() → str + Returns the Request-URL path. This is guaranteed to begin with + "/". It is further guaranteed that no "//" or "/." exists in the + path. The returned value is returned as a UTF-8 string which was + decoded from ISO-8859-1. We assume that percent-encoded characters + were supplied by the client as UTF-8 sequences, which are returned + exactly as the client supplied them, and may therefore may contain + overlong sequences, control codes, NUL characters, and even + numbers which have been banned by the IETF. redbean takes those + things into consideration when performing path safety checks. It + is the responsibility of the caller to impose further restrictions + on validity, if they're desired. + + GetEffectivePath() → str + Returns path as it was resolved by the routing algorithms, which + might contain the virtual host prepended if used. + + GetScheme() → str + Returns scheme from Request-URL, if any. + + GetSslIdentity() → str + Returns certificate subject or PSK identity from the current SSL + session. `nil` is returned for regular (non-SSL) connections. + + GetStatus() → int + Returns current status (as set by an earlier SetStatus call) or + `nil` if the status hasn't been set yet. + + GetTime() → seconds:number + Returns current time as a UNIX timestamp with 0.0001s precision. + + GetUrl() → str + Returns the effective Request-URL as an ASCII string, where + illegal characters or UTF-8 is guaranteed to be percent encoded, + and has been normalized to include either the Host or + X-Forwarded-Host headers, if they exist, and possibly a scheme too + if redbean is being used as an HTTP proxy server. In the future + this API might change to return an object instead. + + GetHttpVersion() → int + Returns the request HTTP protocol version, which can be 9 for + HTTP/0.9, 10 for HTTP/1.0, or 11 for HTTP/1.1. Also available + as GetVersion (deprecated). + + GetRandomBytes([length:int]) → str + Returns string with the specified number of random bytes (1..256). + If no length is specified, then a string of length 16 is returned. + + GetRedbeanVersion() → int + Returns the Redbean version in the format 0xMMmmpp, with major (MM), + minor (mm), and patch (pp) versions encoded. The version value 1.4 + would be represented as 0x010400. + + GetZipPaths([prefix:str]) → {path:str,...} + Returns paths of all assets in the zip central directory, prefixed + by a slash. If prefix parameter is provided, then only paths that + start with the prefix (case sensitive) are returned. + + HasParam(name:str) → bool + Returns true if parameter with name was supplied in either the + Request-URL or an application/x-www-form-urlencoded message body. + + HidePath(prefix:str) + Programs redbean / listing page to not display any paths beginning + with prefix. This function should only be called from /.init.lua. + + IsPublicIp(uint32) → bool + Returns true if IP address is not a private network (10.0.0.0/8, + 172.16.0.0/12, 192.168.0.0/16) and is not localhost (127.0.0.0/8). + Note: we intentionally regard TEST-NET IPs as public. + + IsPrivateIp(uint32) → bool + Returns true if IP address is part of a private network + (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16). + + IsLoopbackIp(uint32) → bool + Returns true if IP address is part of the localhost network + (127.0.0.0/8). + + IsCompressed(path:str) → bool + Returns true if ZIP artifact at path is stored on disk using + DEFLATE compression. + + IndentLines(str[,int]) → str + Adds spaces to beginnings of multiline string. If the int + parameter is not supplied then 1 space will be added. + + LoadAsset(path:str) → str + Returns contents of file as string. The asset may be sourced from + either the zip (decompressed) or the local filesystem if the -D + flag was used. If slurping large file into memory is a concern, + then consider using ServeAsset which can serve directly off disk. + + StoreAsset(path:str,data:str[,mode:int]) + Stores asset to executable's ZIP central directory. This currently + happens in an append-only fashion and is still largely in the + proof-of-concept stages. Currently only supported on Linux, XNU, + and FreeBSD. + + Log(level:int,message:str) + Emits message string to log, if level is less than or equal to + GetLogLevel. If redbean is running in interactive mode, then this + will log to the console. If redbean is running as a daemon or the + -L LOGFILE flag is passed, then this will log to the file. + Reasonable values for level are kLogDebug > kLogVerbose > kLogInfo + > kLogWarn > kLogError > kLogFatal. The logger emits timestamps in + the local timezone with microsecond precision. If log entries are + emitted more frequently than once per second, then the log entry + will display a delta timestamp, showing how much time has elapsed + since the previous log entry. This behavior is useful for quickly + measuring how long various portions of your code take to execute. + + ParseHttpDateTime(rfc1123:str) → seconds:int + Converts RFC1123 string that looks like this: Mon, 29 Mar 2021 + 15:37:13 GMT to a UNIX timestamp. See parsehttpdatetime.c. + + ParseUrl(str) → URL + Parses URL, returning object having the following fields: scheme, + user, pass, host, port, path, params, fragment. This parser is + charset agnostic. Percent encoded bytes are decoded for all + fields. Returned values might contain things like NUL characters, + spaces, control codes, and non-canonical encodings. Absent can be + discerned from empty by checking if the pointer is set. There's no + failure condition for this routine. This is a permissive parser. + This doesn't normalize path segments like `.` or `..` so use + IsAcceptablePath() to check for those. No restrictions are imposed + beyond that which is strictly necessary for parsing. All the data + that is provided will be consumed to the one of the fields. Strict + conformance is enforced on some fields more than others, like + scheme, since it's the most non-deterministically defined field of + them all. Please note this is a URL parser, not a URI parser. + Which means we support everything everything the URI spec says we + should do except for the things we won't do, like tokenizing path + segments into an array and then nesting another array beneath each + of those for storing semicolon parameters. So this parser won't + make SIP easy. What it can do is parse HTTP URLs and most URIs + like data:opaque, better in fact than most things which claim to + be URI parsers. + + EncodeUrl(URL) → str + This function is the inverse of ParseUrl. The output will always + be correctly formatted. The exception is if illegal characters are + supplied in the scheme field, since there's no way of escaping + those. Opaque parts are escaped as though they were paths, since + many URI parsers won't understand things like an unescaped + question mark in path. + + ParseIp(str) → int + Converts IPv4 address string to integer, e.g. "1.2.3.4" → + 0x01020304, or returns -1 for invalid inputs. See also FormatIp + for the inverse operation. + + ProgramAddr(str) + Configures the address on which to listen. Can be used multiple + times to set more than one address. + + ProgramBrand(str) + Changes HTTP Server header, as well as the

title on the / + listing page. The brand string needs to be a UTF-8 value that's + encodable as ISO-8859-1. If the brand is changed to something + other than redbean, then the promotional links will be removed + from the listing page too. This function should only be called + from /.init.lua. + + ProgramCache(seconds:int) + Configures Cache-Control and Expires header generation for static + asset serving. A negative value will disable the headers. Zero + means don't cache. Greater than zero asks public proxies and + browsers to cache for a given number of seconds. This should only + be called from /.init.lua. + + ProgramCertificate(pem:str) + Same as the -C flag if called from .init.lua, e.g. + ProgramCertificate(LoadAsset("/.sign.crt")) for zip loading or + ProgramCertificate(Slurp("/etc/letsencrypt.lol/fullchain.pem")) for + local file system only. + + ProgramHeader(name:str,value:str) + Appends HTTP header to the header buffer for all responses (whereas + SetHeader only appends a header to the current response buffer). + name is case-insensitive and restricted to non-space ASCII. value + is a UTF-8 string that must be encodable as ISO-8859-1. Leading and + trailing whitespace is trimmed automatically. Overlong characters + are canonicalized. C0 and C1 control codes are forbidden, with the + exception of tab. The header buffer is independent of the payload + buffer. This function disallows the setting of certain headers such + as Content-Range and Date, which are abstracted by the transport + layer. + + ProgramPort(uint16) + Hard-codes the port number on which to listen, which can be any + number in the range 1..65535, or alternatively 0 to ask the + operating system to choose a port, which may be revealed later on + by GetServerAddr or the -z flag to stdout. + + ProgramPrivateKey(pem:str) + Same as the -K flag if called from .init.lua, e.g. + ProgramPrivateKey(LoadAsset("/.sign.key")) for zip loading or + ProgramPrivateKey(Slurp("/etc/letsencrypt/fullchain.pem")) for + local file system only. + + ProgramRedirect(code:int,src:str,location:str) + Configures fallback routing for paths which would otherwise return + 404 Not Found. If code is 0 then the path is rewritten internally + as an accelerated redirect. If code is 301, 302, 307, or 308 then + a redirect response will be sent to the client. This should only + be called from /.init.lua. + + ProgramSslTicketLifetime(seconds:int) + Defaults to 86400 (24 hours). This may be set to ≤0 to disable + SSL tickets. It's a good idea to use these since it increases + handshake performance 10x and eliminates a network round trip. + + EvadeDragnetSurveillance(bool) + If this option is programmed then redbean will not transmit a + Server Name Indicator (SNI) when performing Fetch() requests. + + ProgramSslPresharedKey(key:str,identity:str) + This function can be used to enable the PSK ciphersuites + which simplify SSL and enhance its performance in controlled + environments. `key` may contain 1..32 bytes of random binary + data and identity is usually a short plaintext string. The + first time this function is called, the preshared key will + be added to both the client and the server SSL configs. If + it's called multiple times, then the remaining keys will be + added to the server, which is useful if you want to assign + separate keys to each client, each of which needs a separate + identity too. If this function is called multiple times with + the same identity string, then the latter call will overwrite + the prior. If a preshared key is supplied and no certificates + or key-signing-keys are programmed, then redbean won't bother + auto-generating any serving certificates and will instead use + only PSK ciphersuites. + + ProgramSslCiphersuite(name:str) + See https://redbean.dev/ for further details. + + IsDaemon() → bool + Returns true if -d flag was passed to redbean. + + ProgramUid(int) + Same as the -U flag if called from .init.lua for setuid() + + ProgramGid(int) + Same as the -G flag if called from .init.lua for setgid() + + ProgramDirectory(str) + Same as the -D flag if called from .init.lua for overlaying local + file system directories. This may be called multiple times. The + first directory programmed is preferred. These currently do not + show up in the index page listing. + + ProgramLogMessages(bool) + Same as the -m flag if called from .init.lua for logging message + headers only. + + ProgramLogBodies(bool) + Same as the -b flag if called from .init.lua for logging message + bodies as part of POST / PUT / etc. requests. + + ProgramLogPath(str) + Same as the -L flag if called from .init.lua for setting the log + file path on the local file system. It's created if it doesn't + exist. This is called before de-escalating the uesr / group id. + The file is opened in append only mode. If the disk runs out of + space then redbean will truncate the log file if has access to + change the log file after daemonizing. + + ProgramPidPath(str) + Same as the -P flag if called from .init.lua for setting the pid + file path on the local file system. It's useful for reloading + daemonized redbean using `kill -HUP $(cat /var/run/redbean.pid)` + or terminating redbean with `kill $(cat /var/run/redbean.pid)` + which will gracefully terminate all clients. Sending the TERM + signal twice will cause a forceful shutdown, which might make + someone with a slow internet connection who's downloading big + files unhappy. + + ProgramUniprocess([bool]) → bool + Same as the -u flag if called from .init.lua. Can be used to + configure the uniprocess mode. The current value is returned. + + Slurp(filename:str) → str + Reads file data from local file system. + + Sleep(seconds:number) + Sleeps the specified number of seconds (can be fractional). The + smallest interval is a microsecond. + + Route([host:str,[path:str]]) + Instructs redbean to follow the normal HTTP serving path. This + function is useful when writing an OnHttpRequest handler, since + that overrides the serving path entirely. So if the handler + decides it doesn't want to do anything, it can simply call this + function, to hand over control back to the redbean core. By + default, the host and path arguments are supplied from the + resolved GetUrl value. This handler always resolves, since it will + generate a 404 Not Found response if redbean couldn't find an + appropriate endpoint. + + RouteHost([host:str,[path:str]]) → bool + This is the same as Route, except it only implements the subset of + request routing needed for serving virtual-hosted assets, where + redbean tries to prefix the path with the hostname when looking up + a file. This function returns true if the request was resolved. If + it was resolved, then your OnHttpRequest request handler can still + set additional headers. + + RoutePath([path:str]) → bool + This is the same as Route, except it only implements the subset of + request routing needed for serving assets. This function returns + true if the request was resolved. If it was resolved, then your + OnHttpRequest request handler can still set additional headers. + Note that the asset needs to have "read other" permissions; + otherwise this function logs a warning and returns 403 Forbidden. + If this is undesirable, use GetAssetMode and ServeAsset to bypass + the check. + + ServeAsset(path:str) + Instructs redbean to serve static asset at path. This function + causes what would normally happen outside a dynamic handler to + happen. The asset can be sourced from either the zip or local + filesystem if -D is used. This function is mutually exclusive with + SetStatus and other Serve* functions. + + ServeError(code:int[,reason:str]) + Instructs redbean to serve a boilerplate error page. This takes + care of logging the error, setting the reason phrase, and adding a + payload. This function is mutually exclusive with SetStatus and + other Serve* functions. + + ServeRedirect(code:int,location:str) + Instructs redbean to return the specified redirect code along with + the Location header set. This function is mutually exclusive with + SetStatus and other Serve* functions. + + SetLogLevel(level:int) + Sets logger verbosity. Reasonable values for level are kLogDebug > + kLogVerbose > kLogInfo > kLogWarn > kLogError > kLogFatal. This is + reset at the end of the http request, so it can be used to disable + access log and message logging. + + VisualizeControlCodes(str) → str + Replaces C0 control codes with their UNICODE pictures + representation. This function also canonicalizes overlong + encodings. C1 control codes are replaced with a JavaScript-like + escape sequence. + + Underlong(str) → str + Canonicalizes overlong encodings. + + Crc32(initial:int,data:str) → int + Computes 32-bit CRC-32 used by zip/zlib/gzip/etc. + + Crc32c(initial:int,data:str) → int + Computes 32-bit Castagnoli Cyclic Redundancy Check. + + Md5(str) → str + Computes MD5 checksum, returning 16 bytes of binary. + + Sha1(str) → str + Computes SHA1 checksum, returning 20 bytes of binary. + + Sha224(str) → str + Computes SHA224 checksum, returning 28 bytes of binary. + + Sha256(str) → str + Computes SHA256 checksum, returning 32 bytes of binary. + + Sha384(str) → str + Computes SHA384 checksum, returning 48 bytes of binary. + + Sha512(str) → str + Computes SHA512 checksum, returning 64 bytes of binary. + + Bsf(x:int) → int + Returns position of first bit set. Passing 0 will raise an error. + Same as the Intel x86 instruction BSF. + + Bsr(x:int) → int + Returns binary logarithm of 𝑥. Passing 0 will raise an error. Same + as the Intel x86 instruction BSR. + + Popcnt(x:int) → int + Returns number of bits set in integer. + + Rdtsc() → int + Returns CPU timestamp counter. + + Lemur64() → int + Returns fastest pseudorandom non-cryptographic random number. This + linear congruential generator passes practrand and bigcrush. + + Rand64() → int + Returns nondeterministic pseudorandom non-cryptographic number. This + linear congruential generator passes practrand and bigcrush. This + generator is safe across fork(), threads, and signal handlers. - GetCpuCore() → int - Returns 0-indexed CPU core on which process is currently scheduled. + Rdrand() → int + Returns 64-bit hardware random integer from RDRND instruction, with + automatic fallback to getrandom() if not available. - GetCpuNode() → int - Returns 0-indexed NUMA node on which process is currently scheduled. + Rdseed() → int + Returns 64-bit hardware random integer from RDSEED instruction, with + automatic fallback to RDRND and getrandom() if not available. - Decimate(data) → int - Shrinks byte buffer in half using John Costella's magic kernel. - This downscales data 2x using an eight-tap convolution, e.g. + GetCpuCore() → int + Returns 0-indexed CPU core on which process is currently scheduled. - >: Decimate(b'\\xff\\xff\\x00\\x00\\xff\\xff\\x00\\x00\\xff\\xff\\x00\\x00') - b'\\xff\\x00\\xff\\x00\\xff\\x00' + GetCpuNode() → int + Returns 0-indexed NUMA node on which process is currently scheduled. - This is very fast if SSSE3 is available (Intel 2004+ / AMD 2011+). + Decimate(data) → int + Shrinks byte buffer in half using John Costella's magic kernel. + This downscales data 2x using an eight-tap convolution, e.g. - MeasureEntropy(data) → float - Returns Shannon entropy of array. This gives you an idea of - the density of information. Cryptographic random should be in - the ballpark of 7.9 whereas plaintext will be more like 4.5. + >: Decimate(b'\\xff\\xff\\x00\\x00\\xff\\xff\\x00\\x00\\xff\\xff\\x00\\x00') + b'\\xff\\x00\\xff\\x00\\xff\\x00' + This is very fast if SSSE3 is available (Intel 2004+ / AMD 2011+). + + MeasureEntropy(data) → float + Returns Shannon entropy of array. This gives you an idea of + the density of information. Cryptographic random should be in + the ballpark of 7.9 whereas plaintext will be more like 4.5. + + +CONSTANTS + + kLogDebug + Integer for debug logging level. See Log. + + kLogVerbose + Integer for verbose logging level, which is less than kLogDebug. + + kLogInfo + Integer for info logging level, which is less than kLogVerbose. + + kLogWarn + Integer for warn logging level, which is less than kLogVerbose. + + kLogError + Integer for error logging level, which is less than kLogWarn. + + kLogFatal + Integer for fatal logging level, which is less than kLogError. + Logging anything at this level will result in a backtrace and + process exit. + + LSQLITE3 MODULE - Please refer to the LuaSQLite3 Documentation. + Please refer to the LuaSQLite3 Documentation. - For example, you could put the following in your /.init.lua file: + For example, you could put the following in your /.init.lua file: - sqlite3 = require "lsqlite3" - db = sqlite3.open_memory() - db:exec[[ - CREATE TABLE test ( - id INTEGER PRIMARY KEY, - content TEXT - ); - INSERT INTO test (content) VALUES ('Hello World'); - INSERT INTO test (content) VALUES ('Hello Lua'); - INSERT INTO test (content) VALUES ('Hello Sqlite3'); - ]] + sqlite3 = require "lsqlite3" + db = sqlite3.open_memory() + db:exec[[ + CREATE TABLE test ( + id INTEGER PRIMARY KEY, + content TEXT + ); + INSERT INTO test (content) VALUES ('Hello World'); + INSERT INTO test (content) VALUES ('Hello Lua'); + INSERT INTO test (content) VALUES ('Hello Sqlite3'); + ]] - Then, your Lua server pages or OnHttpRequest handler may perform SQL - queries by accessing the db global. The performance is good too, at about - 400k qps. + Then, your Lua server pages or OnHttpRequest handler may perform SQL + queries by accessing the db global. The performance is good too, at about + 400k qps. - for row in db:nrows("SELECT * FROM test") do - Write(row.id.." "..row.content.."
") - end + for row in db:nrows("SELECT * FROM test") do + Write(row.id.." "..row.content.."
") + end - redbean supports a subset of what's defined in the upstream LuaSQLite3 - project. Most of the unsupported APIs relate to pointers and database - notification hooks. + redbean supports a subset of what's defined in the upstream LuaSQLite3 + project. Most of the unsupported APIs relate to pointers and database + notification hooks. - redbean also currently disables SQLite features which don't make sense for - production serving, such as ALTER, VACUUM, ANALYZE, etc. For that reason - we provide an APE build of the SQLite shell which you can use to - administrate your redbean database. See the sqlite3.com download above. + redbean also currently disables SQLite features which don't make sense for + production serving, such as ALTER, VACUUM, ANALYZE, etc. For that reason + we provide an APE build of the SQLite shell which you can use to + administrate your redbean database. See the sqlite3.com download above. + RE MODULE - This module exposes an API for POSIX regular expressions which enable you - to validate input, search for substrings, extract pieces of strings, etc. - Here's a usage example: + This module exposes an API for POSIX regular expressions which enable you + to validate input, search for substrings, extract pieces of strings, etc. + Here's a usage example: - # Example IPv4 Address Regular Expression (see also ParseIP) - p = re.compile([[^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$]]) - m,a,b,c,d = p:search(𝑠) - if m then - print("ok", tonumber(a), tonumber(b), tonumber(c), tonumber(d)) - else - print("not ok") - end + # Example IPv4 Address Regular Expression (see also ParseIP) + p = re.compile([[^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$]]) + m,a,b,c,d = p:search(𝑠) + if m then + print("ok", tonumber(a), tonumber(b), tonumber(c), tonumber(d)) + else + print("not ok") + end - re.search(regex:str,text:str[,flags:int]) → [match[,group_1,...]] - Shortcut for re.compile plus regex_t*:search. + re.search(regex:str,text:str[,flags:int]) → [match[,group_1,...]] + Shortcut for re.compile plus regex_t*:search. - re.compile(regex:str[,flags:int]) → regex_t* - Compiles regular expression, using the POSIX extended syntax. This - has an O(2^𝑛) cost, so it's a good idea to do this from your - /.init.lua file. Flags may contain re.BASIC, re.ICASE, re.NOSUB, - and/or re.NEWLINE. See also regcomp() from libc. + re.compile(regex:str[,flags:int]) → regex_t* + Compiles regular expression, using the POSIX extended syntax. This + has an O(2^𝑛) cost, so it's a good idea to do this from your + /.init.lua file. Flags may contain re.BASIC, re.ICASE, re.NOSUB, + and/or re.NEWLINE. See also regcomp() from libc. - regex_t*:search(text:str[,flags:int]) → [match[,group_1,...]] - Executes regular expression. This has an O(𝑛) cost. This returns - nothing (nil) if the pattern doesn't match anything. Otherwise it - pushes the matched substring and any parenthesis-captured values - too. Flags may contain re.NOTBOL or re.NOTEOL to indicate whether - or not text should be considered at the start and/or end of a - line. + regex_t*:search(text:str[,flags:int]) → [match[,group_1,...]] + Executes regular expression. This has an O(𝑛) cost. This returns + nothing (nil) if the pattern doesn't match anything. Otherwise it + pushes the matched substring and any parenthesis-captured values + too. Flags may contain re.NOTBOL or re.NOTEOL to indicate whether + or not text should be considered at the start and/or end of a + line. - re.BASIC - Use this flag if you prefer the default POSIX regex syntax. We use - extended regex notation by default. For example, an extended - regular expression for matching an IP address might look like - ([0-9]*)\.([0-9]*)\.([0-9]*)\.([0-9]*) whereas with basic syntax - it would look like \([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\). - This flag may only be used with re.compile and re.search. + re.BASIC + Use this flag if you prefer the default POSIX regex syntax. We use + extended regex notation by default. For example, an extended + regular expression for matching an IP address might look like + ([0-9]*)\.([0-9]*)\.([0-9]*)\.([0-9]*) whereas with basic syntax + it would look like \([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\). + This flag may only be used with re.compile and re.search. - re.ICASE - Use this flag to make your pattern case ASCII case-insensitive. - This means [a-z] will mean the same thing as [A-Za-z]. This flag - may only be used with re.compile and re.search. + re.ICASE + Use this flag to make your pattern case ASCII case-insensitive. + This means [a-z] will mean the same thing as [A-Za-z]. This flag + may only be used with re.compile and re.search. - re.NEWLINE - Use this flag to change the handling of NEWLINE (\x0a) characters. - When this flag is set, (1) a NEWLINE shall not be matched by a "." - or any form of a non-matching list, (2) a "^" shall match the - zero-length string immediately after a NEWLINE (regardless of - re.NOTBOL), and (3) a "$" shall match the zero-length string - immediately before a NEWLINE (regardless of re.NOTEOL). + re.NEWLINE + Use this flag to change the handling of NEWLINE (\x0a) characters. + When this flag is set, (1) a NEWLINE shall not be matched by a "." + or any form of a non-matching list, (2) a "^" shall match the + zero-length string immediately after a NEWLINE (regardless of + re.NOTBOL), and (3) a "$" shall match the zero-length string + immediately before a NEWLINE (regardless of re.NOTEOL). - re.NOSUB - Causes re.search to only report success and failure. This is - reported via the API by returning empty string for success. This - flag may only be used with re.compile and re.search. + re.NOSUB + Causes re.search to only report success and failure. This is + reported via the API by returning empty string for success. This + flag may only be used with re.compile and re.search. - re.NOTBOL - The first character of the string pointed to by string is not the - beginning of the line. This flag may only be used with re.search - and regex_t*:search. + re.NOTBOL + The first character of the string pointed to by string is not the + beginning of the line. This flag may only be used with re.search + and regex_t*:search. - re.NOTEOL - The last character of the string pointed to by string is not the - end of the line. This flag may only be used with re.search and - regex_t*:search. + re.NOTEOL + The last character of the string pointed to by string is not the + end of the line. This flag may only be used with re.search and + regex_t*:search. + MAXMIND MODULE - This module may be used to get city/country/asn/etc from IPs, e.g. + This module may be used to get city/country/asn/etc from IPs, e.g. - -- .init.lua - maxmind = require "maxmind" - asndb = maxmind.open('/usr/local/share/maxmind/GeoLite2-ASN.mmdb') + -- .init.lua + maxmind = require "maxmind" + asndb = maxmind.open('/usr/local/share/maxmind/GeoLite2-ASN.mmdb') - -- request handler - as = asndb:lookup(GetRemoteAddr()) - if as then - asnum = as:get("autonomous_system_number") - asorg = as:get("autonomous_system_organization") - Write(EscapeHtml(asnum)) - Write(' ') - Write(EscapeHtml(asorg)) - end + -- request handler + as = asndb:lookup(GetRemoteAddr()) + if as then + asnum = as:get("autonomous_system_number") + asorg = as:get("autonomous_system_organization") + Write(EscapeHtml(asnum)) + Write(' ') + Write(EscapeHtml(asorg)) + end - For further details, please see maxmind.lua in redbean-demo.com. + For further details, please see maxmind.lua in redbean-demo.com. + UNIX MODULE This module exposes the low-level UNIX system call interface. The way @@ -1281,23 +1334,39 @@ UNIX MODULE some kind of error obtaining the required arguments. Once Lua reads the arguments and the call is delegated to the system call interface, all further errors won't be raised, but rather returned as errnos, - which should always be checked. For example, most calls follow: + which should always be checked. For example, most syscalls follow: - rc, errno = unix.foo(...) + errno = unix.foo(...) + if errno then + Log(kLogWarn, 'foo() failed: %s' % {unix.strerror(errno)}) + end - Where the underlying call returning an rc of -1 will map to a Lua - value of nil, and if the system call doesn't return an error number - then errno will be nil and rc will be non-nil. To see which errnos are - possible for which system calls, please see the comprehensive index at - the bottom of this section. + Any POSIX API that's defined as returning 0 on success or -1 on error + is wrapped here to return nil on success and an integer on error. To + see which errnos are possible for which system calls, please see the + comprehensive index at the bottom of this section. - Your system calls are: + In cases where POSIX defines an API as returning codes on success we + wrap the APIs as follows: + + rc, errno = unix.bar(...) + if rc then + Log(kLogWarn, 'foo() succeeded: %d' % {rc}) + else + Log(kLogWarn, 'foo() failed: %s' % {unix.strerror(errno)}) + end + + If the above code succeeds, `rc` will be non-nil and `errno` will be + `nil`. If the above code fails, `rc` will be nil and `errno` will be + an integer greater than zero. + + UNIX FUNCTIONS unix.read(fd:int[, bufsiz:int, offset:int]) → data:str[, errno:int] Reads from file descriptor. - unix.write(fd:int, data[, offset]) → rc:int[, errno:int] + unix.write(fd:int, data[, offset:int]) → wrote:int[, errno:int] Writes to file descriptor. @@ -1343,7 +1412,7 @@ UNIX MODULE already. If it does exist then `nil` is returned along with `errno` set to `EEXIST`. - unix.close(fd:int) → rc:int[, errno:int] + unix.close(fd:int) → errno:int Closes file descriptor. @@ -1354,6 +1423,26 @@ UNIX MODULE will be closed. Any open connections it owns will be reset. This function never returns. + unix.environ() → {str, ...} + + Returns raw environment variables. + + This allocates and constructs the C/C++ `environ` variable as a Lua + table consisting of string keys and string values. + + This data structure preserves casing. On Windows NT, by convention, + environment variable keys are treated in a case-insensitive way. It + is the responsibility of the caller to consider this. + + This data structure preserves valueless variables. It's possible on + both UNIX and Windows to have an environment variable without an + equals, even though it's unusual. + + This data structure preserves duplicates. For example, on Windows, + there's some irregular uses of environment variables such as how the + command prompt inserts multiple environment variables with empty + string as keys, for its internal bookkeeping. + unix.fork() → childpid|0:int[, errno:int] Creates a new process mitosis style. This returns twice. The @@ -1368,14 +1457,14 @@ UNIX MODULE `prog` contains slashes then it's not path searched either and will be returned if it exists. - unix.execve(prog:str[, args:List<*>, env:Map]) → errno:int + unix.execve(prog:str[, args:List<*>[, env:List<*>]]) → errno:int Exits current process, replacing it with a new instance of the specified program. `prog` needs to be an absolute path, see commandv(). `env` defaults to to the current `environ`. Here's a basic usage example: - unix.execve("/bin/ls", {"/bin/ls", "-hal"}, {PATH="/bin"}) + unix.execve("/bin/ls", {"/bin/ls", "-hal"}, {"PATH=/bin"}) unix.exit(127) `prog` needs to be the resolved pathname of your executable. You @@ -1385,12 +1474,16 @@ UNIX MODULE should be `prog`. Values are coerced to strings. This parameter defaults to `{prog}`. - `env` is a key/value table. Keys that aren't strings are - ignored. Values are coerced to strings. This parameter defaults - to environ() in a way that avoids copying. + `env` is a string list table. Values are coerced to strings. No + ordering requirement is imposed. By convention, each string has its + key and value divided by an equals sign without spaces. If this + paremeter is not specified, it'll default to the C/C++ `environ` + variable which is inherited from the shell that launched redbean. + It's the responsibility of the user to supply a sanitized environ + when spawning untrusted processes. - execve() function is normally called after fork() returns 0. If - that isn't the case, then your redbean worker will be destroyed. + execve() is normally called after fork() returns 0. If that isn't + the case, then your redbean worker will be destroyed. This function never returns on success. @@ -1408,7 +1501,7 @@ UNIX MODULE `flags` can have `O_CLOEXEC` which means the returned file descriptors will be automatically closed upon execve(). - unix.pipe([flags]) → reader, writer, errno:int + unix.pipe([flags:int]) → reader:int, writer:int[, errno:int] Creates fifo which enables communication between processes. Returns two file descriptors: one for reading and one for @@ -1485,15 +1578,19 @@ UNIX MODULE end end - unix.getpid() → pid + unix.getpid() → pid:int Returns process id of current process. - unix.getppid() → pid + This function does not fail. + + unix.getppid() → pid:int Returns process id of parent process. - unix.kill(pid, sig) → rc:int[, errno:int] + This function does not fail. + + unix.kill(pid, sig) → errno:int Returns process id of current process. @@ -1502,41 +1599,67 @@ UNIX MODULE Triggers signal in current process. This is pretty much the same as `kill(getpid(), sig)`. - unix.access(path:str, how) → rc:int[, errno:int] + unix.access(path:str, how) → errno:int - Checks if effective user of current process has permission to - access file. `how` can be `R_OK`, `W_OK`, `X_OK`, or `F_OK` to - check for read, write, execute, and existence respectively. + Checks if effective user of current process has permission to access + file. `how` can be `R_OK`, `W_OK`, `X_OK`, or `F_OK` to check for + read, write, execute, and existence respectively. - unix.mkdir(path:str, mode) → rc:int[, errno:int] + unix.mkdir(path:str, mode) → errno:int - Makes directory. `mode` should be octal, e.g. `0755`. + Makes directory. - unix.chdir(path:str) → rc:int[, errno:int] + `path` is the path of the directory you wish to create. + + `mode` is octal permission bits, e.g. `0755`. + + Fails with `EEXIST` if `path` already exists, whether it be a + directory or a file. + + Fails with `ENOENT` if the parent directory of the directory you + want to create doesn't exist. For making `a/really/long/path/` + consider using makedirs() instead. + + Fails with `ENOTDIR` if a parent directory component existed that + wasn't a directory. + + Fails with `EACCES` if the parent directory doesn't grant write + permission to the current user. + + Fails with `ENAMETOOLONG` if the path is too long. + + unix.makedirs(path:str, mode) → errno:int + + Makes directories. + + `path` is the path of the directory you wish to create. + + `mode` is octal permission bits, e.g. `0755`. + + Unlike mkdir() this convenience wrapper will automatically create + parent parent directories as needed. + + unix.chdir(path:str) → errno:int Changes current directory to `path`. - unix.unlink(path:str) → rc:int[, errno:int] + unix.unlink(path:str) → errno:int Removes file at `path`. - unix.rmdir(path:str) → rc:int[, errno:int] + unix.rmdir(path:str) → errno:int Removes empty directory at `path`. - unix.chroot(path:str) → rc:int[, errno:int] + unix.rename(oldpath:str, newpath:str) → errno:int - Changes root directory. Raises `ENOSYS` on Windows. + Renames file or directory. - unix.rename(oldpath:str, newpath:str) → rc:int[, errno:int] - - Renames file. - - unix.link(existingpath:str, newpath:str) → rc:int[, errno:int] + unix.link(existingpath:str, newpath:str) → errno:int Creates hard link, so your underlying inode has two names. - unix.symlink(target:str, linkpath:str) → rc:int[, errno:int] + unix.symlink(target:str, linkpath:str) → errno:int Creates soft link, or a symbolic link. @@ -1545,15 +1668,15 @@ UNIX MODULE Returns absolute path of filename, with `.` and `..` components removed, and symlinks will be resolved. - unix.chown(path:str, uid, gid) → rc:int[, errno:int] + unix.chown(path:str, uid, gid) → errno:int Changes user and gorup on file. - unix.chmod(path:str, mode) → rc:int[, errno:int] + unix.chmod(path:str, mode) → errno:int Changes mode bits on file. - unix.getcwd(path:str, mode) → rc:int[, errno:int] + unix.getcwd() → path:str[, errno:int] Returns current working directory. @@ -1568,62 +1691,165 @@ UNIX MODULE POSIX advisory locks can be controlled by setting `cmd` to `F_UNLCK`, `F_RDLCK`, `F_WRLCK`, `F_SETLK`, or `F_SETLKW`. - unix.getsid(pid) → sid, errno:int + unix.getsid(pid:int) → sid:int[, errno:int] Gets session id. - unix.getpgrp() → pgid, errno:int + unix.getpgrp() → pgid:int[, errno:int] Gets process group id. - unix.setpgrp() → pgid, errno:int + unix.setpgrp() → pgid:int[, errno:int] Sets process group id. This is the same as `setpgid(0,0)`. - unix.setpgid(pid, pgid) → pgid, errno:int + unix.setpgid(pid:int, pgid:int) → pgid:int[, errno:int] Sets process group id the modern way. - unix.getpgid(pid) → pgid, errno:int + unix.getpgid(pid) → pgid:int[, errno:int] Gets process group id the modern wayp. - unix.setsid() → sid, errno:int + unix.setsid() → sid:int[, errno:int] Sets session id. This function can be used to create daemons. + Fails with `ENOSYS` on Windows NT. + unix.getuid() → uid:int - Gets user id. + Gets real user id. + + On Windows this system call is polyfilled by running GetUserNameW() + through Knuth's multiplicative hash. + + This function does not fail. unix.getgid() → gid:int - Sets group id. + Sets real group id. - unix.setuid(uid:int) → rc:int[, errno:int] + On Windows this system call is polyfilled as getuid(). + + This function does not fail. + + unix.geteuid() → uid:int + + Gets effective user id. + + For example, if your redbean is a setuid binary, then getuid() will + return the uid of the user running the program, and geteuid() shall + return zero which means root, assuming that's the file owning user. + + On Windows this system call is polyfilled as getuid(). + + This function does not fail. + + unix.getegid() → gid:int + + Gets effective group id. + + On Windows this system call is polyfilled as getuid(). + + This function does not fail. + + unix.chroot(path:str) → errno:int + + Changes root directory. + + Returns `ENOSYS` on Windows NT. + + unix.setuid(uid:int) → errno:int Sets user id. - unix.setgid(gid:int) → rc:int[, errno:int] + One use case for this function is dropping root privileges. Should + you ever choose to run redbean as root and decide not to use the + `-G` and `-U` flags, you can replicate that behavior in the Lua + processes you spawn as follows: + + errno = unix.setgid(1000) -- check your /etc/groups + if errno then + Log(kLogFatal, setgid() failed: %s' % {unix.strerror(errno)}) + end + errno = unix.setuid(1000) -- check your /etc/passwd + if errno then + Log(kLogFatal, setuid() failed: %s' % {unix.strerror(errno)}) + end + + If your goal is to relinquish privileges because redbean is a setuid + binary, then things are more straightforward: + + errno = unix.setgid(unix.getgid()) + if errno then + Log(kLogFatal, setgid() failed: %s' % {unix.strerror(errno)}) + end + errno = unix.setuid(unix.getuid()) + if errno then + Log(kLogFatal, setuid() failed: %s' % {unix.strerror(errno)}) + end + + See also the setresuid() function and be sure to refer to your local + system manual about the subtleties of changing user id in a way that + isn't restorable. + + Returns `ENOSYS` on Windows NT if `uid` isn't `getuid()`. + + unix.setgid(gid:int) → errno:int Sets group id. - unix.umask(mask) → oldmask:int + Returns `ENOSYS` on Windows NT if `gid` isn't `getgid()`. + + unix.setresuid(real:int, effective:int, saved:int) → errno:int + + Sets real, effective, and saved user ids. + + If any of the above parameters are -1, then it's a no-op. + + Returns `ENOSYS` on Windows NT. + Returns `ENOSYS` on Macintosh and NetBSD if `saved` isn't -1. + + unix.setresgid(real:int, effective:int, saved:int) → errno:int + + Sets real, effective, and saved group ids. + + If any of the above parameters are -1, then it's a no-op. + + Returns `ENOSYS` on Windows NT. + Returns `ENOSYS` on Macintosh and NetBSD if `saved` isn't -1. + + unix.umask(mask:int) → oldmask:int Sets file permission mask and returns the old one. + This is used to remove bits from the `mode` parameter of functions + like open() and mkdir(). The masks typically used are 027 and 022. + Those masks ensure that, even if a file is created with 0666 bits, + it'll be turned into 0640 or 0644 so that users other than the owner + can't modify it. + + To read the mask without changing it, try doing this: + + mask = unix.umask(027) + unix.umask(mask) + + On Windows NT this is a no-op and `mask` is returned. + + This function does not fail. + unix.syslog(priority:int, msg:str) Generates a log message, which will be distributed by syslogd. - `priority` is a bitmask containing the facility value and the - level value. If no facility value is ORed into priority, then - the default value set by openlog() is used. If set to NULL, the - program name is used. Level is one of `LOG_EMERG`, `LOG_ALERT`, - `LOG_CRIT`, `LOG_ERR`, `LOG_WARNING`, `LOG_NOTICE`, `LOG_INFO`, - `LOG_DEBUG`. + `priority` is a bitmask containing the facility value and the level + value. If no facility value is ORed into priority, then the default + value set by openlog() is used. If set to NULL, the program name is + used. Level is one of `LOG_EMERG`, `LOG_ALERT`, `LOG_CRIT`, + `LOG_ERR`, `LOG_WARNING`, `LOG_NOTICE`, `LOG_INFO`, `LOG_DEBUG`. This function currently works on Linux, Windows, and NetBSD. On WIN32 it uses the ReportEvent() facility. @@ -1640,44 +1866,77 @@ UNIX MODULE `CLOCK_REALTIME_ALARM`, and `CLOCK_BOOTTIME_ALARM`, unix.nanosleep(seconds:int[, nanos:int]) - → remseconds:int, remnanos:int, errno:int + → remseconds:int, remnanos:int[, errno:int] Sleeps with nanosecond precision. unix.sync() - unix.fsync(fd:int) → rc:int[, errno:int] - unix.fdatasync(fd:int) → rc:int[, errno:int] + unix.fsync(fd:int) → errno:int + unix.fdatasync(fd:int) → errno:int These functions are used to make programs slower by asking the operating system to flush data to the physical medium. - unix.seek(fd:int, offset:int, whence:int) → newpos:int[, errno:int] + unix.lseek(fd:int, offset:int, whence:int) → newpos:int[, errno:int] Seeks to file position. - `whence` can be one of `SEEK_SET`, `SEEK_CUR`, or `SEEK_END`. + `whence` can be one of: - unix.truncate(path:str, length) → rc:int[, errno:int] - unix.ftruncate(fd:int, length) → rc:int[, errno:int] + - `SEEK_SET`: Sets the file position to `offset` + - `SEEK_CUR`: Sets the file position to `position + offset` + - `SEEK_END`: Sets the file position to `filesize + offset` + + Returns the new position relative to the start of the file. + + unix.truncate(path:str[, length:int]) → errno:int Reduces or extends underlying physical medium of file. If file was originally larger, content >length is lost. - unix.socket([family[, type[, protocol]]]) → fd:int[, errno:int] + `length` defaults to zero. - `SOCK_CLOEXEC` may be or'd into type - `family` defaults to `AF_INET` but can be `AF_UNIX` - `type` defaults to `SOCK_STREAM` but can be `SOCK_DGRAM` - `protocol` defaults to `IPPROTO_TCP` but can be `IPPROTO_UDP` + unix.ftruncate(fd:int[, length:int]) → errno:int - unix.socketpair([family[, type[, protocol]]]) → fd1, fd2, errno:int + Reduces or extends underlying physical medium of open file. + If file was originally larger, content >length is lost. + + `length` defaults to zero. + + unix.socket([family:int[, type:int[, protocol:int]]]) → fd:int[, errno:int] + + `family` defaults to `AF_INET` and can be: + + - `AF_UNIX` + - `AF_INET` + + `type` defaults to `SOCK_STREAM` and can be: + + - `SOCK_STREAM` + - `SOCK_DGRAM` + - `SOCK_RAW` + - `SOCK_RDM` + - `SOCK_SEQPACKET` + + `protocol` defaults to `IPPROTO_TCP` and can be: + + - `IPPROTO_IP` + - `IPPROTO_ICMP` + - `IPPROTO_TCP` + - `IPPROTO_UDP` + - `IPPROTO_RAW` + + `SOCK_CLOEXEC` may be bitwise or'd into `type`. + + unix.socketpair([family:int[, type:int[, protocol:int]]]) + → fd1:int, fd2:int[, errno:int] `SOCK_CLOEXEC` may be or'd into type `family` defaults to `AF_INET` `type` defaults to `SOCK_STREAM` `protocol` defaults to `IPPROTO_TCP` - unix.bind(fd:int[, ip:uint32, port:uint16]) → rc:int[, errno:int] + unix.bind(fd:int[, ip:uint32, port:uint16]) → errno:int Binds socket. @@ -1685,14 +1944,19 @@ UNIX MODULE wanted to listen on `1.2.3.4:31337` you could do any of these unix.bind(sock, 0x01020304, 31337) + unix.bind(sock, ParseIp('1.2.3.4'), 31337) unix.bind(sock, 1 << 24 | 0 << 16 | 0 << 8 | 1, 31337) `ip` and `port` both default to zero. The meaning of bind(0, 0) is to listen on all interfaces with a kernel-assigned ephemeral port number, that can be retrieved and used as follows: - local sock = unix.socket() -- create ipv4 tcp socket - unix.bind(sock) -- all interfaces arbitrary port + local sock = unix.socket() -- create ipv4 tcp socket + errno = unix.bind(sock) -- all interfaces ephemeral port + if errno then + Log(kLogWarn, bind() failed: %s' % {unix.strerror(errno)}) + return + end ip, port = unix.getsockname(sock) print("listening on ip", FormatIp(ip), "port", port) unix.listen(sock) @@ -1710,6 +1974,55 @@ UNIX MODULE Returns list of network adapter addresses. + unix.getsockopt(fd:int, level:int, optname:int) → errno:int, ... + unix.setsockopt(fd:int, level:int, optname:int, ...) → errno:int + + Tunes networking parameters. + + `level` and `optname` may be one of the following. Please note the + type signature for getsockopt() changes depending on these values: + + - `SOL_SOCKET` + `SO_DEBUG`: bool + - `SOL_SOCKET` + `SO_BROADCAST`: bool + - `SOL_SOCKET` + `SO_REUSEADDR`: bool + - `SOL_SOCKET` + `SO_REUSEPORT`: bool + - `SOL_SOCKET` + `SO_KEEPALIVE`: bool + - `SOL_SOCKET` + `SO_DONTROUTE`: bool + - `SOL_SOCKET` + `SO_SNDBUF`: int + - `SOL_SOCKET` + `SO_RCVBUF`: int + - `SOL_SOCKET` + `SO_RCVLOWAT`: int + - `SOL_SOCKET` + `SO_SNDLOWAT`: int + - `SOL_SOCKET` + `SO_RCVTIMEO`: seconds:int[, micros:int] + - `SOL_SOCKET` + `SO_SNDTIMEO`: seconds:int[, micros:int] + - `SOL_TCP` + `TCP_NODELAY`: bool + - `SOL_TCP` + `TCP_CORK`: bool + - `SOL_TCP` + `TCP_QUICKACK`: bool + - `SOL_TCP` + `TCP_FASTOPEN_CONNECT`: bool + - `SOL_TCP` + `TCP_DEFER_ACCEPT`: bool + - `SOL_TCP` + `TCP_KEEPIDLE`: seconds:int + - `SOL_TCP` + `TCP_KEEPINTVL`: seconds:int + - `SOL_TCP` + `TCP_FASTOPEN`: int + - `SOL_TCP` + `TCP_KEEPCNT`: int + - `SOL_TCP` + `TCP_MAXSEG`: int + - `SOL_TCP` + `TCP_SYNCNT`: int + - `SOL_TCP` + `TCP_NOTSENT_LOWAT`: int + - `SOL_TCP` + `TCP_WINDOW_CLAMP`: int + - `SOL_IP` + `IP_TOS`: int + - `SOL_IP` + `IP_MTU`: int + - `SOL_IP` + `IP_TTL`: int + - `SOL_IP` + `IP_HDRINCL`: bool + + Returns `EINVAL` if settings other than the above are used. + + Returns `ENOSYS` if setting isn't supported by the host o/s. + + NOTE: The API for this function diverges from the the norm. `errno` + needs to come first in the results, because otherwise we + wouldn't know the arity of items to push before it. It's + because Cosmopolitan Libc polyfills the magic numbers above as + zero if the host operating system doesn't support them. If + there's no error, then `errno` will be set to nil. + unix.poll({fd:int=events:int, ...}[, timeoutms:int]) → {fd:int=revents:int, ...}[, errno:int] @@ -1728,7 +2041,7 @@ UNIX MODULE Returns hostname of system. - unix.listen(fd:int[, backlog]) → rc:int[, errno:int] + unix.listen(fd:int[, backlog]) → errno:int Begins listening for incoming connections on a socket. @@ -1752,27 +2065,31 @@ UNIX MODULE Retrieves the remote address of a socket. - unix.recv(fd:int[, bufsiz[, flags]]) → data, errno:int + unix.recv(fd:int[, bufsiz:int[, flags:int]]) → data:str[, errno:int] `flags` can have MSG_{WAITALL,DONTROUTE,PEEK,OOB}, etc. - unix.recvfrom(fd:int[, bufsiz[, flags]]) → data, ip, port, errno:int + unix.recvfrom(fd:int[, bufsiz:int[, flags:int]]) + → data:str, ip:uint32, port:uint16, errno:int `flags` can have MSG_{WAITALL,DONTROUTE,PEEK,OOB}, etc. - unix.send(fd:int, data[, flags]) → sent, errno:int + unix.send(fd:int, data:str[, flags:int]) → sent:int[, errno:int] This is the same as `write` except it has a `flags` argument - that's intended for sockets. `flags` can have `MSG_OOB`, - `MSG_DONTROUTE`, or `MSG_NOSIGNAL`. + that's intended for sockets. - unix.sendto(fd:int, data, ip, port[, flags]) → sent, errno:int + `flags` can have `MSG_OOB`, `MSG_DONTROUTE`, or `MSG_NOSIGNAL`. + + unix.sendto(fd:int, data:str, ip:int, port:int[, flags:int]) + → sent:int, errno:int This is useful for sending messages over UDP sockets to specific - addresses. The `flags` parameter can have `MSG_OOB`, - `MSG_DONTROUTE`, or `MSG_NOSIGNAL`. + addresses. - unix.shutdown(fd:int, how:int) → rc:int[, errno:int] + `flags` can have `MSG_OOB`, `MSG_DONTROUTE`, or `MSG_NOSIGNAL`. + + unix.shutdown(fd:int, how:int) → errno:int Partially closes socket. `how` can be `SHUT_RD`, `SHUT_WR`, or `SHUT_RDWR`. @@ -1790,7 +2107,7 @@ UNIX MODULE unix = require "unix" unix.sigaction(unix.SIGUSR1, function(sig) - print(string.format("got %s", unix.strsignal(sig))) + print('got %s' % {unix.strsignal(sig)}) end) unix.sigprocmask(unix.SIG_SETMASK, -1) unix.raise(unix.SIGUSR1) @@ -1798,10 +2115,13 @@ UNIX MODULE It's a good idea to not do too much work in a signal handler. - unix.sigsuspend([mask]) → errno + unix.sigsuspend([mask:int]) → errno:int Waits for signal to be delivered. + The signal mask is temporarily replaced with `mask` during this + system call. `mask` specifies which signals should be blocked. + unix.setitimer(which[, intsec, intmicros, valsec, valmicros]) → intsec, intns, valsec, valns, errno:int @@ -1812,7 +2132,7 @@ UNIX MODULE ticks = 0 unix.sigaction(unix.SIGALRM, function(sig) - print(string.format("tick no. %d", ticks)) + print('tick no. %d' % {ticks}) ticks = ticks + 1 end) unix.setitimer(unix.ITIMER_REAL, 0, 400000, 0, 400000) @@ -1827,18 +2147,27 @@ UNIX MODULE unix.strerrno(errno:int) → str - Turns `errno` code into its symbolic name, e.g. `"EINTR"`. + Turns `errno` code into its symbolic name, e.g. `"EINTR"`. If + `errno` isn't known, this function returns nil. + + unix.strerdoc(errno:int) → str + + Turns `errno` code into a descriptive string. If `errno` isn't + known, this function returns nil. unix.strerror(errno:int) → str - Turns `errno` code into a longer string describing the error. + Turns `errno` code into longest string describing the error. This + includes the output of both strerrno() and strerror() as well as the + error number. On Windows it includes FormatMessage() output too. If + `errno` isn't known, this function still returns a string. unix.strsignal(sig:int) → str Turns platform-specific `sig` code into its name, e.g. `strsignal(9)` always returns `"SIGKILL"`. - unix.setrlimit(resource:int, soft:int[, hard:int]) → rc:int[, errno:int] + unix.setrlimit(resource:int, soft:int[, hard:int]) → errno:int Changes resource limit. @@ -1873,24 +2202,30 @@ UNIX MODULE Returns information about resource limit. - unix.stat(x) → UnixStat*, errno:int + unix.stat(x) → UnixStat*[, errno:int] Gets information about file or directory. `x` may be a file or directory path string, or it may be a file descriptor int that was made by open(). - unix.opendir(path:str) → UnixDir*, errno:int + unix.opendir(path:str) → UnixDir*[, errno:int] Opens directory for listing its contents. - unix.opendir(fd:int) → UnixDir*, errno:int + unix.fdopendir(fd:int) → UnixDir*[, errno:int] - Opens directory for listing its contents, using a file - descriptor from `open(path, O_RDONLY|O_DIRECTORY)`. + Opens directory for listing its contents, via an fd. - UnixDir* Object + `fd` should be created by `open(path, O_RDONLY|O_DIRECTORY)`. The + returned UnixDir* ownership takes ownership of the file descriptor + and will close it automatically when garbage collected. - UnixDir:close() → rc:int[, errno:int] + UNIX DIR OBJECT + + UnixDir* objects are created by opendir() or fdopendir(). The + following methods are available: + + UnixDir:close() → errno:int may be called multiple times called by the garbage collector too @@ -1905,8 +2240,11 @@ UNIX MODULE UnixDir:fd() → fd:int[, errno:int] - EOPNOTSUPP if using /zip/ - EOPNOTSUPP if IsWindows() + Returns file descriptor of open directory object. + + Returns `EOPNOTSUPP` if using a `/zip/...` path. + + Returns `EOPNOTSUPP` if using Windows NT. UnixDir:tell() → offset:int @@ -1916,7 +2254,10 @@ UNIX MODULE Resets stream back to beginning. - UnixStat* object. + UNIX STAT OBJECT + + UnixStat* objects are created by stat() or fstat(). The following + methods are available: UnixStat:size() → bytes:int @@ -1974,313 +2315,317 @@ UNIX MODULE Block size is usually 4096 for file system files. UnixStat:dev() → int + + ID of device containing file. + UnixStat:ino() → int + + Inode number. + UnixStat:rdev() → int - Here are your error numbers: - - - `EINVAL`: Invalid argument. Raised by [pretty much everything]. - - - `ENOSYS`: System call not available on this platform. On Windows - this is raised by chroot(), setuid(), setgid(). - - - `ENOENT`: no such file or directory. Raised by access(), - alloc_hugepages(), bind(), chdir(), chmod(), chown(), chroot(), - clock_getres(), execve(), opendir(), inotify_add_watch(), kcmp(), - link(), mkdir(), mknod(), msgget(), open(), readlink(), rename(), - rmdir(), semget(), shmget(), stat(), swapon(), symlink(), - truncate(), unlink(), utime(), utimensat(). - - - `ESRCH`: No such process. Raised by getpriority(), getrlimit(), - getsid(), ioprio_set(), kill(), setpgid(), tkill(), utimensat(), - - - `EINTR`: The greatest of all errnos; crucial for building real - time reliable software. Raised by accept(), clock_nanosleep(), - close(), connect(), dup(), fcntl(), flock(), getrandom(), - nanosleep(), open(), pause(), poll(), ptrace(), read(), recv(), - select(), send(), sigsuspend(), sigwaitinfo(), truncate(), - wait(), write() - - - `EIO`: Raised by access() acct() chdir() chmod() chown() chroot() - close() copy_file_range() execve() fallocate() fsync() ioperm() - link() madvise() mbind() pciconfig_read() ptrace() read() - readlink() sendfile() statfs() symlink() sync_file_range() - truncate() unlink() write() - - - `ENXIO`: No such device or address. Raised by lseek(), open(), - prctl() - - - `E2BIG`: Argument list too long. Raised by execve(), msgop(), - sched_setattr(), semop() - - - `ENOEXEC`: exec format error. Raised by execve(), kexec_load(), - uselib() - - - `EBADF`: bad file descriptor; cf. EBADFD. Raised by accept(), - access(), bind(), chdir(), chmod(), chown(), close(), connect(), - copy_file_range(), dup(), fcntl(), flock(), fsync(), futimesat(), - opendir(), getpeername(), getsockname(), getsockopt(), - inotify_add_watch(), inotify_rm_watch(), ioctl(), kcmp(), - kexec_load(), link(), listen(), llseek(), lseek(), mkdir(), - mknod(), mmap(), open(), prctl(), read(), readahead(), - readlink(), recv(), rename(), select(), send(), shutdown(), - splice(), stat(), symlink(), sync(), sync_file_range(), - timerfd_create(), truncate(), unlink(), utimensat(), write(), - - - `ECHILD`: no child process. Raised by wait(), waitpid(), - waitid(), wait3(), wait4() - - - `EAGAIN`: resource temporarily unavailable (e.g. SO_RCVTIMEO - expired, too many processes, too much memory locked, read or - write with O_NONBLOCK needs polling, etc.). Raised by accept(), - connect(), eventfd(), fcntl(), fork(), getrandom(), mincore(), - mlock(), mmap(), mremap(), msgop(), poll(), read(), select(), - send(), setresuid(), setreuid(), setuid(), sigwaitinfo(), - splice(), tee(), timer_create(), timerfd_create(), tkill(), - write(), - - - `ENOMEM`: We require more vespene gas. Raised by access(), - bind(), chdir(), chmod(), chown(), chroot(), clone(), - copy_file_range(), create_module(), eventfd(), execve(), - fanotify_init(), fork(), getgroups(), getrlimit(), - inotify_add_watch(), inotify_init(), ioperm(), kexec_load(), - link(), mbind(), memfd_create(), mincore(), mkdir(), mknod(), - mlock(), mmap(), mprotect(), mremap(), msgget(), msgop(), - msync(), open(), poll(), readlink(), recv(), rename(), rmdir(), - select(), semget(), send(), shmget(), sigaltstack(), splice(), - stat(), subpage_prot(), swapon(), symlink(), sync_file_range(), - tee(), timer_create(), timerfd_create(), unlink(). - - - `EACCES`: Permission denied. Raised by access(), bind(), bpf(), - chdir(), chmod(), chown(), chroot(), clock_getres(), connect(), - execve(), fcntl(), getpriority(), inotify_add_watch(), link(), - mkdir(), mknod(), mmap(), mprotect(), msgctl(), msgget(), - msgop(), open(), prctl(), ptrace(), readlink(), rename(), - rmdir(), semget(), send(), setpgid(), shmget(), socket(), stat(), - symlink(), truncate(), unlink(), uselib(), utime(), utimensat(), - - - `EPERM`: Operation not permitted. Raised by accept(), chmod(), - chown(), chroot(), copy_file_range(), execve(), fallocate(), - fanotify_init(), fcntl(), futex(), get_robust_list(), - getdomainname(), getgroups(), gethostname(), getpriority(), - getrlimit(), getsid(), gettimeofday(), idle(), init_module(), - io_submit(), ioctl_console(), ioctl_ficlonerange(), - ioctl_fideduperange(), ioctl_ns(), ioctl_tty(), ioperm(), iopl(), - ioprio_set(), kcmp(), kexec_load(), keyctl(), kill(), link(), - lookup_dcookie(), madvise(), mbind(), membarrier(), - migrate_pages(), mkdir(), mknod(), mlock(), mmap(), mount(), - move_pages(), msgctl(), nice(), open(), open_by_handle_at(), - pciconfig_read(), perf_event_open(), pidfd_getfd(), - pidfd_send_signal(), pivot_root(), prctl(), process_vm_readv(), - ptrace(), quotactl(), reboot(), rename(), request_key(), rmdir(), - rt_sigqueueinfo(), sched_setaffinity(), sched_setattr(), - sched_setparam(), sched_setscheduler(), semctl(), seteuid(), - setfsgid(), setfsuid(), setgid(), setns(), setpgid(), - setresuid(), setreuid(), setsid(), setuid(), setup(), setxattr(), - shmctl(), shmget(), sigaltstack(), spu_create(), stime(), - swapon(), symlink(), syslog(), truncate(), unlink(), utime(), - utimensat(), write() - - - `ENOTBLK`: Block device required. Raised by umount(). - - - `EBUSY`: Device or resource busy. Raised by bdflush(), dup(), - fcntl(), msync(), prctl(), ptrace(), rename(), rmdir(). - - - `EEXIST`: File exists. Raised by bpf(), create_module(), - inotify_add_watch(), link(), mkdir(), mknod(), mmap(), msgget(), - open(), rename(), rmdir(), semget(), shmget(), symlink() - - - `EXDEV`: Improper link. Raised by copy_file_range(), link(), - rename() - - - `ENODEV`: No such device. Raised by arch_prctl(), eventfd(), - mmap(), open(), prctl(), timerfd_create() - - - `ENOTDIR`: Not a directory. This means that a directory component - in a supplied path *existed* but wasn't a directory. For example, - if you try to `open("foo/bar")` and `foo` is a regular file, then - `ENOTDIR` will be returned. Raised by open(), access(), chdir(), - chroot(), execve(), link(), mkdir(), mknod(), opendir(), - readlink(), rename(), rmdir(), stat(), symlink(), truncate(), - unlink(), utimensat(), bind(), chmod(), chown(), fcntl(), - futimesat(), inotify_add_watch(). - - - `EISDIR`: Is a a directory. Raised by copy_file_range(), - execve(), open(), read(), rename(), truncate(), unlink(). - - - `ENFILE`: Too many open files in system. Raised by accept(), - eventfd(), execve(), inotify_init(), memfd_create(), mmap(), - open(), pipe(), shmget(), socket(), socketpair(), swapon(), - timerfd_create(), uselib(), userfaultfd(). - - - `EMFILE`: Too many open files. Raised by accept(), dup(), - eventfd(), execve(), fanotify_init(), fcntl(), inotify_init(), - memfd_create(), open(), pipe(), socket(), socketpair(), - timerfd_create(). - - - `ENOTTY`: Inappropriate i/o control operation. Raised by ioctl(). + Device ID (if special file) + + UNIX ERRORS + + - `EINVAL`: Invalid argument. Raised by [pretty much everything]. + + - `ENOSYS`: System call not available on this platform. On Windows + this is raised by chroot(), setuid(), setgid(), getsid(), setsid(). + + - `ENOENT`: no such file or directory. Raised by access(), + alloc_hugepages(), bind(), chdir(), chmod(), chown(), chroot(), + clock_getres(), execve(), opendir(), inotify_add_watch(), kcmp(), + link(), mkdir(), mknod(), msgget(), open(), readlink(), rename(), + rmdir(), semget(), shmget(), stat(), swapon(), symlink(), + truncate(), unlink(), utime(), utimensat(). + + - `ENOTDIR`: Not a directory. This means that a directory component in + a supplied path *existed* but wasn't a directory. For example, if + you try to `open("foo/bar")` and `foo` is a regular file, then + `ENOTDIR` will be returned. Raised by open(), access(), chdir(), + chroot(), execve(), link(), mkdir(), mknod(), opendir(), readlink(), + rename(), rmdir(), stat(), symlink(), truncate(), unlink(), + utimensat(), bind(), chmod(), chown(), fcntl(), futimesat(), + inotify_add_watch(). + + - `EINTR`: The greatest of all errnos; crucial for building real time + reliable software. Raised by accept(), clock_nanosleep(), close(), + connect(), dup(), fcntl(), flock(), getrandom(), nanosleep(), + open(), pause(), poll(), ptrace(), read(), recv(), select(), send(), + sigsuspend(), sigwaitinfo(), truncate(), wait(), write() + + - `EIO`: Raised by access() acct() chdir() chmod() chown() chroot() + close() copy_file_range() execve() fallocate() fsync() ioperm() + link() madvise() mbind() pciconfig_read() ptrace() read() readlink() + sendfile() statfs() symlink() sync_file_range() truncate() unlink() + write() + + - `ENXIO`: No such device or address. Raised by lseek(), open(), + prctl() + + - `E2BIG`: Argument list too long. Raised by execve(), msgop(), + sched_setattr(), semop() + + - `ENOEXEC`: exec format error. Raised by execve(), kexec_load(), + uselib() + + - `ECHILD`: no child process. Raised by wait(), waitpid(), waitid(), + wait3(), wait4() + + - `ESRCH`: No such process. Raised by getpriority(), getrlimit(), + getsid(), ioprio_set(), kill(), setpgid(), tkill(), utimensat(), + + - `EBADF`: bad file descriptor; cf. EBADFD. Raised by accept(), + access(), bind(), chdir(), chmod(), chown(), close(), connect(), + copy_file_range(), dup(), fcntl(), flock(), fsync(), futimesat(), + opendir(), getpeername(), getsockname(), getsockopt(), + inotify_add_watch(), inotify_rm_watch(), ioctl(), kcmp(), + kexec_load(), link(), listen(), llseek(), lseek(), mkdir(), mknod(), + mmap(), open(), prctl(), read(), readahead(), readlink(), recv(), + rename(), select(), send(), shutdown(), splice(), stat(), symlink(), + sync(), sync_file_range(), timerfd_create(), truncate(), unlink(), + utimensat(), write(), + + - `EAGAIN`: resource temporarily unavailable (e.g. SO_RCVTIMEO + expired, too many processes, too much memory locked, read or write + with O_NONBLOCK needs polling, etc.). Raised by accept(), connect(), + eventfd(), fcntl(), fork(), getrandom(), mincore(), mlock(), mmap(), + mremap(), msgop(), poll(), read(), select(), send(), setresuid(), + setreuid(), setuid(), sigwaitinfo(), splice(), tee(), + timer_create(), timerfd_create(), tkill(), write(), + + - `EPIPE`: Broken pipe. Returned by write(), send(). This happens when + you try to write data to a subprocess via a pipe() but the reader + end has already closed, possibly because the process died. Normally + i/o routines only return this if `SIGPIPE` doesn't kill the process. + Unlike default UNIX programs, redbean currently ignores `SIGPIPE` by + default, so this error code is a distinct possibility when pipes or + sockets are being used. + + - `ENAMETOOLONG`: Filename too long. Cosmopolitan Libc currently + defines `PATH_MAX` as 512 characters. On UNIX, that limit should + only apply to system call wrappers like realpath(). On Windows NT + it's observed by all system calls that accept a pathname. Raised by + access(), bind(), chdir(), chmod(), chown(), chroot(), execve(), + gethostname(), inotify_add_watch(), link(), mkdir(), mknod(), + open(), readlink(), rename(), rmdir(), stat(), symlink(), + truncate(), u unlink(), utimensat() + + - `EACCES`: Permission denied. Raised by access(), bind(), bpf(), + chdir(), chmod(), chown(), chroot(), clock_getres(), connect(), + execve(), fcntl(), getpriority(), inotify_add_watch(), link(), + mkdir(), mknod(), mmap(), mprotect(), msgctl(), msgget(), msgop(), + open(), prctl(), ptrace(), readlink(), rename(), rmdir(), semget(), + send(), setpgid(), shmget(), socket(), stat(), symlink(), + truncate(), unlink(), uselib(), utime(), utimensat(), + + - `ENOMEM`: We require more vespene gas. Raised by access(), bind(), + chdir(), chmod(), chown(), chroot(), clone(), copy_file_range(), + create_module(), eventfd(), execve(), fanotify_init(), fork(), + getgroups(), getrlimit(), inotify_add_watch(), inotify_init(), + ioperm(), kexec_load(), link(), mbind(), memfd_create(), mincore(), + mkdir(), mknod(), mlock(), mmap(), mprotect(), mremap(), msgget(), + msgop(), msync(), open(), poll(), readlink(), recv(), rename(), + rmdir(), select(), semget(), send(), shmget(), sigaltstack(), + splice(), stat(), subpage_prot(), swapon(), symlink(), + sync_file_range(), tee(), timer_create(), timerfd_create(), + unlink(). + + - `EPERM`: Operation not permitted. Raised by accept(), chmod(), + chown(), chroot(), copy_file_range(), execve(), fallocate(), + fanotify_init(), fcntl(), futex(), get_robust_list(), + getdomainname(), getgroups(), gethostname(), getpriority(), + getrlimit(), getsid(), gettimeofday(), idle(), init_module(), + io_submit(), ioctl_console(), ioctl_ficlonerange(), + ioctl_fideduperange(), ioctl_ns(), ioctl_tty(), ioperm(), iopl(), + ioprio_set(), kcmp(), kexec_load(), keyctl(), kill(), link(), + lookup_dcookie(), madvise(), mbind(), membarrier(), migrate_pages(), + mkdir(), mknod(), mlock(), mmap(), mount(), move_pages(), msgctl(), + nice(), open(), open_by_handle_at(), pciconfig_read(), + perf_event_open(), pidfd_getfd(), pidfd_send_signal(), pivot_root(), + prctl(), process_vm_readv(), ptrace(), quotactl(), reboot(), + rename(), request_key(), rmdir(), rt_sigqueueinfo(), + sched_setaffinity(), sched_setattr(), sched_setparam(), + sched_setscheduler(), semctl(), seteuid(), setfsgid(), setfsuid(), + setgid(), setns(), setpgid(), setresuid(), setreuid(), setsid(), + setuid(), setup(), setxattr(), shmctl(), shmget(), sigaltstack(), + spu_create(), stime(), swapon(), symlink(), syslog(), truncate(), + unlink(), utime(), utimensat(), write() + + - `ENOTBLK`: Block device required. Raised by umount(). + + - `EBUSY`: Device or resource busy. Raised by bdflush(), dup(), + fcntl(), msync(), prctl(), ptrace(), rename(), rmdir(). + + - `EEXIST`: File exists. Raised by bpf(), create_module(), + inotify_add_watch(), link(), mkdir(), mknod(), mmap(), msgget(), + open(), rename(), rmdir(), semget(), shmget(), symlink() + + - `EXDEV`: Improper link. Raised by copy_file_range(), link(), + rename() + + - `ENODEV`: No such device. Raised by arch_prctl(), eventfd(), mmap(), + open(), prctl(), timerfd_create() - - `ETXTBSY`: Won't open executable that's executing in write mode. - Raised by access(), copy_file_range(), execve(), mmap(), open(), - truncate(). + - `EISDIR`: Is a a directory. Raised by copy_file_range(), execve(), + open(), read(), rename(), truncate(), unlink(). - - `EFBIG`: File too large. Raised by copy_file_range(), open(), - truncate(), write(). + - `ENFILE`: Too many open files in system. Raised by accept(), + eventfd(), execve(), inotify_init(), memfd_create(), mmap(), open(), + pipe(), shmget(), socket(), socketpair(), swapon(), + timerfd_create(), uselib(), userfaultfd(). - - `ENOSPC`: No space left on device. Raised by copy_file_range(), - fsync(), inotify_add_watch(), link(), mkdir(), mknod(), msgget(), - open(), rename(), semget(), shmget(), symlink(), - sync_file_range(), write(). + - `EMFILE`: Too many open files. Raised by accept(), dup(), eventfd(), + execve(), fanotify_init(), fcntl(), inotify_init(), memfd_create(), + open(), pipe(), socket(), socketpair(), timerfd_create(). - - `EDQUOT`: Disk quota exceeded. Raised by link(), mkdir(), - mknod(), open(), rename(), symlink(), write() + - `ENOTTY`: Inappropriate i/o control operation. Raised by ioctl(). - - `ESPIPE`: Invalid seek. Raised by lseek(), splice(), - sync_file_range(). + - `ETXTBSY`: Won't open executable that's executing in write mode. + Raised by access(), copy_file_range(), execve(), mmap(), open(), + truncate(). - - `EROFS`: Read-only filesystem. Raised by access(), bind(), - chmod(), chown(), link(), mkdir(), mknod(), open(), rename(), - rmdir(), symlink(), truncate(), unlink(), utime(), utimensat() + - `EFBIG`: File too large. Raised by copy_file_range(), open(), + truncate(), write(). - - `EMLINK`: Too many links; raised by link(), mkdir(), rename() + - `ENOSPC`: No space left on device. Raised by copy_file_range(), + fsync(), inotify_add_watch(), link(), mkdir(), mknod(), msgget(), + open(), rename(), semget(), shmget(), symlink(), sync_file_range(), + write(). - - `EPIPE`: Broken pipe. Raised by send(), write(). + - `EDQUOT`: Disk quota exceeded. Raised by link(), mkdir(), mknod(), + open(), rename(), symlink(), write() - - `ERANGE`: Result too large. Raised by prctl(), semop(). + - `ESPIPE`: Invalid seek. Raised by lseek(), splice(), + sync_file_range(). - - `EDEADLK`: Resource deadlock avoided. Raised by fcntl(). + - `EROFS`: Read-only filesystem. Raised by access(), bind(), chmod(), + chown(), link(), mkdir(), mknod(), open(), rename(), rmdir(), + symlink(), truncate(), unlink(), utime(), utimensat() - - `ENAMETOOLONG`: Filename too long. Raised by access(), bind(), - chdir(), chmod(), chown(), chroot(), execve(), gethostname(), - inotify_add_watch(), link(), mkdir(), mknod(), open(), - readlink(), rename(), rmdir(), stat(), symlink(), truncate(), u - unlink(), utimensat() + - `EMLINK`: Too many links; raised by link(), mkdir(), rename() - - `ENOLCK`: No locks available. Raised by fcntl(), flock(). + - `ERANGE`: Result too large. Raised by prctl(), semop(). - - `ENOTEMPTY`: Directory not empty. Raised by rmdir(). + - `EDEADLK`: Resource deadlock avoided. Raised by fcntl(). - - `ELOOP`: Too many levels of symbolic links. Raised by access(), - bind(), chdir(), chmod(), chown(), chroot(), execve(), link(), - mkdir(), mknod(), open(), readlink(), rename(), rmdir(), stat(), - symlink(), truncate(), unlink(), utimensat(). + - `ENOLCK`: No locks available. Raised by fcntl(), flock(). - - `ENOMSG`: Raised by msgop(). + - `ENOTEMPTY`: Directory not empty. Raised by rmdir(). - - `EIDRM`: Identifier removed. Raised by msgctl(), msgget(), - msgop(), shmget(). + - `ELOOP`: Too many levels of symbolic links. Raised by access(), + bind(), chdir(), chmod(), chown(), chroot(), execve(), link(), + mkdir(), mknod(), open(), readlink(), rename(), rmdir(), stat(), + symlink(), truncate(), unlink(), utimensat(). - - `ETIME`: Timer expired; timer expired. Raised by connect(). + - `ENOMSG`: Raised by msgop(). - - `EPROTO`: Raised by accept(), connect(), socket(), socketpair(). + - `EIDRM`: Identifier removed. Raised by msgctl(), msgget(), msgop(), + shmget(). - - `EOVERFLOW`: Raised by copy_file_range(), fanotify_init(), - lseek(), mmap(), open(), stat(), statfs() + - `ETIME`: Timer expired; timer expired. Raised by connect(). - - `ENOTSOCK`: Not a socket. Raised by accept(), bind(), connect(), - getpeername(), getsockname(), getsockopt(), listen(), recv(), - send(), shutdown(). + - `EPROTO`: Raised by accept(), connect(), socket(), socketpair(). - - `EDESTADDRREQ`: Destination address required. Raised by send(), - write(). + - `EOVERFLOW`: Raised by copy_file_range(), fanotify_init(), lseek(), + mmap(), open(), stat(), statfs() - - `EMSGSIZE`: Message too long. Raised by send(). + - `ENOTSOCK`: Not a socket. Raised by accept(), bind(), connect(), + getpeername(), getsockname(), getsockopt(), listen(), recv(), + send(), shutdown(). - - `EPROTOTYPE`: Protocol wrong type for socket. Raised by - connect(). + - `EDESTADDRREQ`: Destination address required. Raised by send(), + write(). - - `ENOPROTOOPT`: Protocol not available. Raised by getsockopt(), - accept(). + - `EMSGSIZE`: Message too long. Raised by send(). - - `EPROTONOSUPPORT`: Protocol not supported. Raised by socket(), - socketpair(). + - `EPROTOTYPE`: Protocol wrong type for socket. Raised by connect(). - - `ESOCKTNOSUPPORT`: Socket type not supported. + - `ENOPROTOOPT`: Protocol not available. Raised by getsockopt(), + accept(). - - `ENOTSUP`: Operation not supported. Raised by chmod(), - clock_getres(), clock_nanosleep(), timer_create() + - `EPROTONOSUPPORT`: Protocol not supported. Raised by socket(), + socketpair(). - - `EOPNOTSUPP`: Socket operation not supported. Raised by accept(), - listen(), mmap(), prctl(), readv(), send(), socketpair(), + - `ESOCKTNOSUPPORT`: Socket type not supported. - - `EPFNOSUPPORT`: protocol family not supported + - `ENOTSUP`: Operation not supported. Raised by chmod(), + clock_getres(), clock_nanosleep(), timer_create() - - `EAFNOSUPPORT`: address family not supported. Raised by - connect(), socket(), socketpair() + - `EOPNOTSUPP`: Socket operation not supported. Raised by accept(), + listen(), mmap(), prctl(), readv(), send(), socketpair(), - - `EADDRINUSE`: address already in use. Raised by bind(), - connect(), listen() + - `EPFNOSUPPORT`: protocol family not supported - - `EADDRNOTAVAIL`: address not available. Raised by bind(), - connect(). + - `EAFNOSUPPORT`: address family not supported. Raised by connect(), + socket(), socketpair() - - `ENETDOWN`: network is down; ; WSAENETDOWN. Raised by accept() + - `EADDRINUSE`: address already in use. Raised by bind(), connect(), + listen() - - `ENETUNREACH`: host is unreachable; ; WSAENETUNREACH. Raised by - accept(), connect() + - `EADDRNOTAVAIL`: address not available. Raised by bind(), connect(). - - `ENETRESET`: connection reset by network + - `ENETDOWN`: network is down; ; WSAENETDOWN. Raised by accept() - - `ECONNABORTED`: connection reset before accept. Raised by - accept() + - `ENETUNREACH`: host is unreachable; ; WSAENETUNREACH. Raised by + accept(), connect() - - `ECONNRESET`: connection reset by client. Raised by send(), + - `ENETRESET`: connection reset by network - - `ENOBUFS`: no buffer space available; raised by getpeername(), - getsockname(), send(), + - `ECONNABORTED`: connection reset before accept. Raised by accept() - - `EISCONN`: socket is connected. Raised by connect(), send(). + - `ECONNRESET`: connection reset by client. Raised by send(), - - `ENOTCONN`: socket is not connected. Raised by getpeername(), - recv(), send(), shutdown(), + - `ENOBUFS`: no buffer space available; raised by getpeername(), + getsockname(), send(), - - `ESHUTDOWN`: cannot send after transport endpoint shutdown; note - that shutdown write is an `EPIPE` + - `EISCONN`: socket is connected. Raised by connect(), send(). - - `ETOOMANYREFS`: too many references: cannot splice. Raised by - sendmsg(), + - `ENOTCONN`: socket is not connected. Raised by getpeername(), + recv(), send(), shutdown(), - - `ETIMEDOUT`: connection timed out; ; WSAETIMEDOUT; raised by - connect(), + - `ESHUTDOWN`: cannot send after transport endpoint shutdown; note + that shutdown write is an `EPIPE` - - `ECONNREFUSED`: system-imposed limit on the number of threads was - encountered.; WSAECONNREFUSED. Raised by connect(), listen(), - recv() + - `ETOOMANYREFS`: too many references: cannot splice. Raised by + sendmsg(), - - `EHOSTDOWN`: Host is down. Raised by accept() + - `ETIMEDOUT`: connection timed out; ; WSAETIMEDOUT; raised by + connect(), - - `EHOSTUNREACH`: Host is unreachable. Raised by accept() + - `ECONNREFUSED`: system-imposed limit on the number of threads was + encountered.; WSAECONNREFUSED. Raised by connect(), listen(), recv() - - `EALREADY`: Connection already in progress. Raised by connect(), - send() + - `EHOSTDOWN`: Host is down. Raised by accept() - - `ENODATA`: No message is available in xsi stream or named pipe is - being closed; no data available; barely in posix; returned by - ioctl; very close in spirit to EPIPE? + - `EHOSTUNREACH`: Host is unreachable. Raised by accept() + - `EALREADY`: Connection already in progress. Raised by connect(), + send() -CONSTANTS + - `ENODATA`: No message is available in xsi stream or named pipe is + being closed; no data available; barely in posix; returned by ioctl; + very close in spirit to EPIPE? - kLogDebug - Integer for debug logging level. See Log. + +LUA ENHANCEMENTS - kLogVerbose - Integer for verbose logging level, which is less than kLogDebug. + We've made some enhancements to the Lua language that should make it + more comfortable for C/C++ and Python developers. Some of these - kLogInfo - Integer for info logging level, which is less than kLogVerbose. + - redbean supports a printf modulus operator, like Python. For + example, you can say `"hello %s" % {"world"}` instead of + `string.format("hello %s", "world")`. - kLogWarn - Integer for warn logging level, which is less than kLogVerbose. + - redbean supports octal (base 8) integer literals. For example + `0644 == 420` is the case in redbean, whereas in upstream Lua + `0644 == 644` would be the case. - kLogError - Integer for error logging level, which is less than kLogWarn. - - kLogFatal - Integer for fatal logging level, which is less than kLogError. - Logging anything at this level will result in a backtrace and - process exit. + - redbean supports the GNU syntax for the ASCII ESC character in + string literals. For example, `"\e"` is the same as `"\x1b"`. + SEE ALSO https://justine.lol/redbean/index.html diff --git a/tool/net/lfuncs.c b/tool/net/lfuncs.c new file mode 100644 index 000000000..a58760bd5 --- /dev/null +++ b/tool/net/lfuncs.c @@ -0,0 +1,562 @@ +/*-*- 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 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "dsp/scale/cdecimate2xuint8x8.h" +#include "libc/bits/popcnt.h" +#include "libc/log/check.h" +#include "libc/log/log.h" +#include "libc/macros.internal.h" +#include "libc/mem/mem.h" +#include "libc/nexgen32e/bsf.h" +#include "libc/nexgen32e/bsr.h" +#include "libc/nexgen32e/crc32.h" +#include "libc/nexgen32e/rdtsc.h" +#include "libc/nexgen32e/rdtscp.h" +#include "libc/rand/rand.h" +#include "libc/runtime/gc.internal.h" +#include "libc/sock/sock.h" +#include "libc/sysv/consts/af.h" +#include "libc/time/time.h" +#include "libc/x/x.h" +#include "net/http/escape.h" +#include "net/http/http.h" +#include "net/http/ip.h" +#include "net/http/url.h" +#include "third_party/lua/cosmo.h" +#include "third_party/lua/lauxlib.h" +#include "third_party/lua/lua.h" +#include "third_party/mbedtls/md.h" +#include "third_party/mbedtls/md5.h" +#include "third_party/mbedtls/platform.h" +#include "third_party/mbedtls/sha1.h" +#include "third_party/mbedtls/sha256.h" +#include "third_party/mbedtls/sha512.h" +#include "tool/net/lfuncs.h" + +int LuaGetTime(lua_State *L) { + lua_pushnumber(L, nowl()); + return 1; +} + +int LuaSleep(lua_State *L) { + usleep(1e6 * luaL_checknumber(L, 1)); + return 0; +} + +int LuaRdtsc(lua_State *L) { + lua_pushinteger(L, rdtsc()); + return 1; +} + +int LuaGetCpuNode(lua_State *L) { + lua_pushinteger(L, TSC_AUX_NODE(rdpid())); + return 1; +} + +int LuaGetCpuCore(lua_State *L) { + lua_pushinteger(L, TSC_AUX_CORE(rdpid())); + return 1; +} + +int LuaGetLogLevel(lua_State *L) { + lua_pushinteger(L, __log_level); + return 1; +} + +int LuaSetLogLevel(lua_State *L) { + __log_level = luaL_checkinteger(L, 1); + return 0; +} + +static int LuaRand(lua_State *L, uint64_t impl(void)) { + lua_pushinteger(L, impl()); + return 1; +} + +int LuaLemur64(lua_State *L) { + return LuaRand(L, lemur64); +} + +int LuaRand64(lua_State *L) { + return LuaRand(L, rand64); +} + +int LuaRdrand(lua_State *L) { + return LuaRand(L, rdrand); +} + +int LuaRdseed(lua_State *L) { + return LuaRand(L, rdseed); +} + +int LuaDecimate(lua_State *L) { + size_t n, m; + const char *s; + unsigned char *p; + s = luaL_checklstring(L, 1, &n); + m = ROUNDUP(n, 16); + p = xmalloc(m); + bzero(p + n, m - n); + cDecimate2xUint8x8(m, p, (signed char[8]){-1, -3, 3, 17, 17, 3, -3, -1}); + lua_pushlstring(L, (char *)p, (n + 1) >> 1); + free(p); + return 1; +} + +int LuaMeasureEntropy(lua_State *L) { + size_t n; + const char *s; + s = luaL_checklstring(L, 1, &n); + lua_pushnumber(L, MeasureEntropy(s, n)); + return 1; +} + +int LuaGetHostOs(lua_State *L) { + const char *s = NULL; + if (IsLinux()) { + s = "LINUX"; + } else if (IsMetal()) { + s = "METAL"; + } else if (IsWindows()) { + s = "WINDOWS"; + } else if (IsXnu()) { + s = "XNU"; + } else if (IsOpenbsd()) { + s = "OPENBSD"; + } else if (IsFreebsd()) { + s = "FREEBSD"; + } else if (IsNetbsd()) { + s = "NETBSD"; + } + if (s) { + lua_pushstring(L, s); + } else { + lua_pushnil(L); + } + return 1; +} + +int LuaFormatIp(lua_State *L) { + char b[16]; + uint32_t ip; + ip = htonl(luaL_checkinteger(L, 1)); + inet_ntop(AF_INET, &ip, b, sizeof(b)); + lua_pushstring(L, b); + return 1; +} + +int LuaParseIp(lua_State *L) { + size_t n; + const char *s; + s = luaL_checklstring(L, 1, &n); + lua_pushinteger(L, ParseIp(s, n)); + return 1; +} + +static int LuaIsIp(lua_State *L, bool IsIp(uint32_t)) { + lua_pushboolean(L, IsIp(luaL_checkinteger(L, 1))); + return 1; +} + +int LuaIsPublicIp(lua_State *L) { + return LuaIsIp(L, IsPublicIp); +} + +int LuaIsPrivateIp(lua_State *L) { + return LuaIsIp(L, IsPrivateIp); +} + +int LuaIsLoopbackIp(lua_State *L) { + return LuaIsIp(L, IsLoopbackIp); +} + +int LuaCategorizeIp(lua_State *L) { + lua_pushstring(L, GetIpCategoryName(CategorizeIp(luaL_checkinteger(L, 1)))); + return 1; +} + +int LuaFormatHttpDateTime(lua_State *L) { + char buf[30]; + lua_pushstring(L, FormatUnixHttpDateTime(buf, luaL_checkinteger(L, 1))); + return 1; +} + +int LuaParseHttpDateTime(lua_State *L) { + size_t n; + const char *s; + s = luaL_checklstring(L, 1, &n); + lua_pushinteger(L, ParseHttpDateTime(s, n)); + return 1; +} + +int LuaParseParams(lua_State *L) { + void *m; + size_t size; + const char *data; + struct UrlParams h; + data = luaL_checklstring(L, 1, &size); + bzero(&h, sizeof(h)); + m = ParseParams(data, size, &h); + LuaPushUrlParams(L, &h); + free(h.p); + free(m); + return 1; +} + +int LuaParseHost(lua_State *L) { + void *m; + size_t n; + struct Url h; + const char *p; + bzero(&h, sizeof(h)); + p = luaL_checklstring(L, 1, &n); + m = ParseHost(p, n, &h); + lua_newtable(L); + LuaPushUrlView(L, &h.host); + LuaPushUrlView(L, &h.port); + free(m); + return 1; +} + +int LuaPopcnt(lua_State *L) { + lua_pushinteger(L, popcnt(luaL_checkinteger(L, 1))); + return 1; +} + +int LuaBsr(lua_State *L) { + long x; + if ((x = luaL_checkinteger(L, 1))) { + lua_pushinteger(L, bsr(x)); + return 1; + } else { + luaL_argerror(L, 1, "zero"); + unreachable; + } +} + +int LuaBsf(lua_State *L) { + long x; + if ((x = luaL_checkinteger(L, 1))) { + lua_pushinteger(L, bsf(x)); + return 1; + } else { + luaL_argerror(L, 1, "zero"); + unreachable; + } +} + +static int LuaHash(lua_State *L, uint32_t H(uint32_t, const void *, size_t)) { + long i; + size_t n; + const char *p; + i = luaL_checkinteger(L, 1); + p = luaL_checklstring(L, 2, &n); + lua_pushinteger(L, H(i, p, n)); + return 1; +} + +int LuaCrc32(lua_State *L) { + return LuaHash(L, crc32_z); +} + +int LuaCrc32c(lua_State *L) { + return LuaHash(L, crc32c); +} + +int LuaIndentLines(lua_State *L) { + void *p; + size_t n, j; + p = luaL_checklstring(L, 1, &n); + j = luaL_optinteger(L, 2, 1); + if (!(0 <= j && j <= 65535)) { + luaL_argerror(L, 2, "not in range 0..65535"); + unreachable; + } + p = IndentLines(p, n, &n, j); + lua_pushlstring(L, p, n); + free(p); + return 1; +} + +int LuaGetMonospaceWidth(lua_State *L) { + int w; + if (lua_isinteger(L, 1)) { + w = wcwidth(lua_tointeger(L, 1)); + } else if (lua_isstring(L, 1)) { + w = strwidth(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0) & 7); + } else { + luaL_argerror(L, 1, "not integer or string"); + unreachable; + } + lua_pushinteger(L, w); + return 1; +} + +int LuaSlurp(lua_State *L) { + char *p, *f; + size_t n; + f = luaL_checkstring(L, 1); + if ((p = xslurp(f, &n))) { + lua_pushlstring(L, p, n); + free(p); + return 1; + } else { + lua_pushnil(L); + lua_pushstring(L, gc(xasprintf("Can't slurp file %`'s: %m", f))); + return 2; + } +} + +static int LuaCheckControlFlags(lua_State *L, int idx) { + int f = luaL_checkinteger(L, idx); + if (f & ~(kControlWs | kControlC0 | kControlC1)) { + luaL_argerror(L, idx, "invalid control flags"); + unreachable; + } + return f; +} + +int LuaHasControlCodes(lua_State *L) { + int f; + size_t n; + const char *p; + p = luaL_checklstring(L, 1, &n); + f = LuaCheckControlFlags(L, 2); + lua_pushboolean(L, HasControlCodes(p, n, f) != -1); + return 1; +} + +int LuaEncodeLatin1(lua_State *L) { + int f; + char *p; + size_t n; + p = luaL_checklstring(L, 1, &n); + f = LuaCheckControlFlags(L, 2); + p = EncodeLatin1(p, n, &n, f); + lua_pushlstring(L, p, n); + free(p); + return 1; +} + +int LuaGetRandomBytes(lua_State *L) { + char *p; + size_t n = luaL_optinteger(L, 1, 16); + if (!(n > 0 && n <= 256)) { + luaL_argerror(L, 1, "not in range 1..256"); + unreachable; + } + p = malloc(n); + CHECK_EQ(n, getrandom(p, n, 0)); + lua_pushlstring(L, p, n); + free(p); + return 1; +} + +int LuaGetHttpReason(lua_State *L) { + lua_pushstring(L, GetHttpReason(luaL_checkinteger(L, 1))); + return 1; +} + +int LuaGetCryptoHash(lua_State *L) { + size_t hl, pl, kl; + uint8_t d[64]; + mbedtls_md_context_t ctx; + // get hash name, payload, and key + void *h = luaL_checklstring(L, 1, &hl); + void *p = luaL_checklstring(L, 2, &pl); + void *k = luaL_optlstring(L, 3, "", &kl); + const mbedtls_md_info_t *digest = mbedtls_md_info_from_string(h); + if (!digest) return luaL_argerror(L, 1, "unknown hash type"); + if (kl == 0) { + // no key provided, run generic hash function + if ((digest->f_md)(p, pl, d)) return luaL_error(L, "bad input data"); + } else if (mbedtls_md_hmac(digest, k, kl, p, pl, d)) { + return luaL_error(L, "bad input data"); + } + lua_pushlstring(L, (void *)d, digest->size); + mbedtls_platform_zeroize(d, sizeof(d)); + return 1; +} + +static dontinline int LuaIsValid(lua_State *L, bool V(const char *, size_t)) { + size_t size; + const char *data; + data = luaL_checklstring(L, 1, &size); + lua_pushboolean(L, V(data, size)); + return 1; +} + +int LuaIsValidHttpToken(lua_State *L) { + return LuaIsValid(L, IsValidHttpToken); +} + +int LuaIsAcceptablePath(lua_State *L) { + return LuaIsValid(L, IsAcceptablePath); +} + +int LuaIsReasonablePath(lua_State *L) { + return LuaIsValid(L, IsReasonablePath); +} + +int LuaIsAcceptableHost(lua_State *L) { + return LuaIsValid(L, IsAcceptableHost); +} + +int LuaIsAcceptablePort(lua_State *L) { + return LuaIsValid(L, IsAcceptablePort); +} + +static dontinline int LuaCoderImpl(lua_State *L, + char *C(const char *, size_t, size_t *)) { + void *p; + size_t n; + p = luaL_checklstring(L, 1, &n); + p = C(p, n, &n); + lua_pushlstring(L, p, n); + free(p); + return 1; +} + +static dontinline int LuaCoder(lua_State *L, + char *C(const char *, size_t, size_t *)) { + return LuaCoderImpl(L, C); +} + +int LuaUnderlong(lua_State *L) { + return LuaCoder(L, Underlong); +} + +int LuaEncodeBase64(lua_State *L) { + return LuaCoder(L, EncodeBase64); +} + +int LuaDecodeBase64(lua_State *L) { + return LuaCoder(L, DecodeBase64); +} + +int LuaDecodeLatin1(lua_State *L) { + return LuaCoder(L, DecodeLatin1); +} + +int LuaEscapeHtml(lua_State *L) { + return LuaCoder(L, EscapeHtml); +} + +int LuaEscapeParam(lua_State *L) { + return LuaCoder(L, EscapeParam); +} + +int LuaEscapePath(lua_State *L) { + return LuaCoder(L, EscapePath); +} + +int LuaEscapeHost(lua_State *L) { + return LuaCoder(L, EscapeHost); +} + +int LuaEscapeIp(lua_State *L) { + return LuaCoder(L, EscapeIp); +} + +int LuaEscapeUser(lua_State *L) { + return LuaCoder(L, EscapeUser); +} + +int LuaEscapePass(lua_State *L) { + return LuaCoder(L, EscapePass); +} + +int LuaEscapeSegment(lua_State *L) { + return LuaCoder(L, EscapeSegment); +} + +int LuaEscapeFragment(lua_State *L) { + return LuaCoder(L, EscapeFragment); +} + +int LuaEscapeLiteral(lua_State *L) { + return LuaCoder(L, EscapeJsStringLiteral); +} + +int LuaVisualizeControlCodes(lua_State *L) { + return LuaCoder(L, VisualizeControlCodes); +} + +static dontinline int LuaHasherImpl(lua_State *L, size_t k, + int H(const void *, size_t, uint8_t *)) { + void *p; + size_t n; + uint8_t d[64]; + p = luaL_checklstring(L, 1, &n); + H(p, n, d); + lua_pushlstring(L, (void *)d, k); + mbedtls_platform_zeroize(d, sizeof(d)); + return 1; +} + +static dontinline int LuaHasher(lua_State *L, size_t k, + int H(const void *, size_t, uint8_t *)) { + return LuaHasherImpl(L, k, H); +} + +int LuaMd5(lua_State *L) { + return LuaHasher(L, 16, mbedtls_md5_ret); +} + +int LuaSha1(lua_State *L) { + return LuaHasher(L, 20, mbedtls_sha1_ret); +} + +int LuaSha224(lua_State *L) { + return LuaHasher(L, 28, mbedtls_sha256_ret_224); +} + +int LuaSha256(lua_State *L) { + return LuaHasher(L, 32, mbedtls_sha256_ret_256); +} + +int LuaSha384(lua_State *L) { + return LuaHasher(L, 48, mbedtls_sha512_ret_384); +} + +int LuaSha512(lua_State *L) { + return LuaHasher(L, 64, mbedtls_sha512_ret_512); +} + +int LuaIsHeaderRepeatable(lua_State *L) { + int h; + bool r; + size_t n; + const char *s; + s = luaL_checklstring(L, 1, &n); + if ((h = GetHttpHeader(s, n)) != -1) { + r = kHttpRepeatable[h]; + } else { + r = false; + } + lua_pushboolean(L, r); + return 1; +} + +void LuaPushUrlView(lua_State *L, struct UrlView *v) { + if (v->p) { + lua_pushlstring(L, v->p, v->n); + } else { + lua_pushnil(L); + } +} diff --git a/tool/net/lfuncs.h b/tool/net/lfuncs.h new file mode 100644 index 000000000..d776272de --- /dev/null +++ b/tool/net/lfuncs.h @@ -0,0 +1,84 @@ +#ifndef COSMOPOLITAN_TOOL_NET_LFUNCS_H_ +#define COSMOPOLITAN_TOOL_NET_LFUNCS_H_ +#include "net/http/url.h" +#include "third_party/lua/lua.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +int LuaMaxmind(lua_State *); +int LuaRe(lua_State *); +int LuaUnix(lua_State *); +int luaopen_argon2(lua_State *); +int luaopen_lsqlite3(lua_State *); + +int LuaBsf(lua_State *); +int LuaBsr(lua_State *); +int LuaCategorizeIp(lua_State *); +int LuaCrc32(lua_State *); +int LuaCrc32c(lua_State *); +int LuaDecimate(lua_State *); +int LuaDecodeBase64(lua_State *); +int LuaDecodeLatin1(lua_State *); +int LuaEncodeBase64(lua_State *); +int LuaEncodeLatin1(lua_State *); +int LuaEscapeFragment(lua_State *); +int LuaEscapeHost(lua_State *); +int LuaEscapeHtml(lua_State *); +int LuaEscapeIp(lua_State *); +int LuaEscapeLiteral(lua_State *); +int LuaEscapeParam(lua_State *); +int LuaEscapePass(lua_State *); +int LuaEscapePath(lua_State *); +int LuaEscapeSegment(lua_State *); +int LuaEscapeUser(lua_State *); +int LuaFormatHttpDateTime(lua_State *); +int LuaFormatIp(lua_State *); +int LuaGetCpuCore(lua_State *); +int LuaGetCpuNode(lua_State *); +int LuaGetCryptoHash(lua_State *); +int LuaGetHostOs(lua_State *); +int LuaGetHttpReason(lua_State *); +int LuaGetLogLevel(lua_State *); +int LuaGetMonospaceWidth(lua_State *); +int LuaGetRandomBytes(lua_State *); +int LuaGetTime(lua_State *); +int LuaHasControlCodes(lua_State *); +int LuaIndentLines(lua_State *); +int LuaIsAcceptableHost(lua_State *); +int LuaIsAcceptablePath(lua_State *); +int LuaIsAcceptablePort(lua_State *); +int LuaIsHeaderRepeatable(lua_State *); +int LuaIsLoopbackIp(lua_State *); +int LuaIsPrivateIp(lua_State *); +int LuaIsPublicIp(lua_State *); +int LuaIsReasonablePath(lua_State *); +int LuaIsValidHttpToken(lua_State *); +int LuaLemur64(lua_State *); +int LuaMd5(lua_State *); +int LuaMeasureEntropy(lua_State *); +int LuaParseHost(lua_State *); +int LuaParseHttpDateTime(lua_State *); +int LuaParseIp(lua_State *); +int LuaParseParams(lua_State *); +int LuaPopcnt(lua_State *); +int LuaRand64(lua_State *); +int LuaRdrand(lua_State *); +int LuaRdseed(lua_State *); +int LuaRdtsc(lua_State *); +int LuaSetLogLevel(lua_State *); +int LuaSha1(lua_State *); +int LuaSha224(lua_State *); +int LuaSha256(lua_State *); +int LuaSha384(lua_State *); +int LuaSha512(lua_State *); +int LuaSleep(lua_State *); +int LuaSlurp(lua_State *); +int LuaUnderlong(lua_State *); +int LuaVisualizeControlCodes(lua_State *); + +void LuaPushUrlView(lua_State *, struct UrlView *); +char *FormatUnixHttpDateTime(char *, int64_t); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_TOOL_NET_LFUNCS_H_ */ diff --git a/tool/net/lunix.c b/tool/net/lunix.c index e20022b9e..677d98aa1 100644 --- a/tool/net/lunix.c +++ b/tool/net/lunix.c @@ -27,15 +27,17 @@ #include "libc/calls/struct/sigset.h" #include "libc/calls/struct/stat.h" #include "libc/calls/struct/timespec.h" +#include "libc/calls/struct/timeval.h" #include "libc/calls/ucontext.h" #include "libc/dns/dns.h" #include "libc/errno.h" #include "libc/fmt/fmt.h" -#include "libc/fmt/kerrornames.internal.h" +#include "libc/fmt/magnumstrs.internal.h" #include "libc/intrin/kprintf.h" #include "libc/log/check.h" #include "libc/log/log.h" #include "libc/macros.internal.h" +#include "libc/mem/fmt.h" #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" #include "libc/sock/sock.h" @@ -47,6 +49,7 @@ #include "libc/sysv/consts/dt.h" #include "libc/sysv/consts/f.h" #include "libc/sysv/consts/fd.h" +#include "libc/sysv/consts/ip.h" #include "libc/sysv/consts/ipproto.h" #include "libc/sysv/consts/itimer.h" #include "libc/sysv/consts/log.h" @@ -61,7 +64,10 @@ #include "libc/sysv/consts/shut.h" #include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/sio.h" +#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/consts/w.h" #include "libc/sysv/errfuns.h" #include "libc/time/time.h" @@ -110,17 +116,34 @@ static dontinline int ReturnTimespec(lua_State *L, struct timespec *ts) { } static int ReturnErrno(lua_State *L, int nils, int olderr) { - int i; + int i, newerr = errno; + if (!IsTiny() && !(0 < newerr && newerr < (!IsWindows() ? 4096 : 65536))) { + WARNF("errno should not be %d", newerr); + } for (i = 0; i < nils; ++i) { lua_pushnil(L); } - lua_pushinteger(L, errno); + lua_pushinteger(L, newerr); errno = olderr; return nils + 1; } +static int Return01(lua_State *L, int rc, int olderr) { + if (!IsTiny() && (rc != 0 && rc != -1)) { + WARNF("syscall supposed to return 0 / -1 but got %d", rc); + } + if (rc != -1) { + return 0; + } else { + return ReturnErrno(L, 0, olderr); + } +} + static int ReturnRc(lua_State *L, int64_t rc, int olderr) { if (rc != -1) { + if (!IsTiny() && olderr != errno) { + WARNF("errno unexpectedly changed %d → %d", olderr, errno); + } lua_pushinteger(L, rc); return 1; } else { @@ -128,37 +151,11 @@ static int ReturnRc(lua_State *L, int64_t rc, int olderr) { } } -static char **ConvertLuaArrayToStringList(lua_State *L, int i) { - int j, n; - char **p; - luaL_checktype(L, i, LUA_TTABLE); - lua_len(L, i); - n = lua_tointeger(L, -1); - lua_pop(L, 1); - p = xcalloc(n + 1, sizeof(*p)); - for (j = 1; j <= n; ++j) { - lua_geti(L, i, j); - p[j - 1] = strdup(lua_tostring(L, -1)); - lua_pop(L, 1); +static void CheckOptvalsize(lua_State *L, uint32_t want, uint32_t got) { + if (!IsTiny()) { + if (want == got) return; + WARNF("getsockopt optvalsize should be %d but was %d", want, got); } - return p; -} - -static char **ConvertLuaTableToEnvList(lua_State *L, int i) { - int j, n; - char **p, *s; - luaL_checktype(L, i, LUA_TTABLE); - p = xcalloc((n = 0) + 1, sizeof(char *)); - lua_pushnil(L); - for (n = 0; lua_next(L, i);) { - if (lua_type(L, -2) == LUA_TSTRING) { - p = xrealloc(p, (++n + 1) * sizeof(*p)); - p[n - 1] = xasprintf("%s=%s", lua_tostring(L, -2), lua_tostring(L, -1)); - } - lua_pop(L, 1); - } - p[n] = 0; - return p; } static void FreeStringList(char **p) { @@ -171,6 +168,30 @@ static void FreeStringList(char **p) { } } +static char **ConvertLuaArrayToStringList(lua_State *L, int i) { + int j, n; + char **p, *s; + luaL_checktype(L, i, LUA_TTABLE); + lua_len(L, i); + n = lua_tointeger(L, -1); + lua_pop(L, 1); + if ((p = calloc(n + 1, sizeof(*p)))) { + for (j = 1; j <= n; ++j) { + lua_geti(L, i, j); + s = strdup(lua_tostring(L, -1)); + lua_pop(L, 1); + if (s) { + p[j - 1] = s; + } else { + FreeStringList(p); + p = 0; + break; + } + } + } + return p; +} + //////////////////////////////////////////////////////////////////////////////// // System Calls @@ -194,6 +215,16 @@ static int LuaUnixGetgid(lua_State *L) { return ReturnInteger(L, getgid()); } +// unix.geteuid() → uid:int +static int LuaUnixGeteuid(lua_State *L) { + return ReturnInteger(L, geteuid()); +} + +// unix.getegid() → gid:int +static int LuaUnixGetegid(lua_State *L) { + return ReturnInteger(L, getegid()); +} + // unix.umask(mask:int) → oldmask:int static int LuaUnixUmask(lua_State *L) { return ReturnInteger(L, umask(luaL_checkinteger(L, 1))); @@ -204,129 +235,86 @@ static wontreturn int LuaUnixExit(lua_State *L) { _Exit(luaL_optinteger(L, 1, 0)); } -// unix.access(path:str, how:int) → rc:int[, errno:int] +// unix.access(path:str, how:int) → errno:int // how can be: R_OK, W_OK, X_OK, F_OK static int LuaUnixAccess(lua_State *L) { - const char *file; - int rc, mode, olderr; - olderr = errno; - file = luaL_checklstring(L, 1, 0); - mode = luaL_checkinteger(L, 2); - rc = access(file, mode); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return Return01(L, access(luaL_checkstring(L, 1), luaL_checkinteger(L, 2)), + olderr); } -// unix.mkdir(path:str[, mode:int]) → rc:int[, errno:int] +// unix.mkdir(path:str[, mode:int]) → errno:int // mode should be octal static int LuaUnixMkdir(lua_State *L) { - const char *file; - int rc, mode, olderr; - olderr = errno; - file = luaL_checklstring(L, 1, 0); - mode = luaL_optinteger(L, 2, 0755); - rc = mkdir(file, mode); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return Return01(L, mkdir(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0755)), + olderr); } -// unix.makedirs(path:str[, mode:int]) → rc:int[, errno:int] +// unix.makedirs(path:str[, mode:int]) → errno:int // mode should be octal static int LuaUnixMakedirs(lua_State *L) { - const char *file; - int rc, mode, olderr; - olderr = errno; - file = luaL_checklstring(L, 1, 0); - mode = luaL_optinteger(L, 2, 0755); - rc = makedirs(file, mode); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return Return01( + L, makedirs(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0755)), olderr); } -// unix.chdir(path:str) → rc:int[, errno:int] +// unix.chdir(path:str) → errno:int static int LuaUnixChdir(lua_State *L) { - int rc, olderr; - const char *file; - olderr = errno; - file = luaL_checklstring(L, 1, 0); - rc = chdir(file); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return Return01(L, chdir(luaL_checkstring(L, 1)), olderr); } -// unix.unlink(path:str) → rc:int[, errno:int] +// unix.unlink(path:str) → errno:int static int LuaUnixUnlink(lua_State *L) { - int rc, olderr; - const char *file; - olderr = errno; - file = luaL_checklstring(L, 1, 0); - rc = unlink(file); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return Return01(L, unlink(luaL_checkstring(L, 1)), olderr); } -// unix.rmdir(path:str) → rc:int[, errno:int] +// unix.rmdir(path:str) → errno:int static int LuaUnixRmdir(lua_State *L) { - const char *file; - int rc, olderr; - olderr = errno; - file = luaL_checklstring(L, 1, 0); - rc = rmdir(file); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return Return01(L, rmdir(luaL_checkstring(L, 1)), olderr); } -// unix.rename(oldpath:str, newpath:str) → rc:int[, errno:int] +// unix.rename(oldpath:str, newpath:str) → errno:int static int LuaUnixRename(lua_State *L) { - const char *oldpath, *newpath; - int rc, olderr; - olderr = errno; - oldpath = luaL_checklstring(L, 1, 0); - newpath = luaL_checklstring(L, 2, 0); - rc = rename(oldpath, newpath); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return Return01(L, rename(luaL_checkstring(L, 1), luaL_checkstring(L, 2)), + olderr); } -// unix.link(existingpath:str, newpath:str) → rc:int[, errno:int] +// unix.link(existingpath:str, newpath:str) → errno:int static int LuaUnixLink(lua_State *L) { - const char *existingpath, *newpath; - int rc, olderr; - olderr = errno; - existingpath = luaL_checklstring(L, 1, 0); - newpath = luaL_checklstring(L, 2, 0); - rc = link(existingpath, newpath); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return Return01(L, link(luaL_checkstring(L, 1), luaL_checkstring(L, 2)), + olderr); } -// unix.symlink(target:str, linkpath:str) → rc:int[, errno:int] +// unix.symlink(target:str, linkpath:str) → errno:int static int LuaUnixSymlink(lua_State *L) { - const char *target, *linkpath; - int rc, olderr; - olderr = errno; - target = luaL_checklstring(L, 1, 0); - linkpath = luaL_checklstring(L, 2, 0); - rc = symlink(target, linkpath); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return Return01(L, symlink(luaL_checkstring(L, 1), luaL_checkstring(L, 2)), + olderr); } -// unix.chown(path:str, uid:int, gid:int) → rc:int[, errno:int] +// unix.chown(path:str, uid:int, gid:int) → errno:int static int LuaUnixChown(lua_State *L) { - const char *file; - int rc, uid, gid, olderr; - olderr = errno; - file = luaL_checklstring(L, 1, 0); - uid = luaL_checkinteger(L, 2); - gid = luaL_checkinteger(L, 3); - rc = chown(file, uid, gid); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return Return01(L, + chown(luaL_checkstring(L, 1), luaL_checkinteger(L, 2), + luaL_checkinteger(L, 3)), + olderr); } -// unix.chmod(path:str, mode:int) → rc:int[, errno:int] +// unix.chmod(path:str, mode:int) → errno:int static int LuaUnixChmod(lua_State *L) { - const char *file; - int rc, mode, olderr; - olderr = errno; - file = luaL_checklstring(L, 1, 0); - mode = luaL_checkinteger(L, 2); - rc = chmod(file, mode); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return Return01(L, chmod(luaL_checkstring(L, 1), luaL_checkinteger(L, 2)), + olderr); } -// unix.getcwd(path:str, mode:int) → rc:int[, errno:int] +// unix.getcwd() → path:str[, errno:int] static int LuaUnixGetcwd(lua_State *L) { int olderr; char *path; @@ -342,10 +330,20 @@ static int LuaUnixGetcwd(lua_State *L) { // unix.fork() → childpid|0:int[, errno:int] static int LuaUnixFork(lua_State *L) { - int rc, olderr; - olderr = errno; - rc = fork(); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return ReturnRc(L, fork(), olderr); +} + +// unix.environ() → {str,...} +static int LuaUnixEnviron(lua_State *L) { + int i; + char **e; + lua_newtable(L); + for (i = 0, e = environ; *e; ++e) { + lua_pushstring(L, *e); + lua_rawseti(L, -2, ++i); + } + return 1; } // unix.execve(prog:str[, args:List<*>, env:Map]) → errno:int @@ -364,14 +362,21 @@ static int LuaUnixExecve(lua_State *L) { olderr = errno; prog = luaL_checkstring(L, 1); if (!lua_isnoneornil(L, 2)) { - argv = ConvertLuaArrayToStringList(L, 2); - freeme1 = argv; - if (!lua_isnoneornil(L, 3)) { - envp = ConvertLuaTableToEnvList(L, 3); - freeme2 = envp; + if ((argv = ConvertLuaArrayToStringList(L, 2))) { + freeme1 = argv; + if (!lua_isnoneornil(L, 3)) { + if ((envp = ConvertLuaArrayToStringList(L, 3))) { + freeme2 = envp; + } else { + FreeStringList(argv); + return ReturnErrno(L, 1, olderr); + } + } else { + envp = environ; + freeme2 = 0; + } } else { - envp = environ; - freeme2 = 0; + return ReturnErrno(L, 1, olderr); } } else { ezargs[0] = prog; @@ -389,13 +394,13 @@ static int LuaUnixExecve(lua_State *L) { // unix.commandv(prog:str) → path:str[, errno:int] static int LuaUnixCommandv(lua_State *L) { - int rc, olderr; + int olderr; const char *prog; char *pathbuf, *resolved; olderr = errno; - if ((pathbuf = malloc(PATH_MAX))) { - prog = luaL_checkstring(L, 1); - if ((resolved = commandv(prog, pathbuf))) { + prog = luaL_checkstring(L, 1); + if ((pathbuf = malloc(PATH_MAX + 1))) { + if ((resolved = commandv(prog, pathbuf, PATH_MAX + 1))) { lua_pushstring(L, resolved); free(pathbuf); return 1; @@ -411,7 +416,7 @@ static int LuaUnixCommandv(lua_State *L) { // unix.realpath(path:str) → path:str[, errno:int] static int LuaUnixRealpath(lua_State *L) { char *resolved; - int rc, olderr; + int olderr; const char *path; olderr = errno; path = luaL_checkstring(L, 1); @@ -430,25 +435,21 @@ static int LuaUnixSyslog(lua_State *L) { return 0; } -// unix.chroot(path:str) → rc:int[, errno:int] +// unix.chroot(path:str) → errno:int static int LuaUnixChroot(lua_State *L) { - int rc, olderr; - const char *path; - olderr = errno; - path = luaL_checkstring(L, 1); - rc = chroot(path); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return Return01(L, chroot(luaL_checkstring(L, 1)), olderr); } -// unix.setrlimit(resource:int, soft:int[, hard:int]) → rc:int[, errno:int] +// unix.setrlimit(resource:int, soft:int[, hard:int]) → errno:int static int LuaUnixSetrlimit(lua_State *L) { - struct rlimit rlim; - int rc, olderr, resource; - olderr = errno; - resource = luaL_checkinteger(L, 1); - rlim.rlim_cur = luaL_checkinteger(L, 2); - rlim.rlim_max = luaL_optinteger(L, 3, rlim.rlim_cur); - return ReturnRc(L, setrlimit(resource, &rlim), olderr); + int olderr = errno; + int64_t soft = luaL_checkinteger(L, 2); + return Return01( + L, + setrlimit(luaL_checkinteger(L, 1), + &(struct rlimit){soft, luaL_optinteger(L, 3, soft)}), + olderr); } // unix.getrlimit(resource:int) → soft:int, hard:int[, errno:int] @@ -466,34 +467,25 @@ static int LuaUnixGetrlimit(lua_State *L) { } } -// unix.kill(pid, sig) → rc:int[, errno:int] +// unix.kill(pid:int, sig:int) → errno:int static int LuaUnixKill(lua_State *L) { - int rc, pid, sig, olderr; - olderr = errno; - pid = luaL_checkinteger(L, 1); - sig = luaL_checkinteger(L, 2); - rc = kill(pid, sig); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return Return01(L, kill(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2)), + olderr); } -// unix.raise(sig) → rc:int[, errno:int] +// unix.raise(sig:int) → rc:int[, errno:int] static int LuaUnixRaise(lua_State *L) { - int rc, sig, olderr; - olderr = errno; - sig = luaL_checkinteger(L, 1); - rc = raise(sig); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return ReturnRc(L, raise(luaL_checkinteger(L, 1)), olderr); } -// unix.wait([pid, options]) → pid, wstatus, nil, errno +// unix.wait([pid:int, options:int]) → pid:int, wstatus:int, nil[, errno:int] static int LuaUnixWait(lua_State *L) { - int rc, pid, olderr, options, wstatus; - olderr = errno; - pid = luaL_optinteger(L, 1, -1); - options = luaL_optinteger(L, 2, 0); - rc = wait4(pid, &wstatus, options, 0); - if (rc != -1) { - lua_pushinteger(L, rc); + int pid, wstatus, olderr = errno; + if ((pid = wait4(luaL_optinteger(L, 1, -1), &wstatus, + luaL_optinteger(L, 2, 0), 0)) != -1) { + lua_pushinteger(L, pid); lua_pushinteger(L, wstatus); return 2; } else { @@ -501,20 +493,16 @@ static int LuaUnixWait(lua_State *L) { } } -// unix.fcntl(fd, cmd[, arg]) → rc:int[, errno:int] +// unix.fcntl(fd:int, cmd:int[, arg:int]) → rc:int[, errno:int] static int LuaUnixFcntl(lua_State *L) { - intptr_t arg; - int rc, fd, cmd, olderr; - olderr = errno; - fd = luaL_checkinteger(L, 1); - cmd = luaL_checkinteger(L, 2); - arg = luaL_optinteger(L, 3, 0); - rc = fcntl(fd, cmd, arg); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return ReturnRc(L, + fcntl(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2), + luaL_optinteger(L, 3, 0)), + olderr); } -// unix.dup(oldfd[, newfd[, flags]]) → newfd, errno -// flags can have O_CLOEXEC +// unix.dup(oldfd:int[, newfd:int[, flags:int]]) → newfd:int[, errno:int] static int LuaUnixDup(lua_State *L) { int rc, oldfd, newfd, flags, olderr; olderr = errno; @@ -529,13 +517,10 @@ static int LuaUnixDup(lua_State *L) { return ReturnRc(L, rc, olderr); } -// unix.pipe([flags]) → reader, writer, errno -// flags can have O_CLOEXEC +// unix.pipe([flags:int]) → reader:int, writer:int[, errno:int] static int LuaUnixPipe(lua_State *L) { - int flags, olderr, pipefd[2]; - olderr = errno; - flags = luaL_optinteger(L, 1, 0); - if (!pipe2(pipefd, flags)) { + int pipefd[2], olderr = errno; + if (!pipe2(pipefd, luaL_optinteger(L, 1, 0))) { lua_pushinteger(L, pipefd[0]); lua_pushinteger(L, pipefd[1]); return 2; @@ -546,78 +531,76 @@ static int LuaUnixPipe(lua_State *L) { // unix.getsid(pid) → sid:int[, errno:int] static int LuaUnixGetsid(lua_State *L) { - int rc, pid, olderr; - olderr = errno; - pid = luaL_checkinteger(L, 1); - rc = getsid(pid); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return ReturnRc(L, getsid(luaL_checkinteger(L, 1)), olderr); } // unix.getpgrp() → pgid:int[, errno:int] static int LuaUnixGetpgrp(lua_State *L) { - int rc, olderr; - olderr = errno; - rc = getpgrp(); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return ReturnRc(L, getpgrp(), olderr); } // unix.getpgid(pid:int) → pgid:int[, errno:int] static int LuaUnixGetpgid(lua_State *L) { - int rc, pid, olderr; - olderr = errno; - pid = luaL_checkinteger(L, 1); - rc = getpgid(pid); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return ReturnRc(L, getpgid(luaL_checkinteger(L, 1)), olderr); } // unix.setpgid(pid:int, pgid:int) → pgid:int[, errno:int] static int LuaUnixSetpgid(lua_State *L) { - int rc, pid, pgid, olderr; - olderr = errno; - pid = luaL_checkinteger(L, 1); - pgid = luaL_checkinteger(L, 2); - rc = setpgid(pid, pgid); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return ReturnRc(L, setpgid(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2)), + olderr); } // unix.setpgrp() → pgid:int[, errno:int] static int LuaUnixSetpgrp(lua_State *L) { - int rc, pid, pgrp, olderr; - olderr = errno; - pid = luaL_checkinteger(L, 1); - pgrp = luaL_checkinteger(L, 2); - rc = setpgrp(); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return ReturnRc(L, setpgrp(), olderr); } // unix.setsid() → sid:int[, errno:int] static int LuaUnixSetsid(lua_State *L) { - int rc, olderr; - olderr = errno; - rc = setsid(); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return ReturnRc(L, setsid(), olderr); } -// unix.setuid(uid:int) → rc:int[, errno:int] +// unix.setuid(uid:int) → errno:int static int LuaUnixSetuid(lua_State *L) { int olderr = errno; - return ReturnRc(L, setuid(luaL_checkinteger(L, 1)), olderr); + return Return01(L, setuid(luaL_checkinteger(L, 1)), olderr); } -// unix.setgid(gid:int) → rc:int[, errno:int] +// unix.setgid(gid:int) → errno:int static int LuaUnixSetgid(lua_State *L) { int olderr = errno; - return ReturnRc(L, setgid(luaL_checkinteger(L, 1)), olderr); + return Return01(L, setgid(luaL_checkinteger(L, 1)), olderr); } -// unix.clock_gettime([clock]) → seconds, nanos, errno +// unix.setresuid(real:int, effective:int, saved:int) → errno:int +static int LuaUnixSetresuid(lua_State *L) { + int olderr = errno; + return Return01(L, + setresuid(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2), + luaL_checkinteger(L, 3)), + olderr); +} + +// unix.setresgid(real:int, effective:int, saved:int) → errno:int +static int LuaUnixSetresgid(lua_State *L) { + int olderr = errno; + return Return01(L, + setresgid(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2), + luaL_checkinteger(L, 3)), + olderr); +} + +// unix.clock_gettime([clock:int]) → seconds:int, nanos:int[, errno:int] static int LuaUnixGettime(lua_State *L) { struct timespec ts; - int rc, clock, olderr; - olderr = errno; - clock = luaL_optinteger(L, 1, CLOCK_REALTIME); - rc = clock_gettime(clock, &ts); - if (rc != -1) { + int rc, olderr = errno; + if (!clock_gettime(luaL_optinteger(L, 1, CLOCK_REALTIME), &ts)) { lua_pushinteger(L, ts.tv_sec); lua_pushinteger(L, ts.tv_nsec); return 2; @@ -626,15 +609,14 @@ static int LuaUnixGettime(lua_State *L) { } } -// unix.nanosleep(seconds, nanos) → remseconds, remnanos, errno +// unix.nanosleep(seconds:int, nanos:int) +// → remseconds:int, remnanos:int[, errno:int] static int LuaUnixNanosleep(lua_State *L) { - int rc, olderr; + int olderr = errno; struct timespec req, rem; - olderr = errno; req.tv_sec = luaL_checkinteger(L, 1); req.tv_nsec = luaL_optinteger(L, 2, 0); - rc = nanosleep(&req, &rem); - if (rc != -1) { + if (!nanosleep(&req, &rem)) { lua_pushinteger(L, rem.tv_sec); lua_pushinteger(L, rem.tv_nsec); return 2; @@ -649,82 +631,59 @@ static int LuaUnixSync(lua_State *L) { return 0; } -// unix.fsync(fd:int) → rc:int[, errno:int] +// unix.fsync(fd:int) → errno:int static int LuaUnixFsync(lua_State *L) { - int rc, fd, olderr; - olderr = errno; - fd = luaL_checkinteger(L, 1); - rc = fsync(fd); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return Return01(L, fsync(luaL_checkinteger(L, 1)), olderr); } -// unix.fdatasync(fd:int) → rc:int[, errno:int] +// unix.fdatasync(fd:int) → errno:int static int LuaUnixFdatasync(lua_State *L) { - int rc, fd, olderr; - olderr = errno; - fd = luaL_checkinteger(L, 1); - rc = fdatasync(fd); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return Return01(L, fdatasync(luaL_checkinteger(L, 1)), olderr); } -// unix.open(path, flags[, mode]) → fd, errno +// unix.open(path:str, flags:int[, mode:int]) → fd:int[, errno:int] static int LuaUnixOpen(lua_State *L) { - const char *file; - int rc, flags, mode, olderr; - olderr = errno; - file = luaL_checklstring(L, 1, 0); - flags = luaL_checkinteger(L, 2); - mode = luaL_optinteger(L, 3, 0); - rc = open(file, flags, mode); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return ReturnRc(L, + open(luaL_checkstring(L, 1), luaL_checkinteger(L, 2), + luaL_optinteger(L, 3, 0)), + olderr); } -// unix.close(fd:int) → rc:int[, errno:int] +// unix.close(fd:int) → errno:int static int LuaUnixClose(lua_State *L) { - int rc, fd, olderr; - olderr = errno; - fd = luaL_checkinteger(L, 1); - rc = close(fd); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return Return01(L, close(luaL_checkinteger(L, 1)), olderr); } -// unix.seek(fd, offset, whence) → newpos, errno +// unix.lseek(fd:int, offset:int[, whence:int]) → newpos:int[, errno:int] // where whence ∈ {SEEK_SET, SEEK_CUR, SEEK_END} // whence defaults to SEEK_SET -static int LuaUnixSeek(lua_State *L) { - int64_t newpos, offset; - int fd, olderr, whence; - olderr = errno; - fd = luaL_checkinteger(L, 1); - offset = luaL_checkinteger(L, 2); - whence = luaL_optinteger(L, 3, SEEK_SET); - newpos = lseek(fd, offset, whence); - return ReturnRc(L, newpos, olderr); +static int LuaUnixLseek(lua_State *L) { + int olderr = errno; + return ReturnRc(L, + lseek(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2), + luaL_optinteger(L, 3, SEEK_SET)), + olderr); } -// unix.truncate(path, length) → rc:int[, errno:int] -// unix.truncate(fd, length) → rc:int[, errno:int] +// unix.truncate(path:str[, length:int]) → errno:int static int LuaUnixTruncate(lua_State *L) { - int64_t length; - const char *path; - int rc, fd, olderr, whence; - olderr = errno; - if (lua_isinteger(L, 1)) { - fd = luaL_checkinteger(L, 1); - length = luaL_checkinteger(L, 2); - rc = ftruncate(fd, length); - } else if (lua_isstring(L, 1)) { - path = luaL_checkstring(L, 1); - length = luaL_checkinteger(L, 2); - rc = truncate(path, length); - } else { - luaL_argerror(L, 1, "not integer or string"); - unreachable; - } - return ReturnRc(L, rc, olderr); + int olderr = errno; + return Return01(L, truncate(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0)), + olderr); } -// unix.read(fd:int[, bufsiz:str, offset:int]) → data:str, errno:int +// unix.ftruncate(fd:int[, length:int]) → errno:int +static int LuaUnixFtruncate(lua_State *L) { + int olderr = errno; + return Return01( + L, ftruncate(luaL_checkinteger(L, 1), luaL_optinteger(L, 2, 0)), olderr); +} + +// unix.read(fd:int[, bufsiz:str, offset:int]) → data:str[, errno:int] static int LuaUnixRead(lua_State *L) { char *buf; size_t got; @@ -774,102 +733,226 @@ static int LuaUnixWrite(lua_State *L) { return ReturnRc(L, rc, olderr); } -// unix.stat(path:str) → UnixStat*[, errno] -// unix.stat(fd:int) → UnixStat*[, errno] -static int LuaUnixStat(lua_State *L) { - const char *path; - int rc, fd, olderr; - struct UnixStat **ust, *st; - olderr = errno; - if ((st = malloc(sizeof(struct UnixStat)))) { - if (lua_isinteger(L, 1)) { - fd = luaL_checkinteger(L, 1); - rc = fstat(fd, &st->st); - } else if (lua_isstring(L, 1)) { - path = luaL_checkstring(L, 1); - rc = stat(path, &st->st); - } else { - free(st); - luaL_argerror(L, 1, "not integer or string"); - unreachable; - } - if (rc != -1) { - st->refs = 1; - ust = lua_newuserdatauv(L, sizeof(st), 1); - luaL_setmetatable(L, "UnixStat*"); - *ust = st; - return 1; - } else { - free(st); - return ReturnErrno(L, 1, olderr); - } - } else { - return ReturnErrno(L, 1, olderr); - } -} - -// unix.opendir(path:str) → UnixDir*[, errno] -// unix.opendir(fd:int) → UnixDir*[, errno] -static int LuaUnixOpendir(lua_State *L) { - DIR *rc; - int fd, olderr; - const char *path; - struct UnixDir **udir, *dir; - olderr = errno; - dir = xcalloc(1, sizeof(struct UnixDir)); - if (lua_isinteger(L, 1)) { - fd = luaL_checkinteger(L, 1); - rc = fdopendir(fd); - } else if (lua_isstring(L, 1)) { - path = luaL_checkstring(L, 1); - rc = opendir(path); - } else { - luaL_argerror(L, 1, "not integer or string"); - unreachable; - } - if (!rc) { - lua_pushnil(L); - lua_pushinteger(L, errno); - errno = olderr; - free(dir); - return 2; - } - dir->refs = 1; - dir->dir = rc; - udir = lua_newuserdatauv(L, sizeof(dir), 1); - luaL_setmetatable(L, "UnixDir*"); - *udir = dir; +static int ReturnStat(lua_State *L, struct UnixStat *ust) { + struct UnixStat **ustp; + ust->refs = 1; + ustp = lua_newuserdatauv(L, sizeof(*ustp), 1); + luaL_setmetatable(L, "UnixStat*"); + *ustp = ust; return 1; } -// unix.socket([family[, type[, protocol]]]) → fd, errno -// SOCK_CLOEXEC may be or'd into type -// family defaults to AF_INET -// type defaults to SOCK_STREAM -// protocol defaults to IPPROTO_TCP -static int LuaUnixSocket(lua_State *L) { - const char *file; - int rc, olderr, family, type, protocol; - olderr = errno; - family = luaL_optinteger(L, 1, AF_INET); - type = luaL_optinteger(L, 2, SOCK_STREAM); - protocol = luaL_optinteger(L, 3, IPPROTO_TCP); - rc = socket(family, type, protocol); - return ReturnRc(L, rc, olderr); +// unix.stat(path:str) → UnixStat*[, errno:int] +static int LuaUnixStat(lua_State *L) { + const char *path; + int olderr = errno; + struct UnixStat *ust; + path = luaL_checkstring(L, 1); + if ((ust = malloc(sizeof(*ust)))) { + if (!stat(path, &ust->st)) { + return ReturnStat(L, ust); + } + free(ust); + } + return ReturnErrno(L, 1, olderr); } -// unix.socketpair([family[, type[, protocol]]]) → fd1, fd2[, errno] -// SOCK_CLOEXEC may be or'd into type -// family defaults to AF_INET -// type defaults to SOCK_STREAM -// protocol defaults to IPPROTO_TCP -static int LuaUnixSocketpair(lua_State *L) { - int olderr, family, type, protocol, sv[2]; +// unix.fstat(fd:int) → UnixFstat*[, errno:int] +static int LuaUnixFstat(lua_State *L) { + int fd, olderr = errno; + struct UnixStat *ust; olderr = errno; - family = luaL_optinteger(L, 1, AF_INET); - type = luaL_optinteger(L, 2, SOCK_STREAM); - protocol = luaL_optinteger(L, 3, IPPROTO_TCP); - if (!socketpair(family, type, protocol, sv)) { + fd = luaL_checkinteger(L, 1); + if ((ust = malloc(sizeof(*ust)))) { + if (!fstat(fd, &ust->st)) { + return ReturnStat(L, ust); + } + free(ust); + } + return ReturnErrno(L, 1, olderr); +} + +static int ReturnDir(lua_State *L, struct UnixDir *udir) { + struct UnixDir **udirp; + udir->refs = 1; + udirp = lua_newuserdatauv(L, sizeof(*udirp), 1); + luaL_setmetatable(L, "UnixDir*"); + *udirp = udir; + return 1; +} + +// unix.opendir(path:str) → UnixDir*[, errno:int] +static int LuaUnixOpendir(lua_State *L) { + int olderr = errno; + const char *path; + struct UnixDir *udir; + path = luaL_checkstring(L, 1); + if ((udir = calloc(1, sizeof(*udir)))) { + if ((udir->dir = opendir(path))) { + return ReturnDir(L, udir); + } + free(udir); + } + return ReturnErrno(L, 1, olderr); +} + +// unix.fdopendir(fd:int) → UnixDir*[, errno:int] +static int LuaUnixFdopendir(lua_State *L) { + int fd, olderr = errno; + struct UnixDir *udir; + fd = luaL_checkinteger(L, 1); + if ((udir = calloc(1, sizeof(*udir)))) { + if ((udir->dir = fdopendir(fd))) { + return ReturnDir(L, udir); + } + free(udir); + } + return ReturnErrno(L, 1, olderr); +} + +static bool IsSockoptBool(int l, int x) { + if (l == SOL_SOCKET) { + return x == SO_DEBUG || // + x == SO_BROADCAST || // + x == SO_REUSEADDR || // + x == SO_REUSEPORT || // + x == SO_KEEPALIVE || // + x == SO_DONTROUTE; // + } else if (l = SOL_TCP) { + return x == TCP_NODELAY || // + x == TCP_CORK || // + x == TCP_QUICKACK || // + x == TCP_FASTOPEN_CONNECT || // + x == TCP_DEFER_ACCEPT; // + } else if (l = SOL_IP) { + return x == IP_HDRINCL; // + } else { + return false; + } +} + +static bool IsSockoptInt(int l, int x) { + if (l == SOL_SOCKET) { + return x == SO_SNDBUF || // + x == SO_RCVBUF || // + x == SO_RCVLOWAT || // + x == SO_SNDLOWAT; // + } else if (l = SOL_TCP) { + return x == TCP_FASTOPEN || // + x == TCP_KEEPCNT || // + x == TCP_MAXSEG || // + x == TCP_SYNCNT || // + x == TCP_NOTSENT_LOWAT || // + x == TCP_WINDOW_CLAMP || // + x == TCP_KEEPIDLE || // + x == TCP_KEEPINTVL; // + } else if (l = SOL_IP) { + return x == IP_TOS || // + x == IP_MTU || // + x == IP_TTL; // + } else { + return false; + } +} + +static bool IsSockoptTimeval(int l, int x) { + if (l == SOL_SOCKET) { + return x == SO_RCVTIMEO || // + x == SO_SNDTIMEO; // + } else { + return false; + } +} + +// unix.setsockopt(fd:int, level:int, optname:int, ...) +// → errno:int +static int LuaUnixSetsockopt(lua_State *L) { + struct timeval tv; + int rc, fd, level, optname, optval, olderr = errno; + fd = luaL_checkinteger(L, 1); + level = luaL_checkinteger(L, 2); + optname = luaL_checkinteger(L, 3); + if (IsSockoptBool(level, optname)) { + optval = lua_toboolean(L, 4); + return Return01(L, setsockopt(fd, level, optname, &optval, sizeof(optval)), + olderr); + } else if (IsSockoptInt(level, optname)) { + optval = luaL_checkinteger(L, 4); + return Return01(L, setsockopt(fd, level, optname, &optval, sizeof(optval)), + olderr); + } else if (IsSockoptTimeval(level, optname)) { + tv.tv_sec = luaL_checkinteger(L, 4); + tv.tv_usec = luaL_optinteger(L, 5, 0); + return Return01(L, setsockopt(fd, level, optname, &tv, sizeof(tv)), olderr); + } else { + lua_pushinteger(L, EINVAL); + return 1; + } +} + +// unix.getsockopt(fd:int, level:int, optname:int) +// → errno:int, ... +static int LuaUnixGetsockopt(lua_State *L) { + struct timeval tv; + uint32_t tvsize, optvalsize; + int rc, fd, level, optname, optval, olderr = errno; + fd = luaL_checkinteger(L, 1); + level = luaL_checkinteger(L, 2); + optname = luaL_checkinteger(L, 3); + if (IsSockoptBool(level, optname)) { + optvalsize = sizeof(optval); + if (getsockopt(fd, level, optname, &optval, &optvalsize) != -1) { + CheckOptvalsize(L, sizeof(optval), optvalsize); + lua_pushnil(L); + lua_pushboolean(L, optval); + return 2; + } else { + return ReturnErrno(L, 0, olderr); + } + } else if (IsSockoptInt(level, optname)) { + optvalsize = sizeof(optval); + if (getsockopt(fd, level, optname, &optval, &optvalsize) != -1) { + CheckOptvalsize(L, sizeof(optval), optvalsize); + lua_pushnil(L); + lua_pushinteger(L, optval); + return 2; + } else { + return ReturnErrno(L, 0, olderr); + } + } else if (IsSockoptTimeval(level, optname)) { + tvsize = sizeof(tv); + if (getsockopt(fd, level, optname, &tv, &tvsize) != -1) { + CheckOptvalsize(L, sizeof(tv), tvsize); + lua_pushnil(L); + lua_pushinteger(L, tv.tv_sec); + lua_pushinteger(L, tv.tv_usec); + return 3; + } else { + return ReturnErrno(L, 0, olderr); + } + } else { + lua_pushinteger(L, EINVAL); + return 1; + } +} + +// unix.socket([family:int[, type:int[, protocol:int]]]) → fd:int[, errno:int] +static int LuaUnixSocket(lua_State *L) { + int olderr = errno; + return ReturnRc( + L, + socket(luaL_optinteger(L, 1, AF_INET), luaL_optinteger(L, 2, SOCK_STREAM), + luaL_optinteger(L, 3, IPPROTO_TCP)), + olderr); +} + +// unix.socketpair([family:int[, type:int[, protocol:int]]]) +// → fd1:int, fd2:int[, errno:int] +static int LuaUnixSocketpair(lua_State *L) { + int sv[2], olderr = errno; + if (!socketpair(luaL_optinteger(L, 1, AF_INET), + luaL_optinteger(L, 2, SOCK_STREAM), + luaL_optinteger(L, 3, IPPROTO_TCP), sv)) { lua_pushinteger(L, sv[0]); lua_pushinteger(L, sv[1]); return 2; @@ -878,54 +961,41 @@ static int LuaUnixSocketpair(lua_State *L) { } } -// unix.bind(fd[, ip, port]) → rc:int[, errno:int] -// SOCK_CLOEXEC may be or'd into type -// family defaults to AF_INET -// type defaults to SOCK_STREAM -// protocol defaults to IPPROTO_TCP +// unix.bind(fd:int[, ip:uint32, port:uint16]) → errno:int static int LuaUnixBind(lua_State *L) { - uint32_t x; - int rc, olderr, fd; - struct sockaddr_in sa; - bzero(&sa, sizeof(sa)); - olderr = errno; - fd = luaL_checkinteger(L, 1); - x = luaL_optinteger(L, 2, 0); - sa.sin_family = AF_INET; - sa.sin_addr.s_addr = htonl(x); - sa.sin_port = htons(luaL_optinteger(L, 3, 0)); - rc = bind(fd, &sa, sizeof(sa)); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return Return01(L, + bind(luaL_checkinteger(L, 1), + &(struct sockaddr_in){ + .sin_family = AF_INET, + .sin_addr.s_addr = htonl(luaL_optinteger(L, 2, 0)), + .sin_port = htons(luaL_optinteger(L, 3, 0)), + }, + sizeof(struct sockaddr_in)), + olderr); } -// unix.connect(fd, ip, port) → rc:int[, errno:int] -// SOCK_CLOEXEC may be or'd into type -// family defaults to AF_INET -// type defaults to SOCK_STREAM -// protocol defaults to IPPROTO_TCP +// unix.connect(fd:int, ip:uint32, port:uint16) → errno:int static int LuaUnixConnect(lua_State *L) { - int rc, olderr, fd; - struct sockaddr_in sa; - bzero(&sa, sizeof(sa)); - olderr = errno; - fd = luaL_checkinteger(L, 1); - sa.sin_addr.s_addr = htonl(luaL_checkinteger(L, 2)); - sa.sin_port = htons(luaL_checkinteger(L, 3)); - rc = connect(fd, &sa, sizeof(sa)); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return Return01(L, + connect(luaL_checkinteger(L, 1), + &(struct sockaddr_in){ + .sin_addr.s_addr = htonl(luaL_checkinteger(L, 2)), + .sin_port = htons(luaL_checkinteger(L, 3)), + }, + sizeof(struct sockaddr_in)), + olderr); } -// unix.listen(fd[, backlog]) → rc:int[, errno:int] +// unix.listen(fd:int[, backlog:int]) → errno:int static int LuaUnixListen(lua_State *L) { - int rc, fd, olderr, backlog; - olderr = errno; - fd = luaL_checkinteger(L, 1); - backlog = luaL_optinteger(L, 2, 10); - rc = listen(fd, backlog); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return Return01(L, listen(luaL_checkinteger(L, 1), luaL_optinteger(L, 2, 10)), + olderr); } -// unix.getsockname(fd) → ip, port, errno +// unix.getsockname(fd:int) → ip:uint32, port:uint16[, errno:int] static int LuaUnixGetsockname(lua_State *L) { int fd, olderr; uint32_t addrsize; @@ -959,7 +1029,8 @@ static int LuaUnixGetpeername(lua_State *L) { } } -// unix.siocgifconf() → {{name:str,ip:uint32,netmask:uint32}, ...}[, errno:int] +// unix.siocgifconf() → {{name:str,ip:uint32,netmask:uint32}, ...}[, +// errno:int] static int LuaUnixSiocgifconf(lua_State *L) { size_t n; char *data; @@ -1024,7 +1095,8 @@ static int LuaUnixGethostname(lua_State *L) { } } -// unix.accept(serverfd:int) → clientfd:int, ip:uint32, port:uint16[, errno:int] +// unix.accept(serverfd:int) +// → clientfd:int, ip:uint32, port:uint16[, errno:int] static int LuaUnixAccept(lua_State *L) { uint32_t addrsize; struct sockaddr_in sa; @@ -1110,7 +1182,7 @@ static int LuaUnixRecvfrom(lua_State *L) { } } -// unix.recv(fd[, bufsiz[, flags]]) → data[, errno] +// unix.recv(fd:int[, bufsiz:int[, flags:int]]) → data:str[, errno:int] static int LuaUnixRecv(lua_State *L) { char *buf; size_t got; @@ -1137,7 +1209,7 @@ static int LuaUnixRecv(lua_State *L) { } } -// unix.send(fd, data[, flags]) → sent, errno +// unix.send(fd:int, data:str[, flags:int]) → sent:int[, errno:int] static int LuaUnixSend(lua_State *L) { char *data; ssize_t rc; @@ -1152,8 +1224,8 @@ static int LuaUnixSend(lua_State *L) { return ReturnRc(L, rc, olderr); } -// unix.sendto(fd, data, ip, port[, flags]) → sent, errno -// flags MSG_OOB, MSG_DONTROUTE, MSG_NOSIGNAL, etc. +// unix.sendto(fd:int, data:str, ip:uint32, port:uint16[, flags:int]) +// → sent:int[, errno:int] static int LuaUnixSendto(lua_State *L) { char *data; ssize_t rc; @@ -1175,12 +1247,9 @@ static int LuaUnixSendto(lua_State *L) { // unix.shutdown(fd, how) → rc:int[, errno:int] // how can be SHUT_RD, SHUT_WR, or SHUT_RDWR static int LuaUnixShutdown(lua_State *L) { - int rc, fd, how, olderr; - olderr = errno; - fd = luaL_checkinteger(L, 1); - how = luaL_checkinteger(L, 2); - rc = shutdown(fd, how); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return Return01(L, shutdown(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2)), + olderr); } // unix.sigprocmask(how[, mask]) → oldmask[, errno] @@ -1368,7 +1437,12 @@ static int LuaUnixStrerror(lua_State *L) { // unix.strerrno(errno) → str static int LuaUnixStrerrno(lua_State *L) { - return ReturnString(L, strerror_short(luaL_checkinteger(L, 1))); + return ReturnString(L, strerrno(luaL_checkinteger(L, 1))); +} + +// unix.strerdoc(errno) → str +static int LuaUnixStrerdoc(lua_State *L) { + return ReturnString(L, strerdoc(luaL_checkinteger(L, 1))); } // unix.strsignal(sig) → str @@ -1530,7 +1604,7 @@ static int FreeUnixDir(struct UnixDir *dir) { return closedir(dir->dir); } -// UnixDir:close() → rc:int[, errno:int] +// UnixDir:close() → errno:int // may be called multiple times // called by the garbage collector too static int LuaUnixDirClose(lua_State *L) { @@ -1541,10 +1615,10 @@ static int LuaUnixDirClose(lua_State *L) { olderr = 0; rc = FreeUnixDir(*udir); *udir = 0; - return ReturnRc(L, rc, olderr); + return Return01(L, rc, olderr); } -// UnixDir:read() → name, kind, ino, off[, errno] +// UnixDir:read() → name:str, kind:int, ino:int, off:int[, errno:int] // returns nil if no more entries // kind can be DT_UNKNOWN/REG/DIR/BLK/LNK/CHR/FIFO/SOCK static int LuaUnixDirRead(lua_State *L) { @@ -1565,7 +1639,7 @@ static int LuaUnixDirRead(lua_State *L) { } } -// UnixDir:fd() → fd, errno +// UnixDir:fd() → fd:int[, errno:int] // EOPNOTSUPP if using /zip/ // EOPNOTSUPP if IsWindows() static int LuaUnixDirFd(lua_State *L) { @@ -1580,7 +1654,7 @@ static int LuaUnixDirFd(lua_State *L) { } } -// UnixDir:tell() → off +// UnixDir:tell() → off:int static int LuaUnixDirTell(lua_State *L) { long off; off = telldir(GetDirOrDie(L)); @@ -1622,10 +1696,11 @@ static void LuaUnixDirObj(lua_State *L) { static const luaL_Reg kLuaUnix[] = { {"exit", LuaUnixExit}, // exit w/o atexit - {"stat", LuaUnixStat}, // get file info + {"stat", LuaUnixStat}, // get file info from path + {"fstat", LuaUnixFstat}, // get file info from fd {"open", LuaUnixOpen}, // open file fd at lowest slot {"close", LuaUnixClose}, // close file or socket - {"seek", LuaUnixSeek}, // seek in file + {"lseek", LuaUnixLseek}, // seek in file {"read", LuaUnixRead}, // read from file or socket {"write", LuaUnixWrite}, // write to file or socket {"access", LuaUnixAccess}, // check my file authorization @@ -1636,6 +1711,7 @@ static const luaL_Reg kLuaUnix[] = { {"getcwd", LuaUnixGetcwd}, // get current directory {"fork", LuaUnixFork}, // make child process via mitosis {"execve", LuaUnixExecve}, // replace process with program + {"environ", LuaUnixEnviron}, // get environment variables {"commandv", LuaUnixCommandv}, // resolve program on $PATH {"realpath", LuaUnixRealpath}, // abspath without dots/symlinks {"syslog", LuaUnixSyslog}, // logs to system log @@ -1648,6 +1724,7 @@ static const luaL_Reg kLuaUnix[] = { {"makedirs", LuaUnixMakedirs}, // make directory and parents too {"rmdir", LuaUnixRmdir}, // remove empty directory {"opendir", LuaUnixOpendir}, // read directory entry list + {"fdopendir", LuaUnixFdopendir}, // read directory entry list {"rename", LuaUnixRename}, // rename file or directory {"link", LuaUnixLink}, // create hard link {"unlink", LuaUnixUnlink}, // remove file @@ -1656,7 +1733,7 @@ static const luaL_Reg kLuaUnix[] = { {"fsync", LuaUnixFsync}, // flush open file {"fdatasync", LuaUnixFdatasync}, // flush open file w/o metadata {"truncate", LuaUnixTruncate}, // shrink or extend file medium - {"ftruncate", LuaUnixTruncate}, // shrink or extend file medium + {"ftruncate", LuaUnixFtruncate}, // shrink or extend file medium {"umask", LuaUnixUmask}, // set default file mask {"chroot", LuaUnixChroot}, // change root directory {"setrlimit", LuaUnixSetrlimit}, // prevent cpu memory bombs @@ -1670,14 +1747,20 @@ static const luaL_Reg kLuaUnix[] = { {"setsid", LuaUnixSetsid}, // create a new session id {"getpid", LuaUnixGetpid}, // get id of this process {"getuid", LuaUnixGetuid}, // get real user id of process + {"geteuid", LuaUnixGeteuid}, // get effective user id of process {"setuid", LuaUnixSetuid}, // set real user id of process + {"setresuid", LuaUnixSetresuid}, // sets real/effective/saved uids {"getgid", LuaUnixGetgid}, // get real group id of process + {"getegid", LuaUnixGetegid}, // get effective group id of process {"setgid", LuaUnixSetgid}, // set real group id of process + {"setresgid", LuaUnixSetresgid}, // sets real/effective/saved gids {"gethostname", LuaUnixGethostname}, // get hostname of this machine {"clock_gettime", LuaUnixGettime}, // get timestamp w/ nano precision {"nanosleep", LuaUnixNanosleep}, // sleep w/ nano precision {"socket", LuaUnixSocket}, // create network communication fd {"socketpair", LuaUnixSocketpair}, // create bidirectional pipe + {"setsockopt", LuaUnixSetsockopt}, // tune socket options + {"getsockopt", LuaUnixGetsockopt}, // get socket tunings {"poll", LuaUnixPoll}, // waits for file descriptor events {"bind", LuaUnixBind}, // reserve network interface address {"listen", LuaUnixListen}, // begin listening for clients @@ -1697,6 +1780,7 @@ static const luaL_Reg kLuaUnix[] = { {"setitimer", LuaUnixSetitimer}, // set alarm clock {"strerror", LuaUnixStrerror}, // turn errno into string {"strerrno", LuaUnixStrerrno}, // turn errno into string + {"strerdoc", LuaUnixStrerdoc}, // turn errno into string {"strsignal", LuaUnixStrsignal}, // turn signal into string {"WIFEXITED", LuaUnixWifexited}, // gets exit code from wait status {"WEXITSTATUS", LuaUnixWexitstatus}, // gets exit status from wait status @@ -1705,10 +1789,17 @@ static const luaL_Reg kLuaUnix[] = { {0}, // }; -int LuaUnix(lua_State *L) { +static void LoadMagnums(lua_State *L, struct MagnumStr *ms, const char *pfx) { int i; - char sigbuf[12]; + char b[64], *p; + p = stpcpy(b, pfx); + for (i = 0; ms[i].x != -123; ++i) { + stpcpy(p, (const char *)((uintptr_t)ms + ms[i].s)); + LuaSetIntField(L, b, *(const int *)((uintptr_t)ms + ms[i].x)); + } +} +int LuaUnix(lua_State *L) { GL = L; luaL_newlib(L, kLuaUnix); LuaUnixStatObj(L); @@ -1716,19 +1807,11 @@ int LuaUnix(lua_State *L) { lua_newtable(L); lua_setglobal(L, "__signal_handlers"); - // errnos - for (i = 0; kErrorNames[i].x; ++i) { - LuaSetIntField(L, (const char *)((uintptr_t)kErrorNames + kErrorNames[i].s), - *(const int *)((uintptr_t)kErrorNames + kErrorNames[i].x)); - } - - // signals - strcpy(sigbuf, "SIG"); - for (i = 0; kStrSignal[i].x; ++i) { - strcpy(sigbuf + 3, (const char *)((uintptr_t)kStrSignal + kStrSignal[i].s)); - LuaSetIntField(L, sigbuf, - *(const int *)((uintptr_t)kStrSignal + kStrSignal[i].x)); - } + LoadMagnums(L, kErrnoNames, ""); + LoadMagnums(L, kSignalNames, "SIG"); + LoadMagnums(L, kIpOptnames, ""); + LoadMagnums(L, kTcpOptnames, ""); + LoadMagnums(L, kSockOptnames, ""); // open() flags LuaSetIntField(L, "O_RDONLY", O_RDONLY); // @@ -1816,11 +1899,17 @@ int LuaUnix(lua_State *L) { // socket() type LuaSetIntField(L, "SOCK_STREAM", SOCK_STREAM); LuaSetIntField(L, "SOCK_DGRAM", SOCK_DGRAM); + LuaSetIntField(L, "SOCK_RAW", SOCK_RAW); + LuaSetIntField(L, "SOCK_RDM", SOCK_RDM); + LuaSetIntField(L, "SOCK_SEQPACKET", SOCK_SEQPACKET); LuaSetIntField(L, "SOCK_CLOEXEC", SOCK_CLOEXEC); // socket() protocol + LuaSetIntField(L, "IPPROTO_IP", IPPROTO_IP); + LuaSetIntField(L, "IPPROTO_ICMP", IPPROTO_ICMP); LuaSetIntField(L, "IPPROTO_TCP", IPPROTO_TCP); LuaSetIntField(L, "IPPROTO_UDP", IPPROTO_UDP); + LuaSetIntField(L, "IPPROTO_RAW", IPPROTO_RAW); // shutdown() how LuaSetIntField(L, "SHUT_RD", SHUT_RD); @@ -1885,5 +1974,11 @@ int LuaUnix(lua_State *L) { LuaSetIntField(L, "LOG_INFO", LOG_INFO); LuaSetIntField(L, "LOG_DEBUG", LOG_DEBUG); + // setsockopt() level + LuaSetIntField(L, "SOL_IP", SOL_IP); + LuaSetIntField(L, "SOL_SOCKET", SOL_SOCKET); + LuaSetIntField(L, "SOL_TCP", SOL_TCP); + LuaSetIntField(L, "SOL_UDP", SOL_UDP); + return 1; } diff --git a/tool/net/net.mk b/tool/net/net.mk index edede4163..b5d763938 100644 --- a/tool/net/net.mk +++ b/tool/net/net.mk @@ -90,7 +90,8 @@ o/$(MODE)/tool/net/%.com.dbg: \ o/$(MODE)/tool/net/redbean.com.dbg: \ $(TOOL_NET_DEPS) \ - o/$(MODE)/tool/net/redbean.o \ + o/$(MODE)/tool/net/redbean.o \ + o/$(MODE)/tool/net/lfuncs.o \ o/$(MODE)/tool/net/lre.o \ o/$(MODE)/tool/net/lunix.o \ o/$(MODE)/tool/net/lmaxmind.o \ @@ -208,7 +209,8 @@ o/$(MODE)/tool/net/demo/virtualbean.html.zip.o: \ o/$(MODE)/tool/net/redbean-demo.com.dbg: \ $(TOOL_NET_DEPS) \ - o/$(MODE)/tool/net/redbean.o \ + o/$(MODE)/tool/net/redbean.o \ + o/$(MODE)/tool/net/lfuncs.o \ o/$(MODE)/tool/net/lre.o \ o/$(MODE)/tool/net/lunix.o \ o/$(MODE)/tool/net/lmaxmind.o \ @@ -326,6 +328,7 @@ o/$(MODE)/tool/net/redbean-unsecure.com: \ o/$(MODE)/tool/net/redbean-unsecure.com.dbg: \ $(TOOL_NET_DEPS) \ o/$(MODE)/tool/net/redbean-unsecure.o \ + o/$(MODE)/tool/net/lfuncs.o \ o/$(MODE)/tool/net/lre.o \ o/$(MODE)/tool/net/lunix.o \ o/$(MODE)/tool/net/lmaxmind.o \ @@ -408,6 +411,9 @@ o/$(MODE)/tool/net/redbean-original.com.dbg: \ o/$(MODE)/tool/net/redbean-original.o: tool/net/redbean.c o/$(MODE)/tool/net/redbean.o @$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.c) -DSTATIC -DUNSECURE -DREDBEAN=\"redbean-original\" $(OUTPUT_OPTION) $< +o/$(MODE)/tool/net/redbean-original.s: tool/net/redbean.c o/$(MODE)/tool/net/redbean.o + @$(COMPILE) -AOBJECTIFY.c $(COMPILE.c) -DSTATIC -DUNSECURE -DREDBEAN=\"redbean-original\" $(OUTPUT_OPTION) $< + # REDBEAN-ASSIMILATE.COM # # Same as REDBEAN.COM except without no-modify-self behavior. diff --git a/tool/net/redbean.c b/tool/net/redbean.c index ca65139e0..33bfa4ca0 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -57,6 +57,7 @@ #include "libc/nt/enum/fileflagandattributes.h" #include "libc/nt/runtime.h" #include "libc/nt/thread.h" +#include "libc/nt/version.h" #include "libc/rand/rand.h" #include "libc/runtime/clktck.h" #include "libc/runtime/directmap.internal.h" @@ -146,6 +147,7 @@ #include "third_party/zlib/zlib.h" #include "tool/build/lib/case.h" #include "tool/build/lib/psk.h" +#include "tool/net/lfuncs.h" #include "tool/net/luacheck.h" #include "tool/net/sandbox.h" @@ -1016,8 +1018,17 @@ static bool IsServerFd(int fd) { } static void ChangeUser(void) { - if (changegid) LOGIFNEG1(setgid(changegid)); - if (changeuid) LOGIFNEG1(setuid(changeuid)); + if (changegid) { + if (setgid(changegid)) { + FATALF("setgid() failed: %m"); + } + } + // order matters + if (changeuid) { + if (setuid(changeuid)) { + FATALF("setuid() failed: %m"); + } + } } static void Daemonize(void) { @@ -1855,6 +1866,13 @@ static bool ClientAcceptsGzip(void) { HeaderHas(&msg, inbuf.p, kHttpAcceptEncoding, "gzip", 4); } +char *FormatUnixHttpDateTime(char *s, int64_t t) { + struct tm tm; + gmtime_r(&t, &tm); + FormatHttpDateTime(s, &tm); + return s; +} + static void UpdateCurrentDate(long double now) { int64_t t; struct tm tm; @@ -1879,13 +1897,6 @@ forceinline int GetMode(struct Asset *a) { return a->file ? a->file->st.st_mode : GetZipCfileMode(zbase + a->cf); } -static char *FormatUnixHttpDateTime(char *s, int64_t t) { - struct tm tm; - gmtime_r(&t, &tm); - FormatHttpDateTime(s, &tm); - return s; -} - forceinline bool IsCompressionMethodSupported(int method) { return method == kZipCompressionNone || method == kZipCompressionDeflate; } @@ -2225,37 +2236,14 @@ static void *LoadAsset(struct Asset *a, size_t *out_size) { static wontreturn void PrintUsage(int fd, int rc) { size_t n; - int pip[2]; const char *p; struct Asset *a; - char buf[PATH_MAX]; - char *args[2] = {0}; if (!(a = GetAssetZip("/help.txt", 9)) || !(p = LoadAsset(a, &n))) { fprintf(stderr, "error: /help.txt is not a zip asset\n"); exit(1); } - if (strcmp(nulltoempty(getenv("TERM")), "dumb") && isatty(0) && isatty(1) && - ((args[0] = commandv("less", buf)) || - (args[0] = commandv("more", buf)))) { - sigaction(SIGPIPE, &(struct sigaction){.sa_handler = SIG_IGN}, 0); - close(0); - pipe(pip); - if (!fork()) { - close(pip[1]); - execv(args[0], args); - _Exit(127); - } - close(0); - WritevAll(pip[1], &(struct iovec){p, n}, 1); - close(pip[1]); - wait(0); - free(p); - exit(0); - } else { - WritevAll(fd, &(struct iovec){p, n}, 1); - free(p); - exit(rc); - } + __paginate(fd, p); + exit(rc); } static void AppendLogo(void) { @@ -2714,7 +2702,8 @@ static void LaunchBrowser(const char *path) { // assign a loopback address if no server or unknown server address if (!servers.n || !addr.s_addr) addr.s_addr = htonl(INADDR_LOOPBACK); if (*path != '/') path = gc(xasprintf("/%s", path)); - if ((prog = commandv(GetSystemUrlLauncherCommand(), gc(malloc(PATH_MAX))))) { + if ((prog = commandv(GetSystemUrlLauncherCommand(), gc(malloc(PATH_MAX + 1)), + PATH_MAX + 1))) { u = gc(xasprintf("http://%s:%d%s", inet_ntoa(addr), port, gc(EscapePath(path, -1, 0)))); DEBUGF("(srvr) opening browser with command %`'s %s", prog, u); @@ -3013,23 +3002,15 @@ static char *GetLuaResponse(void) { return luaheaderp ? luaheaderp : SetStatus(200, "OK"); } -static bool IsLoopbackClient(void) { - uint32_t ip; - uint16_t port; - GetRemoteAddr(&ip, &port); - return IsLoopbackIp(ip); -} - -static bool IsPrivateClient(void) { - uint32_t ip; - uint16_t port; - GetRemoteAddr(&ip, &port); - return IsLoopbackIp(ip) || IsPrivateIp(ip); -} - static bool ShouldServeCrashReportDetails(void) { - if (leakcrashreports) return true; - return IsPrivateClient(); + uint32_t ip; + uint16_t port; + if (leakcrashreports) { + return true; + } else { + GetRemoteAddr(&ip, &port); + return IsLoopbackIp(ip) || IsPrivateIp(ip); + } } static char *LuaOnHttpRequest(void) { @@ -3161,29 +3142,32 @@ static const char *LuaCheckHost(lua_State *L, int idx, size_t *hostlen) { static void OnlyCallFromInitLua(lua_State *L, const char *api) { if (isinitialized) { - luaL_error(L, "%s() should be called from the global scope of .init.lua", - api); + luaL_error(L, "%s() should be called %s", api, + "from the global scope of .init.lua"); unreachable; } } -static void DontCallFromInitLua(lua_State *L, const char *api) { - if (!isinitialized) { - luaL_error(L, "%s() can't be called from .init.lua", api); +static void OnlyCallFromMainProcess(lua_State *L, const char *api) { + if (__isworker) { + luaL_error(L, "%s() should be called %s", api, + "from .init.lua or the repl"); unreachable; } } static void OnlyCallDuringConnection(lua_State *L, const char *api) { if (!ishandlingconnection) { - luaL_error(L, "%s() can only be called while handling a connection", api); + luaL_error(L, "%s() can only be called ", api, + "while handling a connection"); unreachable; } } static void OnlyCallDuringRequest(lua_State *L, const char *api) { if (!ishandlingrequest) { - luaL_error(L, "%s() can only be called while handling a request", api); + luaL_error(L, "%s() can only be called %s", api, + "while handling a request"); unreachable; } } @@ -3674,7 +3658,7 @@ static void LogBody(const char *d, const char *s, size_t n) { } static int LuaFetch(lua_State *L) { -#define ssl nope /* TODO(jart): make this file less huge */ +#define ssl nope // TODO(jart): make this file less huge char *p; ssize_t rc; bool usessl; @@ -4112,55 +4096,59 @@ static int LuaGetRemoteAddr(lua_State *L) { return LuaGetAddr(L, GetRemoteAddr); } -static int LuaFormatIp(lua_State *L) { - char b[16]; - uint32_t ip; - ip = htonl(luaL_checkinteger(L, 1)); - inet_ntop(AF_INET, &ip, b, sizeof(b)); - lua_pushstring(L, b); +static int LuaLog(lua_State *L) { + int level, line; + lua_Debug ar; + const char *msg, *module; + level = luaL_checkinteger(L, 1); + if (LOGGABLE(level)) { + msg = luaL_checkstring(L, 2); + if (lua_getstack(L, 1, &ar) && lua_getinfo(L, "Sl", &ar)) { + module = ar.short_src; + line = ar.currentline; + } else { + module = gc(strndup(effectivepath.p, effectivepath.n)); + line = -1; + } + flogf(level, module, line, NULL, "%s", msg); + } + return 0; +} + +static int LuaEncodeSmth(lua_State *L, + int Encoder(lua_State *, char **, int, char *)) { + int useoutput = false; + int maxdepth = 64; + char *numformat = "%.14g"; + char *p = 0; + if (lua_istable(L, 2)) { + lua_settop(L, 2); // discard any extra arguments + lua_getfield(L, 2, "useoutput"); + // ignore useoutput outside of request handling + if (ishandlingrequest && lua_isboolean(L, -1)) + useoutput = lua_toboolean(L, -1); + lua_getfield(L, 2, "maxdepth"); + maxdepth = luaL_optinteger(L, -1, maxdepth); + lua_getfield(L, 2, "numformat"); + numformat = luaL_optstring(L, -1, numformat); + } + lua_settop(L, 1); // keep the passed argument on top + Encoder(L, useoutput ? &outbuf : &p, maxdepth, numformat); + if (useoutput) { + lua_pushnil(L); + } else { + lua_pushstring(L, p); + free(p); + } return 1; } -static int LuaParseIp(lua_State *L) { - size_t n; - const char *s; - s = luaL_checklstring(L, 1, &n); - lua_pushinteger(L, ParseIp(s, n)); - return 1; +static int LuaEncodeJson(lua_State *L) { + return LuaEncodeSmth(L, LuaEncodeJsonData); } -static int LuaIsIp(lua_State *L, bool IsIp(uint32_t)) { - lua_pushboolean(L, IsIp(luaL_checkinteger(L, 1))); - return 1; -} - -static int LuaIsPublicIp(lua_State *L) { - return LuaIsIp(L, IsPublicIp); -} - -static int LuaIsPrivateIp(lua_State *L) { - return LuaIsIp(L, IsPrivateIp); -} - -static int LuaIsLoopbackIp(lua_State *L) { - return LuaIsIp(L, IsLoopbackIp); -} - -static int LuaIsLoopbackClient(lua_State *L) { - OnlyCallDuringRequest(L, "IsLoopbackClient"); - lua_pushboolean(L, IsLoopbackClient()); - return 1; -} - -static int LuaIsPrivateClient(lua_State *L) { - OnlyCallDuringRequest(L, "IsPrivateClient"); - lua_pushboolean(L, IsPrivateClient()); - return 1; -} - -static int LuaCategorizeIp(lua_State *L) { - lua_pushstring(L, GetIpCategoryName(CategorizeIp(luaL_checkinteger(L, 1)))); - return 1; +static int LuaEncodeLua(lua_State *L) { + return LuaEncodeSmth(L, LuaEncodeLuaData); } static int LuaGetUrl(lua_State *L) { @@ -4173,14 +4161,6 @@ static int LuaGetUrl(lua_State *L) { return 1; } -static void LuaPushUrlView(lua_State *L, struct UrlView *v) { - if (v->p) { - lua_pushlstring(L, v->p, v->n); - } else { - lua_pushnil(L); - } -} - static int LuaGetScheme(lua_State *L) { OnlyCallDuringRequest(L, "GetScheme"); LuaPushUrlView(L, &url.scheme); @@ -4261,21 +4241,8 @@ static int LuaGetPort(lua_State *L) { return 1; } -static int LuaFormatHttpDateTime(lua_State *L) { - char buf[30]; - lua_pushstring(L, FormatUnixHttpDateTime(buf, luaL_checkinteger(L, 1))); - return 1; -} - -static int LuaParseHttpDateTime(lua_State *L) { - size_t n; - const char *s; - s = luaL_checklstring(L, 1, &n); - lua_pushinteger(L, ParseHttpDateTime(s, n)); - return 1; -} - static int LuaGetBody(lua_State *L) { + OnlyCallDuringRequest(L, "GetBody"); lua_pushlstring(L, inbuf.p + hdrsize, payloadlength); return 1; } @@ -4526,35 +4493,6 @@ static int LuaGetParams(lua_State *L) { return 1; } -static int LuaParseParams(lua_State *L) { - void *m; - size_t size; - const char *data; - struct UrlParams h; - data = luaL_checklstring(L, 1, &size); - bzero(&h, sizeof(h)); - m = ParseParams(data, size, &h); - LuaPushUrlParams(L, &h); - free(h.p); - free(m); - return 1; -} - -static int LuaParseHost(lua_State *L) { - void *m; - size_t n; - struct Url h; - const char *p; - bzero(&h, sizeof(h)); - p = luaL_checklstring(L, 1, &n); - m = ParseHost(p, n, &h); - lua_newtable(L); - LuaPushUrlView(L, &h.host); - LuaPushUrlView(L, &h.port); - free(m); - return 1; -} - static int LuaWrite(lua_State *L) { size_t size; const char *data; @@ -4566,350 +4504,6 @@ static int LuaWrite(lua_State *L) { return 0; } -static int LuaCheckControlFlags(lua_State *L, int idx) { - int f = luaL_checkinteger(L, idx); - if (f & ~(kControlWs | kControlC0 | kControlC1)) { - luaL_argerror(L, idx, "invalid control flags"); - unreachable; - } - return f; -} - -static int LuaHasControlCodes(lua_State *L) { - int f; - size_t n; - const char *p; - p = luaL_checklstring(L, 1, &n); - f = LuaCheckControlFlags(L, 2); - lua_pushboolean(L, HasControlCodes(p, n, f) != -1); - return 1; -} - -static int LuaIsValid(lua_State *L, bool V(const char *, size_t)) { - size_t size; - const char *data; - data = luaL_checklstring(L, 1, &size); - lua_pushboolean(L, V(data, size)); - return 1; -} - -static int LuaIsValidHttpToken(lua_State *L) { - return LuaIsValid(L, IsValidHttpToken); -} - -static int LuaIsAcceptablePath(lua_State *L) { - return LuaIsValid(L, IsAcceptablePath); -} - -static int LuaIsReasonablePath(lua_State *L) { - return LuaIsValid(L, IsReasonablePath); -} - -static int LuaIsAcceptableHost(lua_State *L) { - return LuaIsValid(L, IsAcceptableHost); -} - -static int LuaIsAcceptablePort(lua_State *L) { - return LuaIsValid(L, IsAcceptablePort); -} - -static dontinline int LuaCoderImpl(lua_State *L, - char *C(const char *, size_t, size_t *)) { - void *p; - size_t n; - p = luaL_checklstring(L, 1, &n); - p = C(p, n, &n); - lua_pushlstring(L, p, n); - free(p); - return 1; -} - -static dontinline int LuaCoder(lua_State *L, - char *C(const char *, size_t, size_t *)) { - return LuaCoderImpl(L, C); -} - -static int LuaUnderlong(lua_State *L) { - return LuaCoder(L, Underlong); -} - -static int LuaEncodeBase64(lua_State *L) { - return LuaCoder(L, EncodeBase64); -} - -static int LuaDecodeBase64(lua_State *L) { - return LuaCoder(L, DecodeBase64); -} - -static int LuaDecodeLatin1(lua_State *L) { - return LuaCoder(L, DecodeLatin1); -} - -static int LuaEscapeHtml(lua_State *L) { - return LuaCoder(L, EscapeHtml); -} - -static int LuaEscapeParam(lua_State *L) { - return LuaCoder(L, EscapeParam); -} - -static int LuaEscapePath(lua_State *L) { - return LuaCoder(L, EscapePath); -} - -static int LuaEscapeHost(lua_State *L) { - return LuaCoder(L, EscapeHost); -} - -static int LuaEscapeIp(lua_State *L) { - return LuaCoder(L, EscapeIp); -} - -static int LuaEscapeUser(lua_State *L) { - return LuaCoder(L, EscapeUser); -} - -static int LuaEscapePass(lua_State *L) { - return LuaCoder(L, EscapePass); -} - -static int LuaEscapeSegment(lua_State *L) { - return LuaCoder(L, EscapeSegment); -} - -static int LuaEscapeFragment(lua_State *L) { - return LuaCoder(L, EscapeFragment); -} - -static int LuaEscapeLiteral(lua_State *L) { - return LuaCoder(L, EscapeJsStringLiteral); -} - -static int LuaVisualizeControlCodes(lua_State *L) { - return LuaCoder(L, VisualizeControlCodes); -} - -static dontinline int LuaHasherImpl(lua_State *L, size_t k, - int H(const void *, size_t, uint8_t *)) { - void *p; - size_t n; - uint8_t d[64]; - p = luaL_checklstring(L, 1, &n); - H(p, n, d); - lua_pushlstring(L, (void *)d, k); - mbedtls_platform_zeroize(d, sizeof(d)); - return 1; -} - -static dontinline int LuaHasher(lua_State *L, size_t k, - int H(const void *, size_t, uint8_t *)) { - return LuaHasherImpl(L, k, H); -} - -static int LuaMd5(lua_State *L) { - return LuaHasher(L, 16, mbedtls_md5_ret); -} - -static int LuaSha1(lua_State *L) { - return LuaHasher(L, 20, mbedtls_sha1_ret); -} - -static int LuaSha224(lua_State *L) { - return LuaHasher(L, 28, mbedtls_sha256_ret_224); -} - -static int LuaSha256(lua_State *L) { - return LuaHasher(L, 32, mbedtls_sha256_ret_256); -} - -static int LuaSha384(lua_State *L) { - return LuaHasher(L, 48, mbedtls_sha512_ret_384); -} - -static int LuaSha512(lua_State *L) { - return LuaHasher(L, 64, mbedtls_sha512_ret_512); -} - -static dontinline int LuaGetCryptoHash(lua_State *L) { - size_t hl, pl, kl; - uint8_t d[64]; - mbedtls_md_context_t ctx; - // get hash name, payload, and key - void *h = luaL_checklstring(L, 1, &hl); - void *p = luaL_checklstring(L, 2, &pl); - void *k = luaL_optlstring(L, 3, "", &kl); - - const mbedtls_md_info_t *digest = mbedtls_md_info_from_string(h); - if (!digest) return luaL_argerror(L, 1, "unknown hash type"); - - if (kl == 0) { - // no key provided, run generic hash function - if ((digest->f_md)(p, pl, d)) return luaL_error(L, "bad input data"); - } else if (mbedtls_md_hmac(digest, k, kl, p, pl, d)) - return luaL_error(L, "bad input data"); - - lua_pushlstring(L, (void *)d, digest->size); - mbedtls_platform_zeroize(d, sizeof(d)); - return 1; -} - -static int LuaGetRandomBytes(lua_State *L) { - char *p; - size_t n = luaL_optinteger(L, 1, 16); - if (!(n > 0 && n <= 256)) { - luaL_argerror(L, 1, "not in range 1..256"); - unreachable; - } - - p = malloc(n); - CHECK_EQ(n, getrandom(p, n, 0)); - lua_pushlstring(L, p, n); - free(p); - return 1; -} - -static int LuaGetHttpReason(lua_State *L) { - lua_pushstring(L, GetHttpReason(luaL_checkinteger(L, 1))); - return 1; -} - -static int LuaEncodeSmth(lua_State *L, - int Encoder(lua_State *, char **, int, char *)) { - int useoutput = false; - int maxdepth = 64; - char *numformat = "%.14g"; - char *p = 0; - if (lua_istable(L, 2)) { - lua_settop(L, 2); // discard any extra arguments - lua_getfield(L, 2, "useoutput"); - // ignore useoutput outside of request handling - if (ishandlingrequest && lua_isboolean(L, -1)) - useoutput = lua_toboolean(L, -1); - lua_getfield(L, 2, "maxdepth"); - maxdepth = luaL_optinteger(L, -1, maxdepth); - lua_getfield(L, 2, "numformat"); - numformat = luaL_optstring(L, -1, numformat); - } - lua_settop(L, 1); // keep the passed argument on top - Encoder(L, useoutput ? &outbuf : &p, maxdepth, numformat); - if (useoutput) { - lua_pushnil(L); - } else { - lua_pushstring(L, p); - free(p); - } - return 1; -} - -static int LuaEncodeJson(lua_State *L) { - return LuaEncodeSmth(L, LuaEncodeJsonData); -} - -static int LuaEncodeLua(lua_State *L) { - return LuaEncodeSmth(L, LuaEncodeLuaData); -} - -static int LuaEncodeLatin1(lua_State *L) { - int f; - char *p; - size_t n; - p = luaL_checklstring(L, 1, &n); - f = LuaCheckControlFlags(L, 2); - p = EncodeLatin1(p, n, &n, f); - lua_pushlstring(L, p, n); - free(p); - return 1; -} - -static int LuaSlurp(lua_State *L) { - char *p, *f; - size_t n; - f = luaL_checkstring(L, 1); - if ((p = xslurp(f, &n))) { - lua_pushlstring(L, p, n); - free(p); - return 1; - } else { - lua_pushnil(L); - lua_pushstring(L, gc(xasprintf("Can't slurp file %`'s: %m", f))); - return 2; - } -} - -static int LuaIndentLines(lua_State *L) { - void *p; - size_t n, j; - p = luaL_checklstring(L, 1, &n); - j = luaL_optinteger(L, 2, 1); - if (!(0 <= j && j <= 65535)) { - luaL_argerror(L, 2, "not in range 0..65535"); - unreachable; - } - p = IndentLines(p, n, &n, j); - lua_pushlstring(L, p, n); - free(p); - return 1; -} - -static int LuaGetMonospaceWidth(lua_State *L) { - int w; - if (lua_isinteger(L, 1)) { - w = wcwidth(lua_tointeger(L, 1)); - } else if (lua_isstring(L, 1)) { - w = strwidth(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0) & 7); - } else { - luaL_argerror(L, 1, "not integer or string"); - unreachable; - } - lua_pushinteger(L, w); - return 1; -} - -static int LuaPopcnt(lua_State *L) { - lua_pushinteger(L, popcnt(luaL_checkinteger(L, 1))); - return 1; -} - -static int LuaBsr(lua_State *L) { - long x; - if ((x = luaL_checkinteger(L, 1))) { - lua_pushinteger(L, bsr(x)); - return 1; - } else { - luaL_argerror(L, 1, "zero"); - unreachable; - } -} - -static int LuaBsf(lua_State *L) { - long x; - if ((x = luaL_checkinteger(L, 1))) { - lua_pushinteger(L, bsf(x)); - return 1; - } else { - luaL_argerror(L, 1, "zero"); - unreachable; - } -} - -static int LuaHash(lua_State *L, uint32_t H(uint32_t, const void *, size_t)) { - long i; - size_t n; - const char *p; - i = luaL_checkinteger(L, 1); - p = luaL_checklstring(L, 2, &n); - lua_pushinteger(L, H(i, p, n)); - return 1; -} - -static int LuaCrc32(lua_State *L) { - return LuaHash(L, crc32_z); -} - -static int LuaCrc32c(lua_State *L) { - return LuaHash(L, crc32c); -} - static dontinline int LuaProgramInt(lua_State *L, void P(long)) { P(luaL_checkinteger(L, 1)); return 0; @@ -4921,7 +4515,7 @@ static int LuaProgramPort(lua_State *L) { } static int LuaProgramCache(lua_State *L) { - OnlyCallFromInitLua(L, "ProgramCache"); + OnlyCallFromMainProcess(L, "ProgramCache"); return LuaProgramInt(L, ProgramCache); } @@ -4940,6 +4534,18 @@ static int LuaProgramGid(lua_State *L) { return LuaProgramInt(L, ProgramGid); } +static int LuaGetClientFd(lua_State *L) { + OnlyCallDuringConnection(L, "GetClientFd"); + lua_pushinteger(L, client); + return 1; +} + +static int LuaIsClientUsingSsl(lua_State *L) { + OnlyCallDuringConnection(L, "IsClientUsingSsl"); + lua_pushboolean(L, usessl); + return 1; +} + static int LuaProgramSslTicketLifetime(lua_State *L) { OnlyCallFromInitLua(L, "ProgramSslTicketLifetime"); return LuaProgramInt(L, ProgramSslTicketLifetime); @@ -4947,9 +4553,9 @@ static int LuaProgramSslTicketLifetime(lua_State *L) { static int LuaProgramUniprocess(lua_State *L) { OnlyCallFromInitLua(L, "ProgramUniprocess"); - if (!lua_isboolean(L, 1) && !lua_isnoneornil(L, 1)) + if (!lua_isboolean(L, 1) && !lua_isnoneornil(L, 1)) { return luaL_argerror(L, 1, "invalid uniprocess mode; boolean expected"); - + } lua_pushboolean(L, uniprocess); if (lua_isboolean(L, 1)) uniprocess = lua_toboolean(L, 1); return 1; @@ -4966,7 +4572,7 @@ static int LuaProgramAddr(lua_State *L) { } static int LuaProgramBrand(lua_State *L) { - OnlyCallFromInitLua(L, "ProgramBrand"); + OnlyCallFromMainProcess(L, "ProgramBrand"); return LuaProgramString(L, ProgramBrand); } @@ -4988,7 +4594,7 @@ static int LuaProgramSslPresharedKey(lua_State *L) { struct Psk psk; size_t n1, n2, i; const char *p1, *p2; - OnlyCallFromInitLua(L, "ProgramSslPresharedKey"); + OnlyCallFromMainProcess(L, "ProgramSslPresharedKey"); p1 = luaL_checklstring(L, 1, &n1); p2 = luaL_checklstring(L, 2, &n2); if (!n1 || n1 > MBEDTLS_PSK_MAX_LEN || !n2) { @@ -5016,6 +4622,7 @@ static int LuaProgramSslPresharedKey(lua_State *L) { static int LuaProgramSslCiphersuite(lua_State *L) { mbedtls_ssl_ciphersuite_t *suite; + OnlyCallFromInitLua(L, "ProgramSslCiphersuite"); if (!(suite = GetCipherSuite(luaL_checkstring(L, 1)))) { luaL_argerror(L, 1, "unsupported or unknown ciphersuite"); unreachable; @@ -5071,11 +4678,12 @@ static int LuaProgramSslClientVerify(lua_State *L) { } static int LuaProgramSslFetchVerify(lua_State *L) { - OnlyCallFromInitLua(L, "ProgramSslFetchVerify"); + OnlyCallFromMainProcess(L, "ProgramSslFetchVerify"); return LuaProgramBool(L, &sslfetchverify); } static int LuaProgramSslInit(lua_State *L) { + OnlyCallFromInitLua(L, "SslInit"); TlsInit(); return 0; } @@ -5089,7 +4697,6 @@ static int LuaProgramLogBodies(lua_State *L) { } static int LuaEvadeDragnetSurveillance(lua_State *L) { - OnlyCallFromInitLua(L, "EvadeDragnetSurveillance"); return LuaProgramBool(L, &evadedragnetsurveillance); } @@ -5101,16 +4708,6 @@ static int LuaProgramSslCompression(lua_State *L) { return 0; } -static int LuaGetLogLevel(lua_State *L) { - lua_pushinteger(L, __log_level); - return 1; -} - -static int LuaSetLogLevel(lua_State *L) { - __log_level = luaL_checkinteger(L, 1); - return 0; -} - static int LuaHidePath(lua_State *L) { size_t pathlen; const char *path; @@ -5119,35 +4716,6 @@ static int LuaHidePath(lua_State *L) { return 0; } -static int LuaLog(lua_State *L) { - int level, line; - lua_Debug ar; - const char *msg, *module; - level = luaL_checkinteger(L, 1); - if (LOGGABLE(level)) { - msg = luaL_checkstring(L, 2); - if (lua_getstack(L, 1, &ar) && lua_getinfo(L, "Sl", &ar)) { - module = ar.short_src; - line = ar.currentline; - } else { - module = gc(strndup(effectivepath.p, effectivepath.n)); - line = -1; - } - flogf(level, module, line, NULL, "%s", msg); - } - return 0; -} - -static int LuaSleep(lua_State *L) { - usleep(1e6 * luaL_checknumber(L, 1)); - return 0; -} - -static int LuaGetTime(lua_State *L) { - lua_pushnumber(L, nowl()); - return 1; -} - static int LuaIsHiddenPath(lua_State *L) { size_t n; const char *s; @@ -5156,21 +4724,6 @@ static int LuaIsHiddenPath(lua_State *L) { return 1; } -static int LuaIsHeaderRepeatable(lua_State *L) { - int h; - bool r; - size_t n; - const char *s; - s = luaL_checklstring(L, 1, &n); - if ((h = GetHttpHeader(s, n)) != -1) { - r = kHttpRepeatable[h]; - } else { - r = false; - } - lua_pushboolean(L, r); - return 1; -} - static int LuaGetZipPaths(lua_State *L) { char *path; uint8_t *zcf; @@ -5276,31 +4829,6 @@ static int LuaGetAssetComment(lua_State *L) { return 1; } -static int LuaGetHostOs(lua_State *L) { - const char *s = NULL; - if (IsLinux()) { - s = "LINUX"; - } else if (IsMetal()) { - s = "METAL"; - } else if (IsWindows()) { - s = "WINDOWS"; - } else if (IsXnu()) { - s = "XNU"; - } else if (IsOpenbsd()) { - s = "OPENBSD"; - } else if (IsFreebsd()) { - s = "FREEBSD"; - } else if (IsNetbsd()) { - s = "NETBSD"; - } - if (s) { - lua_pushstring(L, s); - } else { - lua_pushnil(L); - } - return 1; -} - static int LuaLaunchBrowser(lua_State *L) { OnlyCallFromInitLua(L, "LaunchBrowser"); launchbrowser = strdup(luaL_optstring(L, 1, "/")); @@ -5331,75 +4859,65 @@ static bool LuaRunAsset(const char *path, bool mandatory) { return !!a; } -static int LuaRdtsc(lua_State *L) { - lua_pushinteger(L, rdtsc()); - return 1; -} - -static int LuaGetCpuNode(lua_State *L) { - lua_pushinteger(L, TSC_AUX_NODE(rdpid())); - return 1; -} - -static int LuaGetCpuCore(lua_State *L) { - lua_pushinteger(L, TSC_AUX_CORE(rdpid())); - return 1; -} - -static int LuaRand(lua_State *L, uint64_t impl(void)) { - lua_pushinteger(L, impl()); - return 1; -} - -static int LuaLemur64(lua_State *L) { - return LuaRand(L, lemur64); -} - -static int LuaRand64(lua_State *L) { - return LuaRand(L, rand64); -} - -static int LuaRdrand(lua_State *L) { - return LuaRand(L, rdrand); -} - -static int LuaRdseed(lua_State *L) { - return LuaRand(L, rdseed); -} - -static int LuaDecimate(lua_State *L) { - size_t n, m; - const char *s; - unsigned char *p; - s = luaL_checklstring(L, 1, &n); - m = ROUNDUP(n, 16); - p = xmalloc(m); - bzero(p + n, m - n); - cDecimate2xUint8x8(m, p, (signed char[8]){-1, -3, 3, 17, 17, 3, -3, -1}); - lua_pushlstring(L, (char *)p, (n + 1) >> 1); - free(p); - return 1; -} - -static int LuaMeasureEntropy(lua_State *L) { - size_t n; - const char *s; - s = luaL_checklstring(L, 1, &n); - lua_pushnumber(L, MeasureEntropy(s, n)); - return 1; -} - -static int LuaGetClientFd(lua_State *L) { - OnlyCallDuringConnection(L, "GetClientFd"); - lua_pushinteger(L, client); - return 1; -} - -static int LuaIsClientUsingSsl(lua_State *L) { - OnlyCallDuringConnection(L, "IsClientUsingSsl"); - lua_pushboolean(L, usessl); - return 1; -} +// +// list of functions that can't be run from the repl +static const char *const kDontAutoComplete[] = { + "GetBody", // + "GetClientAddr", // + "GetClientFd", // + "GetCookie", // + "GetEffectivePath", // + "GetFragment", // + "GetHeader", // + "GetHeaders", // + "GetHost", // + "GetHttpVersion", // + "GetMethod", // + "GetParam", // + "GetParams", // + "GetPass", // + "GetPath", // + "GetPort", // + "GetRemoteAddr", // + "GetScheme", // + "GetServerAddr", // + "GetSslIdentity", // + "GetStatus", // + "GetUrl", // + "GetUser", // + "HasParam", // + "IsClientUsingSsl", // + "LaunchBrowser", // + "ProgramAddr", // TODO + "ProgramBrand", // + "ProgramCertificate", // TODO + "ProgramGid", // + "ProgramLogPath", // TODO + "ProgramPidPath", // TODO + "ProgramPort", // TODO + "ProgramPrivateKey", // TODO + "ProgramSslCiphersuite", // TODO + "ProgramSslClientVerify", // TODO + "ProgramSslCompression", // + "ProgramSslTicketLifetime", // + "ProgramTimeout", // TODO + "ProgramUid", // + "ProgramUniprocess", // + "Respond", // + "Route", // + "RouteHost", // + "RoutePath", // + "ServeAsset", // + "ServeIndex", // + "ServeListing", // + "ServeRedirect", // + "ServeStatusz", // + "SetCookie", // + "SetHeader", // + "SslInit", // TODO + "Write", // +}; +// static const luaL_Reg kLuaFuncs[] = { {"Bsf", LuaBsf}, // @@ -5480,9 +4998,7 @@ static const luaL_Reg kLuaFuncs[] = { {"IsDaemon", LuaIsDaemon}, // {"IsHeaderRepeatable", LuaIsHeaderRepeatable}, // {"IsHiddenPath", LuaIsHiddenPath}, // - {"IsLoopbackClient", LuaIsLoopbackClient}, // {"IsLoopbackIp", LuaIsLoopbackIp}, // - {"IsPrivateClient", LuaIsPrivateClient}, // {"IsPrivateIp", LuaIsPrivateIp}, // {"IsPublicIp", LuaIsPublicIp}, // {"IsReasonablePath", LuaIsReasonablePath}, // @@ -5558,12 +5074,6 @@ static const luaL_Reg kLuaFuncs[] = { #endif }; -int LuaMaxmind(lua_State *); -int LuaRe(lua_State *); -int LuaUnix(lua_State *); -int luaopen_argon2(lua_State *); -int luaopen_lsqlite3(lua_State *); - static const luaL_Reg kLuaLibs[] = { {"re", LuaRe}, // {"unix", LuaUnix}, // @@ -6845,9 +6355,9 @@ static void RestoreApe(void) { struct Asset *a; extern char ape_rom_vaddr[] __attribute__((__weak__)); if (!(SUPPORT_VECTOR & (METAL | WINDOWS | XNU))) return; - if (IsWindows()) return; /* TODO */ - if (IsOpenbsd()) return; /* TODO */ - if (IsNetbsd()) return; /* TODO */ + if (IsWindows()) return; // TODO + if (IsOpenbsd()) return; // TODO + if (IsNetbsd()) return; // TODO if (endswith(zpath, ".com.dbg")) return; if ((a = GetAssetZip("/.ape", 5)) && (p = LoadAsset(a, &n))) { close(zfd); @@ -6859,6 +6369,34 @@ static void RestoreApe(void) { } } +static bool ShouldAutocomplete(const char *s) { + int c, m, l, r; + l = 0; + r = ARRAYLEN(kDontAutoComplete) - 1; + while (l <= r) { + m = (l + r) >> 1; + c = strcmp(kDontAutoComplete[m], s); + if (c < 0) { + l = m + 1; + } else if (c > 0) { + r = m - 1; + } else { + return false; + } + } + return true; +} + +static void HandleCompletions(const char *p, linenoiseCompletions *c) { + size_t i, j; + for (j = i = 0; i < c->len; ++i) { + if (ShouldAutocomplete(c->cvec[i])) { + c->cvec[j++] = c->cvec[i]; + } + } + c->len = j; +} + static int HandleReadline(void) { int status; for (;;) { @@ -6871,9 +6409,8 @@ static int HandleReadline(void) { return -1; } else if (errno == EINTR) { errno = 0; - OnInt(SIGINT); INFOF("got repl interrupt"); - return 0; + return -1; } else if (errno == EAGAIN) { errno = 0; return 0; @@ -6884,7 +6421,7 @@ static int HandleReadline(void) { } write(1, "\r\n", 2); linenoiseDisableRawMode(); - _spinlock(&lualock); + LUA_REPL_LOCK; if (status == LUA_OK) { status = lua_runchunk(GL, 0, LUA_MULTRET); } @@ -6893,7 +6430,7 @@ static int HandleReadline(void) { } else { lua_report(GL, status); } - _spunlock(&lualock); + LUA_REPL_UNLOCK; if (lua_repl_isterminal) { linenoiseEnableRawMode(0); } @@ -6911,21 +6448,24 @@ static int HandlePoll(int ms) { if (polls[pollid].fd < 0) continue; if (polls[pollid].fd) { // handle listen socket + LUA_REPL_LOCK; serverid = pollid - 1; assert(0 <= serverid && serverid < servers.n); serveraddr = &servers.p[serverid].addr; ishandlingconnection = true; - _spinlock(&lualock); rc = HandleConnection(serverid); - _spunlock(&lualock); ishandlingconnection = false; + LUA_REPL_UNLOCK; if (rc == -1) return -1; +#ifndef STATIC } else { // handle standard input rc = HandleReadline(); if (rc == -1) return rc; +#endif } } +#ifndef STATIC } else if (__replmode) { // handle refresh repl line if (!IsWindows()) { @@ -6934,6 +6474,7 @@ static int HandlePoll(int ms) { } else { linenoiseRefreshLine(lua_repl_linenoise); } +#endif } } else { if (errno == EINTR || errno == EAGAIN) { @@ -7029,16 +6570,22 @@ static void HandleShutdown(void) { // this function coroutines with linenoise static int EventLoop(int ms) { long double t; - VERBOSEF("EventLoop()"); + DEBUGF("EventLoop()"); while (!terminated) { errno = 0; if (zombied) { + LUA_REPL_LOCK; ReapZombies(); + LUA_REPL_UNLOCK; } else if (invalidated) { + LUA_REPL_LOCK; HandleReload(); + LUA_REPL_UNLOCK; invalidated = false; } else if (meltdown) { + LUA_REPL_LOCK; EnterMeltdownMode(); + LUA_REPL_UNLOCK; meltdown = false; } else if ((t = nowl()) - lastheartbeat > HEARTBEAT / 1000.) { lastheartbeat = t; @@ -7053,6 +6600,7 @@ static int EventLoop(int ms) { static void ReplEventLoop(void) { DEBUGF("ReplEventLoop()"); polls[0].fd = 0; + lua_repl_completions_callback = HandleCompletions; lua_initrepl(GL, "redbean"); if (lua_repl_isterminal) { linenoiseEnableRawMode(0); @@ -7065,8 +6613,10 @@ static void ReplEventLoop(void) { } static uint32_t WindowsReplThread(void *arg) { + int sig; DEBUGF("WindowsReplThread()"); lua_repl_blocking = true; + lua_repl_completions_callback = HandleCompletions; lua_initrepl(GL, "redbean"); if (lua_repl_isterminal) { linenoiseEnableRawMode(0); @@ -7078,9 +6628,13 @@ static uint32_t WindowsReplThread(void *arg) { } linenoiseDisableRawMode(); lua_freerepl(); - _spinlock(&lualock); + LUA_REPL_LOCK; lua_settop(GL, 0); // clear stack - _spunlock(&lualock); + LUA_REPL_UNLOCK; + if ((sig = linenoiseGetInterrupt())) { + raise(sig); + } + DEBUGF("WindowsReplThread() exiting"); return 0; } @@ -7296,7 +6850,7 @@ void RedBean(int argc, char *argv[]) { #else GetHostsTxt(); // for effect GetResolvConf(); // for effect - if (daemonize || !linenoiseIsTerminal()) { + if (daemonize || uniprocess || !linenoiseIsTerminal()) { EventLoop(HEARTBEAT); } else if (IsWindows()) { uint32_t tid; diff --git a/tool/plinko/lib/gc.c b/tool/plinko/lib/gc.c index 0b270d740..6e12485d8 100644 --- a/tool/plinko/lib/gc.c +++ b/tool/plinko/lib/gc.c @@ -50,7 +50,7 @@ struct Gc *NewGc(int A) { if (B < cHeap) cHeap = B; n = ROUNDUP(A - B, DWBITS) / DWBITS; G = Addr(BANE); - memset(G->M, 0, n * sizeof(G->M[0])); + bzero(G->M, n * sizeof(G->M[0])); G->n = n; G->A = A; G->B = B; diff --git a/tool/viz/bin2asm.c b/tool/viz/bin2asm.c index 7af7011db..4e2e19457 100644 --- a/tool/viz/bin2asm.c +++ b/tool/viz/bin2asm.c @@ -28,7 +28,7 @@ int main(int argc, char *argv[]) { while ((c = getchar()) != -1) { if (col == 0) { printf("\t.byte\t"); - memset(glyphs, 0, sizeof(glyphs)); + bzero(glyphs, sizeof(glyphs)); } ch = c & 0xff; glyphs[col] = kCp437[ch]; diff --git a/tool/viz/derasterize.c b/tool/viz/derasterize.c index 45f5115ae..ed08c6fe4 100644 --- a/tool/viz/derasterize.c +++ b/tool/viz/derasterize.c @@ -248,7 +248,7 @@ static unsigned combinecolors(unsigned char bf[1u << MC][2], const unsigned char bl[CN][YS * XS]) { uint64_t hv, ht[(1u << MC) * 2]; unsigned i, j, n, b, f, h, hi, bu, fu; - memset(ht, 0, sizeof(ht)); + bzero(ht, sizeof(ht)); for (n = b = 0; b < BN && n < (1u << MC); ++b) { bu = bl[2][b] << 020 | bl[1][b] << 010 | bl[0][b]; hi = 0; @@ -295,7 +295,7 @@ static unsigned combinecolors(unsigned char bf[1u << MC][2], const float lb[CN][YS * XS]) { \ unsigned i, k, gu; \ float p[BN], q[BN], fu, bu, r; \ - memset(q, 0, sizeof(q)); \ + bzero(q, sizeof(q)); \ for (k = 0; k < CN; ++k) { \ gu = kGlyphs[g]; \ bu = lb[k][b]; \ @@ -336,7 +336,7 @@ static float adjudicate(unsigned b, unsigned f, unsigned g, const float lb[CN][YS * XS]) { unsigned i, k, gu; float p[BN], q[BN], fu, bu, r; - memset(q, 0, sizeof(q)); + bzero(q, sizeof(q)); for (k = 0; k < CN; ++k) { gu = kGlyphs[g]; bu = lb[k][b]; @@ -487,8 +487,8 @@ static void LoadFileViaImageMagick(const char *path, unsigned yn, unsigned xn, unsigned char rgb[yn][YS][xn][XS][CN]) { const char *convert; int pid, ws, pipefds[2]; - char pathbuf[PATH_MAX], dim[32]; - if (!(convert = commandv("convert", pathbuf))) { + char pathbuf[PATH_MAX + 1], dim[32]; + if (!(convert = commandv("convert", pathbuf, sizeof(pathbuf)))) { fputs("error: `convert` command not found\n" "try: apt-get install imagemagick\n", stderr); diff --git a/tool/viz/lib/bilinearscale.c b/tool/viz/lib/bilinearscale.c index 7836787cc..edfa5b077 100644 --- a/tool/viz/lib/bilinearscale.c +++ b/tool/viz/lib/bilinearscale.c @@ -93,7 +93,7 @@ void *BilinearScale(long dcw, long dyw, long dxw, gc(xmemalign(64, ROUNDUP(dxn, 64))), gc(xmemalign(64, ROUNDUP(sxn, 64) * 2))); } else { - memset(dst[c0], 0, &dst[cn][0][0] - &dst[c0][0][0]); + bzero(dst[c0], &dst[cn][0][0] - &dst[c0][0][0]); } } return dst; diff --git a/tool/viz/lib/convolve.h b/tool/viz/lib/convolve.h index 66ca31813..892af17f3 100644 --- a/tool/viz/lib/convolve.h +++ b/tool/viz/lib/convolve.h @@ -19,12 +19,12 @@ forceinline void convolve(unsigned yn, unsigned xn, __m128 img[yn][xn], int KW, kflip[KW - i - 1][KW - j - 1] = (__v4sf){f, f, f, f}; } } - memset(&g, 0, sizeof(g)); + bzero(&g, sizeof(g)); resizegraphic(&g, yn, xn); tmp = g.b.p; for (y = 0; y < yn - KW; ++y) { for (x = 0; x < xn - KW; ++x) { - memset(&p, 0, sizeof(p)); + bzero(&p, sizeof(p)); for (i = 0; i < KW; ++i) { for (j = 0; j < KW; ++j) { p += img[y + i][x + j] * kflip[i][j] + C2; diff --git a/tool/viz/lib/unsharp.c b/tool/viz/lib/unsharp.c index 56cfbe3ae..db1932c91 100644 --- a/tool/viz/lib/unsharp.c +++ b/tool/viz/lib/unsharp.c @@ -45,7 +45,7 @@ long unsharp(long cn, long yw, long xw, unsigned char img[cn][yw][xw], long yn, for (x = 0; x < xn; ++x) { img[c][y - 3][x] = MIN(255, MAX(0, (*t)[y % 3][x])); } - memset((*t)[y % 3], 0, sizeof(short) * xn); + bzero((*t)[y % 3], sizeof(short) * xn); } if (y < yn) { for (x = 0; x < xn; ++x) { diff --git a/tool/viz/lib/ycbcr2rgb3.c b/tool/viz/lib/ycbcr2rgb3.c index 806d8824b..9d1df4c04 100644 --- a/tool/viz/lib/ycbcr2rgb3.c +++ b/tool/viz/lib/ycbcr2rgb3.c @@ -155,8 +155,8 @@ void YCbCrInit(struct YCbCr **ycbcr, bool yonly, int swing, double gamma, const double gamut[3], const double illuminant[3]) { if (!*ycbcr) *ycbcr = xcalloc(1, sizeof(struct YCbCr)); (*ycbcr)->yonly = yonly; - memset((*ycbcr)->magnums, 0, sizeof((*ycbcr)->magnums)); - memset((*ycbcr)->lighting, 0, sizeof((*ycbcr)->lighting)); + bzero((*ycbcr)->magnums, sizeof((*ycbcr)->magnums)); + bzero((*ycbcr)->lighting, sizeof((*ycbcr)->lighting)); YCbCrComputeCoefficients(swing, gamma, gamut, illuminant, (*ycbcr)->magnums, (*ycbcr)->lighting, (*ycbcr)->transfer[0]); imapxlatab((*ycbcr)->transfer[1]); diff --git a/tool/viz/life.c b/tool/viz/life.c index a648022b3..0b984ab13 100644 --- a/tool/viz/life.c +++ b/tool/viz/life.c @@ -612,7 +612,7 @@ static int LoadFile(const char *path) { } if (yn > byn || xn > bxn) goto ReadError; xchg(&board, &board2); - memset(board, 0, (byn * bxn) >> 3); + bzero(board, (byn * bxn) >> 3); yo = byn / 2 - yn / 2; xo = bxn / 2 - xn / 2; y = 0; @@ -857,7 +857,7 @@ static void Rando2(void) { static void ReadKeyboard(void) { char buf[32], *p = buf; - memset(buf, 0, sizeof(buf)); + bzero(buf, sizeof(buf)); if (readansi(0, buf, sizeof(buf)) == -1) { if (errno == EINTR) return; exit(errno); @@ -895,7 +895,7 @@ static void ReadKeyboard(void) { } break; case 'R': - memset(board, 0, (byn * bxn) >> 3); + bzero(board, (byn * bxn) >> 3); break; case CTRL('T'): OnTurbo(); @@ -1166,7 +1166,7 @@ static void OnMenuOpen(int64_t hwnd) { char buf8[PATH_MAX]; char16_t buf16[PATH_MAX]; struct NtOpenFilename ofn; - memset(&ofn, 0, sizeof(ofn)); + bzero(&ofn, sizeof(ofn)); ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = hwnd; ofn.lpstrFile = buf16; @@ -1370,7 +1370,7 @@ static void Gui(void) { int64_t hwnd, mh; struct NtMsg msg; struct NtWndClass wc; - memset(&wc, 0, sizeof(wc)); + bzero(&wc, sizeof(wc)); wc.lpfnWndProc = NT2SYSV(WindowProc); wc.hInstance = GetModuleHandle(NULL); wc.hCursor = LoadCursor(0, kNtIdcCross); diff --git a/tool/viz/magikarp.c b/tool/viz/magikarp.c index 3cba134aa..920490d10 100644 --- a/tool/viz/magikarp.c +++ b/tool/viz/magikarp.c @@ -192,7 +192,7 @@ static void *MagikarpY(CHAR w, unsigned char p[1u << w][1u << w], char yw, long y, x, yn, xn, ym; unsigned char(*t)[(1u << w) + 2][1u << w]; t = memalign(64, ((1u << w) + 2) * (1u << w)); - memset(t, 0, ((1u << w) + 2) * (1u << w)); + bzero(t, ((1u << w) + 2) * (1u << w)); yn = 1u << yw; xn = 1u << xw; ym = yn >> 1; diff --git a/tool/viz/memzoom.c b/tool/viz/memzoom.c index 4f16e59d9..8793f8776 100644 --- a/tool/viz/memzoom.c +++ b/tool/viz/memzoom.c @@ -496,7 +496,7 @@ static void OnMouse(char *p) { static void ReadKeyboard(void) { char buf[32], *p = buf; - memset(buf, 0, sizeof(buf)); + bzero(buf, sizeof(buf)); if (readansi(0, buf, sizeof(buf)) == -1) { if (errno == EINTR) return; exit(errno); @@ -797,7 +797,7 @@ static void Zoom(long have) { n >>= 1; } if (n < tyn * txn) { - memset(canvas + n, 0, canvassize - n); + bzero(canvas + n, canvassize - n); } if (have != -1) { n = have >> zoom; @@ -817,7 +817,7 @@ static void FileZoom(void) { have = MIN(displaysize, size - offset); have = pread(fd, canvas, have, offset); have = MAX(0, have); - memset(canvas + have, 0, canvassize - have); + bzero(canvas + have, canvassize - have); Zoom(have); Render(); } diff --git a/tool/viz/printdos2errno.c b/tool/viz/printdos2errno.c index 498c89862..87f88fccf 100644 --- a/tool/viz/printdos2errno.c +++ b/tool/viz/printdos2errno.c @@ -26,11 +26,11 @@ int main(int argc, char *argv[]) { int i; for (i = 0; kDos2Errno[i].doscode; ++i) { - kprintf("dos error %10hu maps to rva %10d errno %10d which is %s%n", - kDos2Errno[i].doscode, kDos2Errno[i].systemv, - *(const int *)((intptr_t)kDos2Errno + kDos2Errno[i].systemv), - strerror_short( - *(const int *)((intptr_t)kDos2Errno + kDos2Errno[i].systemv))); + kprintf( + "dos error %10hu maps to rva %10d errno %10d which is %s%n", + kDos2Errno[i].doscode, kDos2Errno[i].systemv, + *(const int *)((intptr_t)kDos2Errno + kDos2Errno[i].systemv), + strerrno(*(const int *)((intptr_t)kDos2Errno + kDos2Errno[i].systemv))); } return 0; } From 451e3f73d9030e6efc86e5fd785b83866f5012a3 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Mon, 25 Apr 2022 08:30:14 -0700 Subject: [PATCH 02/18] Improve redbean - Improve serialization - Add Benchmark() API to redbean - Refactor UNIX API to be assert() friendly - Make the redbean Lua REPL print data structures - Fix recent regressions in linenoise reverse search - Add -i flag so redbean can be a language interpreter --- libc/calls/calls.h | 10 +- libc/calls/calls.mk | 1 + libc/calls/describeclockname.c | 38 + libc/calls/describeopenflags.greg.c | 41 + libc/calls/fstatat-sysv.c | 1 + libc/calls/getegid.c | 2 +- libc/calls/geteuid.c | 2 +- libc/calls/getuid.c | 4 +- libc/calls/kclocknames.S | 47 + libc/calls/kopenflags.S | 64 ++ libc/calls/openat.c | 6 +- libc/calls/umask.c | 4 +- libc/fmt/kerrnodocs.S | 3 +- libc/fmt/kerrnonames.S | 5 +- libc/fmt/magnumstrs.internal.h | 31 +- libc/fmt/strerdoc.greg.c | 2 +- libc/fmt/strerrno.greg.c | 2 +- libc/intrin/describemapflags.greg.c | 2 +- .../describentconsolemodeinputflags.greg.c | 2 +- .../describentconsolemodeoutputflags.greg.c | 2 +- libc/intrin/describentfileaccessflags.greg.c | 2 +- .../describentfileflagsandattributes.greg.c | 2 +- libc/intrin/describentfilemapflags.greg.c | 2 +- libc/intrin/describentfileshareflags.greg.c | 2 +- libc/intrin/describentfiletypeflags.greg.c | 2 +- libc/intrin/describentmovefileflags.greg.c | 2 +- libc/intrin/describentpageflags.greg.c | 2 +- libc/intrin/describentpipemodeflags.greg.c | 2 +- libc/intrin/describentpipeopenflags.greg.c | 2 +- .../describentprocessaccessflags.greg.c | 2 +- libc/intrin/describentstartflags.greg.c | 2 +- .../intrin/describentsymboliclinkflags.greg.c | 2 +- libc/intrin/describeprotflags.greg.c | 2 +- libc/intrin/describeremapflags.greg.c | 2 +- libc/intrin/getmagnumstr.greg.c | 8 +- libc/log/leaks.c | 21 +- libc/nt/struct/linger.h | 13 + libc/sock/describesocklevel.greg.c | 2 +- libc/sock/describesockoptname.greg.c | 12 +- libc/sock/getsockopt-nt.c | 8 + libc/sock/kipoptnames.S | 17 +- libc/sock/ksockoptnames.S | 37 +- libc/sock/ktcpoptnames.S | 35 +- libc/sock/setsockopt-nt.c | 6 +- libc/stdio/system.c | 1 + libc/str/ksignalnames.S | 3 +- libc/str/str.h | 4 +- libc/sysv/consts.sh | 16 +- libc/sysv/consts/CLOCK_BOOTTIME.S | 2 +- libc/sysv/consts/SO_DONTLINGER.S | 2 + libc/sysv/consts/SO_REUSEADDR.S | 2 +- libc/testlib/ezbench.h | 292 +++--- libc/testlib/polluteregisters.S | 20 +- test/libc/calls/stat_test.c | 21 +- test/libc/runtime/mmap_test.c | 58 ++ third_party/linenoise/linenoise.c | 22 +- third_party/lua/cosmo.h | 4 +- third_party/lua/escapeluastring.c | 13 +- third_party/lua/lrepl.c | 57 +- third_party/lua/lua.mk | 10 + third_party/lua/luaencodejsondata.c | 158 ++-- third_party/lua/luaencodeluadata.c | 118 ++- third_party/lua/luaformatstack.c | 22 +- tool/build/blinkenlights.c | 3 +- tool/build/build.mk | 1 + tool/net/demo/unix-info.lua | 8 + tool/net/demo/unix-subprocess.lua | 6 +- tool/net/help.txt | 400 ++++---- tool/net/largon2.c | 8 +- tool/net/lfuncs.c | 54 +- tool/net/lfuncs.h | 1 + tool/net/lsqlite3.c | 2 +- tool/net/lunix.c | 877 ++++++++++-------- tool/net/redbean.c | 164 +++- 74 files changed, 1781 insertions(+), 1024 deletions(-) create mode 100644 libc/calls/describeclockname.c create mode 100644 libc/calls/describeopenflags.greg.c create mode 100644 libc/calls/kclocknames.S create mode 100644 libc/calls/kopenflags.S create mode 100644 libc/nt/struct/linger.h create mode 100644 libc/sysv/consts/SO_DONTLINGER.S diff --git a/libc/calls/calls.h b/libc/calls/calls.h index 9de7af625..2d99b4136 100644 --- a/libc/calls/calls.h +++ b/libc/calls/calls.h @@ -119,6 +119,9 @@ int fsync(int); int ftruncate(int, int64_t); int getdents(unsigned, void *, unsigned, long *); int getdomainname(char *, size_t); +int getegid(void) nosideeffect; +int geteuid(void) nosideeffect; +int getgid(void) nosideeffect; int gethostname(char *, size_t); int getloadavg(double *, int); int getpgid(int); @@ -130,6 +133,7 @@ int getrlimit(int, struct rlimit *); int getrusage(int, struct rusage *); int getsid(int) nosideeffect; int gettid(void); +int getuid(void) nosideeffect; int kill(int, int); int killpg(int, int); int link(const char *, const char *) dontthrow; @@ -196,6 +200,7 @@ int sysinfo(struct sysinfo *); int touch(const char *, uint32_t); int truncate(const char *, uint64_t); int ttyname_r(int, char *, size_t); +int umask(int); int uname(struct utsname *); int unlink(const char *); int unlink_s(const char **); @@ -226,11 +231,6 @@ ssize_t splice(int, int64_t *, int, int64_t *, size_t, uint32_t); ssize_t vmsplice(int, const struct iovec *, int64_t, uint32_t); ssize_t write(int, const void *, size_t); struct dirent *readdir(DIR *); -uint32_t getegid(void) nosideeffect; -uint32_t geteuid(void) nosideeffect; -uint32_t getgid(void) nosideeffect; -uint32_t getuid(void) nosideeffect; -uint32_t umask(uint32_t); void rewinddir(DIR *); void sync(void); diff --git a/libc/calls/calls.mk b/libc/calls/calls.mk index dfa690101..28e882b67 100644 --- a/libc/calls/calls.mk +++ b/libc/calls/calls.mk @@ -111,6 +111,7 @@ o/$(MODE)/libc/calls/execlp.o \ o/$(MODE)/libc/calls/execve-nt.o \ o/$(MODE)/libc/calls/execve-sysv.o \ o/$(MODE)/libc/calls/readlinkat-nt.o \ +o/$(MODE)/libc/calls/describeopenflags.greg.o \ o/$(MODE)/libc/calls/mkntenvblock.o: \ OVERRIDE_CPPFLAGS += \ -DSTACK_FRAME_UNLIMITED diff --git a/libc/calls/describeclockname.c b/libc/calls/describeclockname.c new file mode 100644 index 000000000..5a32ff0cc --- /dev/null +++ b/libc/calls/describeclockname.c @@ -0,0 +1,38 @@ +/*-*- 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 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/itoa.h" +#include "libc/fmt/magnumstrs.internal.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/sol.h" + +/** + * Describes clock_gettime() clock argument. + */ +char *DescribeClockName(int x) { + int i; + char *s; + _Alignas(char) static char buf[32]; + if ((s = GetMagnumStr(kClockNames, x))) { + stpcpy(stpcpy(buf, "CLOCK_"), s); + return buf; + } else { + FormatInt32(buf, x); + return buf; + } +} diff --git a/libc/calls/describeopenflags.greg.c b/libc/calls/describeopenflags.greg.c new file mode 100644 index 000000000..57ab7a46a --- /dev/null +++ b/libc/calls/describeopenflags.greg.c @@ -0,0 +1,41 @@ +/*-*- 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 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/itoa.h" +#include "libc/fmt/magnumstrs.internal.h" +#include "libc/intrin/describeflags.internal.h" +#include "libc/mem/alloca.h" +#include "libc/sysv/consts/sol.h" + +/** + * Describes clock_gettime() clock argument. + */ +char *DescribeOpenFlags(int x) { + char *s; + int i, n; + struct DescribeFlags *d; + _Alignas(char) static char openflags[128]; + // TODO(jart): unify DescribeFlags and MagnumStr data structures + for (n = 0; kOpenFlags[n].x != MAGNUM_TERMINATOR;) ++n; + d = alloca(n * sizeof(struct DescribeFlags)); + for (i = 0; i < n; ++i) { + d[i].flag = MAGNUM_NUMBER(kOpenFlags, i); + d[i].name = MAGNUM_STRING(kOpenFlags, i); + } + return DescribeFlags(openflags, sizeof(openflags), d, n, "O_", x); +} diff --git a/libc/calls/fstatat-sysv.c b/libc/calls/fstatat-sysv.c index 594ef7df5..4c64f3986 100644 --- a/libc/calls/fstatat-sysv.c +++ b/libc/calls/fstatat-sysv.c @@ -28,6 +28,7 @@ */ int32_t sys_fstatat(int32_t dirfd, const char *path, struct stat *st, int32_t flags) { + int rc; void *p; union metastat ms; if (IsAsan() && !__asan_is_valid(path, 1)) return efault(); diff --git a/libc/calls/getegid.c b/libc/calls/getegid.c index 929baeb10..e142d34f1 100644 --- a/libc/calls/getegid.c +++ b/libc/calls/getegid.c @@ -25,7 +25,7 @@ * Returns effective group ID of calling process. * @return group id */ -uint32_t getegid(void) { +int getegid(void) { int rc; if (!IsWindows()) { rc = sys_getegid(); diff --git a/libc/calls/geteuid.c b/libc/calls/geteuid.c index 260c4b6ed..ad16d7419 100644 --- a/libc/calls/geteuid.c +++ b/libc/calls/geteuid.c @@ -24,7 +24,7 @@ * Returns effective user ID of calling process. * @return user id */ -uint32_t geteuid(void) { +int geteuid(void) { int rc; if (!IsWindows()) { rc = sys_geteuid(); diff --git a/libc/calls/getuid.c b/libc/calls/getuid.c index 34eb2702f..4c0d49be4 100644 --- a/libc/calls/getuid.c +++ b/libc/calls/getuid.c @@ -51,7 +51,7 @@ static textwindows dontinline uint32_t GetUserNameHash(void) { * @asyncsignalsafe * @vforksafe */ -uint32_t getuid(void) { +int getuid(void) { int rc; if (!IsWindows()) { rc = sys_getuid(); @@ -71,7 +71,7 @@ uint32_t getuid(void) { * @asyncsignalsafe * @vforksafe */ -uint32_t getgid(void) { +int getgid(void) { int rc; if (!IsWindows()) { rc = sys_getgid(); diff --git a/libc/calls/kclocknames.S b/libc/calls/kclocknames.S new file mode 100644 index 000000000..d302c87d3 --- /dev/null +++ b/libc/calls/kclocknames.S @@ -0,0 +1,47 @@ +/*-*- 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 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/magnumstrs.internal.h" +#include "libc/macros.internal.h" + + .macro .e e s + .long \e - kClockNames + .long 1f - kClockNames + .rodata.str1.1 +1: .string "\s" + .previous + .endm + + .section .rodata + .align 4 + .underrun +kClockNames: + .e CLOCK_REALTIME,"REALTIME" + .e CLOCK_MONOTONIC,"MONOTONIC" + .e CLOCK_MONOTONIC_RAW,"MONOTONIC_RAW" + .e CLOCK_REALTIME_COARSE,"REALTIME_COARSE" + .e CLOCK_MONOTONIC_COARSE,"MONOTONIC_COARSE" + .e CLOCK_PROCESS_CPUTIME_ID,"PROCESS_CPUTIME_ID" + .e CLOCK_TAI,"TAI" + .e CLOCK_PROF,"PROF" + .e CLOCK_BOOTTIME,"BOOTTIME" + .e CLOCK_REALTIME_ALARM,"REALTIME_ALARM" + .e CLOCK_BOOTTIME_ALARM,"BOOTTIME_ALARM" + .long MAGNUM_TERMINATOR + .endobj kClockNames,globl,hidden + .overrun diff --git a/libc/calls/kopenflags.S b/libc/calls/kopenflags.S new file mode 100644 index 000000000..9ced691a0 --- /dev/null +++ b/libc/calls/kopenflags.S @@ -0,0 +1,64 @@ +/*-*- 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 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/magnumstrs.internal.h" +#include "libc/macros.internal.h" + + .macro .e e s + .long \e - kOpenFlags + .long 1f - kOpenFlags + .rodata.str1.1 +1: .string "\s" + .previous + .endm + + .section .rodata + .align 4 + .underrun +kOpenFlags: + .e O_RDWR,"RDWR" // order matters + .e O_RDONLY,"RDONLY" // + .e O_WRONLY,"WRONLY" // + .e O_ACCMODE,"ACCMODE" // mask of prev three + .e O_CREAT,"CREAT" // + .e O_EXCL,"EXCL" // + .e O_TRUNC,"TRUNC" // + .e O_CLOEXEC,"CLOEXEC" // + .e O_DIRECT,"DIRECT" // no-op on xnu/openbsd + .e O_APPEND,"APPEND" // weird on nt + .e O_TMPFILE,"TMPFILE" // linux, windows + .e O_NOFOLLOW,"NOFOLLOW" // unix + .e O_SYNC,"SYNC" // unix + .e O_ASYNC,"ASYNC" // unix + .e O_NOCTTY,"NOCTTY" // unix + .e O_NOATIME,"NOATIME" // linux + .e O_EXEC,"EXEC" // free/openbsd + .e O_SEARCH,"SEARCH" // free/netbsd + .e O_DSYNC,"DSYNC" // linux/xnu/open/netbsd + .e O_RSYNC,"RSYNC" // linux/open/netbsd + .e O_PATH,"PATH" // linux + .e O_VERIFY,"VERIFY" // freebsd + .e O_SHLOCK,"SHLOCK" // bsd + .e O_EXLOCK,"EXLOCK" // bsd + .e O_RANDOM,"RANDOM" // windows + .e O_SEQUENTIAL,"SEQUENTIAL" // windows + .e O_COMPRESSED,"COMPRESSED" // windows + .e O_INDEXED,"INDEXED" // windows + .long MAGNUM_TERMINATOR + .endobj kOpenFlags,globl,hidden + .overrun diff --git a/libc/calls/openat.c b/libc/calls/openat.c index 349430dea..7f07dd379 100644 --- a/libc/calls/openat.c +++ b/libc/calls/openat.c @@ -21,6 +21,7 @@ #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/dce.h" +#include "libc/fmt/magnumstrs.internal.h" #include "libc/intrin/asan.internal.h" #include "libc/log/log.h" #include "libc/str/str.h" @@ -74,7 +75,8 @@ int openat(int dirfd, const char *file, int flags, ...) { } else { rc = efault(); } - STRACE("openat(%s, %#s, %#x, %#o) → %d% m", __strace_dirfd(buf, dirfd), file, - flags, (flags & (O_CREAT | O_TMPFILE)) ? mode : 0, rc); + STRACE("openat(%s, %#s, %s, %#o) → %d% m", __strace_dirfd(buf, dirfd), file, + DescribeOpenFlags(flags), (flags & (O_CREAT | O_TMPFILE)) ? mode : 0, + rc); return rc; } diff --git a/libc/calls/umask.c b/libc/calls/umask.c index e0dec3276..a6a439f8c 100644 --- a/libc/calls/umask.c +++ b/libc/calls/umask.c @@ -27,8 +27,8 @@ * @return previous mask * @note always succeeds */ -unsigned umask(unsigned newmask) { - unsigned oldmask; +int umask(int newmask) { + int oldmask; if (!IsWindows()) { oldmask = sys_umask(newmask); } else { diff --git a/libc/fmt/kerrnodocs.S b/libc/fmt/kerrnodocs.S index cd7e86c9e..d03750d07 100644 --- a/libc/fmt/kerrnodocs.S +++ b/libc/fmt/kerrnodocs.S @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/magnumstrs.internal.h" #include "libc/macros.internal.h" .macro .e e s @@ -115,6 +116,6 @@ kErrnoDocs: .e ENOTRECOVERABLE,"State not recoverable" .e ENONET,"Machine is not on the network" .e ERESTART,"Interrupted system call should be restarted" - .long -123 + .long MAGNUM_TERMINATOR .endobj kErrnoDocs,globl,hidden .overrun diff --git a/libc/fmt/kerrnonames.S b/libc/fmt/kerrnonames.S index 789339e01..85394e0e2 100644 --- a/libc/fmt/kerrnonames.S +++ b/libc/fmt/kerrnonames.S @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/magnumstrs.internal.h" #include "libc/macros.internal.h" .macro .e e @@ -27,7 +28,7 @@ .endm .section .rodata - .align 4 + .align 4 .underrun kErrnoNames: .e EINVAL @@ -116,6 +117,6 @@ kErrnoNames: .e ENONET .e ERESTART .e ENODATA - .long -123 + .long MAGNUM_TERMINATOR .endobj kErrnoNames,globl,hidden .overrun diff --git a/libc/fmt/magnumstrs.internal.h b/libc/fmt/magnumstrs.internal.h index e23dfb2ad..af425fc93 100644 --- a/libc/fmt/magnumstrs.internal.h +++ b/libc/fmt/magnumstrs.internal.h @@ -1,22 +1,35 @@ #ifndef COSMOPOLITAN_LIBC_FMT_MAGNUMSTRS_H_ #define COSMOPOLITAN_LIBC_FMT_MAGNUMSTRS_H_ + +#define MAGNUM_TERMINATOR -123 + #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ +#define MAGNUM_NUMBER(TABLE, INDEX) \ + *(const int *)((uintptr_t)TABLE + TABLE[INDEX].x) + +#define MAGNUM_STRING(TABLE, INDEX) \ + (const char *)((uintptr_t)TABLE + TABLE[INDEX].s) + struct MagnumStr { int x, s; }; -extern const struct MagnumStr kErrnoDocs[]; -extern const struct MagnumStr kErrnoNames[]; -extern const struct MagnumStr kIpOptnames[]; -extern const struct MagnumStr kSignalNames[]; -extern const struct MagnumStr kSockOptnames[]; -extern const struct MagnumStr kTcpOptnames[]; +hidden extern const struct MagnumStr kClockNames[]; +hidden extern const struct MagnumStr kErrnoDocs[]; +hidden extern const struct MagnumStr kErrnoNames[]; +hidden extern const struct MagnumStr kIpOptnames[]; +hidden extern const struct MagnumStr kOpenFlags[]; +hidden extern const struct MagnumStr kSignalNames[]; +hidden extern const struct MagnumStr kSockOptnames[]; +hidden extern const struct MagnumStr kTcpOptnames[]; -const char *DescribeSockLevel(int); -const char *DescribeSockOptname(int, int); -const char *GetMagnumStr(const struct MagnumStr *, int); +char *DescribeClockName(int) hidden; +char *DescribeOpenFlags(int) hidden; +char *DescribeSockLevel(int) hidden; +char *DescribeSockOptname(int, int) hidden; +char *GetMagnumStr(const struct MagnumStr *, int) hidden; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/fmt/strerdoc.greg.c b/libc/fmt/strerdoc.greg.c index abd68b616..b8e89b384 100644 --- a/libc/fmt/strerdoc.greg.c +++ b/libc/fmt/strerdoc.greg.c @@ -23,7 +23,7 @@ * Converts errno value to descriptive sentence. * @return non-null rodata string or null if not found */ -const char *strerdoc(int x) { +char *strerdoc(int x) { if (x) { return GetMagnumStr(kErrnoDocs, x); } else { diff --git a/libc/fmt/strerrno.greg.c b/libc/fmt/strerrno.greg.c index 042ae1942..145727831 100644 --- a/libc/fmt/strerrno.greg.c +++ b/libc/fmt/strerrno.greg.c @@ -23,7 +23,7 @@ * Converts errno value to symbolic name. * @return non-null rodata string or null if not found */ -const char *strerrno(int x) { +char *strerrno(int x) { if (x) { return GetMagnumStr(kErrnoNames, x); } else { diff --git a/libc/intrin/describemapflags.greg.c b/libc/intrin/describemapflags.greg.c index d913ac66b..8b361b72a 100644 --- a/libc/intrin/describemapflags.greg.c +++ b/libc/intrin/describemapflags.greg.c @@ -23,7 +23,7 @@ #include "libc/sysv/consts/prot.h" const char *DescribeMapFlags(int x) { - static char mapflags[256]; + _Alignas(char) static char mapflags[256]; const struct DescribeFlags kMapFlags[] = { {MAP_ANONYMOUS, "ANONYMOUS"}, // {MAP_PRIVATE, "PRIVATE"}, // diff --git a/libc/intrin/describentconsolemodeinputflags.greg.c b/libc/intrin/describentconsolemodeinputflags.greg.c index 0203679bc..1b3fb94eb 100644 --- a/libc/intrin/describentconsolemodeinputflags.greg.c +++ b/libc/intrin/describentconsolemodeinputflags.greg.c @@ -34,7 +34,7 @@ static const struct DescribeFlags kConsoleModeInputFlags[] = { }; const char *DescribeNtConsoleModeInputFlags(uint32_t x) { - static char consolemodeinputflags[256]; + _Alignas(char) static char consolemodeinputflags[256]; return DescribeFlags(consolemodeinputflags, sizeof(consolemodeinputflags), kConsoleModeInputFlags, ARRAYLEN(kConsoleModeInputFlags), "kNtEnable", x); diff --git a/libc/intrin/describentconsolemodeoutputflags.greg.c b/libc/intrin/describentconsolemodeoutputflags.greg.c index b862efae4..139b6e9ed 100644 --- a/libc/intrin/describentconsolemodeoutputflags.greg.c +++ b/libc/intrin/describentconsolemodeoutputflags.greg.c @@ -29,7 +29,7 @@ static const struct DescribeFlags kConsoleModeOutputFlags[] = { }; const char *DescribeNtConsoleModeOutputFlags(uint32_t x) { - static char consolemodeoutputflags[128]; + _Alignas(char) static char consolemodeoutputflags[128]; return DescribeFlags(consolemodeoutputflags, sizeof(consolemodeoutputflags), kConsoleModeOutputFlags, ARRAYLEN(kConsoleModeOutputFlags), "kNt", x); diff --git a/libc/intrin/describentfileaccessflags.greg.c b/libc/intrin/describentfileaccessflags.greg.c index cd09bf6ab..79924afb7 100644 --- a/libc/intrin/describentfileaccessflags.greg.c +++ b/libc/intrin/describentfileaccessflags.greg.c @@ -64,7 +64,7 @@ static const struct DescribeFlags kFileAccessflags[] = { }; const char *DescribeNtFileAccessFlags(uint32_t x) { - static char ntfileaccessflags[512]; + _Alignas(char) static char ntfileaccessflags[512]; return DescribeFlags(ntfileaccessflags, sizeof(ntfileaccessflags), kFileAccessflags, ARRAYLEN(kFileAccessflags), "kNt", x); } diff --git a/libc/intrin/describentfileflagsandattributes.greg.c b/libc/intrin/describentfileflagsandattributes.greg.c index 9e6174477..53a5a47d7 100644 --- a/libc/intrin/describentfileflagsandattributes.greg.c +++ b/libc/intrin/describentfileflagsandattributes.greg.c @@ -52,7 +52,7 @@ static const struct DescribeFlags kFileFlags[] = { }; const char *DescribeNtFileFlagsAndAttributes(uint32_t x) { - static char ntfileflags[256]; + _Alignas(char) static char ntfileflags[256]; if (x == -1u) return "-1u"; return DescribeFlags(ntfileflags, sizeof(ntfileflags), kFileFlags, ARRAYLEN(kFileFlags), "kNtFile", x); diff --git a/libc/intrin/describentfilemapflags.greg.c b/libc/intrin/describentfilemapflags.greg.c index 5ab201575..f5cdabaf5 100644 --- a/libc/intrin/describentfilemapflags.greg.c +++ b/libc/intrin/describentfilemapflags.greg.c @@ -31,7 +31,7 @@ static const struct DescribeFlags kFileMapFlags[] = { }; const char *DescribeNtFileMapFlags(uint32_t x) { - static char filemapflags[64]; + _Alignas(char) static char filemapflags[64]; return DescribeFlags(filemapflags, sizeof(filemapflags), kFileMapFlags, ARRAYLEN(kFileMapFlags), "kNtFileMap", x); } diff --git a/libc/intrin/describentfileshareflags.greg.c b/libc/intrin/describentfileshareflags.greg.c index a5e6d1b29..c322e3b86 100644 --- a/libc/intrin/describentfileshareflags.greg.c +++ b/libc/intrin/describentfileshareflags.greg.c @@ -27,7 +27,7 @@ static const struct DescribeFlags kFileShareflags[] = { }; const char *DescribeNtFileShareFlags(uint32_t x) { - static char ntfileshareflags[64]; + _Alignas(char) static char ntfileshareflags[64]; return DescribeFlags(ntfileshareflags, sizeof(ntfileshareflags), kFileShareflags, ARRAYLEN(kFileShareflags), "kNtFileShare", x); diff --git a/libc/intrin/describentfiletypeflags.greg.c b/libc/intrin/describentfiletypeflags.greg.c index 764749167..70b48ea1b 100644 --- a/libc/intrin/describentfiletypeflags.greg.c +++ b/libc/intrin/describentfiletypeflags.greg.c @@ -29,7 +29,7 @@ static const struct DescribeFlags kFiletypeFlags[] = { }; const char *DescribeNtFiletypeFlags(uint32_t x) { - static char filetypeflags[64]; + _Alignas(char) static char filetypeflags[64]; return DescribeFlags(filetypeflags, sizeof(filetypeflags), kFiletypeFlags, ARRAYLEN(kFiletypeFlags), "kNtFileType", x); } diff --git a/libc/intrin/describentmovefileflags.greg.c b/libc/intrin/describentmovefileflags.greg.c index 9c5c62026..c2f29b869 100644 --- a/libc/intrin/describentmovefileflags.greg.c +++ b/libc/intrin/describentmovefileflags.greg.c @@ -30,7 +30,7 @@ static const struct DescribeFlags kMoveFileInputFlags[] = { }; const char *DescribeNtMoveFileInputFlags(uint32_t x) { - static char movefileflags[256]; + _Alignas(char) static char movefileflags[256]; return DescribeFlags(movefileflags, sizeof(movefileflags), kMoveFileInputFlags, ARRAYLEN(kMoveFileInputFlags), "kNtMovefile", x); diff --git a/libc/intrin/describentpageflags.greg.c b/libc/intrin/describentpageflags.greg.c index 8c37a622a..02a7451c8 100644 --- a/libc/intrin/describentpageflags.greg.c +++ b/libc/intrin/describentpageflags.greg.c @@ -42,7 +42,7 @@ static const struct DescribeFlags kPageFlags[] = { }; const char *DescribeNtPageFlags(uint32_t x) { - static char pageflags[64]; + _Alignas(char) static char pageflags[64]; return DescribeFlags(pageflags, sizeof(pageflags), kPageFlags, ARRAYLEN(kPageFlags), "kNt", x); } diff --git a/libc/intrin/describentpipemodeflags.greg.c b/libc/intrin/describentpipemodeflags.greg.c index 978935805..f8f1f89ee 100644 --- a/libc/intrin/describentpipemodeflags.greg.c +++ b/libc/intrin/describentpipemodeflags.greg.c @@ -33,7 +33,7 @@ static const struct DescribeFlags kPipeModeFlags[] = { }; const char *DescribeNtPipeModeFlags(uint32_t x) { - static char pipemodeflags[64]; + _Alignas(char) static char pipemodeflags[64]; return DescribeFlags(pipemodeflags, sizeof(pipemodeflags), kPipeModeFlags, ARRAYLEN(kPipeModeFlags), "kNtPipe", x); } diff --git a/libc/intrin/describentpipeopenflags.greg.c b/libc/intrin/describentpipeopenflags.greg.c index 7b6fb5c68..10ab98a81 100644 --- a/libc/intrin/describentpipeopenflags.greg.c +++ b/libc/intrin/describentpipeopenflags.greg.c @@ -28,7 +28,7 @@ static const struct DescribeFlags kPipeOpenFlags[] = { }; const char *DescribeNtPipeOpenFlags(uint32_t x) { - static char pipeopenflags[64]; + _Alignas(char) static char pipeopenflags[64]; return DescribeFlags(pipeopenflags, sizeof(pipeopenflags), kPipeOpenFlags, ARRAYLEN(kPipeOpenFlags), "kNtPipeAccess", x); } diff --git a/libc/intrin/describentprocessaccessflags.greg.c b/libc/intrin/describentprocessaccessflags.greg.c index 3c93b5cc6..5f3d7fcdd 100644 --- a/libc/intrin/describentprocessaccessflags.greg.c +++ b/libc/intrin/describentprocessaccessflags.greg.c @@ -38,7 +38,7 @@ static const struct DescribeFlags kProcessAccessflags[] = { }; const char *DescribeNtProcessAccessFlags(uint32_t x) { - static char ntprocessaccessflags[256]; + _Alignas(char) static char ntprocessaccessflags[256]; return DescribeFlags(ntprocessaccessflags, sizeof(ntprocessaccessflags), kProcessAccessflags, ARRAYLEN(kProcessAccessflags), "kNtProcess", x); diff --git a/libc/intrin/describentstartflags.greg.c b/libc/intrin/describentstartflags.greg.c index e7578a56c..d80e2778b 100644 --- a/libc/intrin/describentstartflags.greg.c +++ b/libc/intrin/describentstartflags.greg.c @@ -39,7 +39,7 @@ static const struct DescribeFlags kNtStartFlags[] = { }; const char *DescribeNtStartFlags(uint32_t x) { - static char startflags[128]; + _Alignas(char) static char startflags[128]; return DescribeFlags(startflags, sizeof(startflags), kNtStartFlags, ARRAYLEN(kNtStartFlags), "kNtStartf", x); } diff --git a/libc/intrin/describentsymboliclinkflags.greg.c b/libc/intrin/describentsymboliclinkflags.greg.c index 3f4afffc3..233945f94 100644 --- a/libc/intrin/describentsymboliclinkflags.greg.c +++ b/libc/intrin/describentsymboliclinkflags.greg.c @@ -26,7 +26,7 @@ static const struct DescribeFlags kSymbolicLinkflags[] = { }; const char *DescribeNtSymbolicLinkFlags(uint32_t x) { - static char ntsymboliclinkflags[64]; + _Alignas(char) static char ntsymboliclinkflags[64]; return DescribeFlags(ntsymboliclinkflags, sizeof(ntsymboliclinkflags), kSymbolicLinkflags, ARRAYLEN(kSymbolicLinkflags), "kNtSymbolicLinkFlag", x); diff --git a/libc/intrin/describeprotflags.greg.c b/libc/intrin/describeprotflags.greg.c index 1bcae2e1e..ac30ab5fc 100644 --- a/libc/intrin/describeprotflags.greg.c +++ b/libc/intrin/describeprotflags.greg.c @@ -27,7 +27,7 @@ static const struct DescribeFlags kProtFlags[] = { }; const char *DescribeProtFlags(int x) { - static char protflags[64]; + _Alignas(char) static char protflags[64]; return DescribeFlags(protflags, sizeof(protflags), kProtFlags, ARRAYLEN(kProtFlags), "PROT_", x); } diff --git a/libc/intrin/describeremapflags.greg.c b/libc/intrin/describeremapflags.greg.c index 98b0f1e7c..40ce7219e 100644 --- a/libc/intrin/describeremapflags.greg.c +++ b/libc/intrin/describeremapflags.greg.c @@ -26,7 +26,7 @@ static const struct DescribeFlags kRemapFlags[] = { }; const char *DescribeRemapFlags(int x) { - static char remapflags[64]; + _Alignas(char) static char remapflags[64]; return DescribeFlags(remapflags, sizeof(remapflags), kRemapFlags, ARRAYLEN(kRemapFlags), "MREMAP_", x); } diff --git a/libc/intrin/getmagnumstr.greg.c b/libc/intrin/getmagnumstr.greg.c index 4be124a29..20834e936 100644 --- a/libc/intrin/getmagnumstr.greg.c +++ b/libc/intrin/getmagnumstr.greg.c @@ -18,11 +18,11 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/fmt/magnumstrs.internal.h" -const char *GetMagnumStr(const struct MagnumStr *ms, int x) { +char *GetMagnumStr(const struct MagnumStr *ms, int x) { int i; - for (i = 0; ms[i].x != -123; ++i) { - if (x == *(const int *)((uintptr_t)ms + ms[i].x)) { - return (const char *)((uintptr_t)ms + ms[i].s); + for (i = 0; ms[i].x != MAGNUM_TERMINATOR; ++i) { + if (x == MAGNUM_NUMBER(ms, i)) { + return MAGNUM_STRING(ms, i); } } return 0; diff --git a/libc/log/leaks.c b/libc/log/leaks.c index e074c8355..0f61dda6a 100644 --- a/libc/log/leaks.c +++ b/libc/log/leaks.c @@ -28,6 +28,8 @@ STATIC_YOINK("__get_symbol_by_addr"); +#define MAXLEAKS 1000 + static bool once; static bool hasleaks; @@ -46,15 +48,18 @@ static noasan void CheckLeak(void *x, void *y, size_t n, void *a) { static noasan void OnMemory(void *x, void *y, size_t n, void *a) { static int i; if (n) { - if (++i < 20) { - kprintf("%p %,lu bytes [dlmalloc]", x, n); - if (IsAsan()) { - __asan_print_trace(x); + if (MAXLEAKS) { + if (i < MAXLEAKS) { + ++i; + kprintf("%p %,lu bytes [dlmalloc]", x, n); + if (IsAsan()) { + __asan_print_trace(x); + } + kprintf("\n"); + } else if (i == MAXLEAKS) { + ++i; + kprintf("etc. etc.\n"); } - kprintf("\n"); - } - if (i == 20) { - kprintf("etc. etc.\n"); } } } diff --git a/libc/nt/struct/linger.h b/libc/nt/struct/linger.h new file mode 100644 index 000000000..0267d205a --- /dev/null +++ b/libc/nt/struct/linger.h @@ -0,0 +1,13 @@ +#ifndef COSMOPOLITAN_LIBC_NT_STRUCT_LINGER_H_ +#define COSMOPOLITAN_LIBC_NT_STRUCT_LINGER_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +struct linger_nt { + uint16_t l_onoff; /* on/off */ + uint16_t l_linger; /* seconds */ +}; + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_NT_STRUCT_LINGER_H_ */ diff --git a/libc/sock/describesocklevel.greg.c b/libc/sock/describesocklevel.greg.c index 337d6e822..7dd91118b 100644 --- a/libc/sock/describesocklevel.greg.c +++ b/libc/sock/describesocklevel.greg.c @@ -22,7 +22,7 @@ /** * Describes setsockopt() level arguments. */ -const char *DescribeSockLevel(int x) { +char *DescribeSockLevel(int x) { static char buf[12]; if (x == SOL_IP) return "SOL_IP"; if (x == SOL_TCP) return "SOL_TCP"; diff --git a/libc/sock/describesockoptname.greg.c b/libc/sock/describesockoptname.greg.c index d3f1388fa..09213b2a0 100644 --- a/libc/sock/describesockoptname.greg.c +++ b/libc/sock/describesockoptname.greg.c @@ -18,26 +18,32 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/fmt/itoa.h" #include "libc/fmt/magnumstrs.internal.h" +#include "libc/str/str.h" #include "libc/sysv/consts/sol.h" /** * Describes setsockopt() optname arguments. */ -const char *DescribeSockOptname(int l, int x) { +char *DescribeSockOptname(int l, int x) { int i; - static char buf[12], *s; + char *ps, *s; const struct MagnumStr *ms = 0; + _Alignas(char) static char buf[32]; if (x) { if (l == SOL_SOCKET) { + ps = "SO_"; ms = kSockOptnames; } else if (l == SOL_TCP) { + ps = "TCP_"; ms = kTcpOptnames; } else if (l == SOL_IP) { + ps = "IP_"; ms = kIpOptnames; } } if (ms && (s = GetMagnumStr(ms, x))) { - return s; + stpcpy(stpcpy(buf, ps), s); + return buf; } else { FormatInt32(buf, x); return buf; diff --git a/libc/sock/getsockopt-nt.c b/libc/sock/getsockopt-nt.c index 0ab21dd1f..d64f6c5f6 100644 --- a/libc/sock/getsockopt-nt.c +++ b/libc/sock/getsockopt-nt.c @@ -20,8 +20,10 @@ #include "libc/bits/bits.h" #include "libc/calls/internal.h" #include "libc/calls/struct/timeval.h" +#include "libc/nt/struct/linger.h" #include "libc/nt/winsock.h" #include "libc/sock/internal.h" +#include "libc/sock/sock.h" #include "libc/sock/yoink.inc" #include "libc/str/str.h" #include "libc/sysv/consts/so.h" @@ -33,6 +35,7 @@ textwindows int sys_getsockopt_nt(struct Fd *fd, int level, int optname, uint32_t *inout_optlen) { uint64_t ms; uint32_t in_optlen; + struct linger_nt linger; assert(fd->kind == kFdSocket); if (out_opt_optval && inout_optlen) { @@ -55,6 +58,11 @@ textwindows int sys_getsockopt_nt(struct Fd *fd, int level, int optname, ((struct timeval *)out_opt_optval)->tv_sec = ms / 1000; ((struct timeval *)out_opt_optval)->tv_usec = ms % 1000 * 1000; *inout_optlen = sizeof(struct timeval); + } else if (optname == SO_LINGER && in_optlen == sizeof(struct linger)) { + linger = *(struct linger_nt *)out_opt_optval; + ((struct linger *)out_opt_optval)->l_onoff = !!linger.l_onoff; + ((struct linger *)out_opt_optval)->l_linger = linger.l_linger; + *inout_optlen = sizeof(struct linger); } } diff --git a/libc/sock/kipoptnames.S b/libc/sock/kipoptnames.S index 4d3c8dae4..cee04e63c 100644 --- a/libc/sock/kipoptnames.S +++ b/libc/sock/kipoptnames.S @@ -16,24 +16,25 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/magnumstrs.internal.h" #include "libc/macros.internal.h" - .macro .e e + .macro .e e s .long \e - kIpOptnames .long 1f - kIpOptnames .rodata.str1.1 -1: .string "\e" +1: .string "\s" .previous .endm .section .rodata - .align 4 + .align 4 .underrun kIpOptnames: - .e IP_TOS # int - .e IP_MTU # int - .e IP_TTL # int - .e IP_HDRINCL # bool32 - .long -123 + .e IP_TOS,"TOS" # int + .e IP_MTU,"MTU" # int + .e IP_TTL,"TTL" # int + .e IP_HDRINCL,"HDRINCL" # bool32 + .long MAGNUM_TERMINATOR .endobj kIpOptnames,globl,hidden .overrun diff --git a/libc/sock/ksockoptnames.S b/libc/sock/ksockoptnames.S index b56acbffe..8b29764a0 100644 --- a/libc/sock/ksockoptnames.S +++ b/libc/sock/ksockoptnames.S @@ -16,13 +16,14 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/magnumstrs.internal.h" #include "libc/macros.internal.h" - .macro .e e + .macro .e e s .long \e - kSockOptnames .long 1f - kSockOptnames .rodata.str1.1 -1: .string "\e" +1: .string "\s" .previous .endm @@ -30,20 +31,22 @@ .align 4 .underrun kSockOptnames: - .e SO_DEBUG # bool32 - .e SO_BROADCAST # bool32 - .e SO_REUSEADDR # bool32 - .e SO_REUSEPORT # bool32 - .e SO_KEEPALIVE # bool32 - .e SO_DONTROUTE # bool32 - .e SO_RCVTIMEO # timeval - .e SO_SNDTIMEO # timeval - .e SO_LINGER # linger - .e SO_SNDBUF # int - .e SO_RCVBUF # int - .e SO_RCVLOWAT # int - .e SO_SNDLOWAT # int - .e SO_ERROR # int - .long -123 + .e SO_DEBUG,"DEBUG" # bool32 + .e SO_ACCEPTCONN,"ACCEPTCONN" # bool32 + .e SO_BROADCAST,"BROADCAST" # bool32 + .e SO_REUSEADDR,"REUSEADDR" # bool32 + .e SO_REUSEPORT,"REUSEPORT" # bool32 + .e SO_KEEPALIVE,"KEEPALIVE" # bool32 + .e SO_DONTROUTE,"DONTROUTE" # bool32 + .e SO_RCVTIMEO,"RCVTIMEO" # timeval + .e SO_SNDTIMEO,"SNDTIMEO" # timeval + .e SO_LINGER,"LINGER" # linger + .e SO_TYPE,"TYPE" # int + .e SO_SNDBUF,"SNDBUF" # int + .e SO_RCVBUF,"RCVBUF" # int + .e SO_RCVLOWAT,"RCVLOWAT" # int + .e SO_SNDLOWAT,"SNDLOWAT" # int + .e SO_ERROR,"ERROR" # int + .long MAGNUM_TERMINATOR .endobj kSockOptnames,globl,hidden .overrun diff --git a/libc/sock/ktcpoptnames.S b/libc/sock/ktcpoptnames.S index f5d837ae8..444b35922 100644 --- a/libc/sock/ktcpoptnames.S +++ b/libc/sock/ktcpoptnames.S @@ -16,33 +16,34 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/magnumstrs.internal.h" #include "libc/macros.internal.h" - .macro .e e + .macro .e e s .long \e - kTcpOptnames .long 1f - kTcpOptnames .rodata.str1.1 -1: .string "\e" +1: .string "\s" .previous .endm .section .rodata - .align 4 + .align 4 .underrun kTcpOptnames: - .e TCP_NODELAY # bool32 - .e TCP_CORK # bool32 - .e TCP_QUICKACK # bool32 - .e TCP_FASTOPEN_CONNECT # bool32 - .e TCP_DEFER_ACCEPT # bool32 - .e TCP_KEEPIDLE # int (seconds) - .e TCP_KEEPINTVL # int (seconds) - .e TCP_FASTOPEN # int - .e TCP_KEEPCNT # int - .e TCP_MAXSEG # int - .e TCP_SYNCNT # int - .e TCP_NOTSENT_LOWAT # int - .e TCP_WINDOW_CLAMP # int - .long -123 + .e TCP_NODELAY,"NODELAY" # bool32 + .e TCP_CORK,"CORK" # bool32 + .e TCP_QUICKACK,"QUICKACK" # bool32 + .e TCP_FASTOPEN_CONNECT,"FASTOPEN_CONNECT" # bool32 + .e TCP_DEFER_ACCEPT,"DEFER_ACCEPT" # bool32 + .e TCP_KEEPIDLE,"KEEPIDLE" # int (seconds) + .e TCP_KEEPINTVL,"KEEPINTVL" # int (seconds) + .e TCP_FASTOPEN,"FASTOPEN" # int + .e TCP_KEEPCNT,"KEEPCNT" # int + .e TCP_MAXSEG,"MAXSEG" # int + .e TCP_SYNCNT,"SYNCNT" # int + .e TCP_NOTSENT_LOWAT,"NOTSENT_LOWAT" # int + .e TCP_WINDOW_CLAMP,"WINDOW_CLAMP" # int + .long MAGNUM_TERMINATOR .endobj kTcpOptnames,globl,hidden .overrun diff --git a/libc/sock/setsockopt-nt.c b/libc/sock/setsockopt-nt.c index 24c753517..4f637d8cd 100644 --- a/libc/sock/setsockopt-nt.c +++ b/libc/sock/setsockopt-nt.c @@ -19,16 +19,12 @@ #include "libc/calls/struct/timeval.h" #include "libc/limits.h" #include "libc/macros.internal.h" +#include "libc/nt/struct/linger.h" #include "libc/sock/internal.h" #include "libc/sysv/consts/so.h" #include "libc/sysv/consts/sol.h" #include "libc/sysv/errfuns.h" -struct linger_nt { /* Linux+XNU+BSD ABI */ - uint16_t l_onoff; /* on/off */ - uint16_t l_linger; /* seconds */ -}; - textwindows int sys_setsockopt_nt(struct Fd *fd, int level, int optname, const void *optval, uint32_t optlen) { int64_t ms; diff --git a/libc/stdio/system.c b/libc/stdio/system.c index dd1fee19b..ee88289a5 100644 --- a/libc/stdio/system.c +++ b/libc/stdio/system.c @@ -22,6 +22,7 @@ #include "libc/calls/struct/sigaction.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/log/log.h" #include "libc/paths.h" #include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" diff --git a/libc/str/ksignalnames.S b/libc/str/ksignalnames.S index 5939eb3e9..0e58a1b70 100644 --- a/libc/str/ksignalnames.S +++ b/libc/str/ksignalnames.S @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/magnumstrs.internal.h" #include "libc/macros.internal.h" .macro .e e s @@ -65,6 +66,6 @@ kSignalNames: .e SIGRTMIN,"RTMIN" .e SIGEMT,"EMT" .e SIGPWR,"PWR" - .long -123 + .long MAGNUM_TERMINATOR .endobj kSignalNames,globl,hidden .overrun diff --git a/libc/str/str.h b/libc/str/str.h index 6ec97a339..fbf461e9a 100644 --- a/libc/str/str.h +++ b/libc/str/str.h @@ -262,8 +262,8 @@ wint_t towctrans(wint_t, wctrans_t); char *strsignal(int) returnsnonnull libcesque; char *strerror(int) returnsnonnull dontthrow nocallback; -const char *strerrno(int) nosideeffect libcesque; -const char *strerdoc(int) nosideeffect libcesque; +char *strerrno(int) nosideeffect libcesque; +char *strerdoc(int) nosideeffect libcesque; int strerror_r(int, char *, size_t) dontthrow nocallback; int strerror_wr(int, uint32_t, char *, size_t) dontthrow nocallback; diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index 48fec094f..febb4bb42 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -620,7 +620,7 @@ syscon clock CLOCK_MONOTONIC_RAW 4 4 0x4000 0x4000 0x4000 4 # actu syscon clock CLOCK_REALTIME_COARSE 5 -1 -1 -1 -1 -1 # Linux 2.6.32+; bsd consensus; not available on RHEL5 syscon clock CLOCK_MONOTONIC_COARSE 6 -1 -1 -1 -1 -1 # Linux 2.6.32+; bsd consensus; not available on RHEL5 syscon clock CLOCK_PROF -1 -1 2 -1 2 -1 # -syscon clock CLOCK_BOOTTIME 7 -1 -1 6 6 -1 # +syscon clock CLOCK_BOOTTIME 7 -1 -1 6 -1 -1 # syscon clock CLOCK_REALTIME_ALARM 8 -1 -1 -1 -1 -1 # syscon clock CLOCK_BOOTTIME_ALARM 9 -1 -1 -1 -1 -1 # syscon clock CLOCK_TAI 11 -1 -1 -1 -1 -1 # @@ -669,16 +669,19 @@ syscon epoll EPOLLET 0x80000000 0x80000000 0x80000000 0x80000000 0x80000 # * -1 we define as no-op # # group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary +syscon so SO_DEBUG 1 1 1 1 1 1 # debugging is enabled; consensus +syscon so SO_TYPE 3 0x1008 0x1008 0x1008 0x1008 0x1008 # bsd consensus +syscon so SO_ERROR 4 0x1007 0x1007 0x1007 0x1007 0x1007 # takes int pointer and stores/clears the pending error code; bsd consensus +syscon so SO_ACCEPTCONN 30 2 2 2 2 2 # takes int pointer and stores boolean indicating if listen() was called on fd; bsd consensus syscon so SO_REUSEPORT 15 0x0200 0x0200 0x0200 0x0200 4 # bsd consensus (NT calls it SO_REUSEADDR) -syscon so SO_REUSEADDR 2 4 4 4 4 0 # bsd consensus (default behavior on NT) +syscon so SO_REUSEADDR 2 4 4 4 4 4 # bsd consensus (default behavior on NT) +syscon so SO_EXCLUSIVEADDRUSE 0 0 0 0 0 ~4 # bsd consensus (default behavior on NT) syscon so SO_KEEPALIVE 9 8 8 8 8 8 # bsd consensus syscon so SO_DONTROUTE 5 0x10 0x10 0x10 0x10 0x10 # bsd consensus -syscon so SO_BROADCAST 6 0x20 0x20 0x20 0x20 0x20 # bsd consensus +syscon so SO_BROADCAST 6 0x20 0x20 0x20 0x20 0x20 # socket is configured for broadcast messages; bsd consensus syscon so SO_USELOOPBACK 0 0x40 0x40 0x40 0x40 0x40 # bsd consensus syscon so SO_LINGER 13 0x80 0x80 0x80 0x80 0x80 # takes struct linger; causes close() return value to actually mean something; bsd consensus -syscon so SO_DEBUG 1 1 1 1 1 1 # consensus -syscon so SO_ACCEPTCONN 30 2 2 2 2 2 # takes int pointer and stores boolean indicating if listen() was called on fd; bsd consensus -syscon so SO_ERROR 4 0x1007 0x1007 0x1007 0x1007 0x1007 # takes int pointer and stores/clears the pending error code; bsd consensus +syscon so SO_DONTLINGER 0 0 0 0 0 ~0x80 # disables so_linger on windows syscon so SO_OOBINLINE 10 0x0100 0x0100 0x0100 0x0100 0x0100 # bsd consensus syscon so SO_SNDBUF 7 0x1001 0x1001 0x1001 0x1001 0x1001 # bsd consensus syscon so SO_RCVBUF 8 0x1002 0x1002 0x1002 0x1002 0x1002 # bsd consensus @@ -686,7 +689,6 @@ syscon so SO_RCVTIMEO 20 0x1006 0x1006 0x1006 0x100c 0x1006 # rec syscon so SO_SNDTIMEO 21 0x1005 0x1005 0x1005 0x100b 0x1005 # send timeout; takes struct timeval; bsd consensus syscon so SO_RCVLOWAT 18 0x1004 0x1004 0x1004 0x1004 0x1004 # bsd consensus syscon so SO_SNDLOWAT 19 0x1003 0x1003 0x1003 0x1003 0x1003 # bsd consensus -syscon so SO_TYPE 3 0x1008 0x1008 0x1008 0x1008 0x1008 # bsd consensus syscon so SO_TIMESTAMP 29 0x0400 0x0400 0x0800 0x2000 0 syscon so SO_SETFIB 0 0 0x1014 0 0 0 syscon so SO_DOMAIN 39 0 0x1019 0x1024 0 0 diff --git a/libc/sysv/consts/CLOCK_BOOTTIME.S b/libc/sysv/consts/CLOCK_BOOTTIME.S index 4a093dab4..05ce54ce7 100644 --- a/libc/sysv/consts/CLOCK_BOOTTIME.S +++ b/libc/sysv/consts/CLOCK_BOOTTIME.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon clock,CLOCK_BOOTTIME,7,-1,-1,6,6,-1 +.syscon clock,CLOCK_BOOTTIME,7,-1,-1,6,-1,-1 diff --git a/libc/sysv/consts/SO_DONTLINGER.S b/libc/sysv/consts/SO_DONTLINGER.S new file mode 100644 index 000000000..6d61ff314 --- /dev/null +++ b/libc/sysv/consts/SO_DONTLINGER.S @@ -0,0 +1,2 @@ +#include "libc/sysv/consts/syscon.internal.h" +.syscon so,SO_DONTLINGER,0,0,0,0,0,~0x80 diff --git a/libc/sysv/consts/SO_REUSEADDR.S b/libc/sysv/consts/SO_REUSEADDR.S index 7c52c3253..b78e3b8d5 100644 --- a/libc/sysv/consts/SO_REUSEADDR.S +++ b/libc/sysv/consts/SO_REUSEADDR.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon so,SO_REUSEADDR,2,4,4,4,4,0 +.syscon so,SO_REUSEADDR,2,4,4,4,4,4 diff --git a/libc/testlib/ezbench.h b/libc/testlib/ezbench.h index 977321c08..7f8714343 100644 --- a/libc/testlib/ezbench.h +++ b/libc/testlib/ezbench.h @@ -1,6 +1,7 @@ #ifndef COSMOPOLITAN_LIBC_TESTLIB_EZBENCH_H_ #define COSMOPOLITAN_LIBC_TESTLIB_EZBENCH_H_ #include "libc/macros.internal.h" +#include "libc/nexgen32e/bench.h" #include "libc/nexgen32e/x86feature.h" #include "libc/sysv/consts/clock.h" #include "libc/testlib/bench.h" @@ -9,151 +10,162 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ +#define EZBENCH_COUNT 128 +#define EZBENCH_TRIES 10 + #define EZBENCH(INIT, EXPR) EZBENCH2(#EXPR, INIT, EXPR) -#define EZBENCH2(NAME, INIT, EXPR) \ - do { \ - int Core, Tries, Interrupts; \ - int64_t Speculative, MemoryStrict; \ - Tries = 0; \ - do { \ - __testlib_yield(); \ - Core = __testlib_getcore(); \ - Interrupts = __testlib_getinterrupts(); \ - INIT; \ - EXPR; \ - Speculative = BENCHLOOP(__startbench, __endbench, 128, ({ \ - INIT; \ - polluteregisters(); \ - }), \ - (EXPR)); \ - } while (++Tries < 10 && (__testlib_getcore() != Core && \ - __testlib_getinterrupts() > Interrupts)); \ - if (Tries == 10) __testlib_ezbenchwarn(" speculative"); \ - Tries = 0; \ - do { \ - __testlib_yield(); \ - Core = __testlib_getcore(); \ - Interrupts = __testlib_getinterrupts(); \ - INIT; \ - EXPR; \ - MemoryStrict = BENCHLOOP(__startbench_m, __endbench_m, 32, ({ \ - INIT; \ - thrashcodecache(); \ - polluteregisters(); \ - }), \ - (EXPR)); \ - } while (++Tries < 10 && (__testlib_getcore() != Core && \ - __testlib_getinterrupts() > Interrupts)); \ - if (Tries == 10) __testlib_ezbenchwarn(" memory strict"); \ - __testlib_ezbenchreport( \ - NAME, MAX(0, Speculative - __testlib_ezbenchcontrol()), \ - MAX(0, MemoryStrict - __testlib_ezbenchcontrol())); \ +#define EZBENCH2(NAME, INIT, EXPR) \ + do { \ + int Core, Tries, Interrupts; \ + int64_t Speculative, MemoryStrict; \ + Tries = 0; \ + do { \ + __testlib_yield(); \ + Core = __testlib_getcore(); \ + Interrupts = __testlib_getinterrupts(); \ + INIT; \ + EXPR; \ + Speculative = BENCHLOOP(__startbench, __endbench, EZBENCH_COUNT, ({ \ + INIT; \ + __polluteregisters(); \ + }), \ + (EXPR)); \ + } while (++Tries < EZBENCH_TRIES && \ + (__testlib_getcore() != Core && \ + __testlib_getinterrupts() > Interrupts)); \ + if (Tries == EZBENCH_TRIES) __testlib_ezbenchwarn(" speculative"); \ + Tries = 0; \ + do { \ + __testlib_yield(); \ + Core = __testlib_getcore(); \ + Interrupts = __testlib_getinterrupts(); \ + INIT; \ + EXPR; \ + MemoryStrict = BENCHLOOP(__startbench_m, __endbench_m, 32, ({ \ + INIT; \ + thrashcodecache(); \ + __polluteregisters(); \ + }), \ + (EXPR)); \ + } while (++Tries < EZBENCH_TRIES && \ + (__testlib_getcore() != Core && \ + __testlib_getinterrupts() > Interrupts)); \ + if (Tries == EZBENCH_TRIES) __testlib_ezbenchwarn(" memory strict"); \ + __testlib_ezbenchreport( \ + NAME, MAX(0, Speculative - __testlib_ezbenchcontrol()), \ + MAX(0, MemoryStrict - __testlib_ezbenchcontrol())); \ } while (0) -#define EZBENCH3(NAME, NUM, INIT, EXPR) \ - do { \ - int Core, Tries, Interrupts; \ - int64_t Speculative, MemoryStrict; \ - Tries = 0; \ - do { \ - __testlib_yield(); \ - Core = __testlib_getcore(); \ - Interrupts = __testlib_getinterrupts(); \ - INIT; \ - EXPR; \ - Speculative = BENCHLOOP(__startbench, __endbench, NUM, ({ \ - INIT; \ - polluteregisters(); \ - }), \ - (EXPR)); \ - } while (++Tries < 10 && (__testlib_getcore() != Core && \ - __testlib_getinterrupts() > Interrupts)); \ - if (Tries == 10) __testlib_ezbenchwarn(" speculative"); \ - Tries = 0; \ - do { \ - __testlib_yield(); \ - Core = __testlib_getcore(); \ - Interrupts = __testlib_getinterrupts(); \ - INIT; \ - EXPR; \ - MemoryStrict = BENCHLOOP(__startbench_m, __endbench_m, NUM, ({ \ - INIT; \ - thrashcodecache(); \ - polluteregisters(); \ - }), \ - (EXPR)); \ - } while (++Tries < 10 && (__testlib_getcore() != Core && \ - __testlib_getinterrupts() > Interrupts)); \ - if (Tries == 10) __testlib_ezbenchwarn(" memory strict"); \ - __testlib_ezbenchreport( \ - NAME, MAX(0, Speculative - __testlib_ezbenchcontrol()), \ - MAX(0, MemoryStrict - __testlib_ezbenchcontrol())); \ +#define EZBENCH3(NAME, NUM, INIT, EXPR) \ + do { \ + int Core, Tries, Interrupts; \ + int64_t Speculative, MemoryStrict; \ + Tries = 0; \ + do { \ + __testlib_yield(); \ + Core = __testlib_getcore(); \ + Interrupts = __testlib_getinterrupts(); \ + INIT; \ + EXPR; \ + Speculative = BENCHLOOP(__startbench, __endbench, NUM, ({ \ + INIT; \ + __polluteregisters(); \ + }), \ + (EXPR)); \ + } while (++Tries < EZBENCH_TRIES && \ + (__testlib_getcore() != Core && \ + __testlib_getinterrupts() > Interrupts)); \ + if (Tries == EZBENCH_TRIES) __testlib_ezbenchwarn(" speculative"); \ + Tries = 0; \ + do { \ + __testlib_yield(); \ + Core = __testlib_getcore(); \ + Interrupts = __testlib_getinterrupts(); \ + INIT; \ + EXPR; \ + MemoryStrict = BENCHLOOP(__startbench_m, __endbench_m, NUM, ({ \ + INIT; \ + thrashcodecache(); \ + __polluteregisters(); \ + }), \ + (EXPR)); \ + } while (++Tries < EZBENCH_TRIES && \ + (__testlib_getcore() != Core && \ + __testlib_getinterrupts() > Interrupts)); \ + if (Tries == EZBENCH_TRIES) __testlib_ezbenchwarn(" memory strict"); \ + __testlib_ezbenchreport( \ + NAME, MAX(0, Speculative - __testlib_ezbenchcontrol()), \ + MAX(0, MemoryStrict - __testlib_ezbenchcontrol())); \ } while (0) -#define EZBENCH_C(NAME, CONTROL, EXPR) \ - do { \ - int Core, Tries, Interrupts; \ - int64_t Control, Speculative, MemoryStrict; \ - Tries = 0; \ - do { \ - __testlib_yield(); \ - Core = __testlib_getcore(); \ - Interrupts = __testlib_getinterrupts(); \ - Control = BENCHLOOP(__startbench_m, __endbench_m, 128, ({ \ - thrashcodecache(); \ - polluteregisters(); \ - }), \ - (CONTROL)); \ - } while (++Tries < 10 && (__testlib_getcore() != Core && \ - __testlib_getinterrupts() > Interrupts)); \ - if (Tries == 10) __testlib_ezbenchwarn(" control"); \ - Tries = 0; \ - do { \ - __testlib_yield(); \ - Core = __testlib_getcore(); \ - Interrupts = __testlib_getinterrupts(); \ - EXPR; \ - Speculative = BENCHLOOP(__startbench, __endbench, 128, \ - polluteregisters(), (EXPR)); \ - } while (++Tries < 10 && (__testlib_getcore() != Core && \ - __testlib_getinterrupts() > Interrupts)); \ - if (Tries == 10) __testlib_ezbenchwarn(" speculative"); \ - Tries = 0; \ - do { \ - __testlib_yield(); \ - Core = __testlib_getcore(); \ - Interrupts = __testlib_getinterrupts(); \ - EXPR; \ - MemoryStrict = BENCHLOOP(__startbench_m, __endbench_m, 8, ({ \ - thrashcodecache(); \ - polluteregisters(); \ - }), \ - (EXPR)); \ - } while (++Tries < 10 && (__testlib_getcore() != Core && \ - __testlib_getinterrupts() > Interrupts)); \ - if (Tries == 10) __testlib_ezbenchwarn(" memory strict"); \ - __testlib_ezbenchreport(NAME, MAX(0, Speculative - Control), \ - MAX(0, MemoryStrict - Control)); \ +#define EZBENCH_C(NAME, CONTROL, EXPR) \ + do { \ + int Core, Tries, Interrupts; \ + int64_t Control, Speculative, MemoryStrict; \ + Tries = 0; \ + do { \ + __testlib_yield(); \ + Core = __testlib_getcore(); \ + Interrupts = __testlib_getinterrupts(); \ + Control = BENCHLOOP(__startbench_m, __endbench_m, EZBENCH_COUNT, ({ \ + thrashcodecache(); \ + __polluteregisters(); \ + }), \ + (CONTROL)); \ + } while (++Tries < EZBENCH_TRIES && \ + (__testlib_getcore() != Core && \ + __testlib_getinterrupts() > Interrupts)); \ + if (Tries == EZBENCH_TRIES) __testlib_ezbenchwarn(" control"); \ + Tries = 0; \ + do { \ + __testlib_yield(); \ + Core = __testlib_getcore(); \ + Interrupts = __testlib_getinterrupts(); \ + EXPR; \ + Speculative = BENCHLOOP(__startbench, __endbench, EZBENCH_COUNT, \ + __polluteregisters(), (EXPR)); \ + } while (++Tries < EZBENCH_TRIES && \ + (__testlib_getcore() != Core && \ + __testlib_getinterrupts() > Interrupts)); \ + if (Tries == EZBENCH_TRIES) __testlib_ezbenchwarn(" speculative"); \ + Tries = 0; \ + do { \ + __testlib_yield(); \ + Core = __testlib_getcore(); \ + Interrupts = __testlib_getinterrupts(); \ + EXPR; \ + MemoryStrict = BENCHLOOP(__startbench_m, __endbench_m, 8, ({ \ + thrashcodecache(); \ + __polluteregisters(); \ + }), \ + (EXPR)); \ + } while (++Tries < EZBENCH_TRIES && \ + (__testlib_getcore() != Core && \ + __testlib_getinterrupts() > Interrupts)); \ + if (Tries == EZBENCH_TRIES) __testlib_ezbenchwarn(" memory strict"); \ + __testlib_ezbenchreport(NAME, MAX(0, Speculative - Control), \ + MAX(0, MemoryStrict - Control)); \ } while (0) -#define EZBENCH_N(NAME, N, EXPR) \ - do { \ - int64_t Speculative, Toto; \ - int Core, Tries, Interrupts; \ - Tries = 0; \ - do { \ - __testlib_yield(); \ - Core = __testlib_getcore(); \ - Interrupts = __testlib_getinterrupts(); \ - EXPR; \ - Speculative = \ - BENCHLOOP(__startbench, __endbench, 32, polluteregisters(), (EXPR)); \ - } while (++Tries < 10 && (__testlib_getcore() != Core && \ - __testlib_getinterrupts() > Interrupts)); \ - if (Tries == 10) __testlib_ezbenchwarn(""); \ - __testlib_ezbenchreport_n( \ - NAME, 'n', N, MAX(0, Speculative - __testlib_ezbenchcontrol())); \ +#define EZBENCH_N(NAME, N, EXPR) \ + do { \ + int64_t Speculative, Toto; \ + int Core, Tries, Interrupts; \ + Tries = 0; \ + do { \ + __testlib_yield(); \ + Core = __testlib_getcore(); \ + Interrupts = __testlib_getinterrupts(); \ + EXPR; \ + Speculative = BENCHLOOP(__startbench, __endbench, 32, \ + __polluteregisters(), (EXPR)); \ + } while (++Tries < EZBENCH_TRIES && \ + (__testlib_getcore() != Core && \ + __testlib_getinterrupts() > Interrupts)); \ + if (Tries == EZBENCH_TRIES) __testlib_ezbenchwarn(""); \ + __testlib_ezbenchreport_n( \ + NAME, 'n', N, MAX(0, Speculative - __testlib_ezbenchcontrol())); \ } while (0) #define EZBENCH_K(NAME, K, EXPR) \ @@ -164,14 +176,14 @@ COSMOPOLITAN_C_START_ __testlib_yield(); \ Core = __testlib_getcore(); \ EXPR; \ - Speculative = \ - BENCHLOOP(__startbench, __endbench, 128, donothing, (EXPR)); \ + Speculative = BENCHLOOP(__startbench, __endbench, EZBENCH_COUNT, \ + donothing, (EXPR)); \ } while (Core != __testlib_getcore()); \ __testlib_ezbenchreport_n( \ NAME, 'k', K, MAX(0, Speculative - __testlib_ezbenchcontrol())); \ } while (0) -void polluteregisters(void); +void __polluteregisters(void); void __testlib_yield(void); int __testlib_getcore(void); int64_t __testlib_getinterrupts(void); diff --git a/libc/testlib/polluteregisters.S b/libc/testlib/polluteregisters.S index 7b8a1c1be..62971dc97 100644 --- a/libc/testlib/polluteregisters.S +++ b/libc/testlib/polluteregisters.S @@ -19,15 +19,15 @@ #include "libc/nexgen32e/x86feature.h" #include "libc/macros.internal.h" -polluteregisters: +__polluteregisters: .leafprologue xor %eax,%eax - mov %ecx,%ecx - mov %edx,%edx - mov %r8d,%r8d - mov %r9d,%r9d - mov %r10d,%r10d - mov %r11d,%r11d + xor %ecx,%ecx + xor %edx,%edx + xor %r8d,%r8d + xor %r9d,%r9d + xor %r10d,%r10d + xor %r11d,%r11d testb X86_HAVE(AVX)+kCpuids(%rip) jz .Lsse vpxor %xmm0,%xmm0,%xmm0 @@ -48,13 +48,13 @@ polluteregisters: xorps %xmm6,%xmm6 xorps %xmm7,%xmm7 .leafepilogue - .endfn polluteregisters,globl + .endfn __polluteregisters,globl .end // Fill registers with junk data to create false dependencies. // Which shall create the problem that happens w/o vzeroupper. // Or the Core Architecture errata regarding BSR/BSF w/ 64bit. -polluteregisters: +__polluteregisters: .leafprologue mov $-1,%rax mov %rax,%rcx @@ -92,4 +92,4 @@ polluteregisters: punpcklqdq %xmm0,%xmm6 punpcklqdq %xmm0,%xmm7 .leafepilogue - .endfn polluteregisters,globl + .endfn __polluteregisters,globl diff --git a/test/libc/calls/stat_test.c b/test/libc/calls/stat_test.c index b32ed2306..61526d11f 100644 --- a/test/libc/calls/stat_test.c +++ b/test/libc/calls/stat_test.c @@ -17,6 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/calls/struct/metastat.internal.h" #include "libc/calls/struct/stat.h" #include "libc/dce.h" #include "libc/errno.h" @@ -24,6 +26,7 @@ #include "libc/runtime/gc.internal.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" +#include "libc/sysv/consts/at.h" #include "libc/sysv/consts/nr.h" #include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" @@ -68,18 +71,34 @@ static long Stat(const char *path, struct stat *st) { return ax; } +static long Fstatat(const char *path, struct stat *st) { + long ax, di, si, dx; + register long r10 asm("r10") = 0; + asm volatile("syscall" + : "=a"(ax), "=D"(di), "=S"(si), "=d"(dx), "+r"(r10) + : "0"(__NR_fstatat), "1"(AT_FDCWD), "2"(path), "3"(st) + : "rcx", "r8", "r9", "r11", "memory", "cc"); + return ax; +} + BENCH(stat, bench) { struct stat st; + union metastat ms; EXPECT_SYS(0, 0, makedirs(".python/test", 0755)); + EZBENCH2("__stat2cosmo", donothing, __stat2cosmo(&st, &ms)); EXPECT_SYS(0, 0, touch(".python/test/" "tokenize_tests-latin1-coding-cookie-and-utf8-bom-sig.txt", 0644)); - if (!IsWindows()) { + if (!IsWindows() && !IsFreebsd()) { EZBENCH2("stat syscall", donothing, Stat(".python/test/" "tokenize_tests-latin1-coding-cookie-and-utf8-bom-sig.txt", &st)); + EZBENCH2("fstatat syscall", donothing, + Fstatat(".python/test/" + "tokenize_tests-latin1-coding-cookie-and-utf8-bom-sig.txt", + &st)); } EZBENCH2("stat() fs", donothing, stat(".python/test/" diff --git a/test/libc/runtime/mmap_test.c b/test/libc/runtime/mmap_test.c index 1f56f6b38..316517888 100644 --- a/test/libc/runtime/mmap_test.c +++ b/test/libc/runtime/mmap_test.c @@ -19,8 +19,11 @@ #include "libc/bits/bits.h" #include "libc/bits/xchg.internal.h" #include "libc/calls/calls.h" +#include "libc/dce.h" #include "libc/fmt/fmt.h" #include "libc/intrin/kprintf.h" +#include "libc/linux/mmap.h" +#include "libc/linux/munmap.h" #include "libc/log/log.h" #include "libc/mem/mem.h" #include "libc/rand/rand.h" @@ -33,6 +36,7 @@ #include "libc/sysv/consts/msync.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/prot.h" +#include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" #include "libc/x/x.h" @@ -281,3 +285,57 @@ TEST(mmap, sharedFileMapFork) { EXPECT_NE(-1, close(fd)); EXPECT_NE(-1, unlink(path)); } + +//////////////////////////////////////////////////////////////////////////////// +// BENCHMARKS + +#define N (EZBENCH_COUNT * EZBENCH_TRIES) + +int count; +void *ptrs[N]; + +void BenchUnmap(void) { + int i; + for (i = 0; i < count; ++i) { + if (ptrs[i]) { + ASSERT_EQ(0, munmap(ptrs[i], FRAMESIZE)); + } + } +} + +void BenchMmapPrivate(void) { + void *p; + p = mmap(0, FRAMESIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, + -1, 0); + if (p == MAP_FAILED) abort(); + ptrs[count++] = p; +} + +BENCH(mmap, bench) { + EZBENCH2("mmap", donothing, BenchMmapPrivate()); + BenchUnmap(); +} + +void BenchUnmapLinux(void) { + int i; + for (i = 0; i < count; ++i) { + if (ptrs[i]) { + ASSERT_EQ(0, LinuxMunmap(ptrs[i], FRAMESIZE)); + } + } +} + +void BenchMmapPrivateLinux(void) { + void *p; + p = (void *)LinuxMmap(0, FRAMESIZE, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (p == MAP_FAILED) abort(); + ptrs[count++] = p; +} + +BENCH(mmap, benchLinux) { + void *p; + if (!IsLinux()) return; + EZBENCH2("mmap (linux)", donothing, BenchMmapPrivateLinux()); + BenchUnmapLinux(); +} diff --git a/third_party/linenoise/linenoise.c b/third_party/linenoise/linenoise.c index 6c7a727f3..7d04b7c43 100644 --- a/third_party/linenoise/linenoise.c +++ b/third_party/linenoise/linenoise.c @@ -193,13 +193,14 @@ Copyright 2010-2013 Pieter Noordhuis \""); #define DEBUG(L, ...) (void)0 #endif -#define DUFF_ROUTINE_LOOP 0 -#define DUFF_ROUTINE_START 5 +#define DUFF_ROUTINE_LOOP 0 +#define DUFF_ROUTINE_SEARCH 1 +#define DUFF_ROUTINE_START 5 #define DUFF_ROUTINE_LABEL(STATE) \ case STATE: \ linenoiseRefreshLineForce(l); \ - l->state = DUFF_ROUTINE_LOOP + l->state = STATE #define DUFF_ROUTINE_READ(STATE) \ DUFF_ROUTINE_LABEL(STATE); \ @@ -469,11 +470,11 @@ static const char *FindSubstringReverse(const char *p, size_t n, const char *q, n -= m; do { for (i = 0; i < m; ++i) { - if (p[n + i] != q[i]) { + if (kToLower[p[n + i] & 255] != kToLower[q[i] & 255]) { break; } } - if (i == m) { + if (kToLower[i & 255] == kToLower[m & 255]) { return p + n; } } while (n--); @@ -1852,7 +1853,8 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf, char seq[16]; gotint = 0; - if (prompt && (!l->prompt || strcmp(prompt, l->prompt))) { + if (prompt && l->state != DUFF_ROUTINE_SEARCH && + (!l->prompt || strcmp(prompt, l->prompt))) { free(l->prompt); l->prompt = strdup(prompt); } @@ -1885,7 +1887,7 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf, for (fail = l->matlen = 0;;) { free(l->prompt); l->prompt = linenoiseMakeSearchPrompt(fail, l->ab.b, l->matlen); - DUFF_ROUTINE_READ(1); + DUFF_ROUTINE_READ(DUFF_ROUTINE_SEARCH); fail = 1; added = 0; l->j = l->pos; @@ -1906,7 +1908,6 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf, } else if (seq[0] == CTRL('G')) { linenoiseEditHistoryGoto(l, l->oldindex); l->pos = l->olderpos; - rc = 0; break; } else if (iswcntrl(seq[0])) { // only sees canonical c0 break; @@ -1953,7 +1954,7 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf, rc = 0; linenoiseFreeCompletions(&l->lc); i = Backwards(l, l->pos, iswname); - j = Forwards(l, l->pos, iswname); + j = l->pos; { char *s = strndup(l->buf + i, j - i); completionCallback(s, &l->lc); @@ -1966,7 +1967,8 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf, if (linenoiseGrow(l, n + 1)) { memmove(l->buf + i + m, l->buf + i + j, l->len - j + 1); memcpy(l->buf + i, l->lc.cvec[0], m); - l->len = l->pos = n; + l->pos = i + m; + l->len = n; } continue; } diff --git a/third_party/lua/cosmo.h b/third_party/lua/cosmo.h index 10a0bbc04..6b8980b8b 100644 --- a/third_party/lua/cosmo.h +++ b/third_party/lua/cosmo.h @@ -8,8 +8,8 @@ COSMOPOLITAN_C_START_ char *LuaFormatStack(lua_State *) dontdiscard; int LuaCallWithTrace(lua_State *, int, int, lua_State *); -int LuaEncodeJsonData(lua_State *, char **, int, char *); -int LuaEncodeLuaData(lua_State *, char **, int, char *); +int LuaEncodeJsonData(lua_State *, char **, int, char *, int); +int LuaEncodeLuaData(lua_State *, char **, int, char *, int); int LuaEncodeUrl(lua_State *); int LuaParseUrl(lua_State *); int LuaPushHeader(lua_State *, struct HttpMessage *, char *, int); diff --git a/third_party/lua/escapeluastring.c b/third_party/lua/escapeluastring.c index 9574b80cc..24d9f730c 100644 --- a/third_party/lua/escapeluastring.c +++ b/third_party/lua/escapeluastring.c @@ -16,20 +16,25 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/bits.h" #include "libc/stdio/append.internal.h" +#include "libc/str/str.h" #include "third_party/lua/cosmo.h" #include "third_party/lua/lua.h" void EscapeLuaString(char *s, size_t len, char **buf) { appendw(buf, '"'); for (size_t i = 0; i < len; i++) { - if (s[i] == '\\' || s[i] == '\"' || s[i] == '\n' || s[i] == '\0' || - s[i] == '\r') { + if (' ' <= s[i] && s[i] <= 0x7e) { + appendw(buf, s[i]); + } else if (s[i] == '\n') { + appendw(buf, '\\' | 'n' << 8); + } else if (s[i] == '\\' || s[i] == '\'' || s[i] == '\"') { + appendw(buf, '\\' | s[i] << 8); + } else { appendw(buf, '\\' | 'x' << 010 | "0123456789abcdef"[(s[i] & 0xF0) >> 4] << 020 | "0123456789abcdef"[(s[i] & 0x0F) >> 0] << 030); - } else { - appendd(buf, s + i, 1); } } appendw(buf, '"'); diff --git a/third_party/lua/lrepl.c b/third_party/lua/lrepl.c index 0fb0d1372..cf3d131e4 100644 --- a/third_party/lua/lrepl.c +++ b/third_party/lua/lrepl.c @@ -32,6 +32,7 @@ #include "libc/intrin/kprintf.h" #include "libc/intrin/nomultics.internal.h" #include "libc/log/check.h" +#include "libc/macros.internal.h" #include "libc/runtime/gc.h" #include "libc/runtime/runtime.h" #include "libc/sysv/consts/sa.h" @@ -50,6 +51,17 @@ Copyright 1994–2021 Lua.org, PUC-Rio.\""); asm(".include \"libc/disclaimer.inc\""); +static const char *const kKeywordHints[] = { + "else", // + "elseif", // + "function", // + "function", // + "repeat", // + "then", // + "until", // + "while", // +}; + bool lua_repl_blocking; bool lua_repl_isterminal; linenoiseCompletionCallback *lua_repl_completions_callback; @@ -74,46 +86,59 @@ static const char *g_historypath; #define LUA_MAXINPUT 512 #endif -static void lua_readline_addcompletion(linenoiseCompletions *c, char *s) { - char **p = c->cvec; - size_t n = c->len + 1; - if ((p = realloc(p, n * sizeof(*p)))) { - p[n - 1] = s; + +static void lua_readline_addcompletion (linenoiseCompletions *c, const char *s) { + char **p, *t; + if ((p = realloc(c->cvec, (c->len + 1) * sizeof(*p)))) { c->cvec = p; - c->len = n; + if ((t = strdup(s))) { + c->cvec[c->len++] = t; + } } } -void lua_readline_completions(const char *p, linenoiseCompletions *c) { + +void lua_readline_completions (const char *p, linenoiseCompletions *c) { + int i; lua_State *L; const char *name; + for (i = 0; i < ARRAYLEN(kKeywordHints); ++i) { + if (startswithi(kKeywordHints[i], p)) { + lua_readline_addcompletion(c, kKeywordHints[i]); + } + } L = globalL; lua_pushglobaltable(L); lua_pushnil(L); while (lua_next(L, -2) != 0) { name = lua_tostring(L, -2); if (startswithi(name, p)) { - lua_readline_addcompletion(c, strdup(name)); + lua_readline_addcompletion(c, name); } lua_pop(L, 1); } lua_pop(L, 1); - lua_repl_completions_callback(p, c); + if (lua_repl_completions_callback) { + lua_repl_completions_callback(p, c); + } } -char *lua_readline_hint(const char *p, const char **ansi1, const char **ansi2) { - char *h = 0; + +char *lua_readline_hint (const char *p, const char **ansi1, const char **ansi2) { + char *h; linenoiseCompletions c = {0}; lua_readline_completions(p, &c); - if (c.len == 1) h = strdup(c.cvec[0] + strlen(p)); + h = c.len == 1 ? strdup(c.cvec[0] + strlen(p)) : 0; linenoiseFreeCompletions(&c); return h; } + static void lua_freeline (lua_State *L, char *b) { free(b); } + /* ** Return the string to be used as a prompt by the interpreter. Leave ** the string (or nil, if using the default value) on the stack, to keep @@ -129,6 +154,7 @@ static const char *get_prompt (lua_State *L, int firstline) { } } + /* mark in error messages for incomplete statements */ #define EOFMARK "" #define marklen (sizeof(EOFMARK)/sizeof(char) - 1) @@ -165,7 +191,7 @@ static ssize_t pushline (lua_State *L, int firstline) { prmt = strdup(get_prompt(L, firstline)); lua_pop(L, 1); /* remove prompt */ LUA_REPL_UNLOCK; - rc = linenoiseEdit(lua_repl_linenoise, prmt, &b, !firstline || lua_repl_blocking); + rc = linenoiseEdit(lua_repl_linenoise, 0, &b, !firstline || lua_repl_blocking); free(prmt); if (rc != -1) { if (b && *b) { @@ -187,10 +213,11 @@ static ssize_t pushline (lua_State *L, int firstline) { l = strlen(b); if (l > 0 && b[l-1] == '\n') /* line ends with newline? */ b[--l] = '\0'; /* remove it */ - if (firstline && b[0] == '=') /* for compatibility with 5.2, ... */ + if (firstline && b[0] == '=') { /* for compatibility with 5.2, ... */ lua_pushfstring(L, "return %s", b + 1); /* change '=' to 'return' */ - else + } else { lua_pushlstring(L, b, l); + } lua_freeline(L, b); return 1; } diff --git a/third_party/lua/lua.mk b/third_party/lua/lua.mk index 2fc49188b..4b892d8d4 100644 --- a/third_party/lua/lua.mk +++ b/third_party/lua/lua.mk @@ -80,6 +80,14 @@ o/$(MODE)/third_party/lua/lua.com: \ @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ o/$(MODE)/third_party/lua/.lua/.symtab +o//third_party/lua/lgc.o: \ + OVERRIDE_CFLAGS += \ + -O2 + +o/$(MODE)/third_party/lua/lvm.o: \ + OVERRIDE_CFLAGS += \ + -fno-gcse + o/$(MODE)/third_party/lua/lauxlib.o: \ OVERRIDE_CFLAGS += \ -DSTACK_FRAME_UNLIMITED @@ -89,6 +97,8 @@ $(THIRD_PARTY_LUA_OBJS): \ -ffunction-sections \ -fdata-sections +$(THIRD_PARTY_LUA_OBJS): third_party/lua/lua.mk + .PHONY: o/$(MODE)/third_party/lua o/$(MODE)/third_party/lua: \ $(THIRD_PARTY_LUA_BINS) \ diff --git a/third_party/lua/luaencodejsondata.c b/third_party/lua/luaencodejsondata.c index 6cc64cd5a..d894ff0e5 100644 --- a/third_party/lua/luaencodejsondata.c +++ b/third_party/lua/luaencodejsondata.c @@ -16,6 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/bits.h" +#include "libc/fmt/itoa.h" #include "libc/runtime/gc.internal.h" #include "libc/stdio/append.internal.h" #include "net/http/escape.h" @@ -23,63 +25,115 @@ #include "third_party/lua/lauxlib.h" #include "third_party/lua/lua.h" -int LuaEncodeJsonData(lua_State *L, char **buf, int level, char *numformat) { - size_t idx = -1; - size_t tbllen, buflen; +int LuaEncodeJsonData(lua_State *L, char **buf, int level, char *numformat, + int idx) { + char *s; bool isarray; - int t = lua_type(L, idx); - if (level < 0) return luaL_argerror(L, 1, "too many nested tables"); - if (LUA_TSTRING == t) { - appendw(buf, '"'); - appends(buf, gc(EscapeJsStringLiteral(lua_tostring(L, idx), -1, 0))); - appendw(buf, '"'); - } else if (LUA_TNUMBER == t) { - appendf(buf, numformat, lua_tonumber(L, idx)); - } else if (LUA_TBOOLEAN == t) { - appends(buf, lua_toboolean(L, idx) ? "true" : "false"); - } else if (LUA_TTABLE == t) { - tbllen = lua_rawlen(L, idx); - // encode tables with numeric indices and empty tables as arrays - isarray = tbllen > 0 || // integer keys present - (lua_pushnil(L), lua_next(L, -2) == 0) || // no non-integer keys - (lua_pop(L, 2), false); // pop key/value pushed by lua_next - appendw(buf, isarray ? '[' : '{'); - if (isarray) { - for (int i = 1; i <= tbllen; i++) { - if (i > 1) appendw(buf, ','); - lua_rawgeti(L, -1, i); // table/-2, value/-1 - LuaEncodeJsonData(L, buf, level - 1, numformat); - lua_pop(L, 1); - } - } else { - int i = 1; - lua_pushnil(L); // push the first key - while (lua_next(L, -2) != 0) { - if (!lua_isstring(L, -2)) - return luaL_argerror(L, 1, "expected number or string as key value"); - if (i++ > 1) appendw(buf, ','); + size_t tbllen, z; + char ibuf[21], fmt[] = "%.14g"; + if (level > 0) { + switch (lua_type(L, idx)) { + case LUA_TSTRING: + s = lua_tolstring(L, idx, &z); + s = EscapeJsStringLiteral(s, z, &z); appendw(buf, '"'); - if (lua_type(L, -2) == LUA_TSTRING) { - appends(buf, gc(EscapeJsStringLiteral(lua_tostring(L, -2), -1, 0))); + appendd(buf, s, z); + appendw(buf, '"'); + free(s); + return 0; + case LUA_TNIL: + appendw(buf, READ32LE("null")); + return 0; + case LUA_TFUNCTION: + appendf(buf, "\"func@%p\"", lua_touserdata(L, idx)); + return 0; + case LUA_TUSERDATA: + appendf(buf, "\"udata@%p\"", lua_touserdata(L, idx)); + return 0; + case LUA_TLIGHTUSERDATA: + appendf(buf, "\"light@%p\"", lua_touserdata(L, idx)); + return 0; + case LUA_TTHREAD: + appendf(buf, "\"thread@%p\"", lua_touserdata(L, idx)); + return 0; + case LUA_TNUMBER: + if (lua_isinteger(L, idx)) { + appendd(buf, ibuf, + FormatInt64(ibuf, luaL_checkinteger(L, idx)) - ibuf); } else { - // we'd still prefer to use lua_tostring on a numeric index, but can't - // use it in-place, as it breaks lua_next (changes numeric key to a - // string) - lua_pushvalue(L, -2); // table/-4, key/-3, value/-2, key/-1 - appends(buf, lua_tostring(L, idx)); // use the copy - lua_remove(L, -1); // remove copied key: table/-3, key/-2, value/-1 + // TODO(jart): replace this api + while (*numformat == '%' || *numformat == '.' || + isdigit(*numformat)) { + ++numformat; + } + switch (*numformat) { + case 'a': + case 'g': + case 'f': + fmt[4] = *numformat; + break; + default: + return luaL_error(L, "numformat string not allowed"); + } + appendf(buf, fmt, lua_tonumber(L, idx)); } - appendw(buf, '"' | ':' << 010); - LuaEncodeJsonData(L, buf, level - 1, numformat); - lua_pop(L, 1); // table/-2, key/-1 - } - // stack: table/-1, as the key was popped by lua_next + return 0; + case LUA_TBOOLEAN: + appends(buf, lua_toboolean(L, idx) ? "true" : "false"); + return 0; + case LUA_TTABLE: + tbllen = lua_rawlen(L, idx); + // encode tables with numeric indices and empty tables as arrays + isarray = + tbllen > 0 || // integer keys present + (lua_pushnil(L), lua_next(L, -2) == 0) || // no non-integer keys + (lua_pop(L, 2), false); // pop key/value pushed by lua_next + appendw(buf, isarray ? '[' : '{'); + if (isarray) { + for (size_t i = 1; i <= tbllen; i++) { + if (i > 1) appendw(buf, ','); + lua_rawgeti(L, -1, i); // table/-2, value/-1 + LuaEncodeJsonData(L, buf, level - 1, numformat, -1); + lua_pop(L, 1); + } + } else { + int i = 1; + lua_pushnil(L); // push the first key + while (lua_next(L, -2)) { + if (!lua_isstring(L, -2)) { + luaL_error(L, "expected number or string as key value"); + unreachable; + } + if (i++ > 1) appendw(buf, ','); + appendw(buf, '"'); + if (lua_type(L, -2) == LUA_TSTRING) { + s = lua_tolstring(L, -2, &z); + s = EscapeJsStringLiteral(s, z, &z); + appendd(buf, s, z); + free(s); + } else { + // we'd still prefer to use lua_tostring on a numeric index, but + // can't use it in-place, as it breaks lua_next (changes numeric + // key to a string) + lua_pushvalue(L, -2); // table/-4, key/-3, value/-2, key/-1 + s = lua_tolstring(L, idx, &z); + appendd(buf, s, z); // use the copy + lua_remove(L, -1); // remove copied key: tab/-3, key/-2, val/-1 + } + appendw(buf, '"' | ':' << 010); + LuaEncodeJsonData(L, buf, level - 1, numformat, -1); + lua_pop(L, 1); // table/-2, key/-1 + } + // stack: table/-1, as the key was popped by lua_next + } + appendw(buf, isarray ? ']' : '}'); + return 0; + default: + luaL_error(L, "can't serialize value of this type"); + unreachable; } - appendw(buf, isarray ? ']' : '}'); - } else if (LUA_TNIL == t) { - appendd(buf, "null", 4); } else { - return luaL_argerror(L, 1, "can't serialize value of this type"); + luaL_error(L, "too many nested tables"); + unreachable; } - return 0; } diff --git a/third_party/lua/luaencodeluadata.c b/third_party/lua/luaencodeluadata.c index fbee64e63..b07bf852c 100644 --- a/third_party/lua/luaencodeluadata.c +++ b/third_party/lua/luaencodeluadata.c @@ -16,51 +16,97 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/bits.h" +#include "libc/fmt/itoa.h" #include "libc/math.h" #include "libc/stdio/append.internal.h" #include "third_party/lua/cosmo.h" #include "third_party/lua/lauxlib.h" #include "third_party/lua/lua.h" -int LuaEncodeLuaData(lua_State *L, char **buf, int level, char *numformat) { - size_t idx = -1; - size_t tbllen, buflen, slen; +int LuaEncodeLuaData(lua_State *L, char **buf, int level, char *numformat, + int idx) { char *s; int ktype; - int t = lua_type(L, idx); - if (level < 0) return luaL_argerror(L, 1, "too many nested tables"); - if (LUA_TSTRING == t) { - s = lua_tolstring(L, idx, &slen); - EscapeLuaString(s, slen, buf); - } else if (LUA_TNUMBER == t) { - appendf(buf, numformat, lua_tonumber(L, idx)); - } else if (LUA_TBOOLEAN == t) { - appends(buf, lua_toboolean(L, idx) ? "true" : "false"); - } else if (LUA_TTABLE == t) { - appendw(buf, '{'); - int i = 0; - lua_pushnil(L); // push the first key - while (lua_next(L, -2) != 0) { - ktype = lua_type(L, -2); - if (ktype == LUA_TTABLE) - return luaL_argerror(L, 1, "can't serialize key of this type"); - if (i++ > 0) appendd(buf, ",", 1); - if (ktype != LUA_TNUMBER || floor(lua_tonumber(L, -2)) != i) { - appendw(buf, '['); - lua_pushvalue(L, -2); // table/-4, key/-3, value/-2, key/-1 - LuaEncodeLuaData(L, buf, level, numformat); - lua_remove(L, -1); // remove copied key: table/-3, key/-2, value/-1 - appendw(buf, ']' | '=' << 010); - } - LuaEncodeLuaData(L, buf, level - 1, numformat); - lua_pop(L, 1); // table/-2, key/-1 + lua_Integer i; + size_t tbllen, buflen, slen; + char ibuf[21], fmt[] = "%.14g"; + if (level > 0) { + switch (lua_type(L, idx)) { + case LUA_TNIL: + appendw(buf, READ32LE("nil")); + return 0; + case LUA_TSTRING: + s = lua_tolstring(L, idx, &slen); + EscapeLuaString(s, slen, buf); + return 0; + case LUA_TFUNCTION: + appendf(buf, "\"func@%p\"", lua_touserdata(L, idx)); + return 0; + case LUA_TUSERDATA: + appendf(buf, "\"udata@%p\"", lua_touserdata(L, idx)); + return 0; + case LUA_TLIGHTUSERDATA: + appendf(buf, "\"light@%p\"", lua_touserdata(L, idx)); + return 0; + case LUA_TTHREAD: + appendf(buf, "\"thread@%p\"", lua_touserdata(L, idx)); + return 0; + case LUA_TNUMBER: + if (lua_isinteger(L, idx)) { + appendd(buf, ibuf, + FormatInt64(ibuf, luaL_checkinteger(L, idx)) - ibuf); + } else { + // TODO(jart): replace this api + while (*numformat == '%' || *numformat == '.' || + isdigit(*numformat)) { + ++numformat; + } + switch (*numformat) { + case 'a': + case 'g': + case 'f': + fmt[4] = *numformat; + break; + default: + return luaL_error(L, "numformat string not allowed"); + } + appendf(buf, fmt, lua_tonumber(L, idx)); + } + return 0; + case LUA_TBOOLEAN: + if (lua_toboolean(L, idx)) { + appendw(buf, READ32LE("true")); + } else { + appendw(buf, READ64LE("false\0\0")); + } + return 0; + case LUA_TTABLE: + i = 0; + appendw(buf, '{'); + lua_pushnil(L); // push the first key + while (lua_next(L, -2) != 0) { + ktype = lua_type(L, -2); + if (i++ > 0) appendw(buf, ','); + if (ktype != LUA_TNUMBER || lua_tointeger(L, -2) != i) { + appendw(buf, '['); + lua_pushvalue(L, -2); // table/-4, key/-3, value/-2, key/-1 + LuaEncodeLuaData(L, buf, level - 1, numformat, -1); + lua_remove(L, -1); // remove copied key: table/-3, key/-2, value/-1 + appendw(buf, ']' | '=' << 010); + } + LuaEncodeLuaData(L, buf, level - 1, numformat, -1); + lua_pop(L, 1); // table/-2, key/-1 + } + // stack: table/-1, as the key was popped by lua_next + appendw(buf, '}'); + return 0; + default: + luaL_error(L, "can't serialize value of this type"); + unreachable; } - // stack: table/-1, as the key was popped by lua_next - appendw(buf, '}'); - } else if (LUA_TNIL == t) { - appendd(buf, "nil", 3); } else { - return luaL_argerror(L, 1, "can't serialize value of this type"); + luaL_error(L, "too many nested tables"); + unreachable; } - return 0; } diff --git a/third_party/lua/luaformatstack.c b/third_party/lua/luaformatstack.c index 936a658f2..bf155443c 100644 --- a/third_party/lua/luaformatstack.c +++ b/third_party/lua/luaformatstack.c @@ -18,31 +18,17 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/stdio/append.internal.h" #include "third_party/lua/cosmo.h" +#include "third_party/lua/lauxlib.h" dontdiscard char *LuaFormatStack(lua_State *L) { + size_t l; int i, top; - char *b = 0; + char *p, *b = 0; top = lua_gettop(L); for (i = 1; i <= top; i++) { if (i > 1) appendw(&b, '\n'); appendf(&b, "\t%d\t%s\t", i, luaL_typename(L, i)); - switch (lua_type(L, i)) { - case LUA_TNUMBER: - appendf(&b, "%g", lua_tonumber(L, i)); - break; - case LUA_TSTRING: - appends(&b, lua_tostring(L, i)); - break; - case LUA_TBOOLEAN: - appends(&b, lua_toboolean(L, i) ? "true" : "false"); - break; - case LUA_TNIL: - appends(&b, "nil"); - break; - default: - appendf(&b, "%p", lua_topointer(L, i)); - break; - } + LuaEncodeLuaData(L, &b, 64, "g", -1); } return b; } diff --git a/tool/build/blinkenlights.c b/tool/build/blinkenlights.c index 620ddae04..6bdc99e08 100644 --- a/tool/build/blinkenlights.c +++ b/tool/build/blinkenlights.c @@ -2270,8 +2270,7 @@ static void OnVidyaServiceTeletypeOutput(void) { char buf[12]; n = 0 /* FormatCga(m->bx[0], buf) */; w = tpenc(VidyaServiceXlatTeletype(m->ax[0])); - do - buf[n++] = w; + do buf[n++] = w; while ((w >>= 8)); PtyWrite(pty, buf, n); } diff --git a/tool/build/build.mk b/tool/build/build.mk index eb949f080..b2d1b9eb9 100644 --- a/tool/build/build.mk +++ b/tool/build/build.mk @@ -89,6 +89,7 @@ o/$(MODE)/tool/build/blinkenlights.com.dbg: \ $(APE_NO_MODIFY_SELF) @$(APELINK) +.PRECIOUS: o/$(MODE)/tool/build/blinkenlights.com o/$(MODE)/tool/build/blinkenlights.com: \ o/$(MODE)/tool/build/blinkenlights.com.dbg \ o/$(MODE)/third_party/zip/zip.com \ diff --git a/tool/net/demo/unix-info.lua b/tool/net/demo/unix-info.lua index 611eab9bf..11ca45aa3 100644 --- a/tool/net/demo/unix-info.lua +++ b/tool/net/demo/unix-info.lua @@ -88,6 +88,14 @@ else Write('
%s\r\n' % {enabled}) end +errno, enabled = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_ACCEPTCONN) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_ACCEPTCONN)\r\n') +if errno then + Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) +else + Write('
%s\r\n' % {enabled}) +end + errno, enabled = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEADDR) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEADDR)\r\n') if errno then diff --git a/tool/net/demo/unix-subprocess.lua b/tool/net/demo/unix-subprocess.lua index 5e0fbe7b9..f4a8b3ea5 100644 --- a/tool/net/demo/unix-subprocess.lua +++ b/tool/net/demo/unix-subprocess.lua @@ -13,9 +13,9 @@ function main() syscall = 'pipe' reader, writer, errno = unix.pipe() if reader then - -- oldint = unix.sigaction(unix.SIGINT, unix.SIG_IGN) - -- oldquit = unix.sigaction(unix.SIGQUIT, unix.SIG_IGN) - -- oldmask = unix.sigprocmask(unix.SIG_BLOCK, unix.SIGCHLD) + oldint = unix.sigaction(unix.SIGINT, unix.SIG_IGN) + oldquit = unix.sigaction(unix.SIGQUIT, unix.SIG_IGN) + oldmask = unix.sigprocmask(unix.SIG_BLOCK, (1 << (unix.SIGCHLD - 1))) syscall = 'fork' child, errno = unix.fork() if child then diff --git a/tool/net/help.txt b/tool/net/help.txt index 47da46e38..9a1c60ce4 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -42,6 +42,7 @@ FLAGS -u uniprocess -z print port -m log messages + -i interpreter mode -b log message bodies -a log resource usage -g log handler latency @@ -236,9 +237,9 @@ USAGE REPL - Your redbean displays a REPL that lets you modify the state of the - main server process while your server is running. Any changes will - propagate into forked clients. + Your redbean displays a Read-Eval-Print-Loop that lets you modify the + state of the main server process while your server is running. Any + changes will propagate into forked clients. Your REPL is displayed only when redbean is run as a non-daemon in a UNIX terminal or the Windows 10 command prompt or powershell. Since @@ -250,6 +251,20 @@ REPL A history of your commands is saved to `~/.redbean_history`. + If you love the redbean repl and want to use it as your language + interpreter then you can pass the `-i` flag to put redbean into + interpreter mode. + + redbean.com -i binarytrees.lua 15 + + In this mode redbean won't start a web server and instead functions + like the `lua` command. The first command line argument becomes the + script you want to run. If you don't supply a script, then the repl + without a web server is displayed. + + This can be useful for testing, since the redbean extensions and + modules for the Lua language are still made available. + SECURITY @@ -357,12 +372,36 @@ SPECIAL PATHS GLOBALS - argv: array[str] + arg: array[str] + Array of command line arguments, excluding those parsed by getopt() in the C code, which stops parsing at the first non-hyphenated arg. In some cases you can use the magic -- argument to delimit C from Lua arguments. + For example, if you launch your redbean as follows: + + redbean.com -v arg1 arg2 + + Then your `/.init.lua` file will have the `arg` array like: + + arg[-1] = '/usr/bin/redbean.com' + arg[ 0] = '/zip/.init.lua' + arg[ 1] = 'arg1' + arg[ 2] = 'arg2' + + If you launch redbean in interpreter mode (rather than web + server) mode, then an invocation like this: + + ./redbean.com -i script.lua arg1 arg2 + + Would have an `arg` array like this: + + arg[-1] = './redbean.com' + arg[ 0] = 'script.lua' + arg[ 1] = 'arg1' + arg[ 2] = 'arg2' + HOOKS @@ -522,7 +561,8 @@ FUNCTIONS - useoutput: (bool=false) encodes the result directly to the output buffer and returns `nil` value. This option is ignored if used outside of request handling code. - - numformat: (string="%.14g") sets numeric format to be used. + - numformat: sets numeric format to be used, which can be 'g', + 'f', or 'a' [experimental api] - maxdepth: (number=64) sets the max number of nested tables. EncodeLua(value[,options:table]) → json:str @@ -531,7 +571,6 @@ FUNCTIONS - useoutput: (bool=false) encodes the result directly to the output buffer and returns `nil` value. This option is ignored if used outside of request handling code. - - numformat: (string="%.14g") sets numeric format to be used. - maxdepth: (number=64) sets the max number of nested tables. EncodeLatin1(utf-8:str[,flags:int]) → iso-8859-1:str @@ -1329,52 +1368,25 @@ MAXMIND MODULE UNIX MODULE - This module exposes the low-level UNIX system call interface. The way - these functions work is they'll only throw a Lua exception if there's - some kind of error obtaining the required arguments. Once Lua reads - the arguments and the call is delegated to the system call interface, - all further errors won't be raised, but rather returned as errnos, - which should always be checked. For example, most syscalls follow: + This module exposes the low-level UNIX system call interface. This + module works on all supported platforms, including Windows NT. - errno = unix.foo(...) - if errno then - Log(kLogWarn, 'foo() failed: %s' % {unix.strerror(errno)}) - end - - Any POSIX API that's defined as returning 0 on success or -1 on error - is wrapped here to return nil on success and an integer on error. To - see which errnos are possible for which system calls, please see the - comprehensive index at the bottom of this section. - - In cases where POSIX defines an API as returning codes on success we - wrap the APIs as follows: - - rc, errno = unix.bar(...) - if rc then - Log(kLogWarn, 'foo() succeeded: %d' % {rc}) - else - Log(kLogWarn, 'foo() failed: %s' % {unix.strerror(errno)}) - end - - If the above code succeeds, `rc` will be non-nil and `errno` will be - `nil`. If the above code fails, `rc` will be nil and `errno` will be - an integer greater than zero. - - UNIX FUNCTIONS - - unix.read(fd:int[, bufsiz:int, offset:int]) → data:str[, errno:int] - - Reads from file descriptor. - - unix.write(fd:int, data[, offset:int]) → wrote:int[, errno:int] - - Writes to file descriptor. - - unix.open(path:str, flags:int[, mode:int]) → fd:int[, errno:int] + unix.open(path:str, flags:int[, mode:int]) → fd:int, unix.Errno Opens file. - `flags` should have one of `O_RDONLY`, `O_WRONLY`, or `O_RDWR`. + Returns a file descriptor integer that needs to be closed, e.g. + + fd = assert(open("/etc/passwd", unix.O_RDONLY)) + print(unix.read(fd)) + unix.close(fd) + + `flags` should have one of: + + - `O_RDONLY`: open for reading + - `O_WRONLY`: open for writing + - `O_RDWR`: open for reading and writing + The following values may also be OR'd into `flags`: - `O_CREAT`: create file if it doesn't exist @@ -1412,10 +1424,18 @@ UNIX MODULE already. If it does exist then `nil` is returned along with `errno` set to `EEXIST`. - unix.close(fd:int) → errno:int + unix.close(fd:int) → ok:bool, unix.Errno Closes file descriptor. + unix.read(fd:int[, bufsiz:int, offset:int]) → data:str, unix.Errno + + Reads from file descriptor. + + unix.write(fd:int, data[, offset:int]) → wrote:int, unix.Errno + + Writes to file descriptor. + unix.exit([exitcode]) → ⊥ Invokes `_Exit(exitcode)` on the process. This will immediately @@ -1443,21 +1463,27 @@ UNIX MODULE command prompt inserts multiple environment variables with empty string as keys, for its internal bookkeeping. - unix.fork() → childpid|0:int[, errno:int] + unix.fork() → childpid|0:int, unix.Errno Creates a new process mitosis style. This returns twice. The parent process gets the nonzero pid. The child gets zero. - unix.commandv(prog:str) → path:str[, errno:int] + unix.commandv(prog:str) → path:str, unix.Errno - Performs `$PATH` lookup of executable. We automatically suffix - `.com` and `.exe` for all platforms when path searching. - By default, the current directory is not on the path. - If `prog` is an absolute path, then it's returned as-is. If - `prog` contains slashes then it's not path searched either and - will be returned if it exists. + Performs `$PATH` lookup of executable. - unix.execve(prog:str[, args:List<*>[, env:List<*>]]) → errno:int + unix = require "unix" + prog = assert(unix.commandv("ls")) + unix.execve(prog, {prog, "-hal", "."}, {PATH="/bin"}) + unix.exit(127) + + We automatically suffix `.com` and `.exe` for all platforms when + path searching. By default, the current directory is not on the + path. If `prog` is an absolute path, then it's returned as-is. If + `prog` contains slashes then it's not path searched either and will + be returned if it exists. + + unix.execve(prog:str[, args:List<*>[, env:List<*>]]) → false, unix.Errno Exits current process, replacing it with a new instance of the specified program. `prog` needs to be an absolute path, see @@ -1490,7 +1516,7 @@ UNIX MODULE `EAGAIN` is returned if you've enforced a max number of processes using `setrlimit(RLIMIT_NPROC)`. - unix.dup(oldfd:int[, newfd:int[, flags:int]]) → newfd:int, errno:int + unix.dup(oldfd:int[, newfd:int[, flags:int]]) → newfd:int, unix.Errno Duplicates file descriptor. @@ -1501,7 +1527,7 @@ UNIX MODULE `flags` can have `O_CLOEXEC` which means the returned file descriptors will be automatically closed upon execve(). - unix.pipe([flags:int]) → reader:int, writer:int[, errno:int] + unix.pipe([flags:int]) → reader:int, unix.Errno, writer:int Creates fifo which enables communication between processes. Returns two file descriptors: one for reading and one for @@ -1537,7 +1563,7 @@ UNIX MODULE end unix.wait([pid:int, options:int]) - → pid:int, wstatus:int, nil, errno:int + → pid:int, unix.Errno, wstatus:int Waits for subprocess to terminate. @@ -1590,22 +1616,22 @@ UNIX MODULE This function does not fail. - unix.kill(pid, sig) → errno:int + unix.kill(pid, sig) → ok:bool, unix.Errno Returns process id of current process. - unix.raise(sig) → rc:int[, errno:int] + unix.raise(sig) → rc:int, unix.Errno Triggers signal in current process. This is pretty much the same as `kill(getpid(), sig)`. - unix.access(path:str, how) → errno:int + unix.access(path:str, how) → ok:bool, unix.Errno Checks if effective user of current process has permission to access file. `how` can be `R_OK`, `W_OK`, `X_OK`, or `F_OK` to check for read, write, execute, and existence respectively. - unix.mkdir(path:str, mode) → errno:int + unix.mkdir(path:str, mode) → ok:bool, unix.Errno Makes directory. @@ -1628,7 +1654,7 @@ UNIX MODULE Fails with `ENAMETOOLONG` if the path is too long. - unix.makedirs(path:str, mode) → errno:int + unix.makedirs(path:str, mode) → ok:bool, unix.Errno Makes directories. @@ -1639,48 +1665,48 @@ UNIX MODULE Unlike mkdir() this convenience wrapper will automatically create parent parent directories as needed. - unix.chdir(path:str) → errno:int + unix.chdir(path:str) → ok:bool, unix.Errno Changes current directory to `path`. - unix.unlink(path:str) → errno:int + unix.unlink(path:str) → ok:bool, unix.Errno Removes file at `path`. - unix.rmdir(path:str) → errno:int + unix.rmdir(path:str) → ok:bool, unix.Errno Removes empty directory at `path`. - unix.rename(oldpath:str, newpath:str) → errno:int + unix.rename(oldpath:str, newpath:str) → ok:bool, unix.Errno Renames file or directory. - unix.link(existingpath:str, newpath:str) → errno:int + unix.link(existingpath:str, newpath:str) → ok:bool, unix.Errno Creates hard link, so your underlying inode has two names. - unix.symlink(target:str, linkpath:str) → errno:int + unix.symlink(target:str, linkpath:str) → ok:bool, unix.Errno Creates soft link, or a symbolic link. - unix.realpath(filename:str) → abspath:str[, errno:int] + unix.realpath(filename:str) → abspath:str, unix.Errno Returns absolute path of filename, with `.` and `..` components removed, and symlinks will be resolved. - unix.chown(path:str, uid, gid) → errno:int + unix.chown(path:str, uid, gid) → ok:bool, unix.Errno Changes user and gorup on file. - unix.chmod(path:str, mode) → errno:int + unix.chmod(path:str, mode) → ok:bool, unix.Errno Changes mode bits on file. - unix.getcwd() → path:str[, errno:int] + unix.getcwd() → path:str, unix.Errno Returns current working directory. - unix.fcntl(fd:int, cmd:int[, arg:int]) → rc:int[, errno:int] + unix.fcntl(fd:int, cmd:int[, arg:int]) → rc:int, unix.Errno Manipulates file descriptor. @@ -1691,27 +1717,27 @@ UNIX MODULE POSIX advisory locks can be controlled by setting `cmd` to `F_UNLCK`, `F_RDLCK`, `F_WRLCK`, `F_SETLK`, or `F_SETLKW`. - unix.getsid(pid:int) → sid:int[, errno:int] + unix.getsid(pid:int) → sid:int, unix.Errno Gets session id. - unix.getpgrp() → pgid:int[, errno:int] + unix.getpgrp() → pgid:int, unix.Errno Gets process group id. - unix.setpgrp() → pgid:int[, errno:int] + unix.setpgrp() → pgid:int, unix.Errno Sets process group id. This is the same as `setpgid(0,0)`. - unix.setpgid(pid:int, pgid:int) → pgid:int[, errno:int] + unix.setpgid(pid:int, pgid:int) → pgid:int, unix.Errno Sets process group id the modern way. - unix.getpgid(pid) → pgid:int[, errno:int] + unix.getpgid(pid) → pgid:int, unix.Errno Gets process group id the modern wayp. - unix.setsid() → sid:int[, errno:int] + unix.setsid() → sid:int, unix.Errno Sets session id. @@ -1756,13 +1782,13 @@ UNIX MODULE This function does not fail. - unix.chroot(path:str) → errno:int + unix.chroot(path:str) → ok:bool, unix.Errno Changes root directory. Returns `ENOSYS` on Windows NT. - unix.setuid(uid:int) → errno:int + unix.setuid(uid:int) → ok:bool, unix.Errno Sets user id. @@ -1798,13 +1824,13 @@ UNIX MODULE Returns `ENOSYS` on Windows NT if `uid` isn't `getuid()`. - unix.setgid(gid:int) → errno:int + unix.setgid(gid:int) → ok:bool, unix.Errno Sets group id. Returns `ENOSYS` on Windows NT if `gid` isn't `getgid()`. - unix.setresuid(real:int, effective:int, saved:int) → errno:int + unix.setresuid(real:int, effective:int, saved:int) → ok:bool, unix.Errno Sets real, effective, and saved user ids. @@ -1813,7 +1839,7 @@ UNIX MODULE Returns `ENOSYS` on Windows NT. Returns `ENOSYS` on Macintosh and NetBSD if `saved` isn't -1. - unix.setresgid(real:int, effective:int, saved:int) → errno:int + unix.setresgid(real:int, effective:int, saved:int) → ok:bool, unix.Errno Sets real, effective, and saved group ids. @@ -1854,30 +1880,40 @@ UNIX MODULE This function currently works on Linux, Windows, and NetBSD. On WIN32 it uses the ReportEvent() facility. - unix.clock_gettime([clock]) → seconds, nanos, errno:int + unix.clock_gettime([clock:int]) → seconds:int, unix.Errno, nanos:int Returns nanosecond precision timestamp from the system. - `clock` should be `CLOCK_REALTIME`, `CLOCK_MONOTONIC`, or - `CLOCK_MONOTONIC_RAW` since they work across platforms. - You may also try your luck with `CLOCK_REALTIME_COARSE`, - `CLOCK_MONOTONIC_COARSE`, `CLOCK_PROCESS_CPUTIME_ID`, - `CLOCK_TAI`, `CLOCK_PROF`, `CLOCK_BOOTTIME`, - `CLOCK_REALTIME_ALARM`, and `CLOCK_BOOTTIME_ALARM`, + `clock` can be any one of of: + + - `CLOCK_REALTIME`: universally supported + - `CLOCK_MONOTONIC`: universally supported + - `CLOCK_MONOTONIC_RAW`: nearly universally supported + - `CLOCK_PROCESS_CPUTIME_ID`: linux and bsd + - `CLOCK_THREAD_CPUTIME_ID`: linux and bsd + - `CLOCK_REALTIME_COARSE`: : linux and openbsd + - `CLOCK_MONOTONIC_COARSE`: linux + - `CLOCK_PROF`: linux and netbsd + - `CLOCK_BOOTTIME`: linux and openbsd + - `CLOCK_REALTIME_ALARM`: linux-only + - `CLOCK_BOOTTIME_ALARM`: linux-only + - `CLOCK_TAI`: ilnux-only + + Returns `EINVAL` if clock isn't supported on platform. unix.nanosleep(seconds:int[, nanos:int]) - → remseconds:int, remnanos:int[, errno:int] + → remseconds:int, unix.Errno, remnanos:int Sleeps with nanosecond precision. unix.sync() - unix.fsync(fd:int) → errno:int - unix.fdatasync(fd:int) → errno:int + unix.fsync(fd:int) → ok:bool, unix.Errno + unix.fdatasync(fd:int) → ok:bool, unix.Errno These functions are used to make programs slower by asking the operating system to flush data to the physical medium. - unix.lseek(fd:int, offset:int, whence:int) → newpos:int[, errno:int] + unix.lseek(fd:int, offset:int, whence:int) → newpos:int, unix.Errno Seeks to file position. @@ -1889,21 +1925,21 @@ UNIX MODULE Returns the new position relative to the start of the file. - unix.truncate(path:str[, length:int]) → errno:int + unix.truncate(path:str[, length:int]) → ok:bool, unix.Errno Reduces or extends underlying physical medium of file. If file was originally larger, content >length is lost. `length` defaults to zero. - unix.ftruncate(fd:int[, length:int]) → errno:int + unix.ftruncate(fd:int[, length:int]) → ok:bool, unix.Errno Reduces or extends underlying physical medium of open file. If file was originally larger, content >length is lost. `length` defaults to zero. - unix.socket([family:int[, type:int[, protocol:int]]]) → fd:int[, errno:int] + unix.socket([family:int[, type:int[, protocol:int]]]) → fd:int, unix.Errno `family` defaults to `AF_INET` and can be: @@ -1929,14 +1965,14 @@ UNIX MODULE `SOCK_CLOEXEC` may be bitwise or'd into `type`. unix.socketpair([family:int[, type:int[, protocol:int]]]) - → fd1:int, fd2:int[, errno:int] + → fd1:int, unix.Errno, fd2:int `SOCK_CLOEXEC` may be or'd into type `family` defaults to `AF_INET` `type` defaults to `SOCK_STREAM` `protocol` defaults to `IPPROTO_TCP` - unix.bind(fd:int[, ip:uint32, port:uint16]) → errno:int + unix.bind(fd:int[, ip:uint32, port:uint16]) → ok:bool, unix.Errno Binds socket. @@ -1970,19 +2006,21 @@ UNIX MODULE Further note that calling `unix.bind(sock)` is equivalent to not calling bind() at all, since the above behavior is the default. - unix.siocgifconf() → {{name:str,ip:uint32,netmask:uint32}, ...}[, errno:int] + unix.siocgifconf() → {{name:str,ip:uint32,netmask:uint32}, ...}, unix.Errno Returns list of network adapter addresses. - unix.getsockopt(fd:int, level:int, optname:int) → errno:int, ... - unix.setsockopt(fd:int, level:int, optname:int, ...) → errno:int + unix.getsockopt(fd:int, level:int, optname:int) → ok:bool, unix.Errno, ... + unix.setsockopt(fd:int, level:int, optname:int, ...) → ok:bool, unix.Errno Tunes networking parameters. - `level` and `optname` may be one of the following. Please note the - type signature for getsockopt() changes depending on these values: + `level` and `optname` may be one of the following. The ellipses type + signature above changes depending on which options are used. + - `SOL_SOCKET` + `SO_TYPE`: bool - `SOL_SOCKET` + `SO_DEBUG`: bool + - `SOL_SOCKET` + `SO_ACCEPTCONN`: bool - `SOL_SOCKET` + `SO_BROADCAST`: bool - `SOL_SOCKET` + `SO_REUSEADDR`: bool - `SOL_SOCKET` + `SO_REUSEPORT`: bool @@ -2016,15 +2054,8 @@ UNIX MODULE Returns `ENOSYS` if setting isn't supported by the host o/s. - NOTE: The API for this function diverges from the the norm. `errno` - needs to come first in the results, because otherwise we - wouldn't know the arity of items to push before it. It's - because Cosmopolitan Libc polyfills the magic numbers above as - zero if the host operating system doesn't support them. If - there's no error, then `errno` will be set to nil. - unix.poll({fd:int=events:int, ...}[, timeoutms:int]) - → {fd:int=revents:int, ...}[, errno:int] + → {fd:int=revents:int, ...}, unix.Errno Checks for events on a set of file descriptors. @@ -2037,19 +2068,25 @@ UNIX MODULE then that means block as long as it takes until there's an event or an interrupt. If the timeout expires, an empty table is returned. - unix.gethostname() → host:str[, errno:int] + unix.gethostname() → host:str, unix.Errno Returns hostname of system. - unix.listen(fd:int[, backlog]) → errno:int + unix.listen(fd:int[, backlog:int]) → ok:bool, unix.Errno Begins listening for incoming connections on a socket. - unix.accept(serverfd) → clientfd:int, ip:uint32, port:uint16[, errno:int] + unix.accept(serverfd:int[, flags:int]) + → clientfd:int, unix.Errno, ip:uint32, port:uint16 Accepts new client socket descriptor for a listening tcp socket. - unix.connect(fd:int, ip:uint32, port:uint16) → rc:int[, errno:int] + `flags` can have any of: + + - `SOCK_CLOEXEC` + - `SOCK_NONBLOCK` + + unix.connect(fd:int, ip:uint32, port:uint16) → ok:bool, unix.Errno Connects a TCP socket to a remote host. @@ -2057,24 +2094,34 @@ UNIX MODULE remembers the intended address so that send() or write() may be used rather than sendto(). - unix.getsockname(fd:int) → ip:uint32, port:uint16[, errno:int] + unix.getsockname(fd:int) → ip:uint32, unix.Errno, port:uint16 Retrieves the local address of a socket. - unix.getpeername(fd:int) → ip:uint32, port:uint16[, errno:int] + unix.getpeername(fd:int) → ip:uint32, unix.Errno, port:uint16 Retrieves the remote address of a socket. - unix.recv(fd:int[, bufsiz:int[, flags:int]]) → data:str[, errno:int] + unix.recv(fd:int[, bufsiz:int[, flags:int]]) → data:str, unix.Errno - `flags` can have MSG_{WAITALL,DONTROUTE,PEEK,OOB}, etc. + `flags` can have: + + - `MSG_WAITALL` + - `MSG_DONTROUTE` + - `MSG_PEEK` + - `MSG_OOB` unix.recvfrom(fd:int[, bufsiz:int[, flags:int]]) - → data:str, ip:uint32, port:uint16, errno:int + → data:str, unix.Errno, ip:uint32, port:uint16 - `flags` can have MSG_{WAITALL,DONTROUTE,PEEK,OOB}, etc. + `flags` can have: - unix.send(fd:int, data:str[, flags:int]) → sent:int[, errno:int] + - `MSG_WAITALL` + - `MSG_DONTROUTE` + - `MSG_PEEK` + - `MSG_OOB` + + unix.send(fd:int, data:str[, flags:int]) → sent:int, unix.Errno This is the same as `write` except it has a `flags` argument that's intended for sockets. @@ -2082,24 +2129,36 @@ UNIX MODULE `flags` can have `MSG_OOB`, `MSG_DONTROUTE`, or `MSG_NOSIGNAL`. unix.sendto(fd:int, data:str, ip:int, port:int[, flags:int]) - → sent:int, errno:int + → sent:int, unix.Errno This is useful for sending messages over UDP sockets to specific addresses. `flags` can have `MSG_OOB`, `MSG_DONTROUTE`, or `MSG_NOSIGNAL`. - unix.shutdown(fd:int, how:int) → errno:int + unix.shutdown(fd:int, how:int) → ok:bool, unix.Errno Partially closes socket. `how` can be `SHUT_RD`, `SHUT_WR`, or `SHUT_RDWR`. - unix.sigprocmask(how[, mask]) → oldmask, errno:int + unix.sigprocmask(how:int[, mask:int]) → oldmask:int, unix.Errno - `how` can be `SIG_BLOCK`, `SIG_UNBLOCK`, `SIG_SETMASK` + Manipulates bitset of signals blocked by process. + + `how` can be one of: + + - `SIG_BLOCK`: bitwise ors `mask` into set of blocked signals + - `SIG_UNBLOCK`: removes bits in `mask` from set of blocked signals + - `SIG_SETMASK`: replaces process signal mask with `mask` + + `mask` is a word encoded bitset of signals. Valid signal numbers + start at 1 and vary between platforms. The most famous `SIGKILL` + can't be masked, but if it could, it's assigned the number `9` + across all platforms, so if you wanted to add it to a bitset, you + would say, `1 << 8` or in general terms `1 << (sig - 1)`. unix.sigaction(sig:int[, handler:func|int[, flags:int[, mask:int]]]) - → oldhandler:func|int, flags:int, mask:int, errno:int + → oldhandler:func|int, unix.Errno, flags:int, mask:int `handler` can be `SIG_IGN`, `SIG_DFL`, `intptr_t`, or a Lua function. `sig` can be `SIGINT`, `SIGQUIT`, `SIGTERM`, etc. @@ -2115,7 +2174,7 @@ UNIX MODULE It's a good idea to not do too much work in a signal handler. - unix.sigsuspend([mask:int]) → errno:int + unix.sigsuspend([mask]) → false, unix.Errno Waits for signal to be delivered. @@ -2123,7 +2182,7 @@ UNIX MODULE system call. `mask` specifies which signals should be blocked. unix.setitimer(which[, intsec, intmicros, valsec, valmicros]) - → intsec, intns, valsec, valns, errno:int + → intsec, unix.Errno, intns, valsec, valns Causes `SIGALRM` signals to be generated at some point(s) in the future. The `which` parameter should be `ITIMER_REAL`. @@ -2145,17 +2204,17 @@ UNIX MODULE unix.sigaction(unix.SIGALRM, MyOnSigAlrm, unix.SA_RESETHAND) unix.setitimer(unix.ITIMER_REAL, 0, 0, 1, 0) - unix.strerrno(errno:int) → str + unix.strerrno(unix.Errno) → str Turns `errno` code into its symbolic name, e.g. `"EINTR"`. If `errno` isn't known, this function returns nil. - unix.strerdoc(errno:int) → str + unix.strerdoc(unix.Errno) → str Turns `errno` code into a descriptive string. If `errno` isn't known, this function returns nil. - unix.strerror(errno:int) → str + unix.strerror(unix.Errno) → str Turns `errno` code into longest string describing the error. This includes the output of both strerrno() and strerror() as well as the @@ -2167,7 +2226,7 @@ UNIX MODULE Turns platform-specific `sig` code into its name, e.g. `strsignal(9)` always returns `"SIGKILL"`. - unix.setrlimit(resource:int, soft:int[, hard:int]) → errno:int + unix.setrlimit(resource:int, soft:int[, hard:int]) → ok:bool, unix.Errno Changes resource limit. @@ -2198,72 +2257,79 @@ UNIX MODULE 127. On most platforms these limits are enforced by the kernel and as such are inherited by subprocesses. - unix.getrlimit(resource:int) → soft:int, hard:int[, errno:int] + unix.getrlimit(resource:int) → soft:int, unix.Errno, hard:int Returns information about resource limit. - unix.stat(x) → UnixStat*[, errno:int] + unix.stat(x) → unix.Stat, Errno* Gets information about file or directory. `x` may be a file or directory path string, or it may be a file descriptor int that was made by open(). - unix.opendir(path:str) → UnixDir*[, errno:int] + unix.opendir(path:str) → unix.Dir, Errno* Opens directory for listing its contents. - unix.fdopendir(fd:int) → UnixDir*[, errno:int] + unix.fdopendir(fd:int) → unix.Dir, Errno* Opens directory for listing its contents, via an fd. `fd` should be created by `open(path, O_RDONLY|O_DIRECTORY)`. The - returned UnixDir* ownership takes ownership of the file descriptor + returned unix.Dir ownership takes ownership of the file descriptor and will close it automatically when garbage collected. UNIX DIR OBJECT - UnixDir* objects are created by opendir() or fdopendir(). The + unix.Dir objects are created by opendir() or fdopendir(). The following methods are available: - UnixDir:close() → errno:int + unix.Dir:close() → ok:bool, unix.Errno may be called multiple times called by the garbage collector too - UnixDir:read() → name:str, kind:int, ino:int, off:int[, errno:int] + unix.Dir:read() → name:str, unix.Errno, kind:int, ino:int, off:int Returns `nil` if there are no more entries. Or error, `nil` will be returned and `errno` will be non-nil. - `kind` can be `DT_UNKNOWN`, `DT_REG`, `DT_DIR`, `DT_BLK`, - `DT_LNK`, `DT_CHR`, `DT_FIFO`, or `DT_SOCK`. + `kind` can be any of: - UnixDir:fd() → fd:int[, errno:int] + - `DT_UNKNOWN` + - `DT_REG` + - `DT_DIR` + - `DT_BLK` + - `DT_LNK` + - `DT_CHR` + - `DT_FIFO` + - `DT_SOCK` + + unix.Dir:fd() → fd:int, unix.Errno Returns file descriptor of open directory object. Returns `EOPNOTSUPP` if using a `/zip/...` path. - Returns `EOPNOTSUPP` if using Windows NT. - UnixDir:tell() → offset:int + unix.Dir:tell() → offset:int Returns current arbitrary offset into stream. - UnixDir:rewind() + unix.Dir:rewind() Resets stream back to beginning. UNIX STAT OBJECT - UnixStat* objects are created by stat() or fstat(). The following + unix.Stat objects are created by stat() or fstat(). The following methods are available: - UnixStat:size() → bytes:int + unix.Stat:size() → bytes:int Size of file in bytes. - UnixStat:mode() → mode:int + unix.Stat:mode() → mode:int Contains file type and permissions. @@ -2280,49 +2346,49 @@ UNIX MODULE - `(st:mode() & 0170000) == 0120000` means symbolic link - `(st:mode() & 0170000) == 0140000` means socket - UnixStat:atim() → secs:int, nanos:int + unix.Stat:atim() → secs:int, nanos:int Size of file in bytes. - UnixStat:uid() → int + unix.Stat:uid() → int User ID of file owner. - UnixStat:gid() → int + unix.Stat:gid() → int Group ID of file owner. - UnixStat:mtim() → secs:int, nanos:int + unix.Stat:mtim() → secs:int, nanos:int Last modified time. - UnixStat:birthtim() → secs:int, nanos:int + unix.Stat:birthtim() → secs:int, nanos:int Creation time. Note that on Linux this is the mimimum of atom/mtim/ctim. - UnixStat:ctim() → secs:int, nanos:int + unix.Stat:ctim() → secs:int, nanos:int Complicated time. Means time file status was last changed on UNIX. Means creation time on Windows. - UnixStat:blocks() → int + unix.Stat:blocks() → int Number of blocks used by storage medium. - UnixStat:blksize() → int + unix.Stat:blksize() → int Block size is usually 4096 for file system files. - UnixStat:dev() → int + unix.Stat:dev() → int ID of device containing file. - UnixStat:ino() → int + unix.Stat:ino() → int Inode number. - UnixStat:rdev() → int + unix.Stat:rdev() → int Device ID (if special file) diff --git a/tool/net/largon2.c b/tool/net/largon2.c index 157ae854d..cb0f4e208 100644 --- a/tool/net/largon2.c +++ b/tool/net/largon2.c @@ -531,16 +531,16 @@ luaopen_argon2(lua_State *L) largon2_push_argon2_variants_table(L); lua_setfield(L, -2, "variants"); - lua_pushstring(L, "3.0.1"); + lua_pushliteral(L, "3.0.1"); lua_setfield(L, -2, "_VERSION"); - lua_pushstring(L, "Thibault Charbonnier"); + lua_pushliteral(L, "Thibault Charbonnier"); lua_setfield(L, -2, "_AUTHOR"); - lua_pushstring(L, "MIT"); + lua_pushliteral(L, "MIT"); lua_setfield(L, -2, "_LICENSE"); - lua_pushstring(L, "https://github.com/thibaultcha/lua-argon2"); + lua_pushliteral(L, "https://github.com/thibaultcha/lua-argon2"); lua_setfield(L, -2, "_URL"); return 1; diff --git a/tool/net/lfuncs.c b/tool/net/lfuncs.c index a58760bd5..d1be7df68 100644 --- a/tool/net/lfuncs.c +++ b/tool/net/lfuncs.c @@ -18,10 +18,14 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "dsp/scale/cdecimate2xuint8x8.h" #include "libc/bits/popcnt.h" +#include "libc/calls/calls.h" +#include "libc/calls/struct/rusage.h" +#include "libc/intrin/kprintf.h" #include "libc/log/check.h" #include "libc/log/log.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" +#include "libc/nexgen32e/bench.h" #include "libc/nexgen32e/bsf.h" #include "libc/nexgen32e/bsr.h" #include "libc/nexgen32e/crc32.h" @@ -31,6 +35,7 @@ #include "libc/runtime/gc.internal.h" #include "libc/sock/sock.h" #include "libc/sysv/consts/af.h" +#include "libc/sysv/consts/rusage.h" #include "libc/time/time.h" #include "libc/x/x.h" #include "net/http/escape.h" @@ -48,6 +53,10 @@ #include "third_party/mbedtls/sha512.h" #include "tool/net/lfuncs.h" +static int Rdpid(void) { + return rdpid(); +} + int LuaGetTime(lua_State *L) { lua_pushnumber(L, nowl()); return 1; @@ -64,12 +73,12 @@ int LuaRdtsc(lua_State *L) { } int LuaGetCpuNode(lua_State *L) { - lua_pushinteger(L, TSC_AUX_NODE(rdpid())); + lua_pushinteger(L, TSC_AUX_NODE(Rdpid())); return 1; } int LuaGetCpuCore(lua_State *L) { - lua_pushinteger(L, TSC_AUX_CORE(rdpid())); + lua_pushinteger(L, TSC_AUX_CORE(Rdpid())); return 1; } @@ -560,3 +569,44 @@ void LuaPushUrlView(lua_State *L, struct UrlView *v) { lua_pushnil(L); } } + +static int64_t GetInterrupts(void) { + struct rusage ru; + if (!getrusage(RUSAGE_SELF, &ru)) { + return ru.ru_nivcsw; + } else { + return 0; + } +} + +int LuaBenchmark(lua_State *L) { + double avgticks; + uint64_t t1, t2; + int64_t interrupts; + int core, iter, count, tries, attempts, maxattempts; + luaL_checktype(L, 1, LUA_TFUNCTION); + count = luaL_optinteger(L, 2, 100); + maxattempts = luaL_optinteger(L, 3, 10); + for (attempts = 0;;) { + sched_yield(); + core = TSC_AUX_CORE(Rdpid()); + interrupts = GetInterrupts(); + for (avgticks = iter = 1; iter < count; ++iter) { + t1 = __startbench(); + lua_pushvalue(L, 1); + lua_call(L, 0, 0); + t2 = __endbench(); + avgticks += 1. / iter * ((int)(t2 - t1) - avgticks); + } + ++attempts; + if (TSC_AUX_CORE(Rdpid()) == core && GetInterrupts() == interrupts) { + break; + } else if (attempts >= maxattempts) { + return luaL_error(L, "system is under too much load to run benchmark"); + } + } + lua_pushnumber(L, ConvertTicksToNanos(avgticks)); + lua_pushinteger(L, avgticks); + lua_pushinteger(L, attempts); + return 3; +} diff --git a/tool/net/lfuncs.h b/tool/net/lfuncs.h index d776272de..b731d8a03 100644 --- a/tool/net/lfuncs.h +++ b/tool/net/lfuncs.h @@ -11,6 +11,7 @@ int LuaUnix(lua_State *); int luaopen_argon2(lua_State *); int luaopen_lsqlite3(lua_State *); +int LuaBenchmark(lua_State *); int LuaBsf(lua_State *); int LuaBsr(lua_State *); int LuaCategorizeIp(lua_State *); diff --git a/tool/net/lsqlite3.c b/tool/net/lsqlite3.c index ee749d61b..3757f4811 100644 --- a/tool/net/lsqlite3.c +++ b/tool/net/lsqlite3.c @@ -1936,7 +1936,7 @@ static const luaL_Reg sqlitelib[] = { static void create_meta(lua_State *L, const char *name, const luaL_Reg *lib) { luaL_newmetatable(L, name); - lua_pushstring(L, "__index"); + lua_pushliteral(L, "__index"); lua_pushvalue(L, -2); /* push metatable */ lua_rawset(L, -3); /* metatable.__index = metatable */ diff --git a/tool/net/lunix.c b/tool/net/lunix.c index 677d98aa1..683c54ce5 100644 --- a/tool/net/lunix.c +++ b/tool/net/lunix.c @@ -72,6 +72,7 @@ #include "libc/sysv/errfuns.h" #include "libc/time/time.h" #include "libc/x/x.h" +#include "third_party/lua/cosmo.h" #include "third_party/lua/lauxlib.h" #include "third_party/lua/lua.h" #include "third_party/lua/luaconf.h" @@ -82,14 +83,19 @@ * @support Linux, Mac, Windows, FreeBSD, NetBSD, OpenBSD */ +struct UnixDir { + int refs; + DIR *dir; +}; + struct UnixStat { int refs; struct stat st; }; -struct UnixDir { +struct UnixErrno { int refs; - DIR *dir; + int errno; }; static lua_State *GL; @@ -109,37 +115,56 @@ static dontinline int ReturnString(lua_State *L, const char *x) { return 1; } -static dontinline int ReturnTimespec(lua_State *L, struct timespec *ts) { - lua_pushinteger(L, ts->tv_sec); - lua_pushinteger(L, ts->tv_nsec); - return 2; +static void LuaUnixPushErrno(lua_State *L, int err) { + // TODO: How do we solve "error object is a userdata value"? + lua_pushinteger(L, err); + return; + //////////////////////////////////////////////////////////// + struct UnixErrno *ue, **uep; + ue = xcalloc(1, sizeof(struct UnixErrno)); + ue->refs = 1; + ue->errno = err; + uep = lua_newuserdatauv(L, sizeof(*uep), 1); + luaL_setmetatable(L, "unix.Errno"); + *uep = ue; } -static int ReturnErrno(lua_State *L, int nils, int olderr) { +static dontinline int SysretErrnoImpl(lua_State *L, int olderr, bool usebool) { int i, newerr = errno; if (!IsTiny() && !(0 < newerr && newerr < (!IsWindows() ? 4096 : 65536))) { WARNF("errno should not be %d", newerr); } - for (i = 0; i < nils; ++i) { + if (usebool) { + lua_pushboolean(L, false); + } else { lua_pushnil(L); } - lua_pushinteger(L, newerr); + LuaUnixPushErrno(L, newerr); errno = olderr; - return nils + 1; + return 2; } -static int Return01(lua_State *L, int rc, int olderr) { +static dontinline int SysretErrnoNil(lua_State *L, int olderr) { + return SysretErrnoImpl(L, olderr, false); +} + +static dontinline int SysretErrnoBool(lua_State *L, int olderr) { + return SysretErrnoImpl(L, olderr, true); +} + +static int SysretBool(lua_State *L, int rc, int olderr) { if (!IsTiny() && (rc != 0 && rc != -1)) { WARNF("syscall supposed to return 0 / -1 but got %d", rc); } if (rc != -1) { + lua_pushboolean(L, true); return 0; } else { - return ReturnErrno(L, 0, olderr); + return SysretErrnoBool(L, olderr); } } -static int ReturnRc(lua_State *L, int64_t rc, int olderr) { +static int SysretInteger(lua_State *L, int64_t rc, int olderr) { if (rc != -1) { if (!IsTiny() && olderr != errno) { WARNF("errno unexpectedly changed %d → %d", olderr, errno); @@ -147,7 +172,7 @@ static int ReturnRc(lua_State *L, int64_t rc, int olderr) { lua_pushinteger(L, rc); return 1; } else { - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } } @@ -195,34 +220,38 @@ static char **ConvertLuaArrayToStringList(lua_State *L, int i) { //////////////////////////////////////////////////////////////////////////////// // System Calls +static dontinline int LuaUnixGetid(lua_State *L, int f(void)) { + return ReturnInteger(L, f()); +} + // unix.getpid() → pid:int static int LuaUnixGetpid(lua_State *L) { - return ReturnInteger(L, getpid()); + return LuaUnixGetid(L, getpid); } // unix.getppid() → pid:int static int LuaUnixGetppid(lua_State *L) { - return ReturnInteger(L, getppid()); + return LuaUnixGetid(L, getppid); } // unix.getuid() → uid:int static int LuaUnixGetuid(lua_State *L) { - return ReturnInteger(L, getuid()); + return LuaUnixGetid(L, getuid); } // unix.getgid() → gid:int static int LuaUnixGetgid(lua_State *L) { - return ReturnInteger(L, getgid()); + return LuaUnixGetid(L, getgid); } // unix.geteuid() → uid:int static int LuaUnixGeteuid(lua_State *L) { - return ReturnInteger(L, geteuid()); + return LuaUnixGetid(L, geteuid); } // unix.getegid() → gid:int static int LuaUnixGetegid(lua_State *L) { - return ReturnInteger(L, getegid()); + return LuaUnixGetid(L, getegid); } // unix.umask(mask:int) → oldmask:int @@ -235,86 +264,86 @@ static wontreturn int LuaUnixExit(lua_State *L) { _Exit(luaL_optinteger(L, 1, 0)); } -// unix.access(path:str, how:int) → errno:int +// unix.access(path:str, how:int) → ok:bool, unix.Errno // how can be: R_OK, W_OK, X_OK, F_OK static int LuaUnixAccess(lua_State *L) { int olderr = errno; - return Return01(L, access(luaL_checkstring(L, 1), luaL_checkinteger(L, 2)), - olderr); + return SysretBool(L, access(luaL_checkstring(L, 1), luaL_checkinteger(L, 2)), + olderr); } -// unix.mkdir(path:str[, mode:int]) → errno:int +// unix.mkdir(path:str[, mode:int]) → ok:bool, unix.Errno // mode should be octal static int LuaUnixMkdir(lua_State *L) { int olderr = errno; - return Return01(L, mkdir(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0755)), - olderr); + return SysretBool( + L, mkdir(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0755)), olderr); } -// unix.makedirs(path:str[, mode:int]) → errno:int +// unix.makedirs(path:str[, mode:int]) → ok:bool, unix.Errno // mode should be octal static int LuaUnixMakedirs(lua_State *L) { int olderr = errno; - return Return01( + return SysretBool( L, makedirs(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0755)), olderr); } -// unix.chdir(path:str) → errno:int +// unix.chdir(path:str) → ok:bool, unix.Errno static int LuaUnixChdir(lua_State *L) { int olderr = errno; - return Return01(L, chdir(luaL_checkstring(L, 1)), olderr); + return SysretBool(L, chdir(luaL_checkstring(L, 1)), olderr); } -// unix.unlink(path:str) → errno:int +// unix.unlink(path:str) → ok:bool, unix.Errno static int LuaUnixUnlink(lua_State *L) { int olderr = errno; - return Return01(L, unlink(luaL_checkstring(L, 1)), olderr); + return SysretBool(L, unlink(luaL_checkstring(L, 1)), olderr); } -// unix.rmdir(path:str) → errno:int +// unix.rmdir(path:str) → ok:bool, unix.Errno static int LuaUnixRmdir(lua_State *L) { int olderr = errno; - return Return01(L, rmdir(luaL_checkstring(L, 1)), olderr); + return SysretBool(L, rmdir(luaL_checkstring(L, 1)), olderr); } -// unix.rename(oldpath:str, newpath:str) → errno:int +// unix.rename(oldpath:str, newpath:str) → ok:bool, unix.Errno static int LuaUnixRename(lua_State *L) { int olderr = errno; - return Return01(L, rename(luaL_checkstring(L, 1), luaL_checkstring(L, 2)), - olderr); + return SysretBool(L, rename(luaL_checkstring(L, 1), luaL_checkstring(L, 2)), + olderr); } -// unix.link(existingpath:str, newpath:str) → errno:int +// unix.link(existingpath:str, newpath:str) → ok:bool, unix.Errno static int LuaUnixLink(lua_State *L) { int olderr = errno; - return Return01(L, link(luaL_checkstring(L, 1), luaL_checkstring(L, 2)), - olderr); + return SysretBool(L, link(luaL_checkstring(L, 1), luaL_checkstring(L, 2)), + olderr); } -// unix.symlink(target:str, linkpath:str) → errno:int +// unix.symlink(target:str, linkpath:str) → ok:bool, unix.Errno static int LuaUnixSymlink(lua_State *L) { int olderr = errno; - return Return01(L, symlink(luaL_checkstring(L, 1), luaL_checkstring(L, 2)), - olderr); + return SysretBool(L, symlink(luaL_checkstring(L, 1), luaL_checkstring(L, 2)), + olderr); } -// unix.chown(path:str, uid:int, gid:int) → errno:int +// unix.chown(path:str, uid:int, gid:int) → ok:bool, unix.Errno static int LuaUnixChown(lua_State *L) { int olderr = errno; - return Return01(L, - chown(luaL_checkstring(L, 1), luaL_checkinteger(L, 2), - luaL_checkinteger(L, 3)), - olderr); + return SysretBool(L, + chown(luaL_checkstring(L, 1), luaL_checkinteger(L, 2), + luaL_checkinteger(L, 3)), + olderr); } -// unix.chmod(path:str, mode:int) → errno:int +// unix.chmod(path:str, mode:int) → ok:bool, unix.Errno static int LuaUnixChmod(lua_State *L) { int olderr = errno; - return Return01(L, chmod(luaL_checkstring(L, 1), luaL_checkinteger(L, 2)), - olderr); + return SysretBool(L, chmod(luaL_checkstring(L, 1), luaL_checkinteger(L, 2)), + olderr); } -// unix.getcwd() → path:str[, errno:int] +// unix.getcwd() → path:str, unix.Errno static int LuaUnixGetcwd(lua_State *L) { int olderr; char *path; @@ -324,14 +353,14 @@ static int LuaUnixGetcwd(lua_State *L) { free(path); return 1; } else { - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } } -// unix.fork() → childpid|0:int[, errno:int] +// unix.fork() → childpid|0:int, unix.Errno static int LuaUnixFork(lua_State *L) { int olderr = errno; - return ReturnRc(L, fork(), olderr); + return SysretInteger(L, fork(), olderr); } // unix.environ() → {str,...} @@ -346,15 +375,7 @@ static int LuaUnixEnviron(lua_State *L) { return 1; } -// unix.execve(prog:str[, args:List<*>, env:Map]) → errno:int -// -// unix = require "unix" -// prog = unix.commandv("ls") -// unix.execve(prog, {prog, "-hal", "."}, {PATH="/bin"}) -// unix.exit(127) -// -// prog needs to be absolute, see commandv() -// envp defaults to environ +// unix.execve(prog:str[, args:List<*>, env:List<*>]) → false, unix.Errno static int LuaUnixExecve(lua_State *L) { int olderr; const char *prog; @@ -369,14 +390,14 @@ static int LuaUnixExecve(lua_State *L) { freeme2 = envp; } else { FreeStringList(argv); - return ReturnErrno(L, 1, olderr); + return SysretErrnoBool(L, olderr); } } else { envp = environ; freeme2 = 0; } } else { - return ReturnErrno(L, 1, olderr); + return SysretErrnoBool(L, olderr); } } else { ezargs[0] = prog; @@ -389,10 +410,10 @@ static int LuaUnixExecve(lua_State *L) { execve(prog, argv, envp); FreeStringList(freeme1); FreeStringList(freeme2); - return ReturnErrno(L, 1, olderr); + return SysretErrnoBool(L, olderr); } -// unix.commandv(prog:str) → path:str[, errno:int] +// unix.commandv(prog:str) → path:str, unix.Errno static int LuaUnixCommandv(lua_State *L) { int olderr; const char *prog; @@ -406,14 +427,14 @@ static int LuaUnixCommandv(lua_State *L) { return 1; } else { free(pathbuf); - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } } else { - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } } -// unix.realpath(path:str) → path:str[, errno:int] +// unix.realpath(path:str) → path:str, unix.Errno static int LuaUnixRealpath(lua_State *L) { char *resolved; int olderr; @@ -425,7 +446,7 @@ static int LuaUnixRealpath(lua_State *L) { free(resolved); return 1; } else { - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } } @@ -435,24 +456,24 @@ static int LuaUnixSyslog(lua_State *L) { return 0; } -// unix.chroot(path:str) → errno:int +// unix.chroot(path:str) → ok:bool, unix.Errno static int LuaUnixChroot(lua_State *L) { int olderr = errno; - return Return01(L, chroot(luaL_checkstring(L, 1)), olderr); + return SysretBool(L, chroot(luaL_checkstring(L, 1)), olderr); } -// unix.setrlimit(resource:int, soft:int[, hard:int]) → errno:int +// unix.setrlimit(resource:int, soft:int[, hard:int]) → ok:bool, unix.Errno static int LuaUnixSetrlimit(lua_State *L) { int olderr = errno; int64_t soft = luaL_checkinteger(L, 2); - return Return01( + return SysretBool( L, setrlimit(luaL_checkinteger(L, 1), &(struct rlimit){soft, luaL_optinteger(L, 3, soft)}), olderr); } -// unix.getrlimit(resource:int) → soft:int, hard:int[, errno:int] +// unix.getrlimit(resource:int) → soft:int, unix.Errno, hard:int static int LuaUnixGetrlimit(lua_State *L) { struct rlimit rlim; int rc, olderr, resource; @@ -460,49 +481,51 @@ static int LuaUnixGetrlimit(lua_State *L) { resource = luaL_checkinteger(L, 1); if (!getrlimit(resource, &rlim)) { lua_pushinteger(L, rlim.rlim_cur < RLIM_INFINITY ? rlim.rlim_cur : -1); + lua_pushnil(L); lua_pushinteger(L, rlim.rlim_max < RLIM_INFINITY ? rlim.rlim_max : -1); - return 2; + return 3; } else { - return ReturnErrno(L, 2, olderr); + return SysretErrnoNil(L, olderr); } } -// unix.kill(pid:int, sig:int) → errno:int +// unix.kill(pid:int, sig:int) → ok:bool, unix.Errno static int LuaUnixKill(lua_State *L) { int olderr = errno; - return Return01(L, kill(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2)), - olderr); + return SysretBool(L, kill(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2)), + olderr); } -// unix.raise(sig:int) → rc:int[, errno:int] +// unix.raise(sig:int) → rc:int, unix.Errno static int LuaUnixRaise(lua_State *L) { int olderr = errno; - return ReturnRc(L, raise(luaL_checkinteger(L, 1)), olderr); + return SysretInteger(L, raise(luaL_checkinteger(L, 1)), olderr); } -// unix.wait([pid:int, options:int]) → pid:int, wstatus:int, nil[, errno:int] +// unix.wait([pid:int, options:int]) → pid:int, unix.Errno, wstatus:int static int LuaUnixWait(lua_State *L) { int pid, wstatus, olderr = errno; if ((pid = wait4(luaL_optinteger(L, 1, -1), &wstatus, luaL_optinteger(L, 2, 0), 0)) != -1) { lua_pushinteger(L, pid); + lua_pushnil(L); lua_pushinteger(L, wstatus); - return 2; + return 3; } else { - return ReturnErrno(L, 3, olderr); + return SysretErrnoNil(L, olderr); } } -// unix.fcntl(fd:int, cmd:int[, arg:int]) → rc:int[, errno:int] +// unix.fcntl(fd:int, cmd:int[, arg:int]) → rc:int, unix.Errno static int LuaUnixFcntl(lua_State *L) { int olderr = errno; - return ReturnRc(L, - fcntl(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2), - luaL_optinteger(L, 3, 0)), - olderr); + return SysretInteger(L, + fcntl(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2), + luaL_optinteger(L, 3, 0)), + olderr); } -// unix.dup(oldfd:int[, newfd:int[, flags:int]]) → newfd:int[, errno:int] +// unix.dup(oldfd:int[, newfd:int[, flags:int]]) → newfd:int, unix.Errno static int LuaUnixDup(lua_State *L) { int rc, oldfd, newfd, flags, olderr; olderr = errno; @@ -514,103 +537,111 @@ static int LuaUnixDup(lua_State *L) { } else { rc = dup3(oldfd, newfd, flags); } - return ReturnRc(L, rc, olderr); + return SysretInteger(L, rc, olderr); } -// unix.pipe([flags:int]) → reader:int, writer:int[, errno:int] +// unix.pipe([flags:int]) → reader:int, unix.Errno, writer:int static int LuaUnixPipe(lua_State *L) { int pipefd[2], olderr = errno; if (!pipe2(pipefd, luaL_optinteger(L, 1, 0))) { lua_pushinteger(L, pipefd[0]); + lua_pushnil(L); lua_pushinteger(L, pipefd[1]); return 2; } else { - return ReturnErrno(L, 2, olderr); + return SysretErrnoNil(L, olderr); } } -// unix.getsid(pid) → sid:int[, errno:int] +// unix.getsid(pid) → sid:int, unix.Errno static int LuaUnixGetsid(lua_State *L) { int olderr = errno; - return ReturnRc(L, getsid(luaL_checkinteger(L, 1)), olderr); + return SysretInteger(L, getsid(luaL_checkinteger(L, 1)), olderr); } -// unix.getpgrp() → pgid:int[, errno:int] -static int LuaUnixGetpgrp(lua_State *L) { +static dontinline int LuaUnixRc0(lua_State *L, int f(void)) { int olderr = errno; - return ReturnRc(L, getpgrp(), olderr); + return SysretInteger(L, f(), olderr); } -// unix.getpgid(pid:int) → pgid:int[, errno:int] +// unix.getpgrp() → pgid:int, unix.Errno +static int LuaUnixGetpgrp(lua_State *L) { + return LuaUnixRc0(L, getpgrp); +} + +// unix.setpgrp() → pgid:int, unix.Errno +static int LuaUnixSetpgrp(lua_State *L) { + return LuaUnixRc0(L, setpgrp); +} + +// unix.setsid() → sid:int, unix.Errno +static int LuaUnixSetsid(lua_State *L) { + return LuaUnixRc0(L, setsid); +} + +// unix.getpgid(pid:int) → pgid:int, unix.Errno static int LuaUnixGetpgid(lua_State *L) { int olderr = errno; - return ReturnRc(L, getpgid(luaL_checkinteger(L, 1)), olderr); + return SysretInteger(L, getpgid(luaL_checkinteger(L, 1)), olderr); } -// unix.setpgid(pid:int, pgid:int) → pgid:int[, errno:int] +// unix.setpgid(pid:int, pgid:int) → pgid:int, unix.Errno static int LuaUnixSetpgid(lua_State *L) { int olderr = errno; - return ReturnRc(L, setpgid(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2)), - olderr); + return SysretInteger( + L, setpgid(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2)), olderr); } -// unix.setpgrp() → pgid:int[, errno:int] -static int LuaUnixSetpgrp(lua_State *L) { +static dontinline int LuaUnixSetid(lua_State *L, int f(int)) { int olderr = errno; - return ReturnRc(L, setpgrp(), olderr); + return SysretBool(L, f(luaL_checkinteger(L, 1)), olderr); } -// unix.setsid() → sid:int[, errno:int] -static int LuaUnixSetsid(lua_State *L) { - int olderr = errno; - return ReturnRc(L, setsid(), olderr); -} - -// unix.setuid(uid:int) → errno:int +// unix.setuid(uid:int) → ok:bool, unix.Errno static int LuaUnixSetuid(lua_State *L) { - int olderr = errno; - return Return01(L, setuid(luaL_checkinteger(L, 1)), olderr); + return LuaUnixSetid(L, setuid); } -// unix.setgid(gid:int) → errno:int +// unix.setgid(gid:int) → ok:bool, unix.Errno static int LuaUnixSetgid(lua_State *L) { - int olderr = errno; - return Return01(L, setgid(luaL_checkinteger(L, 1)), olderr); + return LuaUnixSetid(L, setgid); } -// unix.setresuid(real:int, effective:int, saved:int) → errno:int +static dontinline int LuaUnixSetresid(lua_State *L, + int f(uint32_t, uint32_t, uint32_t)) { + int olderr = errno; + return SysretBool(L, + f(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2), + luaL_checkinteger(L, 3)), + olderr); +} + +// unix.setresuid(real:int, effective:int, saved:int) → ok:bool, unix.Errno static int LuaUnixSetresuid(lua_State *L) { - int olderr = errno; - return Return01(L, - setresuid(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2), - luaL_checkinteger(L, 3)), - olderr); + return LuaUnixSetresid(L, setresuid); } -// unix.setresgid(real:int, effective:int, saved:int) → errno:int +// unix.setresgid(real:int, effective:int, saved:int) → ok:bool, unix.Errno static int LuaUnixSetresgid(lua_State *L) { - int olderr = errno; - return Return01(L, - setresgid(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2), - luaL_checkinteger(L, 3)), - olderr); + return LuaUnixSetresid(L, setresgid); } -// unix.clock_gettime([clock:int]) → seconds:int, nanos:int[, errno:int] +// unix.clock_gettime([clock:int]) → seconds:int, unix.Errno, nanos:int static int LuaUnixGettime(lua_State *L) { struct timespec ts; int rc, olderr = errno; if (!clock_gettime(luaL_optinteger(L, 1, CLOCK_REALTIME), &ts)) { lua_pushinteger(L, ts.tv_sec); + lua_pushnil(L); lua_pushinteger(L, ts.tv_nsec); - return 2; + return 3; } else { - return ReturnErrno(L, 2, olderr); + return SysretErrnoNil(L, olderr); } } // unix.nanosleep(seconds:int, nanos:int) -// → remseconds:int, remnanos:int[, errno:int] +// → remseconds:int, unix.Errno, remnanos:int static int LuaUnixNanosleep(lua_State *L) { int olderr = errno; struct timespec req, rem; @@ -618,10 +649,11 @@ static int LuaUnixNanosleep(lua_State *L) { req.tv_nsec = luaL_optinteger(L, 2, 0); if (!nanosleep(&req, &rem)) { lua_pushinteger(L, rem.tv_sec); + lua_pushnil(L); lua_pushinteger(L, rem.tv_nsec); - return 2; + return 3; } else { - return ReturnErrno(L, 2, olderr); + return SysretErrnoNil(L, olderr); } } @@ -631,59 +663,57 @@ static int LuaUnixSync(lua_State *L) { return 0; } -// unix.fsync(fd:int) → errno:int +// unix.fsync(fd:int) → ok:bool, unix.Errno static int LuaUnixFsync(lua_State *L) { int olderr = errno; - return Return01(L, fsync(luaL_checkinteger(L, 1)), olderr); + return SysretBool(L, fsync(luaL_checkinteger(L, 1)), olderr); } -// unix.fdatasync(fd:int) → errno:int +// unix.fdatasync(fd:int) → ok:bool, unix.Errno static int LuaUnixFdatasync(lua_State *L) { int olderr = errno; - return Return01(L, fdatasync(luaL_checkinteger(L, 1)), olderr); + return SysretBool(L, fdatasync(luaL_checkinteger(L, 1)), olderr); } -// unix.open(path:str, flags:int[, mode:int]) → fd:int[, errno:int] +// unix.open(path:str, flags:int[, mode:int]) → fd:int, unix.Errno static int LuaUnixOpen(lua_State *L) { int olderr = errno; - return ReturnRc(L, - open(luaL_checkstring(L, 1), luaL_checkinteger(L, 2), - luaL_optinteger(L, 3, 0)), - olderr); + return SysretInteger(L, + open(luaL_checkstring(L, 1), luaL_checkinteger(L, 2), + luaL_optinteger(L, 3, 0)), + olderr); } -// unix.close(fd:int) → errno:int +// unix.close(fd:int) → ok:bool, unix.Errno static int LuaUnixClose(lua_State *L) { int olderr = errno; - return Return01(L, close(luaL_checkinteger(L, 1)), olderr); + return SysretBool(L, close(luaL_checkinteger(L, 1)), olderr); } -// unix.lseek(fd:int, offset:int[, whence:int]) → newpos:int[, errno:int] -// where whence ∈ {SEEK_SET, SEEK_CUR, SEEK_END} -// whence defaults to SEEK_SET +// unix.lseek(fd:int, offset:int[, whence:int]) → newpos:int, unix.Errno static int LuaUnixLseek(lua_State *L) { int olderr = errno; - return ReturnRc(L, - lseek(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2), - luaL_optinteger(L, 3, SEEK_SET)), - olderr); + return SysretInteger(L, + lseek(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2), + luaL_optinteger(L, 3, SEEK_SET)), + olderr); } -// unix.truncate(path:str[, length:int]) → errno:int +// unix.truncate(path:str[, length:int]) → ok:bool, unix.Errno static int LuaUnixTruncate(lua_State *L) { int olderr = errno; - return Return01(L, truncate(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0)), - olderr); + return SysretBool( + L, truncate(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0)), olderr); } -// unix.ftruncate(fd:int[, length:int]) → errno:int +// unix.ftruncate(fd:int[, length:int]) → ok:bool, unix.Errno static int LuaUnixFtruncate(lua_State *L) { int olderr = errno; - return Return01( + return SysretBool( L, ftruncate(luaL_checkinteger(L, 1), luaL_optinteger(L, 2, 0)), olderr); } -// unix.read(fd:int[, bufsiz:str, offset:int]) → data:str[, errno:int] +// unix.read(fd:int[, bufsiz:str, offset:int]) → data:str, unix.Errno static int LuaUnixRead(lua_State *L) { char *buf; size_t got; @@ -707,14 +737,14 @@ static int LuaUnixRead(lua_State *L) { return 1; } else { free(buf); - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } } else { - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } } -// unix.write(fd:int, data:str[, offset:int]) → rc:int[, errno:int] +// unix.write(fd:int, data:str[, offset:int]) → rc:int, unix.Errno static int LuaUnixWrite(lua_State *L) { size_t size; int fd, olderr; @@ -730,19 +760,19 @@ static int LuaUnixWrite(lua_State *L) { } else { rc = pwrite(fd, data, size, offset); } - return ReturnRc(L, rc, olderr); + return SysretInteger(L, rc, olderr); } static int ReturnStat(lua_State *L, struct UnixStat *ust) { struct UnixStat **ustp; ust->refs = 1; ustp = lua_newuserdatauv(L, sizeof(*ustp), 1); - luaL_setmetatable(L, "UnixStat*"); + luaL_setmetatable(L, "unix.Stat"); *ustp = ust; return 1; } -// unix.stat(path:str) → UnixStat*[, errno:int] +// unix.stat(path:str) → unix.Stat, unix.Errno static int LuaUnixStat(lua_State *L) { const char *path; int olderr = errno; @@ -754,10 +784,10 @@ static int LuaUnixStat(lua_State *L) { } free(ust); } - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } -// unix.fstat(fd:int) → UnixFstat*[, errno:int] +// unix.fstat(fd:int) → unix.Stat, unix.Errno static int LuaUnixFstat(lua_State *L) { int fd, olderr = errno; struct UnixStat *ust; @@ -769,19 +799,19 @@ static int LuaUnixFstat(lua_State *L) { } free(ust); } - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } static int ReturnDir(lua_State *L, struct UnixDir *udir) { struct UnixDir **udirp; udir->refs = 1; udirp = lua_newuserdatauv(L, sizeof(*udirp), 1); - luaL_setmetatable(L, "UnixDir*"); + luaL_setmetatable(L, "unix.UnixDir"); *udirp = udir; return 1; } -// unix.opendir(path:str) → UnixDir*[, errno:int] +// unix.opendir(path:str) → unix.Dir, unix.Errno static int LuaUnixOpendir(lua_State *L) { int olderr = errno; const char *path; @@ -793,10 +823,10 @@ static int LuaUnixOpendir(lua_State *L) { } free(udir); } - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } -// unix.fdopendir(fd:int) → UnixDir*[, errno:int] +// unix.fdopendir(fd:int) → unix.Dir, unix.Errno static int LuaUnixFdopendir(lua_State *L) { int fd, olderr = errno; struct UnixDir *udir; @@ -807,17 +837,20 @@ static int LuaUnixFdopendir(lua_State *L) { } free(udir); } - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } static bool IsSockoptBool(int l, int x) { if (l == SOL_SOCKET) { - return x == SO_DEBUG || // - x == SO_BROADCAST || // - x == SO_REUSEADDR || // - x == SO_REUSEPORT || // - x == SO_KEEPALIVE || // - x == SO_DONTROUTE; // + return x == SO_TYPE || // + x == SO_DEBUG || // + x == SO_ERROR || // + x == SO_BROADCAST || // + x == SO_REUSEADDR || // + x == SO_REUSEPORT || // + x == SO_KEEPALIVE || // + x == SO_ACCEPTCONN || // + x == SO_DONTROUTE; // } else if (l = SOL_TCP) { return x == TCP_NODELAY || // x == TCP_CORK || // @@ -865,8 +898,9 @@ static bool IsSockoptTimeval(int l, int x) { } // unix.setsockopt(fd:int, level:int, optname:int, ...) -// → errno:int +// → ok:bool, unix.Errno static int LuaUnixSetsockopt(lua_State *L) { + struct linger l; struct timeval tv; int rc, fd, level, optname, optval, olderr = errno; fd = luaL_checkinteger(L, 1); @@ -874,32 +908,38 @@ static int LuaUnixSetsockopt(lua_State *L) { optname = luaL_checkinteger(L, 3); if (IsSockoptBool(level, optname)) { optval = lua_toboolean(L, 4); - return Return01(L, setsockopt(fd, level, optname, &optval, sizeof(optval)), - olderr); + return SysretBool( + L, setsockopt(fd, level, optname, &optval, sizeof(optval)), olderr); } else if (IsSockoptInt(level, optname)) { optval = luaL_checkinteger(L, 4); - return Return01(L, setsockopt(fd, level, optname, &optval, sizeof(optval)), - olderr); + return SysretBool( + L, setsockopt(fd, level, optname, &optval, sizeof(optval)), olderr); } else if (IsSockoptTimeval(level, optname)) { tv.tv_sec = luaL_checkinteger(L, 4); tv.tv_usec = luaL_optinteger(L, 5, 0); - return Return01(L, setsockopt(fd, level, optname, &tv, sizeof(tv)), olderr); + return SysretBool(L, setsockopt(fd, level, optname, &tv, sizeof(tv)), + olderr); + } else if (level == SOL_SOCKET && optname == SO_LINGER) { + l.l_onoff = lua_toboolean(L, 4); + l.l_linger = luaL_checkinteger(L, 5); + return SysretBool(L, setsockopt(fd, level, optname, &l, sizeof(l)), olderr); } else { - lua_pushinteger(L, EINVAL); - return 1; + einval(); + return SysretErrnoBool(L, olderr); } } -// unix.getsockopt(fd:int, level:int, optname:int) -// → errno:int, ... static int LuaUnixGetsockopt(lua_State *L) { + struct linger l; struct timeval tv; - uint32_t tvsize, optvalsize; + uint32_t lsize, tvsize, optvalsize; int rc, fd, level, optname, optval, olderr = errno; fd = luaL_checkinteger(L, 1); level = luaL_checkinteger(L, 2); optname = luaL_checkinteger(L, 3); if (IsSockoptBool(level, optname)) { + // unix.getsockopt(fd:int, level:int, optname:int) + // → bool, unix.Errno optvalsize = sizeof(optval); if (getsockopt(fd, level, optname, &optval, &optvalsize) != -1) { CheckOptvalsize(L, sizeof(optval), optvalsize); @@ -907,7 +947,7 @@ static int LuaUnixGetsockopt(lua_State *L) { lua_pushboolean(L, optval); return 2; } else { - return ReturnErrno(L, 0, olderr); + return SysretErrnoNil(L, olderr); } } else if (IsSockoptInt(level, optname)) { optvalsize = sizeof(optval); @@ -917,7 +957,7 @@ static int LuaUnixGetsockopt(lua_State *L) { lua_pushinteger(L, optval); return 2; } else { - return ReturnErrno(L, 0, olderr); + return SysretErrnoNil(L, olderr); } } else if (IsSockoptTimeval(level, optname)) { tvsize = sizeof(tv); @@ -928,18 +968,30 @@ static int LuaUnixGetsockopt(lua_State *L) { lua_pushinteger(L, tv.tv_usec); return 3; } else { - return ReturnErrno(L, 0, olderr); + return SysretErrnoNil(L, olderr); + } + } else if (level == SOL_SOCKET && optname == SO_LINGER) { + lsize = sizeof(l); + if (getsockopt(fd, level, optname, &l, &lsize) != -1) { + CheckOptvalsize(L, sizeof(l), lsize); + lua_pushnil(L); + lua_pushinteger(L, l.l_onoff); + lua_pushinteger(L, l.l_linger); + return 3; + } else { + return SysretErrnoNil(L, olderr); } } else { - lua_pushinteger(L, EINVAL); - return 1; + einval(); + return SysretErrnoNil(L, olderr); } } -// unix.socket([family:int[, type:int[, protocol:int]]]) → fd:int[, errno:int] +// unix.socket([family:int[, type:int[, protocol:int]]]) → fd:int[, +// unix.Errno] static int LuaUnixSocket(lua_State *L) { int olderr = errno; - return ReturnRc( + return SysretInteger( L, socket(luaL_optinteger(L, 1, AF_INET), luaL_optinteger(L, 2, SOCK_STREAM), luaL_optinteger(L, 3, IPPROTO_TCP)), @@ -947,55 +999,57 @@ static int LuaUnixSocket(lua_State *L) { } // unix.socketpair([family:int[, type:int[, protocol:int]]]) -// → fd1:int, fd2:int[, errno:int] +// → fd1:int, unix.Errno, fd2:int static int LuaUnixSocketpair(lua_State *L) { int sv[2], olderr = errno; if (!socketpair(luaL_optinteger(L, 1, AF_INET), luaL_optinteger(L, 2, SOCK_STREAM), luaL_optinteger(L, 3, IPPROTO_TCP), sv)) { lua_pushinteger(L, sv[0]); + lua_pushnil(L); lua_pushinteger(L, sv[1]); return 2; } else { - return ReturnErrno(L, 2, olderr); + return SysretErrnoNil(L, olderr); } } -// unix.bind(fd:int[, ip:uint32, port:uint16]) → errno:int +// unix.bind(fd:int[, ip:uint32, port:uint16]) → ok:bool, unix.Errno static int LuaUnixBind(lua_State *L) { int olderr = errno; - return Return01(L, - bind(luaL_checkinteger(L, 1), - &(struct sockaddr_in){ - .sin_family = AF_INET, - .sin_addr.s_addr = htonl(luaL_optinteger(L, 2, 0)), - .sin_port = htons(luaL_optinteger(L, 3, 0)), - }, - sizeof(struct sockaddr_in)), - olderr); + return SysretBool(L, + bind(luaL_checkinteger(L, 1), + &(struct sockaddr_in){ + .sin_family = AF_INET, + .sin_addr.s_addr = htonl(luaL_optinteger(L, 2, 0)), + .sin_port = htons(luaL_optinteger(L, 3, 0)), + }, + sizeof(struct sockaddr_in)), + olderr); } -// unix.connect(fd:int, ip:uint32, port:uint16) → errno:int +// unix.connect(fd:int, ip:uint32, port:uint16) → ok:bool, unix.Errno static int LuaUnixConnect(lua_State *L) { int olderr = errno; - return Return01(L, - connect(luaL_checkinteger(L, 1), - &(struct sockaddr_in){ - .sin_addr.s_addr = htonl(luaL_checkinteger(L, 2)), - .sin_port = htons(luaL_checkinteger(L, 3)), - }, - sizeof(struct sockaddr_in)), - olderr); + return SysretBool( + L, + connect(luaL_checkinteger(L, 1), + &(struct sockaddr_in){ + .sin_addr.s_addr = htonl(luaL_checkinteger(L, 2)), + .sin_port = htons(luaL_checkinteger(L, 3)), + }, + sizeof(struct sockaddr_in)), + olderr); } -// unix.listen(fd:int[, backlog:int]) → errno:int +// unix.listen(fd:int[, backlog:int]) → ok:bool, unix.Errno static int LuaUnixListen(lua_State *L) { int olderr = errno; - return Return01(L, listen(luaL_checkinteger(L, 1), luaL_optinteger(L, 2, 10)), - olderr); + return SysretBool( + L, listen(luaL_checkinteger(L, 1), luaL_optinteger(L, 2, 10)), olderr); } -// unix.getsockname(fd:int) → ip:uint32, port:uint16[, errno:int] +// unix.getsockname(fd:int) → ip:uint32, unix.Errno, port:uint16 static int LuaUnixGetsockname(lua_State *L) { int fd, olderr; uint32_t addrsize; @@ -1005,14 +1059,15 @@ static int LuaUnixGetsockname(lua_State *L) { fd = luaL_checkinteger(L, 1); if (!getsockname(fd, &sa, &addrsize)) { lua_pushinteger(L, ntohl(sa.sin_addr.s_addr)); + lua_pushnil(L); lua_pushinteger(L, ntohs(sa.sin_port)); - return 2; + return 3; } else { - return ReturnErrno(L, 2, olderr); + return SysretErrnoNil(L, olderr); } } -// unix.getpeername(fd:int) → ip:uint32, port:uint16[, errno:int] +// unix.getpeername(fd:int) → ip:uint32, unix.Errno, port:uint16 static int LuaUnixGetpeername(lua_State *L) { int fd, olderr; uint32_t addrsize; @@ -1022,15 +1077,16 @@ static int LuaUnixGetpeername(lua_State *L) { fd = luaL_checkinteger(L, 1); if (!getpeername(fd, &sa, &addrsize)) { lua_pushinteger(L, ntohl(sa.sin_addr.s_addr)); + lua_pushnil(L); lua_pushinteger(L, ntohs(sa.sin_port)); - return 2; + return 3; } else { - return ReturnErrno(L, 2, olderr); + return SysretErrnoNil(L, olderr); } } -// unix.siocgifconf() → {{name:str,ip:uint32,netmask:uint32}, ...}[, -// errno:int] +// unix.siocgifconf() +// → {{name:str,ip:uint32,netmask:uint32}, ...}, unix.Errno static int LuaUnixSiocgifconf(lua_State *L) { size_t n; char *data; @@ -1039,33 +1095,33 @@ static int LuaUnixSiocgifconf(lua_State *L) { struct ifconf conf; olderr = errno; if (!(data = malloc((n = 4096)))) { - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)) == -1) { free(data); - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } conf.ifc_buf = data; conf.ifc_len = n; if (ioctl(fd, SIOCGIFCONF, &conf) == -1) { close(fd); free(data); - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } lua_newtable(L); i = 0; for (ifr = (struct ifreq *)data; (char *)ifr < data + conf.ifc_len; ++ifr) { if (ifr->ifr_addr.sa_family != AF_INET) continue; lua_createtable(L, 0, 3); - lua_pushstring(L, "name"); + lua_pushliteral(L, "name"); lua_pushstring(L, ifr->ifr_name); lua_settable(L, -3); - lua_pushstring(L, "ip"); + lua_pushliteral(L, "ip"); lua_pushinteger( L, ntohl(((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr.s_addr)); lua_settable(L, -3); if (ioctl(fd, SIOCGIFNETMASK, ifr) != -1) { - lua_pushstring(L, "netmask"); + lua_pushliteral(L, "netmask"); lua_pushinteger( L, ntohl(((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr.s_addr)); lua_settable(L, -3); @@ -1077,7 +1133,7 @@ static int LuaUnixSiocgifconf(lua_State *L) { return 1; } -// unix.gethostname() → host:str[, errno:int] +// unix.gethostname() → host:str, unix.Errno static int LuaUnixGethostname(lua_State *L) { int rc, olderr; char buf[DNS_NAME_MAX + 1]; @@ -1088,35 +1144,37 @@ static int LuaUnixGethostname(lua_State *L) { return 1; } else { enomem(); - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } } else { - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } } -// unix.accept(serverfd:int) -// → clientfd:int, ip:uint32, port:uint16[, errno:int] +// unix.accept(serverfd:int[, flags:int]) +// → clientfd:int, unix.Errno, ip:uint32, port:uint16 static int LuaUnixAccept(lua_State *L) { uint32_t addrsize; struct sockaddr_in sa; - int clientfd, serverfd, olderr; + int clientfd, serverfd, olderr, flags; olderr = errno; addrsize = sizeof(sa); serverfd = luaL_checkinteger(L, 1); - clientfd = accept(serverfd, &sa, &addrsize); + flags = luaL_optinteger(L, 2, 0); + clientfd = accept4(serverfd, &sa, &addrsize, flags); if (clientfd != -1) { lua_pushinteger(L, clientfd); + lua_pushnil(L); lua_pushinteger(L, ntohl(sa.sin_addr.s_addr)); lua_pushinteger(L, ntohs(sa.sin_port)); - return 3; + return 4; } else { - return ReturnErrno(L, 3, olderr); + return SysretErrnoNil(L, olderr); } } // unix.poll({fd:int=events:int, ...}[, timeoutms:int]) -// → {fd:int=revents:int, ...}[, errno:int] +// → {fd:int=revents:int, ...}, unix.Errno static int LuaUnixPoll(lua_State *L) { size_t nfds; struct pollfd *fds; @@ -1145,12 +1203,12 @@ static int LuaUnixPoll(lua_State *L) { return 1; } else { free(fds); - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } } -// unix.recvfrom(fd[, bufsiz[, flags]]) → data, ip, port[, errno] -// flags can have MSG_{WAITALL,DONTROUTE,PEEK,OOB}, etc. +// unix.recvfrom(fd:int[, bufsiz:str[, flags:int]]) +// → data:str, unix.Errno, ip:uint32, port:uint16 static int LuaUnixRecvfrom(lua_State *L) { char *buf; size_t got; @@ -1169,20 +1227,21 @@ static int LuaUnixRecvfrom(lua_State *L) { if (rc != -1) { got = rc; lua_pushlstring(L, buf, got); + lua_pushnil(L); lua_pushinteger(L, ntohl(sa.sin_addr.s_addr)); lua_pushinteger(L, ntohs(sa.sin_port)); free(buf); - return 3; + return 4; } else { free(buf); - return ReturnErrno(L, 3, olderr); + return SysretErrnoNil(L, olderr); } } else { - return ReturnErrno(L, 3, olderr); + return SysretErrnoNil(L, olderr); } } -// unix.recv(fd:int[, bufsiz:int[, flags:int]]) → data:str[, errno:int] +// unix.recv(fd:int[, bufsiz:int[, flags:int]]) → data:str, unix.Errno static int LuaUnixRecv(lua_State *L) { char *buf; size_t got; @@ -1202,14 +1261,14 @@ static int LuaUnixRecv(lua_State *L) { return 1; } else { free(buf); - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } } else { - return ReturnErrno(L, 3, olderr); + return SysretErrnoNil(L, olderr); } } -// unix.send(fd:int, data:str[, flags:int]) → sent:int[, errno:int] +// unix.send(fd:int, data:str[, flags:int]) → sent:int, unix.Errno static int LuaUnixSend(lua_State *L) { char *data; ssize_t rc; @@ -1220,15 +1279,13 @@ static int LuaUnixSend(lua_State *L) { data = luaL_checklstring(L, 2, &size); size = MIN(size, 0x7ffff000); flags = luaL_optinteger(L, 5, 0); - rc = send(fd, data, size, flags); - return ReturnRc(L, rc, olderr); + return SysretInteger(L, send(fd, data, size, flags), olderr); } // unix.sendto(fd:int, data:str, ip:uint32, port:uint16[, flags:int]) -// → sent:int[, errno:int] +// → sent:int, unix.Errno static int LuaUnixSendto(lua_State *L) { char *data; - ssize_t rc; size_t sent, size; struct sockaddr_in sa; int fd, flags, bufsiz, olderr; @@ -1240,20 +1297,18 @@ static int LuaUnixSendto(lua_State *L) { sa.sin_addr.s_addr = htonl(luaL_checkinteger(L, 3)); sa.sin_port = htons(luaL_checkinteger(L, 4)); flags = luaL_optinteger(L, 5, 0); - rc = sendto(fd, data, size, flags, &sa, sizeof(sa)); - return ReturnRc(L, rc, olderr); + return SysretInteger(L, sendto(fd, data, size, flags, &sa, sizeof(sa)), + olderr); } -// unix.shutdown(fd, how) → rc:int[, errno:int] -// how can be SHUT_RD, SHUT_WR, or SHUT_RDWR +// unix.shutdown(fd:int, how:int) → ok:bool, unix.Errno static int LuaUnixShutdown(lua_State *L) { int olderr = errno; - return Return01(L, shutdown(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2)), - olderr); + return SysretBool( + L, shutdown(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2)), olderr); } -// unix.sigprocmask(how[, mask]) → oldmask[, errno] -// how can be SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK +// unix.sigprocmask(how:int[, mask:int]) → oldmask:int, unix.Errno static int LuaUnixSigprocmask(lua_State *L) { uint64_t imask; int how, olderr; @@ -1276,10 +1331,7 @@ static int LuaUnixSigprocmask(lua_State *L) { lua_pushinteger(L, oldmask.__bits[0]); return 1; } else { - lua_pushnil(L); - lua_pushinteger(L, errno); - errno = olderr; - return 2; + return SysretErrnoNil(L, olderr); } } @@ -1295,7 +1347,8 @@ static void LuaUnixOnSignal(int sig, siginfo_t *si, ucontext_t *ctx) { } } -// unix.sigaction(sig[,handler[,flags[,mask]]]) → handler,flags,mask[,errno] +// unix.sigaction(sig:int[, handler:func|int[, flags:int[, mask:int]]]) +// → oldhandler:func|int, unix.Errno, flags:int, mask:int // // unix = require "unix" // unix.sigaction(unix.SIGUSR1, function(sig) @@ -1369,42 +1422,25 @@ static int LuaUnixSigaction(lua_State *L) { } // remove the signal handler table from stack lua_remove(L, -2); - // finish pushing the last 2/3 results + // finish pushing the last 3/4 results + lua_pushnil(L); lua_pushinteger(L, oldsa.sa_flags); lua_pushinteger(L, oldsa.sa_mask.__bits[0]); - return 3; + return 4; } else { - return ReturnErrno(L, 3, olderr); + return SysretErrnoNil(L, olderr); } } -// unix.sigsuspend([mask]) → errno +// unix.sigsuspend([mask]) → false, unix.Errno static int LuaUnixSigsuspend(lua_State *L) { - int olderr; - sigset_t mask; - olderr = errno; - mask.__bits[0] = luaL_optinteger(L, 1, 0); - mask.__bits[1] = 0; - sigsuspend(&mask); - lua_pushinteger(L, errno); - errno = olderr; - return 1; + int olderr = errno; + sigsuspend(&(struct sigset){{luaL_optinteger(L, 1, 0)}}); + return SysretErrnoBool(L, olderr); } // unix.setitimer(which[, intsec, intmicros, valsec, valmicros]) -// → intsec, intns, valsec, valns[, errno] -// -// ticks = 0 -// unix.sigaction(unix.SIGALRM, function(sig) -// print(string.format("tick no. %d", ticks)) -// ticks = ticks + 1 -// end) -// unix.setitimer(unix.ITIMER_REAL, 0, 400000, 0, 400000) -// while true do -// unix.sigsuspend() -// end -// -// which should be ITIMER_REAL +// → intsec:int, unix.Errno, intns:int, valsec:int, valns:int static int LuaUnixSetitimer(lua_State *L) { int which, olderr; struct itimerval it, oldit, *itptr; @@ -1421,33 +1457,38 @@ static int LuaUnixSetitimer(lua_State *L) { } if (!setitimer(which, itptr, &oldit)) { lua_pushinteger(L, oldit.it_interval.tv_sec); + lua_pushnil(L); lua_pushinteger(L, oldit.it_interval.tv_usec); lua_pushinteger(L, oldit.it_value.tv_sec); lua_pushinteger(L, oldit.it_value.tv_usec); - return 4; + return 5; } else { - return ReturnErrno(L, 4, olderr); + return SysretErrnoNil(L, olderr); } } -// unix.strerror(errno) → str -static int LuaUnixStrerror(lua_State *L) { - return ReturnString(L, strerror(luaL_checkinteger(L, 1))); -} - -// unix.strerrno(errno) → str -static int LuaUnixStrerrno(lua_State *L) { - return ReturnString(L, strerrno(luaL_checkinteger(L, 1))); +static dontinline int LuaUnixStr(lua_State *L, char *f(int)) { + return ReturnString(L, f(luaL_checkinteger(L, 1))); } // unix.strerdoc(errno) → str static int LuaUnixStrerdoc(lua_State *L) { - return ReturnString(L, strerdoc(luaL_checkinteger(L, 1))); + return LuaUnixStr(L, strerdoc); } // unix.strsignal(sig) → str static int LuaUnixStrsignal(lua_State *L) { - return ReturnString(L, strsignal(luaL_checkinteger(L, 1))); + return LuaUnixStr(L, strsignal); +} + +// unix.strerror(errno) → str +static int LuaUnixStrerror(lua_State *L) { + return LuaUnixStr(L, strerror); +} + +// unix.strerrno(errno) → str|nil +static int LuaUnixStrerrno(lua_State *L) { + return LuaUnixStr(L, strerrno); } // unix.WIFEXITED(wstatus) → int @@ -1471,11 +1512,11 @@ static int LuaUnixWtermsig(lua_State *L) { } //////////////////////////////////////////////////////////////////////////////// -// UnixStat* object +// unix.Stat object static dontinline struct stat *GetUnixStat(lua_State *L) { struct UnixStat **ust; - ust = luaL_checkudata(L, 1, "UnixStat*"); + ust = luaL_checkudata(L, 1, "unix.Stat"); return &(*ust)->st; } @@ -1519,20 +1560,26 @@ static int LuaUnixStatBlksize(lua_State *L) { return ReturnInteger(L, GetUnixStat(L)->st_blksize); } +static dontinline int UnixStatTim(lua_State *L, struct timespec *ts) { + lua_pushinteger(L, ts->tv_sec); + lua_pushinteger(L, ts->tv_nsec); + return 2; +} + static int LuaUnixStatAtim(lua_State *L) { - return ReturnTimespec(L, &GetUnixStat(L)->st_atim); + return UnixStatTim(L, &GetUnixStat(L)->st_atim); } static int LuaUnixStatMtim(lua_State *L) { - return ReturnTimespec(L, &GetUnixStat(L)->st_mtim); + return UnixStatTim(L, &GetUnixStat(L)->st_mtim); } static int LuaUnixStatCtim(lua_State *L) { - return ReturnTimespec(L, &GetUnixStat(L)->st_ctim); + return UnixStatTim(L, &GetUnixStat(L)->st_ctim); } static int LuaUnixStatBirthtim(lua_State *L) { - return ReturnTimespec(L, &GetUnixStat(L)->st_birthtim); + return UnixStatTim(L, &GetUnixStat(L)->st_birthtim); } static void FreeUnixStat(struct UnixStat *stat) { @@ -1541,9 +1588,15 @@ static void FreeUnixStat(struct UnixStat *stat) { } } +static int LuaUnixStatToString(lua_State *L) { + struct stat *st = GetUnixStat(L); + lua_pushstring(L, "unix.Stat{}"); + return 1; +} + static int LuaUnixStatGc(lua_State *L) { struct UnixStat **ust; - ust = luaL_checkudata(L, 1, "UnixStat*"); + ust = luaL_checkudata(L, 1, "unix.Stat"); if (*ust) { FreeUnixStat(*ust); *ust = 0; @@ -1552,30 +1605,31 @@ static int LuaUnixStatGc(lua_State *L) { } static const luaL_Reg kLuaUnixStatMeth[] = { - {"size", LuaUnixStatSize}, // - {"mode", LuaUnixStatMode}, // + {"atim", LuaUnixStatAtim}, // + {"birthtim", LuaUnixStatBirthtim}, // + {"blksize", LuaUnixStatBlksize}, // + {"blocks", LuaUnixStatBlocks}, // + {"ctim", LuaUnixStatCtim}, // {"dev", LuaUnixStatDev}, // + {"gid", LuaUnixStatGid}, // {"ino", LuaUnixStatIno}, // + {"mode", LuaUnixStatMode}, // + {"mtim", LuaUnixStatMtim}, // {"nlink", LuaUnixStatNlink}, // {"rdev", LuaUnixStatRdev}, // + {"size", LuaUnixStatSize}, // {"uid", LuaUnixStatUid}, // - {"gid", LuaUnixStatGid}, // - {"atim", LuaUnixStatAtim}, // - {"mtim", LuaUnixStatMtim}, // - {"ctim", LuaUnixStatCtim}, // - {"birthtim", LuaUnixStatBirthtim}, // - {"blocks", LuaUnixStatBlocks}, // - {"blksize", LuaUnixStatBlksize}, // {0}, // }; static const luaL_Reg kLuaUnixStatMeta[] = { - {"__gc", LuaUnixStatGc}, // - {0}, // + {"__tostring", LuaUnixStatToString}, // + {"__gc", LuaUnixStatGc}, // + {0}, // }; static void LuaUnixStatObj(lua_State *L) { - luaL_newmetatable(L, "UnixStat*"); + luaL_newmetatable(L, "unix.Stat"); luaL_setfuncs(L, kLuaUnixStatMeta, 0); luaL_newlibtable(L, kLuaUnixStatMeth); luaL_setfuncs(L, kLuaUnixStatMeth, 0); @@ -1584,10 +1638,81 @@ static void LuaUnixStatObj(lua_State *L) { } //////////////////////////////////////////////////////////////////////////////// -// UnixDir* object +// unix.Errno object + +static dontinline struct UnixErrno *GetUnixErrno(lua_State *L) { + struct UnixErrno **ue; + ue = luaL_checkudata(L, 1, "unix.Errno"); + return *ue; +} + +static int LuaUnixErrnoName(lua_State *L) { + struct UnixErrno *e = GetUnixErrno(L); + lua_pushstring(L, strerrno(e->errno)); + return 1; +} + +static int LuaUnixErrnoDoc(lua_State *L) { + struct UnixErrno *e = GetUnixErrno(L); + lua_pushstring(L, strerdoc(e->errno)); + return 1; +} + +static int LuaUnixErrnoError(lua_State *L) { + struct UnixErrno *e = GetUnixErrno(L); + lua_pushstring(L, strerror(e->errno)); + return 1; +} + +static int LuaUnixErrnoToString(lua_State *L) { + struct UnixErrno *e = GetUnixErrno(L); + lua_pushfstring(L, "error: system call failed: %s", strerror(e->errno)); + return 1; +} + +static void FreeUnixErrno(struct UnixErrno *errno) { + if (!--errno->refs) { + free(errno); + } +} + +static int LuaUnixErrnoGc(lua_State *L) { + struct UnixErrno **ue; + ue = luaL_checkudata(L, 1, "unix.Errno"); + if (*ue) { + FreeUnixErrno(*ue); + *ue = 0; + } + return 0; +} + +static const luaL_Reg kLuaUnixErrnoMeth[] = { + {"error", LuaUnixErrnoError}, // + {"name", LuaUnixErrnoName}, // + {"doc", LuaUnixErrnoDoc}, // + {0}, // +}; + +static const luaL_Reg kLuaUnixErrnoMeta[] = { + {"__tostring", LuaUnixErrnoToString}, // + {"__gc", LuaUnixErrnoGc}, // + {0}, // +}; + +static void LuaUnixErrnoObj(lua_State *L) { + luaL_newmetatable(L, "unix.Errno"); + luaL_setfuncs(L, kLuaUnixErrnoMeta, 0); + luaL_newlibtable(L, kLuaUnixErrnoMeth); + luaL_setfuncs(L, kLuaUnixErrnoMeth, 0); + lua_setfield(L, -2, "__index"); + lua_pop(L, 1); +} + +//////////////////////////////////////////////////////////////////////////////// +// unix.Dir object static struct UnixDir **GetUnixDirSelf(lua_State *L) { - return luaL_checkudata(L, 1, "UnixDir*"); + return luaL_checkudata(L, 1, "unix.Dir"); } static DIR *GetDirOrDie(lua_State *L) { @@ -1595,7 +1720,7 @@ static DIR *GetDirOrDie(lua_State *L) { udir = GetUnixDirSelf(L); assert((*udir)->dir); if (*udir) return (*udir)->dir; - luaL_argerror(L, 1, "UnixDir* is closed"); + luaL_argerror(L, 1, "unix.UnixDir is closed"); unreachable; } @@ -1604,7 +1729,7 @@ static int FreeUnixDir(struct UnixDir *dir) { return closedir(dir->dir); } -// UnixDir:close() → errno:int +// unix.Dir:close() → ok:bool, unix.Errno // may be called multiple times // called by the garbage collector too static int LuaUnixDirClose(lua_State *L) { @@ -1615,12 +1740,10 @@ static int LuaUnixDirClose(lua_State *L) { olderr = 0; rc = FreeUnixDir(*udir); *udir = 0; - return Return01(L, rc, olderr); + return SysretBool(L, rc, olderr); } -// UnixDir:read() → name:str, kind:int, ino:int, off:int[, errno:int] -// returns nil if no more entries -// kind can be DT_UNKNOWN/REG/DIR/BLK/LNK/CHR/FIFO/SOCK +// unix.Dir:read() → name:str, unix.Errno, kind:int, ino:int, off:int static int LuaUnixDirRead(lua_State *L) { int olderr; struct dirent *ent; @@ -1628,6 +1751,7 @@ static int LuaUnixDirRead(lua_State *L) { errno = 0; if ((ent = readdir(GetDirOrDie(L)))) { lua_pushlstring(L, ent->d_name, strnlen(ent->d_name, sizeof(ent->d_name))); + lua_pushnil(L); lua_pushinteger(L, ent->d_type); lua_pushinteger(L, ent->d_ino); lua_pushinteger(L, ent->d_off); @@ -1635,13 +1759,11 @@ static int LuaUnixDirRead(lua_State *L) { } else if (!ent && !errno) { return 0; // end of listing } else { - return ReturnErrno(L, 4, olderr); + return SysretErrnoNil(L, olderr); } } -// UnixDir:fd() → fd:int[, errno:int] -// EOPNOTSUPP if using /zip/ -// EOPNOTSUPP if IsWindows() +// unix.Dir:fd() → fd:int, unix.Errno static int LuaUnixDirFd(lua_State *L) { int fd, olderr; olderr = errno; @@ -1650,11 +1772,11 @@ static int LuaUnixDirFd(lua_State *L) { lua_pushinteger(L, fd); return 1; } else { - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } } -// UnixDir:tell() → off:int +// unix.Dir:tell() → off:int static int LuaUnixDirTell(lua_State *L) { long off; off = telldir(GetDirOrDie(L)); @@ -1662,7 +1784,7 @@ static int LuaUnixDirTell(lua_State *L) { return 1; } -// UnixDir:rewind() +// unix.Dir:rewind() static int LuaUnixDirRewind(lua_State *L) { rewinddir(GetDirOrDie(L)); return 0; @@ -1683,7 +1805,7 @@ static const luaL_Reg kLuaUnixDirMeta[] = { }; static void LuaUnixDirObj(lua_State *L) { - luaL_newmetatable(L, "UnixDir*"); + luaL_newmetatable(L, "unix.Dir"); luaL_setfuncs(L, kLuaUnixDirMeta, 0); luaL_newlibtable(L, kLuaUnixDirMeth); luaL_setfuncs(L, kLuaUnixDirMeth, 0); @@ -1793,9 +1915,9 @@ static void LoadMagnums(lua_State *L, struct MagnumStr *ms, const char *pfx) { int i; char b[64], *p; p = stpcpy(b, pfx); - for (i = 0; ms[i].x != -123; ++i) { - stpcpy(p, (const char *)((uintptr_t)ms + ms[i].s)); - LuaSetIntField(L, b, *(const int *)((uintptr_t)ms + ms[i].x)); + for (i = 0; ms[i].x != MAGNUM_TERMINATOR; ++i) { + stpcpy(p, MAGNUM_STRING(ms, i)); + LuaSetIntField(L, b, MAGNUM_NUMBER(ms, i)); } } @@ -1808,40 +1930,12 @@ int LuaUnix(lua_State *L) { lua_setglobal(L, "__signal_handlers"); LoadMagnums(L, kErrnoNames, ""); + LoadMagnums(L, kOpenFlags, "O_"); LoadMagnums(L, kSignalNames, "SIG"); - LoadMagnums(L, kIpOptnames, ""); - LoadMagnums(L, kTcpOptnames, ""); - LoadMagnums(L, kSockOptnames, ""); - - // open() flags - LuaSetIntField(L, "O_RDONLY", O_RDONLY); // - LuaSetIntField(L, "O_WRONLY", O_WRONLY); // - LuaSetIntField(L, "O_RDWR", O_RDWR); // - LuaSetIntField(L, "O_ACCMODE", O_ACCMODE); // mask of prev three - LuaSetIntField(L, "O_CREAT", O_CREAT); // - LuaSetIntField(L, "O_EXCL", O_EXCL); // - LuaSetIntField(L, "O_TRUNC", O_TRUNC); // - LuaSetIntField(L, "O_CLOEXEC", O_CLOEXEC); // - LuaSetIntField(L, "O_DIRECT", O_DIRECT); // no-op on xnu/openbsd - LuaSetIntField(L, "O_APPEND", O_APPEND); // weird on nt - LuaSetIntField(L, "O_TMPFILE", O_TMPFILE); // linux, windows - LuaSetIntField(L, "O_NOFOLLOW", O_NOFOLLOW); // unix - LuaSetIntField(L, "O_SYNC", O_SYNC); // unix - LuaSetIntField(L, "O_ASYNC", O_ASYNC); // unix - LuaSetIntField(L, "O_NOCTTY", O_NOCTTY); // unix - LuaSetIntField(L, "O_NOATIME", O_NOATIME); // linux - LuaSetIntField(L, "O_EXEC", O_EXEC); // free/openbsd - LuaSetIntField(L, "O_SEARCH", O_SEARCH); // free/netbsd - LuaSetIntField(L, "O_DSYNC", O_DSYNC); // linux/xnu/open/netbsd - LuaSetIntField(L, "O_RSYNC", O_RSYNC); // linux/open/netbsd - LuaSetIntField(L, "O_PATH", O_PATH); // linux - LuaSetIntField(L, "O_VERIFY", O_VERIFY); // freebsd - LuaSetIntField(L, "O_SHLOCK", O_SHLOCK); // bsd - LuaSetIntField(L, "O_EXLOCK", O_EXLOCK); // bsd - LuaSetIntField(L, "O_RANDOM", O_RANDOM); // windows - LuaSetIntField(L, "O_SEQUENTIAL", O_SEQUENTIAL); // windows - LuaSetIntField(L, "O_COMPRESSED", O_COMPRESSED); // windows - LuaSetIntField(L, "O_INDEXED", O_INDEXED); // windows + LoadMagnums(L, kIpOptnames, "IP_"); + LoadMagnums(L, kTcpOptnames, "TCP_"); + LoadMagnums(L, kSockOptnames, "SO_"); + LoadMagnums(L, kClockNames, "CLOCK_"); // seek() whence LuaSetIntField(L, "SEEK_SET", SEEK_SET); @@ -1878,19 +1972,6 @@ int LuaUnix(lua_State *L) { LuaSetIntField(L, "WNOHANG", WNOHANG); LuaSetIntField(L, "WNOHANG", WNOHANG); - // gettime() clocks - LuaSetIntField(L, "CLOCK_REALTIME", CLOCK_REALTIME); // portable - LuaSetIntField(L, "CLOCK_MONOTONIC", CLOCK_MONOTONIC); // portable - LuaSetIntField(L, "CLOCK_MONOTONIC_RAW", CLOCK_MONOTONIC_RAW); // portable - LuaSetIntField(L, "CLOCK_REALTIME_COARSE", CLOCK_REALTIME_COARSE); - LuaSetIntField(L, "CLOCK_MONOTONIC_COARSE", CLOCK_MONOTONIC_COARSE); - LuaSetIntField(L, "CLOCK_PROCESS_CPUTIME_ID", CLOCK_PROCESS_CPUTIME_ID); - LuaSetIntField(L, "CLOCK_TAI", CLOCK_TAI); - LuaSetIntField(L, "CLOCK_PROF", CLOCK_PROF); - LuaSetIntField(L, "CLOCK_BOOTTIME", CLOCK_BOOTTIME); - LuaSetIntField(L, "CLOCK_REALTIME_ALARM", CLOCK_REALTIME_ALARM); - LuaSetIntField(L, "CLOCK_BOOTTIME_ALARM", CLOCK_BOOTTIME_ALARM); - // socket() family LuaSetIntField(L, "AF_UNSPEC", AF_UNSPEC); LuaSetIntField(L, "AF_UNIX", AF_UNIX); diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 33bfa4ca0..b6cb5e42c 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -191,10 +191,10 @@ STATIC_YOINK("zip_uri_support"); #define HeaderEqualCase(H, S) \ SlicesEqualCase(S, strlen(S), HeaderData(H), HeaderLength(H)) -// letters not used: EIJNOQWXYinoqwxy +// letters not used: EIJNOQWXYnoqwxy // digits not used: 0123456789 // puncts not used: !"#$%&'()*+,-./;<=>@[\]^_`{|}~ -#define GETOPTS "BSVZabdfghjkmsuvzA:C:D:F:G:H:K:L:M:P:R:T:U:c:e:l:p:r:t:" +#define GETOPTS "BSVZabdfghijkmsuvzA:C:D:F:G:H:K:L:M:P:R:T:U:c:e:l:p:r:t:" static const uint8_t kGzipHeader[] = { 0x1F, // MAGNUM @@ -379,6 +379,7 @@ static bool checkedmethod; static bool sslinitialized; static bool sslfetchverify; static bool hascontenttype; +static bool interpretermode; static bool sslclientverify; static bool connectionclose; static bool hasonworkerstop; @@ -1185,10 +1186,10 @@ static void ReportWorkerExit(int pid, int ws) { static void ReportWorkerResources(int pid, struct rusage *ru) { char *s, *b = 0; if (logrusage || LOGGABLE(kLogDebug)) { - AppendResourceReport(&b, ru, "\r\n"); + AppendResourceReport(&b, ru, "\n"); if (b) { if ((s = IndentLines(b, appendz(b).i - 1, 0, 1))) { - LOGF(kLogDebug, "(stat) resource report for pid %d\r\n%s", pid, s); + LOGF(kLogDebug, "(stat) resource report for pid %d\n%s", pid, s); free(s); } free(b); @@ -4116,7 +4117,7 @@ static int LuaLog(lua_State *L) { } static int LuaEncodeSmth(lua_State *L, - int Encoder(lua_State *, char **, int, char *)) { + int Encoder(lua_State *, char **, int, char *, int)) { int useoutput = false; int maxdepth = 64; char *numformat = "%.14g"; @@ -4133,7 +4134,7 @@ static int LuaEncodeSmth(lua_State *L, numformat = luaL_optstring(L, -1, numformat); } lua_settop(L, 1); // keep the passed argument on top - Encoder(L, useoutput ? &outbuf : &p, maxdepth, numformat); + Encoder(L, useoutput ? &outbuf : &p, maxdepth, numformat, -1); if (useoutput) { lua_pushnil(L); } else { @@ -4920,6 +4921,7 @@ static const char *const kDontAutoComplete[] = { // static const luaL_Reg kLuaFuncs[] = { + {"Benchmark", LuaBenchmark}, // {"Bsf", LuaBsf}, // {"Bsr", LuaBsr}, // {"CategorizeIp", LuaCategorizeIp}, // @@ -5085,13 +5087,21 @@ static const luaL_Reg kLuaLibs[] = { }; static void LuaSetArgv(lua_State *L) { - size_t i; + int i, j = -1; lua_newtable(L); + lua_pushstring(L, __argv[0]); + lua_seti(L, -2, j++); + if (!interpretermode) { + lua_pushstring(L, "/zip/.init.lua"); + lua_seti(L, -2, j++); + } for (i = optind; i < __argc; ++i) { lua_pushstring(L, __argv[i]); - lua_seti(L, -2, i - optind + 1); + lua_seti(L, -2, j++); } - lua_setglobal(L, "argv"); + lua_pushvalue(L, -1); + lua_setglobal(L, "argv"); // deprecated + lua_setglobal(L, "arg"); } static void LuaSetConstant(lua_State *L, const char *s, long x) { @@ -5133,10 +5143,111 @@ static void LuaStart(void) { #endif } +static bool ShouldAutocomplete(const char *s) { + int c, m, l, r; + l = 0; + r = ARRAYLEN(kDontAutoComplete) - 1; + while (l <= r) { + m = (l + r) >> 1; + c = strcmp(kDontAutoComplete[m], s); + if (c < 0) { + l = m + 1; + } else if (c > 0) { + r = m - 1; + } else { + return false; + } + } + return true; +} + +static void HandleCompletions(const char *p, linenoiseCompletions *c) { + size_t i, j; + for (j = i = 0; i < c->len; ++i) { + if (ShouldAutocomplete(c->cvec[i])) { + c->cvec[j++] = c->cvec[i]; + } else { + free(c->cvec[i]); + } + } + c->len = j; +} + +static void LuaPrint(lua_State *L) { + int i, n; + char *b = 0; + const char *s; + n = lua_gettop(L); + for (i = 1; i <= n; i++) { + if (i > 1) appendw(&b, '\t'); + LuaEncodeLuaData(L, &b, 64, "g", i); + } + appendw(&b, '\n'); + WRITE(1, b, appendz(b).i); + free(b); +} + +static void LuaInterpreter(lua_State *L) { + int i, n, sig, status; + const char *script; + if (optind < __argc) { + script = __argv[optind]; + if (!strcmp(script, "-")) script = 0; + if ((status = luaL_loadfile(L, script)) == LUA_OK) { + lua_getglobal(L, "arg"); + n = luaL_len(L, -1); + luaL_checkstack(L, n + 3, "too many script args"); + for (i = 1; i <= n; i++) lua_rawgeti(L, -i, i); + lua_remove(L, -i); // remove arg table from stack + status = lua_runchunk(L, n, LUA_MULTRET); + } + lua_report(L, status); + } else { + lua_repl_blocking = true; + lua_repl_completions_callback = HandleCompletions; + lua_initrepl(GL, "redbean"); + if (lua_repl_isterminal) { + linenoiseEnableRawMode(0); + } + for (;;) { + status = lua_loadline(L); + write(1, "\n", 1); + if (status == -1) break; // eof + if (status == -2) { + if (errno == EINTR) { + if ((sig = linenoiseGetInterrupt())) { + raise(sig); + } + } + fprintf(stderr, "i/o error: %m\n"); + exit(1); + } + if (status == LUA_OK) { + status = lua_runchunk(GL, 0, LUA_MULTRET); + } + if (status == LUA_OK) { + LuaPrint(GL); + } else { + lua_report(GL, status); + } + } + linenoiseDisableRawMode(); + lua_freerepl(); + lua_settop(GL, 0); // clear stack + if ((sig = linenoiseGetInterrupt())) { + raise(sig); + } + } +} + static void LuaInit(void) { #ifndef STATIC lua_State *L = GL; LuaSetArgv(L); + if (interpretermode) { + LuaInterpreter(L); + exit(0); + } if (LuaRunAsset("/.init.lua", true)) { hasonhttprequest = IsHookDefined("OnHttpRequest"); hasonclientconnection = IsHookDefined("OnClientConnection"); @@ -6369,34 +6480,6 @@ static void RestoreApe(void) { } } -static bool ShouldAutocomplete(const char *s) { - int c, m, l, r; - l = 0; - r = ARRAYLEN(kDontAutoComplete) - 1; - while (l <= r) { - m = (l + r) >> 1; - c = strcmp(kDontAutoComplete[m], s); - if (c < 0) { - l = m + 1; - } else if (c > 0) { - r = m - 1; - } else { - return false; - } - } - return true; -} - -static void HandleCompletions(const char *p, linenoiseCompletions *c) { - size_t i, j; - for (j = i = 0; i < c->len; ++i) { - if (ShouldAutocomplete(c->cvec[i])) { - c->cvec[j++] = c->cvec[i]; - } - } - c->len = j; -} - static int HandleReadline(void) { int status; for (;;) { @@ -6405,7 +6488,7 @@ static int HandleReadline(void) { if (status == -1) { OnTerm(SIGHUP); // eof INFOF("got repl eof"); - write(1, "\r\n", 2); + write(1, "\n", 1); return -1; } else if (errno == EINTR) { errno = 0; @@ -6419,14 +6502,14 @@ static int HandleReadline(void) { return -1; } } - write(1, "\r\n", 2); + write(1, "\n", 1); linenoiseDisableRawMode(); LUA_REPL_LOCK; if (status == LUA_OK) { status = lua_runchunk(GL, 0, LUA_MULTRET); } if (status == LUA_OK) { - lua_l_print(GL); + LuaPrint(GL); } else { lua_report(GL, status); } @@ -6781,6 +6864,7 @@ static void GetOpts(int argc, char *argv[]) { #ifndef STATIC CASE('e', LuaEvalCode(optarg)); CASE('F', LuaEvalFile(optarg)); + CASE('i', interpretermode = true); CASE('E', leakcrashreports = true); CASE('A', storeasset = true; StorePath(optarg)); #endif From 72e9be5c208f2239a4700df45ff5f7060922a474 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Mon, 25 Apr 2022 09:31:28 -0700 Subject: [PATCH 03/18] Update redbean lua example code --- third_party/linenoise/linenoise.c | 6 ++++ tool/net/demo/unix-info.lua | 54 +++++++++++++++---------------- tool/net/demo/unix-rawsocket.lua | 2 +- tool/net/demo/unix-subprocess.lua | 8 ++--- tool/net/demo/unix-webserver.lua | 6 ++-- tool/net/lunix.c | 17 ++++------ tool/net/redbean.c | 14 ++++---- 7 files changed, 56 insertions(+), 51 deletions(-) diff --git a/third_party/linenoise/linenoise.c b/third_party/linenoise/linenoise.c index 7d04b7c43..e091faa90 100644 --- a/third_party/linenoise/linenoise.c +++ b/third_party/linenoise/linenoise.c @@ -781,7 +781,9 @@ static ssize_t linenoiseRead(int fd, char *buf, size_t size, if (l && gotwinch) refreshme = 1; if (refreshme) linenoiseRefreshLine(l); if (!block && linenoisePoll(l, fd) == -1) return -1; + --__strace; rc = readansi(fd, buf, size); + ++__strace; if (rc == -1 && errno == EINTR) { if (!block) break; } else { @@ -1277,11 +1279,15 @@ StartOver: } void linenoiseRefreshLine(struct linenoiseState *l) { + --__strace; linenoiseRefreshLineImpl(l, 0); + ++__strace; } static void linenoiseRefreshLineForce(struct linenoiseState *l) { + --__strace; linenoiseRefreshLineImpl(l, 1); + ++__strace; } static void linenoiseEditInsert(struct linenoiseState *l, const char *p, diff --git a/tool/net/demo/unix-info.lua b/tool/net/demo/unix-info.lua index 11ca45aa3..b772d069b 100644 --- a/tool/net/demo/unix-info.lua +++ b/tool/net/demo/unix-info.lua @@ -35,7 +35,7 @@ Write('
unix.getcwd()\r\n') Write('
%s\r\n' % {EscapeHtml(unix.getcwd())}) function PrintResourceLimit(name, id) - soft, hard, errno = unix.getrlimit(id) + soft, errno, hard = unix.getrlimit(id) Write('
getrlimit(%s)\r\n' % {name}) if soft then Write('
') @@ -80,7 +80,7 @@ else Write('%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) end -errno, enabled = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_DEBUG) +enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_DEBUG) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_DEBUG)\r\n') if errno then Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) @@ -88,7 +88,7 @@ else Write('
%s\r\n' % {enabled}) end -errno, enabled = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_ACCEPTCONN) +enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_ACCEPTCONN) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_ACCEPTCONN)\r\n') if errno then Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) @@ -96,7 +96,7 @@ else Write('
%s\r\n' % {enabled}) end -errno, enabled = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEADDR) +enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEADDR) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEADDR)\r\n') if errno then Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) @@ -104,7 +104,7 @@ else Write('
%s\r\n' % {enabled}) end -errno, enabled = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEPORT) +enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEPORT) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEPORT)\r\n') if errno then Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) @@ -112,7 +112,7 @@ else Write('
%s\r\n' % {enabled}) end -errno, enabled = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_KEEPALIVE) +enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_KEEPALIVE) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_KEEPALIVE)\r\n') if errno then Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) @@ -120,7 +120,7 @@ else Write('
%s\r\n' % {enabled}) end -errno, enabled = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_NODELAY) +enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_NODELAY) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_NODELAY)\r\n') if errno then Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) @@ -128,7 +128,7 @@ else Write('
%s\r\n' % {enabled}) end -errno, secs, micros = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVTIMEO) +secs, errno, micros = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVTIMEO) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVTIMEO)\r\n') if errno then Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) @@ -136,7 +136,7 @@ else Write('
%d sec %d µs\r\n' % {secs, micros}) end -errno, secs, micros = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDTIMEO) +secs, errno, micros = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDTIMEO) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDTIMEO)\r\n') if errno then Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) @@ -144,7 +144,7 @@ else Write('
%d sec %d µs\r\n' % {secs, micros}) end -errno, enabled = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_DONTROUTE) +enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_DONTROUTE) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_DONTROUTE)\r\n') if errno then Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) @@ -152,7 +152,7 @@ else Write('
%s\r\n' % {enabled}) end -errno, bytes = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDBUF) +bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDBUF) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDBUF)\r\n') if errno then Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) @@ -160,7 +160,7 @@ else Write('
%d\r\n' % {bytes}) end -errno, bytes = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVBUF) +bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVBUF) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVBUF)\r\n') if errno then Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) @@ -168,7 +168,7 @@ else Write('
%d\r\n' % {bytes}) end -errno, bytes = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_FASTOPEN) +bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_FASTOPEN) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_FASTOPEN)\r\n') if errno then Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) @@ -176,7 +176,7 @@ else Write('
%d\r\n' % {bytes}) end -errno, enabled = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_BROADCAST) +enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_BROADCAST) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_BROADCAST)\r\n') if errno then Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) @@ -184,7 +184,7 @@ else Write('
%s\r\n' % {enabled}) end -errno, enabled = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_CORK) +enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_CORK) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_CORK)\r\n') if errno then Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) @@ -192,7 +192,7 @@ else Write('
%s\r\n' % {enabled}) end -errno, enabled = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_QUICKACK) +enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_QUICKACK) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_QUICKACK)\r\n') if errno then Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) @@ -200,7 +200,7 @@ else Write('
%s\r\n' % {enabled}) end -errno, enabled = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_DEFER_ACCEPT) +enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_DEFER_ACCEPT) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_DEFER_ACCEPT)\r\n') if errno then Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) @@ -208,7 +208,7 @@ else Write('
%s\r\n' % {enabled}) end -errno, enabled = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_FASTOPEN_CONNECT) +enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_FASTOPEN_CONNECT) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_FASTOPEN_CONNECT)\r\n') if errno then Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) @@ -216,7 +216,7 @@ else Write('
%s\r\n' % {enabled}) end -errno, bytes = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDLOWAT) +bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDLOWAT) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDLOWAT)\r\n') if errno then Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) @@ -224,7 +224,7 @@ else Write('
%d\r\n' % {bytes}) end -errno, bytes = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVLOWAT) +bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVLOWAT) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVLOWAT)\r\n') if errno then Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) @@ -232,7 +232,7 @@ else Write('
%d\r\n' % {bytes}) end -errno, bytes = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPCNT) +bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPCNT) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPCNT)\r\n') if errno then Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) @@ -240,7 +240,7 @@ else Write('
%d\r\n' % {bytes}) end -errno, bytes = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_MAXSEG) +bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_MAXSEG) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_MAXSEG)\r\n') if errno then Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) @@ -248,7 +248,7 @@ else Write('
%d\r\n' % {bytes}) end -errno, bytes = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_SYNCNT) +bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_SYNCNT) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_SYNCNT)\r\n') if errno then Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) @@ -256,7 +256,7 @@ else Write('
%d\r\n' % {bytes}) end -errno, bytes = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_NOTSENT_LOWAT) +bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_NOTSENT_LOWAT) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_NOTSENT_LOWAT)\r\n') if errno then Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) @@ -264,7 +264,7 @@ else Write('
%d\r\n' % {bytes}) end -errno, bytes = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_WINDOW_CLAMP) +bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_WINDOW_CLAMP) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_WINDOW_CLAMP)\r\n') if errno then Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) @@ -272,7 +272,7 @@ else Write('
%d\r\n' % {bytes}) end -errno, bytes = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPIDLE) +bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPIDLE) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPIDLE)\r\n') if errno then Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) @@ -280,7 +280,7 @@ else Write('
%d\r\n' % {bytes}) end -errno, bytes = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPINTVL) +bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPINTVL) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPINTVL)\r\n') if errno then Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) diff --git a/tool/net/demo/unix-rawsocket.lua b/tool/net/demo/unix-rawsocket.lua index bae11971e..38be17135 100644 --- a/tool/net/demo/unix-rawsocket.lua +++ b/tool/net/demo/unix-rawsocket.lua @@ -111,7 +111,7 @@ local function main() if dir then unix.write(fd, '
    \r\n') while true do - name, kind, ino, off = dir:read() + name, errno, kind, ino, off = dir:read() if not name then break end diff --git a/tool/net/demo/unix-subprocess.lua b/tool/net/demo/unix-subprocess.lua index f4a8b3ea5..ae387d945 100644 --- a/tool/net/demo/unix-subprocess.lua +++ b/tool/net/demo/unix-subprocess.lua @@ -8,14 +8,14 @@ function main() cmd = 'ls' end syscall = 'commandv' - ls, errno = unix.commandv(cmd) + ls = assert(unix.commandv(cmd)) if ls then syscall = 'pipe' reader, writer, errno = unix.pipe() if reader then - oldint = unix.sigaction(unix.SIGINT, unix.SIG_IGN) - oldquit = unix.sigaction(unix.SIGQUIT, unix.SIG_IGN) - oldmask = unix.sigprocmask(unix.SIG_BLOCK, (1 << (unix.SIGCHLD - 1))) + -- oldint = assert(unix.sigaction(unix.SIGINT, unix.SIG_IGN)) + -- oldquit = assert(unix.sigaction(unix.SIGQUIT, unix.SIG_IGN)) + -- oldmask = assert(unix.sigprocmask(unix.SIG_BLOCK, (1 << (unix.SIGCHLD - 1)))) syscall = 'fork' child, errno = unix.fork() if child then diff --git a/tool/net/demo/unix-webserver.lua b/tool/net/demo/unix-webserver.lua index 8c1bf6605..69c4ef444 100644 --- a/tool/net/demo/unix-webserver.lua +++ b/tool/net/demo/unix-webserver.lua @@ -33,7 +33,7 @@ function main() pollfds = {} pollfds[mainfd] = unix.POLLIN - ifs, errno = unix.siocgifconf() + ifs = assert(unix.siocgifconf()) for i = 1,#ifs do if (IsLoopbackIp(mainip) and (IsPublicIp(ifs[i].ip) or IsPrivateIp(ifs[i].ip) or @@ -45,7 +45,7 @@ function main() server = unix.socket() unix.bind(server, ifs[i].ip) unix.listen(server) - ip, port = unix.getsockname(server) + ip, errno, port = unix.getsockname(server) addr = '%s:%d' % {FormatIp(ip), port} url = 'http://%s' % {addr} Log(kLogInfo, 'listening on %s' % {addr}) @@ -79,7 +79,7 @@ function main() unix.write(mainfd, data) elseif servers[fd] then unix.write(mainfd, 'preparing to accept from %d
    \r\n' % {fd}) - client, clientip, clientport = unix.accept(fd) + client, errno, clientip, clientport = unix.accept(fd) unix.write(mainfd, 'preparing to accept from %d
    \r\n' % {fd}) addr = '%s:%d' % {FormatIp(clientip), clientport} addrs[client] = addr diff --git a/tool/net/lunix.c b/tool/net/lunix.c index 683c54ce5..7b04f8f9e 100644 --- a/tool/net/lunix.c +++ b/tool/net/lunix.c @@ -806,7 +806,7 @@ static int ReturnDir(lua_State *L, struct UnixDir *udir) { struct UnixDir **udirp; udir->refs = 1; udirp = lua_newuserdatauv(L, sizeof(*udirp), 1); - luaL_setmetatable(L, "unix.UnixDir"); + luaL_setmetatable(L, "unix.Dir"); *udirp = udir; return 1; } @@ -943,9 +943,8 @@ static int LuaUnixGetsockopt(lua_State *L) { optvalsize = sizeof(optval); if (getsockopt(fd, level, optname, &optval, &optvalsize) != -1) { CheckOptvalsize(L, sizeof(optval), optvalsize); - lua_pushnil(L); lua_pushboolean(L, optval); - return 2; + return 1; } else { return SysretErrnoNil(L, olderr); } @@ -953,9 +952,8 @@ static int LuaUnixGetsockopt(lua_State *L) { optvalsize = sizeof(optval); if (getsockopt(fd, level, optname, &optval, &optvalsize) != -1) { CheckOptvalsize(L, sizeof(optval), optvalsize); - lua_pushnil(L); lua_pushinteger(L, optval); - return 2; + return 1; } else { return SysretErrnoNil(L, olderr); } @@ -963,8 +961,8 @@ static int LuaUnixGetsockopt(lua_State *L) { tvsize = sizeof(tv); if (getsockopt(fd, level, optname, &tv, &tvsize) != -1) { CheckOptvalsize(L, sizeof(tv), tvsize); - lua_pushnil(L); lua_pushinteger(L, tv.tv_sec); + lua_pushnil(L); lua_pushinteger(L, tv.tv_usec); return 3; } else { @@ -974,8 +972,8 @@ static int LuaUnixGetsockopt(lua_State *L) { lsize = sizeof(l); if (getsockopt(fd, level, optname, &l, &lsize) != -1) { CheckOptvalsize(L, sizeof(l), lsize); - lua_pushnil(L); lua_pushinteger(L, l.l_onoff); + lua_pushnil(L); lua_pushinteger(L, l.l_linger); return 3; } else { @@ -1745,9 +1743,8 @@ static int LuaUnixDirClose(lua_State *L) { // unix.Dir:read() → name:str, unix.Errno, kind:int, ino:int, off:int static int LuaUnixDirRead(lua_State *L) { - int olderr; + int olderr = errno; struct dirent *ent; - olderr = errno; errno = 0; if ((ent = readdir(GetDirOrDie(L)))) { lua_pushlstring(L, ent->d_name, strnlen(ent->d_name, sizeof(ent->d_name))); @@ -1755,7 +1752,7 @@ static int LuaUnixDirRead(lua_State *L) { lua_pushinteger(L, ent->d_type); lua_pushinteger(L, ent->d_ino); lua_pushinteger(L, ent->d_off); - return 4; + return 5; } else if (!ent && !errno) { return 0; // end of listing } else { diff --git a/tool/net/redbean.c b/tool/net/redbean.c index b6cb5e42c..b8413236e 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -5178,13 +5178,15 @@ static void LuaPrint(lua_State *L) { char *b = 0; const char *s; n = lua_gettop(L); - for (i = 1; i <= n; i++) { - if (i > 1) appendw(&b, '\t'); - LuaEncodeLuaData(L, &b, 64, "g", i); + if (n > 0) { + for (i = 1; i <= n; i++) { + if (i > 1) appendw(&b, '\t'); + LuaEncodeLuaData(L, &b, 64, "g", i); + } + appendw(&b, '\n'); + WRITE(1, b, appendz(b).i); + free(b); } - appendw(&b, '\n'); - WRITE(1, b, appendz(b).i); - free(b); } static void LuaInterpreter(lua_State *L) { From e6fab847a070f91f1db8eb1f9b4d699e0f1f19cd Mon Sep 17 00:00:00 2001 From: Paul Kulchenko Date: Mon, 25 Apr 2022 21:14:36 -0700 Subject: [PATCH 04/18] Update redbean to stringify error objects (#393) --- third_party/lua/luacallwithtrace.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/third_party/lua/luacallwithtrace.c b/third_party/lua/luacallwithtrace.c index 0e89c912c..c7640d19d 100644 --- a/third_party/lua/luacallwithtrace.c +++ b/third_party/lua/luacallwithtrace.c @@ -20,6 +20,26 @@ #include "third_party/lua/cosmo.h" #include "third_party/lua/lauxlib.h" + +// this is called with the error objects/string at -1 +// and returns the stringified error object at -1 +// @param L is main Lua interpreter +void expanderr(lua_State *L) { + if (lua_tostring(L, -1) == NULL) { // is error object not a string? + if (luaL_callmeta(L, -1, "__tostring")) { // is there a metamethod? + if (lua_type(L, -1) != LUA_TSTRING) { // returns not a string? + lua_pushfstring(L, "(error object returned a %s value)", + luaL_typename(L, -1)); + lua_remove(L, -2); // remove non-string value from __tostring + } + } else { + lua_pushfstring(L, "(error object is a %s value)", + luaL_typename(L, -1)); + } + lua_remove(L, -2); // remove the error object + } +} + // calling convention for lua stack of L is: // -2 is function // -1 is is argument (assuming nargs == 1) @@ -44,19 +64,19 @@ int LuaCallWithTrace(lua_State *L, int nargs, int nres, lua_State *C) { status = lua_resume(C, L, nargs, &nresults); // remove coroutine (still) at the bottom, but only if not yielding // keep it when yielding to anchor, so it's not GC-collected - // it's going to be removed at the beggining of the request handling + // it's going to be removed at the beginning of the request handling if (!canyield) lua_remove(L, 1); if (status != LUA_OK && status != LUA_YIELD) { - // move the error message - lua_xmove(C, L, 1); + lua_xmove(C, L, 1); // move the error message + expanderr(L); // handle non-string error objects // replace the error with the traceback on failure luaL_traceback2(L, C, lua_tostring(L, -1), 0); lua_remove(L, -2); // remove the error message } else { if (!lua_checkstack(L, MAX(nresults, nres))) { - lua_pop(C, nresults); /* remove results anyway */ + lua_pop(C, nresults); // remove results anyway lua_pushliteral(L, "too many results to resume"); - return LUA_ERRRUN; /* error flag */ + return LUA_ERRRUN; // error flag } lua_xmove(C, L, nresults); // move results to the main stack // grow the stack in case returned fewer results From 860ea18a879b749ab147ff58221903c5d77b73ac Mon Sep 17 00:00:00 2001 From: Paul Kulchenko Date: Tue, 26 Apr 2022 05:20:53 -0700 Subject: [PATCH 05/18] Fix errno object handling in redbean (#392) --- tool/net/lunix.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tool/net/lunix.c b/tool/net/lunix.c index 7b04f8f9e..cc3b47d10 100644 --- a/tool/net/lunix.c +++ b/tool/net/lunix.c @@ -116,10 +116,6 @@ static dontinline int ReturnString(lua_State *L, const char *x) { } static void LuaUnixPushErrno(lua_State *L, int err) { - // TODO: How do we solve "error object is a userdata value"? - lua_pushinteger(L, err); - return; - //////////////////////////////////////////////////////////// struct UnixErrno *ue, **uep; ue = xcalloc(1, sizeof(struct UnixErrno)); ue->refs = 1; @@ -158,7 +154,7 @@ static int SysretBool(lua_State *L, int rc, int olderr) { } if (rc != -1) { lua_pushboolean(L, true); - return 0; + return 1; } else { return SysretErrnoBool(L, olderr); } @@ -1923,6 +1919,7 @@ int LuaUnix(lua_State *L) { luaL_newlib(L, kLuaUnix); LuaUnixStatObj(L); LuaUnixDirObj(L); + LuaUnixErrnoObj(L); lua_newtable(L); lua_setglobal(L, "__signal_handlers"); From d57b81aac737328751a16ff71e020f9224a221c5 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Mon, 25 Apr 2022 21:16:05 -0700 Subject: [PATCH 06/18] Make improvements - Add GetCpuCount() API to redbean - Add unix.gmtime() API to redbean - Add unix.readlink() API to redbean - Add unix.localtime() API to redbean - Perfect the new redbean UNIX module APIs - Integrate with Linux clock_gettime() vDSO - Run Lua garbage collector when malloc() fails - Fix another regression quirk with linenoise repl - Fix GetProgramExecutableName() for systemwide installs - Fix a build flake with test/libc/mem/test.mk SRCS list --- libc/calls/calls.mk | 1 + libc/calls/clock_gettime.c | 24 +- libc/calls/faccessat.c | 2 +- libc/calls/fstat-nt.c | 2 +- libc/calls/getexecutablename.c | 121 ++ libc/calls/gettimeofday.c | 15 +- libc/calls/internal.h | 2 + libc/calls/now.c | 41 +- libc/calls/oldbench.c | 83 + libc/calls/pipe-nt.c | 7 +- libc/calls/program_executable_name.c | 148 -- libc/calls/sigaddset.c | 9 +- libc/calls/sigdelset.c | 7 +- libc/calls/sigismember.c | 9 +- libc/calls/struct/stat.h | 4 +- libc/calls/vdsofunc.greg.c | 94 + libc/elf/elf.h | 2 + libc/elf/getelfdynstringtable.c | 40 + libc/elf/getelfdynsymboltable.c | 37 + libc/fmt/strerror_wr.greg.c | 13 +- libc/integral/normalize.inc | 2 +- libc/runtime/getinterpreterexecutablename.c | 18 +- libc/runtime/printargs.greg.c | 4 +- libc/runtime/runtime.h | 1 - libc/runtime/winmain.greg.c | 3 - libc/sysv/consts.sh | 8 +- libc/sysv/consts/PIPE_BUF.S | 2 +- test/libc/calls/access_test.c | 2 +- .../libc/calls/clock_gettime_test.c | 34 +- test/libc/calls/dup_test.c | 4 +- test/libc/log/backtrace_test.c | 28 +- test/libc/mem/test.mk | 5 +- third_party/linenoise/linenoise.c | 1 + third_party/lua/llimits.h | 1 + third_party/mbedtls/test/lib.c | 2 +- third_party/python/Modules/getpath.c | 2 +- tool/decode/dumpvdso.c | 32 + tool/decode/elf.c | 19 + tool/net/demo/script.lua | 2 + tool/net/demo/unix-dir.lua | 93 + tool/net/demo/unix-info.lua | 58 +- tool/net/demo/unix-raise.lua | 11 + tool/net/demo/unix-rawsocket.lua | 33 +- tool/net/demo/unix-subprocess.lua | 83 +- tool/net/demo/unix-webserver.lua | 7 +- tool/net/help.txt | 1830 ++++++++++++----- tool/net/lfuncs.c | 6 + tool/net/lfuncs.h | 1 + tool/net/lunix.c | 1498 +++++++++----- tool/net/net.mk | 2 + tool/net/redbean.c | 38 +- 51 files changed, 3096 insertions(+), 1395 deletions(-) create mode 100644 libc/calls/getexecutablename.c create mode 100644 libc/calls/oldbench.c delete mode 100644 libc/calls/program_executable_name.c create mode 100644 libc/calls/vdsofunc.greg.c create mode 100644 libc/elf/getelfdynstringtable.c create mode 100644 libc/elf/getelfdynsymboltable.c rename libc/calls/nowl.S => test/libc/calls/clock_gettime_test.c (71%) create mode 100644 tool/decode/dumpvdso.c create mode 100755 tool/net/demo/script.lua create mode 100644 tool/net/demo/unix-dir.lua create mode 100755 tool/net/demo/unix-raise.lua diff --git a/libc/calls/calls.mk b/libc/calls/calls.mk index 28e882b67..72322331c 100644 --- a/libc/calls/calls.mk +++ b/libc/calls/calls.mk @@ -65,6 +65,7 @@ $(LIBC_CALLS_A).pkg: \ $(LIBC_CALLS_A_OBJS) \ $(foreach x,$(LIBC_CALLS_A_DIRECTDEPS),$($(x)_A).pkg) +o/$(MODE)/libc/calls/vdsofunc.greg.o \ o/$(MODE)/libc/calls/directmap.o \ o/$(MODE)/libc/calls/directmap-nt.o \ o/$(MODE)/libc/calls/raise.o: \ diff --git a/libc/calls/clock_gettime.c b/libc/calls/clock_gettime.c index 4d206301c..f091d6719 100644 --- a/libc/calls/clock_gettime.c +++ b/libc/calls/clock_gettime.c @@ -26,6 +26,8 @@ #include "libc/nt/synchronization.h" #include "libc/sysv/errfuns.h" +static typeof(sys_clock_gettime) *__clock_gettime = sys_clock_gettime; + /** * Returns nanosecond time. * @@ -52,7 +54,7 @@ noinstrument int clock_gettime(int clockid, struct timespec *ts) { rc = einval(); } else if (!IsWindows()) { e = errno; - if ((rc = sys_clock_gettime(clockid, ts))) { + if ((rc = __clock_gettime(clockid, ts))) { errno = e; ad = sys_gettimeofday((struct timeval *)ts, NULL, NULL); assert(ad.ax != -1); @@ -72,3 +74,23 @@ noinstrument int clock_gettime(int clockid, struct timespec *ts) { } return rc; } + +/** + * Returns fast system clock_gettime() if it exists. + */ +void *__get_clock_gettime(void) { + void *vdso; + static bool once; + static void *result; + if (!once) { + if ((vdso = __vdsofunc("__vdso_clock_gettime"))) { + __clock_gettime = result = vdso; + } + once = true; + } + return result; +} + +const void *const __clock_gettime_ctor[] initarray = { + __get_clock_gettime, +}; diff --git a/libc/calls/faccessat.c b/libc/calls/faccessat.c index 6b2c7ac3b..cadba32f0 100644 --- a/libc/calls/faccessat.c +++ b/libc/calls/faccessat.c @@ -33,7 +33,7 @@ * file is a relative path, then file is opened relative to dirfd * @param path is a filename or directory * @param mode can be R_OK, W_OK, X_OK, F_OK - * @param flags should be 0 + * @param flags can have AT_EACCESS, AT_SYMLINK_NOFOLLOW * @return 0 if ok, or -1 and sets errno * @asyncsignalsafe */ diff --git a/libc/calls/fstat-nt.c b/libc/calls/fstat-nt.c index 01ffa70c5..125ef8e09 100644 --- a/libc/calls/fstat-nt.c +++ b/libc/calls/fstat-nt.c @@ -106,7 +106,7 @@ textwindows int sys_fstat_nt(int64_t handle, struct stat *st) { st->st_size = (uint64_t)wst.nFileSizeHigh << 32 | wst.nFileSizeLow; st->st_blksize = PAGESIZE; st->st_dev = wst.dwVolumeSerialNumber; - st->st_rdev = wst.dwVolumeSerialNumber; + st->st_rdev = 0; st->st_ino = (uint64_t)wst.nFileIndexHigh << 32 | wst.nFileIndexLow; st->st_nlink = wst.nNumberOfLinks; if (S_ISLNK(st->st_mode)) { diff --git a/libc/calls/getexecutablename.c b/libc/calls/getexecutablename.c new file mode 100644 index 000000000..a191df534 --- /dev/null +++ b/libc/calls/getexecutablename.c @@ -0,0 +1,121 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/dce.h" +#include "libc/errno.h" +#include "libc/macros.internal.h" +#include "libc/nt/runtime.h" +#include "libc/runtime/runtime.h" +#include "libc/sysv/consts/at.h" +#include "libc/sysv/consts/ok.h" + +#define SIZE 1024 +#define CTL_KERN 1 +#define KERN_PROC 14 +#define KERN_PROC_PATHNAME_FREEBSD 12 +#define KERN_PROC_PATHNAME_NETBSD 5 + +char program_executable_name[PATH_MAX + 1]; + +static inline void GetProgramExecutableNameImpl(char *p, char *e) { + char *q; + ssize_t rc; + size_t i, n; + union { + int cmd[4]; + char16_t path16[PATH_MAX + 1]; + } u; + + if (IsWindows()) { + n = GetModuleFileName(0, u.path16, ARRAYLEN(u.path16)); + for (i = 0; i < n; ++i) { + if (u.path16[i] == '\\') { + u.path16[i] = '/'; + } + } + if (isalpha(u.path16[0]) && u.path16[1] == ':' && u.path16[2] == '/') { + p[0] = '/'; + p[1] = '/'; + p[2] = '?'; + p[3] = '/'; + p += 4; + } + tprecode16to8(p, e - p, u.path16); + return; + } + + if (__argc && (q = __argv[0]) && !sys_faccessat(AT_FDCWD, q, F_OK, 0)) { + if (*q != '/') { + if (q[0] == '.' && q[1] == '/') { + q += 2; + } + if (getcwd(p, e - p)) { + while (*p) ++p; + *p++ = '/'; + } + } + for (i = 0; *q && p + 1 < e; ++p, ++q) { + *p = *q; + } + *p = 0; + return; + } + + if ((rc = sys_readlinkat(AT_FDCWD, "/proc/self/exe", p, e - p - 1)) > 0 || + (rc = sys_readlinkat(AT_FDCWD, "/proc/curproc/file", p, e - p - 1)) > 0) { + p[rc] = 0; + return; + } + + if (IsFreebsd() || IsNetbsd()) { + u.cmd[0] = CTL_KERN; + u.cmd[1] = KERN_PROC; + if (IsFreebsd()) { + u.cmd[2] = KERN_PROC_PATHNAME_FREEBSD; + } else { + u.cmd[2] = KERN_PROC_PATHNAME_NETBSD; + } + u.cmd[3] = -1; // current process + n = e - p; + if (sysctl(u.cmd, ARRAYLEN(u.cmd), p, &n, 0, 0) != -1) { + return; + } + } +} + +/** + * Returns absolute path of executable. + */ +char *GetProgramExecutableName(void) { + int e; + static bool once; + if (!once) { + e = errno; + GetProgramExecutableNameImpl( + program_executable_name, + program_executable_name + sizeof(program_executable_name)); + errno = e; + } + return program_executable_name; +} + +const void *const GetProgramExecutableNameCtor[] initarray = { + GetProgramExecutableName, +}; diff --git a/libc/calls/gettimeofday.c b/libc/calls/gettimeofday.c index 425549707..927e785a4 100644 --- a/libc/calls/gettimeofday.c +++ b/libc/calls/gettimeofday.c @@ -25,6 +25,8 @@ #include "libc/time/struct/timezone.h" #include "libc/time/time.h" +static typeof(sys_gettimeofday) *__gettimeofday = sys_gettimeofday; + /** * Returns system wall time in microseconds. * @@ -40,7 +42,7 @@ int gettimeofday(struct timeval *tv, struct timezone *tz) { return efault(); } if (!IsWindows() && !IsMetal()) { - ad = sys_gettimeofday(tv, tz, NULL); + ad = __gettimeofday(tv, tz, NULL); assert(ad.ax != -1); if (SupportsXnu() && ad.ax && tv) { tv->tv_sec = ad.ax; @@ -53,3 +55,14 @@ int gettimeofday(struct timeval *tv, struct timezone *tz) { return sys_gettimeofday_nt(tv, tz); } } + +static textstartup void __gettimeofday_init(void) { + void *vdso; + if ((vdso = __vdsofunc("__vdso_gettimeofday"))) { + __gettimeofday = vdso; + } +} + +const void *const __gettimeofday_ctor[] initarray = { + __gettimeofday_init, +}; diff --git a/libc/calls/internal.h b/libc/calls/internal.h index 283b6f9fb..12aa6e8af 100644 --- a/libc/calls/internal.h +++ b/libc/calls/internal.h @@ -245,6 +245,8 @@ void sys_exit(int) hidden; ╚────────────────────────────────────────────────────────────────────────────│*/ void __onfork(void) hidden; +void *__vdsofunc(const char *) hidden; +void *__get_clock_gettime(void) hidden; i32 __fixupnewfd(i32, i32) hidden; void __restore_rt() hidden; int sys_utimensat_xnu(int, const char *, const struct timespec *, int) hidden; diff --git a/libc/calls/now.c b/libc/calls/now.c index 2e5cf754a..a7ec88b88 100644 --- a/libc/calls/now.c +++ b/libc/calls/now.c @@ -31,9 +31,9 @@ #include "libc/time/time.h" static struct Now { - bool once; uint64_t k0; long double r0, cpn; + typeof(sys_clock_gettime) *clock_gettime; } g_now; static long double GetTimeSample(void) { @@ -73,22 +73,39 @@ void RefreshTime(void) { now.cpn = MeasureNanosPerCycle(); now.r0 = dtime(CLOCK_REALTIME); now.k0 = rdtsc(); - now.once = true; memcpy(&g_now, &now, sizeof(now)); } -long double ConvertTicksToNanos(uint64_t ticks) { - if (!g_now.once) RefreshTime(); - return ticks * g_now.cpn; /* pico scale */ -} - -long double nowl_sys(void) { +static long double nowl_sys(void) { return dtime(CLOCK_REALTIME); } -long double nowl_art(void) { - uint64_t ticks; - if (!g_now.once) RefreshTime(); - ticks = unsignedsubtract(rdtsc(), g_now.k0); +static long double nowl_art(void) { + uint64_t ticks = rdtsc() - g_now.k0; return g_now.r0 + (1 / 1e9L * (ticks * g_now.cpn)); } + +static long double nowl_vdso(void) { + long double secs; + struct timespec tv; + g_now.clock_gettime(CLOCK_REALTIME, &tv); + secs = tv.tv_nsec; + secs *= 1 / 1e9L; + secs += tv.tv_sec; + return secs; +} + +long double nowl_setup(void) { + uint64_t ticks; + if ((g_now.clock_gettime = __get_clock_gettime())) { + nowl = nowl_vdso; + } else if (X86_HAVE(INVTSC)) { + RefreshTime(); + nowl = nowl_art; + } else { + nowl = nowl_sys; + } + return nowl(); +} + +long double (*nowl)(void) = nowl_setup; diff --git a/libc/calls/oldbench.c b/libc/calls/oldbench.c new file mode 100644 index 000000000..ee1ab4c41 --- /dev/null +++ b/libc/calls/oldbench.c @@ -0,0 +1,83 @@ +/*-*- 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 │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/bits.h" +#include "libc/bits/initializer.internal.h" +#include "libc/bits/safemacros.internal.h" +#include "libc/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/calls/strace.internal.h" +#include "libc/dce.h" +#include "libc/macros.internal.h" +#include "libc/nexgen32e/rdtsc.h" +#include "libc/nexgen32e/x86feature.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/clock.h" +#include "libc/time/time.h" + +static struct Now { + bool once; + uint64_t k0; + long double r0, cpn; +} g_now; + +static long double GetTimeSample(void) { + uint64_t tick1, tick2; + long double time1, time2; + sched_yield(); + time1 = dtime(CLOCK_REALTIME); + tick1 = rdtsc(); + nanosleep(&(struct timespec){0, 1000000}, NULL); + time2 = dtime(CLOCK_REALTIME); + tick2 = rdtsc(); + return (time2 - time1) * 1e9 / MAX(1, tick2 - tick1); +} + +static long double MeasureNanosPerCycle(void) { + bool tc; + int i, n; + long double avg, samp; + tc = __time_critical; + __time_critical = true; + if (IsWindows()) { + n = 30; + } else { + n = 20; + } + for (avg = 1.0L, i = 1; i < n; ++i) { + samp = GetTimeSample(); + avg += (samp - avg) / i; + } + __time_critical = tc; + STRACE("MeasureNanosPerCycle cpn*1000=%d", (long)(avg * 1000)); + return avg; +} + +static void Refresh(void) { + struct Now now; + now.cpn = MeasureNanosPerCycle(); + now.r0 = dtime(CLOCK_REALTIME); + now.k0 = rdtsc(); + now.once = true; + memcpy(&g_now, &now, sizeof(now)); +} + +long double ConvertTicksToNanos(uint64_t ticks) { + if (!g_now.once) Refresh(); + return ticks * g_now.cpn; /* pico scale */ +} diff --git a/libc/calls/pipe-nt.c b/libc/calls/pipe-nt.c index 7728acdcd..0c0db0823 100644 --- a/libc/calls/pipe-nt.c +++ b/libc/calls/pipe-nt.c @@ -23,6 +23,7 @@ #include "libc/nt/enum/fileflagandattributes.h" #include "libc/nt/ipc.h" #include "libc/nt/runtime.h" +#include "libc/sysv/consts/limits.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/errfuns.h" @@ -42,9 +43,9 @@ textwindows int sys_pipe_nt(int pipefd[2], unsigned flags) { } else { mode = kNtPipeTypeMessage | kNtPipeReadmodeMessage; } - if ((hin = CreateNamedPipe(pipename, - kNtPipeAccessInbound | kNtFileFlagOverlapped, mode, - 1, 512, 512, 0, &kNtIsInheritable)) != -1) { + if ((hin = CreateNamedPipe( + pipename, kNtPipeAccessInbound | kNtFileFlagOverlapped, mode, 1, + PIPE_BUF, PIPE_BUF, 0, &kNtIsInheritable)) != -1) { if ((hout = CreateFile(pipename, kNtGenericWrite, 0, &kNtIsInheritable, kNtOpenExisting, kNtFileFlagOverlapped, 0)) != -1) { g_fds.p[reader].kind = kFdFile; diff --git a/libc/calls/program_executable_name.c b/libc/calls/program_executable_name.c deleted file mode 100644 index 316147b28..000000000 --- a/libc/calls/program_executable_name.c +++ /dev/null @@ -1,148 +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 2021 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/assert.h" -#include "libc/bits/bits.h" -#include "libc/calls/calls.h" -#include "libc/calls/internal.h" -#include "libc/calls/strace.internal.h" -#include "libc/dce.h" -#include "libc/errno.h" -#include "libc/intrin/kprintf.h" -#include "libc/intrin/spinlock.h" -#include "libc/log/libfatal.internal.h" -#include "libc/macros.internal.h" -#include "libc/mem/alloca.h" -#include "libc/nt/runtime.h" -#include "libc/runtime/runtime.h" -#include "libc/str/path.h" -#include "libc/str/str.h" -#include "libc/str/tpenc.h" -#include "libc/str/utf16.h" -#include "libc/sysv/consts/at.h" -#include "libc/sysv/consts/auxv.h" -#include "libc/sysv/consts/ok.h" -#include "libc/sysv/consts/prot.h" - -#define SIZE 1024 -#define CTL_KERN 1 -#define KERN_PROC 14 -#define KERN_PROC_PATHNAME_FREEBSD 12 -#define KERN_PROC_PATHNAME_NETBSD 5 - -char program_executable_name[SIZE]; - -static textwindows bool GetNtExePath(char exe[SIZE]) { - bool32 rc; - uint64_t w; - wint_t x, y; - uint32_t i, j; - char16_t p[PATH_MAX + 1]; - p[0] = 0; - rc = GetModuleFileName(0, p, ARRAYLEN(p)); - NTTRACE("GetModuleFileName(0, [%#hs]) → %hhhd", p, rc); - if (!rc) return false; - j = 0; - if (p[0] != '\\' || p[1] != '\\' || p[2] != '?' || p[3] != '\\') { - exe[j++] = '/'; - exe[j++] = '/'; - exe[j++] = '?'; - exe[j++] = '/'; - } - for (i = 0; (x = p[i++] & 0xffff);) { - if (!IsUcs2(x)) { - y = p[i++] & 0xffff; - x = MergeUtf16(x, y); - } - if (x == '\\') x = '/'; - w = tpenc(x); - do { - exe[j] = w; - if (++j == SIZE) { - return false; - } - } while ((w >>= 8)); - } - exe[j] = 0; - return true; -} - -static void ReadProgramExecutableName(char exe[SIZE], char *argv0, - uintptr_t *auxv) { - int e; - size_t m; - ssize_t n; - int cmd[4]; - char *p, *t; - e = errno; - if (!IsWindows() || !GetNtExePath(exe)) { - for (p = 0; *auxv; auxv += 2) { - if (*auxv == AT_EXECFN) { - p = (char *)auxv[1]; - break; - } - } - n = 0; - if (!p) p = argv0; - if (p) { - if (!_isabspath(p)) { - if (getcwd(exe, SIZE - 1)) { - n = strlen(exe); - exe[n++] = '/'; - } - } - for (; *p; ++p) { - if (n + 1 < SIZE) { - exe[n++] = *p; - } - } - } - exe[n] = 0; - } - errno = e; -} - -/** - * Returns absolute path of executable. - * - * This variable is initialized automatically at startup. The path is - * basically `argv[0]` except some extra vetting is done to provide - * stronger assurance that the path can be counted upon to exist. - * - * For example, if your program is executed as a relative path and then - * your program calls `chdir()`, then `argv[0]` will be incorrect; but - * `program_executable_name` will work, because it prefixed `getcwd()` - * early in the initialization phase. - * - * @see GetInterpreterExecutableName() - * @see program_invocation_short_name - * @see program_invocation_name - */ -char *GetProgramExecutableName(void) { - static bool once; - if (!once) { - ReadProgramExecutableName(program_executable_name, __argv[0], __auxv); - once = true; - } - return program_executable_name; -} - -// try our best to memoize it before a chdir() happens -const void *const program_executable_name_init_ctor[] initarray = { - GetProgramExecutableName, -}; diff --git a/libc/calls/sigaddset.c b/libc/calls/sigaddset.c index e730d90b7..260b80304 100644 --- a/libc/calls/sigaddset.c +++ b/libc/calls/sigaddset.c @@ -22,13 +22,14 @@ /** * Adds signal to set. * - * @return true, false, or -1 w/ errno + * @return 0 on success, or -1 w/ errno + * @raises EINVAL if `1 ≤ sig ≤ NSIG` isn't the case * @asyncsignalsafe */ int sigaddset(sigset_t *set, int sig) { - unsigned i = sig - 1; - if (i < sizeof(set->__bits) * 8) { - set->__bits[i >> 6] |= 1ull << (i & 63); + _Static_assert(sizeof(set->__bits[0]) * CHAR_BIT == 64, ""); + if (1 <= sig && sig <= NSIG) { + set->__bits[(sig - 1) >> 6] |= 1ull << ((sig - 1) & 63); return 0; } else { return einval(); diff --git a/libc/calls/sigdelset.c b/libc/calls/sigdelset.c index 856573808..84ca3129b 100644 --- a/libc/calls/sigdelset.c +++ b/libc/calls/sigdelset.c @@ -23,12 +23,13 @@ * Removes signal from set. * * @return 0 on success, or -1 w/ errno + * @raises EINVAL if `1 ≤ sig ≤ NSIG` isn't the case * @asyncsignalsafe */ int sigdelset(sigset_t *set, int sig) { - unsigned i = sig - 1; - if (i < sizeof(set->__bits) * 8) { - set->__bits[i >> 6] &= ~(1ull << (i & 63)); + _Static_assert(sizeof(set->__bits[0]) * CHAR_BIT == 64, ""); + if (1 <= sig && sig <= NSIG) { + set->__bits[(sig - 1) >> 6] &= ~(1ull << ((sig - 1) & 63)); return 0; } else { return einval(); diff --git a/libc/calls/sigismember.c b/libc/calls/sigismember.c index bbb57fe73..246ac55db 100644 --- a/libc/calls/sigismember.c +++ b/libc/calls/sigismember.c @@ -22,14 +22,15 @@ /** * Returns true if signal is member of set. * - * @return true, false, or -1 w/ errno + * @return 1 if set, 0 if not set, or -1 w/ errno + * @raises EINVAL if `1 ≤ sig ≤ NSIG` isn't the case * @asyncsignalsafe * @vforksafe */ int sigismember(const sigset_t *set, int sig) { - unsigned i = sig - 1; - if (i < sizeof(set->__bits) * 8) { - return (set->__bits[i >> 6] >> (i & 63)) & 1; + _Static_assert(sizeof(set->__bits[0]) * CHAR_BIT == 64, ""); + if (1 <= sig && sig <= NSIG) { + return !!(set->__bits[(sig - 1) >> 6] & (1ull << ((sig - 1) & 63))); } else { return einval(); } diff --git a/libc/calls/struct/stat.h b/libc/calls/struct/stat.h index fc39ec4a5..b744420cd 100644 --- a/libc/calls/struct/stat.h +++ b/libc/calls/struct/stat.h @@ -10,7 +10,7 @@ struct stat { /* cosmo abi */ uint32_t st_mode; /* 24: octal file mask thing */ uint32_t st_uid; /* 28: user id of owner */ uint32_t st_gid; /* group id of owning group */ - uint32_t st_flags; /* flags (bsd-only) */ + uint32_t st_flags; /* nt/xnu/bsd-only */ uint64_t st_rdev; /* id of device if a special file */ int64_t st_size; /* bytes in file */ int64_t st_blksize; /* preferred chunking for underlying filesystem */ @@ -19,7 +19,7 @@ struct stat { /* cosmo abi */ struct timespec st_mtim; /* modified time */ struct timespec st_ctim; /* complicated time */ struct timespec st_birthtim; - uint64_t st_gen; + uint64_t st_gen; /* xnu/bsd only */ }; #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/calls/vdsofunc.greg.c b/libc/calls/vdsofunc.greg.c new file mode 100644 index 000000000..843b45822 --- /dev/null +++ b/libc/calls/vdsofunc.greg.c @@ -0,0 +1,94 @@ +/*-*- 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 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/bits.h" +#include "libc/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/elf/scalar.h" +#include "libc/elf/struct/ehdr.h" +#include "libc/elf/struct/shdr.h" +#include "libc/elf/struct/sym.h" +#include "libc/log/libfatal.internal.h" +#include "libc/runtime/runtime.h" +#include "libc/sysv/consts/auxv.h" + +#define LAZY_RHEL7_RELOCATION 0xfffff + +#define GetStr(tab, rva) ((char *)(tab) + (rva)) +#define GetSection(e, s) ((void *)((intptr_t)(e) + (size_t)(s)->sh_offset)) +#define GetShstrtab(e) GetSection(e, GetShdr(e, (e)->e_shstrndx)) +#define GetSectionName(e, s) GetStr(GetShstrtab(e), (s)->sh_name) +#define GetPhdr(e, i) \ + ((Elf64_Phdr *)((intptr_t)(e) + (e)->e_phoff + \ + (size_t)(e)->e_phentsize * (i))) +#define GetShdr(e, i) \ + ((Elf64_Shdr *)((intptr_t)(e) + (e)->e_shoff + \ + (size_t)(e)->e_shentsize * (i))) + +static char *GetDynamicStringTable(Elf64_Ehdr *e, size_t *n) { + char *name; + Elf64_Half i; + Elf64_Shdr *shdr; + for (i = 0; i < e->e_shnum; ++i) { + shdr = GetShdr(e, i); + name = GetSectionName(e, GetShdr(e, i)); + if (shdr->sh_type == SHT_STRTAB) { + name = GetSectionName(e, GetShdr(e, i)); + if (name && READ64LE(name) == READ64LE(".dynstr")) { + if (n) *n = shdr->sh_size; + return GetSection(e, shdr); + } + } + } + return 0; +} + +static Elf64_Sym *GetDynamicSymbolTable(Elf64_Ehdr *e, Elf64_Xword *n) { + Elf64_Half i; + Elf64_Shdr *shdr; + for (i = e->e_shnum; i > 0; --i) { + shdr = GetShdr(e, i - 1); + if (shdr->sh_type == SHT_DYNSYM) { + if (shdr->sh_entsize != sizeof(Elf64_Sym)) continue; + if (n) *n = shdr->sh_size / shdr->sh_entsize; + return GetSection(e, shdr); + } + } + return 0; +} + +/** + * Returns Linux Kernel Virtual Dynamic Shared Object function address. + */ +void *__vdsofunc(const char *name) { + size_t m; + char *names; + Elf64_Ehdr *ehdr; + Elf64_Xword i, n; + Elf64_Sym *symtab, *sym; + if ((ehdr = (Elf64_Ehdr *)getauxval(AT_SYSINFO_EHDR)) && + (names = GetDynamicStringTable(ehdr, &m)) && + (symtab = GetDynamicSymbolTable(ehdr, &n))) { + for (i = 0; i < n; ++i) { + if (!__strcmp(names + symtab[i].st_name, name)) { + return (char *)ehdr + (symtab[i].st_value & LAZY_RHEL7_RELOCATION); + } + } + } + return 0; +} diff --git a/libc/elf/elf.h b/libc/elf/elf.h index ed9abee8f..9df8827ac 100644 --- a/libc/elf/elf.h +++ b/libc/elf/elf.h @@ -26,6 +26,8 @@ void GetElfVirtualAddressRange(const Elf64_Ehdr *, size_t, intptr_t *, intptr_t *); char *GetElfString(const Elf64_Ehdr *, size_t, const char *, Elf64_Word); const char *GetElfSectionName(const Elf64_Ehdr *, size_t, Elf64_Shdr *); +Elf64_Sym *GetElfDynSymbolTable(const Elf64_Ehdr *, size_t, Elf64_Xword *); +char *GetElfDynStringTable(const Elf64_Ehdr *, size_t); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/elf/getelfdynstringtable.c b/libc/elf/getelfdynstringtable.c new file mode 100644 index 000000000..6ed29f0a9 --- /dev/null +++ b/libc/elf/getelfdynstringtable.c @@ -0,0 +1,40 @@ +/*-*- 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 │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/elf/def.h" +#include "libc/elf/elf.h" +#include "libc/str/str.h" + +char *GetElfDynStringTable(const Elf64_Ehdr *elf, size_t mapsize) { + char *name; + Elf64_Half i; + Elf64_Shdr *shdr; + if (elf->e_shentsize) { + for (i = 0; i < elf->e_shnum; ++i) { + shdr = GetElfSectionHeaderAddress(elf, mapsize, i); + if (shdr->sh_type == SHT_STRTAB) { + name = GetElfSectionName(elf, mapsize, + GetElfSectionHeaderAddress(elf, mapsize, i)); + if (name && !strcmp(name, ".dynstr")) { + return GetElfSectionAddress(elf, mapsize, shdr); + } + } + } + } + return NULL; +} diff --git a/libc/elf/getelfdynsymboltable.c b/libc/elf/getelfdynsymboltable.c new file mode 100644 index 000000000..09256fda6 --- /dev/null +++ b/libc/elf/getelfdynsymboltable.c @@ -0,0 +1,37 @@ +/*-*- 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 │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/elf/def.h" +#include "libc/elf/elf.h" + +Elf64_Sym *GetElfDynSymbolTable(const Elf64_Ehdr *elf, size_t mapsize, + Elf64_Xword *out_count) { + Elf64_Half i; + Elf64_Shdr *shdr; + if (elf->e_shentsize) { + for (i = elf->e_shnum; i > 0; --i) { + shdr = GetElfSectionHeaderAddress(elf, mapsize, i - 1); + if (shdr->sh_type == SHT_DYNSYM) { + if (shdr->sh_entsize != sizeof(Elf64_Sym)) continue; + if (out_count) *out_count = shdr->sh_size / shdr->sh_entsize; + return GetElfSectionAddress(elf, mapsize, shdr); + } + } + } + return NULL; +} diff --git a/libc/fmt/strerror_wr.greg.c b/libc/fmt/strerror_wr.greg.c index dc3d2fc77..93b8616c4 100644 --- a/libc/fmt/strerror_wr.greg.c +++ b/libc/fmt/strerror_wr.greg.c @@ -16,7 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#define ShouldUseMsabiAttribute() 1 #include "libc/bits/safemacros.internal.h" #include "libc/dce.h" #include "libc/fmt/fmt.h" @@ -44,18 +43,18 @@ int strerror_wr(int err, uint32_t winerr, char *buf, size_t size) { for (; (c = *sym++); --size) if (size > 1) *buf++ = c; if (size) *buf = 0; - } else if (!IsWindows()) { - ksnprintf(buf, size, "%s[%d][%s]", sym, err, msg); + } else if (!IsWindows() || err == winerr || !winerr) { + ksnprintf(buf, size, "%s:%d:%s", sym, err, msg); } else { - if ((n = __imp_FormatMessageW( + if ((n = FormatMessage( kNtFormatMessageFromSystem | kNtFormatMessageIgnoreInserts, 0, winerr, MAKELANGID(kNtLangNeutral, kNtSublangDefault), winmsg, ARRAYLEN(winmsg), 0))) { while ((n && winmsg[n - 1] <= ' ') || winmsg[n - 1] == '.') --n; - ksnprintf(buf, size, "%s[%d][%s][%.*hs][%d]", sym, err, msg, n, winmsg, - winerr); + ksnprintf(buf, size, "%s:%d:%s:%d:%.*hs", sym, err, msg, winerr, n, + winmsg); } else { - ksnprintf(buf, size, "%s[%d][%s][%d]", sym, err, msg, winerr); + ksnprintf(buf, size, "%s:%d:%s:%d", sym, err, msg, winerr); } } return 0; diff --git a/libc/integral/normalize.inc b/libc/integral/normalize.inc index 335a24b9b..a268369b6 100644 --- a/libc/integral/normalize.inc +++ b/libc/integral/normalize.inc @@ -71,7 +71,7 @@ #define CACHELINE 0x40 /* nexgen32e */ #define CHAR_BIT 8 /* b/c von neumann */ #define ARG_MAX 0x8000 /* b/c windows */ -#define PATH_MAX 512 /* b/c bloat */ +#define PATH_MAX 511 /* b/c bloat */ #define NAME_MAX 63 /* b/c dns */ #define CHILD_MAX 25 /* only if malloc isn't linked */ #define OPEN_MAX 16 /* only if malloc isn't linked */ diff --git a/libc/runtime/getinterpreterexecutablename.c b/libc/runtime/getinterpreterexecutablename.c index fd4331b8a..2f3aa6c24 100644 --- a/libc/runtime/getinterpreterexecutablename.c +++ b/libc/runtime/getinterpreterexecutablename.c @@ -62,17 +62,17 @@ char *GetInterpreterExecutableName(char *p, size_t n) { } else if ((rc = sys_readlinkat(AT_FDCWD, "/proc/curproc/file", p, n - 1)) > 0) { errno = e; - p[n] = 0; + p[rc] = 0; return p; } else if (IsFreebsd() || IsNetbsd()) { - cmd[0] = 1 /* CTL_KERN */; - cmd[1] = 14 /* KERN_PROC */; - if (IsFreebsd()) { - cmd[2] = 12 /* KERN_PROC_PATHNAME */; - } else { - cmd[2] = 5 /* KERN_PROC_PATHNAME */; - } - cmd[3] = -1; /* current process */ + cmd[0] = 1; // CTL_KERN + cmd[1] = 14; // KERN_PROC + if (IsFreebsd()) { // + cmd[2] = 12; // KERN_PROC_PATHNAME + } else { // + cmd[2] = 5; // KERN_PROC_PATHNAME + } // + cmd[3] = -1; // current process if (sysctl(cmd, ARRAYLEN(cmd), p, &n, 0, 0) != -1) { errno = e; return p; diff --git a/libc/runtime/printargs.greg.c b/libc/runtime/printargs.greg.c index 322de9e2f..d5a83c32e 100644 --- a/libc/runtime/printargs.greg.c +++ b/libc/runtime/printargs.greg.c @@ -324,8 +324,8 @@ textstartup void __printargs(const char *prologue) { PRINT(" ☼ %s = %#s", "kTmpPath", kTmpPath); PRINT(" ☼ %s = %#s", "kNtSystemDirectory", kNtSystemDirectory); PRINT(" ☼ %s = %#s", "kNtWindowsDirectory", kNtWindowsDirectory); - PRINT(" ☼ %s = %#s", "program_executable_name", GetProgramExecutableName()); - PRINT(" ☼ %s = %#s", "GetInterpreterExecutableName()", + PRINT(" ☼ %s = %#s", "GetProgramExecutableName", GetProgramExecutableName()); + PRINT(" ☼ %s = %#s", "GetInterpreterExecutableName", GetInterpreterExecutableName(path, sizeof(path))); PRINT(" ☼ %s = %p", "RSP", __builtin_frame_address(0)); PRINT(" ☼ %s = %p", "GetStackAddr()", GetStackAddr(0)); diff --git a/libc/runtime/runtime.h b/libc/runtime/runtime.h index 2da6ea692..82142b687 100644 --- a/libc/runtime/runtime.h +++ b/libc/runtime/runtime.h @@ -14,7 +14,6 @@ extern char **__argv; /* CRT */ extern char **__envp; /* CRT */ extern unsigned long *__auxv; /* CRT */ extern intptr_t __oldstack; /* CRT */ -extern char program_executable_name[]; /* RII */ extern char *program_invocation_name; /* RII */ extern char *program_invocation_short_name; /* RII */ extern int g_ftrace; /* CRT */ diff --git a/libc/runtime/winmain.greg.c b/libc/runtime/winmain.greg.c index 0ad345803..ea72891ae 100644 --- a/libc/runtime/winmain.greg.c +++ b/libc/runtime/winmain.greg.c @@ -219,9 +219,6 @@ __msabi static textwindows wontreturn void WinMainNew(const char16_t *cmdline) { } } env16 = GetEnvironmentStrings(); - for (char16_t *e = env16; *e; e += StrLen16(e) + 1) { - NTTRACE("GetEnvironmentStrings() → %!#hs", e); - } NTTRACE("WinMainNew() loading environment"); GetDosEnviron(env16, wa->envblock, ARRAYLEN(wa->envblock) - 8, wa->envp, ARRAYLEN(wa->envp) - 1); diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index febb4bb42..ec140561e 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -436,10 +436,10 @@ syscon ioctl TIOCINQ 0x541b 0x4004667f 0x4004667f 0x4004667f 0x4004667f # group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary syscon at AT_FDCWD -100 -2 -100 -100 -100 -100 # faked nt syscon at AT_SYMLINK_NOFOLLOW 0x0100 0x20 0x0200 2 0x200 0x0100 # faked nt +syscon at AT_SYMLINK_FOLLOW 0x0400 0x40 0x0400 4 0x400 0 # see linkat(2) syscon at AT_REMOVEDIR 0x0200 0x80 0x0800 8 0x800 0x0200 # faked nt syscon at AT_EACCESS 0x0200 0x10 0x0100 1 0x100 0 syscon at AT_EMPTY_PATH 0x1000 0 0 0 0 0 # linux 2.6.39+; see unlink, O_TMPFILE, etc. -syscon at AT_SYMLINK_FOLLOW 0x0400 0x40 0x0400 4 0x400 0 # uhhh wut # memfd_create() flags # @@ -1229,6 +1229,11 @@ syscon mount MNT_NOCLUSTERR 0 0 0x40000000 0 0 0 # disable cluster syscon mount MNT_NOCLUSTERW 0 0 0x80000000 0 0 0 # disable cluster write syscon mount MNT_SNAPSHOT 0 0x40000000 0x01000000 0 0 0 # confusing +# limits +# +# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary +syscon misc PIPE_BUF 4096 512 512 512 512 4096 # bsd consensus + # unmount() flags # a.k.a. umount2() on linux # @@ -3055,7 +3060,6 @@ syscon misc NGREG 23 0 0 0 0 0 syscon misc NOGROUP -1 0xffff 0xffff 0xffff 0xffff 0 # bsd consensus syscon misc ORDERED_QUEUE_TAG 34 0 0 0 0 0 syscon misc ORIG_RAX 15 0 0 0 0 0 -syscon misc PIPE_BUF 0x1000 0x0200 0x0200 0x0200 0x0200 0 # bsd consensus syscon misc PRE_FETCH 52 0 0 0 0 0 syscon misc QUEUE_FULL 20 0 0 0 0 0 syscon misc REASSIGN_BLOCKS 7 0 0 0 0 0 diff --git a/libc/sysv/consts/PIPE_BUF.S b/libc/sysv/consts/PIPE_BUF.S index 8a0b5b01c..2d0310f41 100644 --- a/libc/sysv/consts/PIPE_BUF.S +++ b/libc/sysv/consts/PIPE_BUF.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon misc,PIPE_BUF,0x1000,0x0200,0x0200,0x0200,0x0200,0 +.syscon misc,PIPE_BUF,4096,512,512,512,512,4096 diff --git a/test/libc/calls/access_test.c b/test/libc/calls/access_test.c index 88e101a7a..07587ab20 100644 --- a/test/libc/calls/access_test.c +++ b/test/libc/calls/access_test.c @@ -72,5 +72,5 @@ TEST(access, testRequestWriteOnReadOnly_returnsEaccess) { } TEST(access, runThisExecutable) { - ASSERT_SYS(0, 0, access(program_executable_name, R_OK | X_OK)); + ASSERT_SYS(0, 0, access(GetProgramExecutableName(), R_OK | X_OK)); } diff --git a/libc/calls/nowl.S b/test/libc/calls/clock_gettime_test.c similarity index 71% rename from libc/calls/nowl.S rename to test/libc/calls/clock_gettime_test.c index 9c2d8bad0..e67d61473 100644 --- a/libc/calls/nowl.S +++ b/test/libc/calls/clock_gettime_test.c @@ -1,7 +1,7 @@ -/*-*- 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│ +/*-*- 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 │ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ │ │ │ Permission to use, copy, modify, and/or distribute this software for │ │ any purpose with or without fee is hereby granted, provided that the │ @@ -16,22 +16,14 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/nexgen32e/x86feature.h" -#include "libc/macros.internal.h" +#include "libc/calls/calls.h" +#include "libc/calls/struct/timespec.h" +#include "libc/sysv/consts/clock.h" +#include "libc/testlib/ezbench.h" +#include "libc/testlib/testlib.h" -// Returns timestamp without needing system calls. -// -// @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 +BENCH(clock_gettime, bench) { + struct timespec ts; + EZBENCH2("nowl", donothing, nowl()); + EZBENCH2("clock_gettime", donothing, clock_gettime(CLOCK_REALTIME, &ts)); +} diff --git a/test/libc/calls/dup_test.c b/test/libc/calls/dup_test.c index 1f7ff126a..5c8734786 100644 --- a/test/libc/calls/dup_test.c +++ b/test/libc/calls/dup_test.c @@ -51,8 +51,8 @@ TEST(dup, clearsCloexecFlag) { ASSERT_NE(-1, (ws = xspawn(0))); if (ws == -2) { dup2(3, 0); - execv(program_executable_name, - (char *const[]){program_executable_name, "boop", 0}); + execv(GetProgramExecutableName(), + (char *const[]){GetProgramExecutableName(), "boop", 0}); _exit(127); } ASSERT_EQ(72, WEXITSTATUS(ws)); diff --git a/test/libc/log/backtrace_test.c b/test/libc/log/backtrace_test.c index eb8ab23cd..e283d19cc 100644 --- a/test/libc/log/backtrace_test.c +++ b/test/libc/log/backtrace_test.c @@ -177,8 +177,8 @@ TEST(ShowCrashReports, testMemoryLeakCrash) { if (!pid) { dup2(fds[1], 1); dup2(fds[1], 2); - execv(program_executable_name, - (char *const[]){program_executable_name, "6", 0}); + execv(GetProgramExecutableName(), + (char *const[]){GetProgramExecutableName(), "6", 0}); _exit(127); } close(fds[1]); @@ -255,8 +255,8 @@ TEST(ShowCrashReports, testStackOverrunCrash) { if (!pid) { dup2(fds[1], 1); dup2(fds[1], 2); - execv(program_executable_name, - (char *const[]){program_executable_name, "5", 0}); + execv(GetProgramExecutableName(), + (char *const[]){GetProgramExecutableName(), "5", 0}); _exit(127); } close(fds[1]); @@ -364,8 +364,8 @@ TEST(ShowCrashReports, testDivideByZero) { if (!pid) { dup2(fds[1], 1); dup2(fds[1], 2); - execv(program_executable_name, - (char *const[]){program_executable_name, "1", 0}); + execv(GetProgramExecutableName(), + (char *const[]){GetProgramExecutableName(), "1", 0}); _exit(127); } close(fds[1]); @@ -486,8 +486,8 @@ TEST(ShowCrashReports, testBssOverrunCrash) { if (!pid) { dup2(fds[1], 1); dup2(fds[1], 2); - execv(program_executable_name, - (char *const[]){program_executable_name, "2", 0}); + execv(GetProgramExecutableName(), + (char *const[]){GetProgramExecutableName(), "2", 0}); _exit(127); } close(fds[1]); @@ -565,8 +565,8 @@ TEST(ShowCrashReports, testNpeCrash) { if (!pid) { dup2(fds[1], 1); dup2(fds[1], 2); - execv(program_executable_name, - (char *const[]){program_executable_name, "7", 0}); + execv(GetProgramExecutableName(), + (char *const[]){GetProgramExecutableName(), "7", 0}); _exit(127); } close(fds[1]); @@ -625,8 +625,8 @@ TEST(ShowCrashReports, testDataOverrunCrash) { if (!pid) { dup2(fds[1], 1); dup2(fds[1], 2); - execv(program_executable_name, - (char *const[]){program_executable_name, "4", 0}); + execv(GetProgramExecutableName(), + (char *const[]){GetProgramExecutableName(), "4", 0}); _exit(127); } close(fds[1]); @@ -680,8 +680,8 @@ TEST(ShowCrashReports, testNpeCrashAfterFinalize) { if (!pid) { dup2(fds[1], 1); dup2(fds[1], 2); - execv(program_executable_name, - (char *const[]){program_executable_name, "8", 0}); + execv(GetProgramExecutableName(), + (char *const[]){GetProgramExecutableName(), "8", 0}); _exit(127); } close(fds[1]); diff --git a/test/libc/mem/test.mk b/test/libc/mem/test.mk index 63b50a2d4..cdb4dc420 100644 --- a/test/libc/mem/test.mk +++ b/test/libc/mem/test.mk @@ -4,6 +4,7 @@ PKGS += TEST_LIBC_MEM TEST_LIBC_MEM_FILES := $(wildcard test/libc/mem/*) +TEST_LIBC_MEM_SRCS = $(TEST_LIBC_MEM_SRCS_C) $(TEST_LIBC_MEM_SRCS_CC) TEST_LIBC_MEM_SRCS_C = $(filter %_test.c,$(TEST_LIBC_MEM_FILES)) TEST_LIBC_MEM_SRCS_CC = $(filter %_test.cc,$(TEST_LIBC_MEM_FILES)) @@ -12,7 +13,7 @@ TEST_LIBC_MEM_OBJS = \ $(TEST_LIBC_MEM_SRCS_CC:%.cc=o/$(MODE)/%.o) TEST_LIBC_MEM_COMS = \ - $(TEST_LIBC_MEM_SRCS_C:%.c=o/$(MODE)/%.com) \ + $(TEST_LIBC_MEM_SRCS_C:%.c=o/$(MODE)/%.com) \ $(TEST_LIBC_MEM_SRCS_CC:%.cc=o/$(MODE)/%.com) TEST_LIBC_MEM_BINS = \ @@ -20,7 +21,7 @@ TEST_LIBC_MEM_BINS = \ $(TEST_LIBC_MEM_COMS:%=%.dbg) TEST_LIBC_MEM_TESTS = \ - $(TEST_LIBC_MEM_SRCS_C:%.c=o/$(MODE)/%.com.ok) \ + $(TEST_LIBC_MEM_SRCS_C:%.c=o/$(MODE)/%.com.ok) \ $(TEST_LIBC_MEM_SRCS_CC:%.cc=o/$(MODE)/%.com.ok) TEST_LIBC_MEM_CHECKS = \ diff --git a/third_party/linenoise/linenoise.c b/third_party/linenoise/linenoise.c index e091faa90..964ed1e24 100644 --- a/third_party/linenoise/linenoise.c +++ b/third_party/linenoise/linenoise.c @@ -1810,6 +1810,7 @@ struct linenoiseState *linenoiseBegin(const char *prompt, int ifd, int ofd) { } void linenoiseReset(struct linenoiseState *l) { + l->buf[0] = 0; l->dirty = true; l->final = 0; l->hindex = 0; diff --git a/third_party/lua/llimits.h b/third_party/lua/llimits.h index 941ea9219..03c2fd812 100644 --- a/third_party/lua/llimits.h +++ b/third_party/lua/llimits.h @@ -1,6 +1,7 @@ #ifndef llimits_h #define llimits_h +#include "libc/limits.h" #include "libc/math.h" #include "third_party/lua/lua.h" diff --git a/third_party/mbedtls/test/lib.c b/third_party/mbedtls/test/lib.c index a95392471..2d6532367 100644 --- a/third_party/mbedtls/test/lib.c +++ b/third_party/mbedtls/test/lib.c @@ -1011,7 +1011,7 @@ int execute_tests(int argc, const char **argv, const char *default_filename) { file = fopen(test_filename, "r"); if (file == NULL) { WRITE("%s (%s) failed to open test file: %s %m\n", - program_invocation_short_name, program_executable_name, + program_invocation_short_name, GetProgramExecutableName(), test_filename); if (outcome_file != NULL) fclose(outcome_file); return 1; diff --git a/third_party/python/Modules/getpath.c b/third_party/python/Modules/getpath.c index d45728be0..59178a01c 100644 --- a/third_party/python/Modules/getpath.c +++ b/third_party/python/Modules/getpath.c @@ -663,7 +663,7 @@ Py_GetProgramFullPath(void) { static bool once; if (_cmpxchg(&once, false, true)) { - progpath = utf8toutf32(program_executable_name, -1, 0); + progpath = utf8toutf32(GetProgramExecutableName(), -1, 0); __cxa_atexit(free, progpath, 0); } return progpath; diff --git a/tool/decode/dumpvdso.c b/tool/decode/dumpvdso.c new file mode 100644 index 000000000..e0f64fd46 --- /dev/null +++ b/tool/decode/dumpvdso.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 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/elf/struct/ehdr.h" +#include "libc/runtime/runtime.h" +#include "libc/sysv/consts/auxv.h" + +noasan int main(int argc, char *argv[]) { + int i = 0; + Elf64_Ehdr *ehdr = (Elf64_Ehdr *)getauxval(AT_SYSINFO_EHDR); + if (isatty(1)) exit(1); + for (;;) { + write(1, ((char *)ehdr) + i++, 1); + } + return 0; +} diff --git a/tool/decode/elf.c b/tool/decode/elf.c index 4d9a5c4a3..4a6a4b6e3 100644 --- a/tool/decode/elf.c +++ b/tool/decode/elf.c @@ -27,8 +27,10 @@ #include "libc/log/check.h" #include "libc/log/log.h" #include "libc/macros.internal.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/map.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/prot.h" @@ -231,6 +233,21 @@ static void printelfsymboltable(void) { } } +static void printelfdynsymboltable(void) { + size_t i, symcount = 0; + Elf64_Sym *symtab = GetElfDynSymbolTable(elf, st->st_size, &symcount); + char *strtab = GetElfDynStringTable(elf, st->st_size); + char *shstrtab = GetElfSectionNameStringTable(elf, st->st_size); + if (symtab && strtab) { + printf("\n\n"); + printf("\t.org\t%#x\n", (intptr_t)symtab - (intptr_t)elf); + for (i = 0; i < symcount; ++i) { + printf(".Lsym%d:\n", i); + printelfsymbol(&symtab[i], strtab, shstrtab); + } + } +} + static char *getelfsymbolname(const Elf64_Ehdr *elf, size_t mapsize, const char *strtab, const char *shstrtab, const Elf64_Sym *sym) { @@ -324,12 +341,14 @@ int main(int argc, char *argv[]) { fprintf(stderr, "error: not an elf executable: %'s\n", path); exit(1); } + elf = (Elf64_Ehdr *)getauxval(AT_SYSINFO_EHDR); startfile(); printelfehdr(); printelfsegmentheaders(); printelfsectionheaders(); printelfrelocations(); printelfsymboltable(); + printelfdynsymboltable(); munmap(elf, st->st_size); close(fd); return 0; diff --git a/tool/net/demo/script.lua b/tool/net/demo/script.lua new file mode 100755 index 000000000..e92b4e4dc --- /dev/null +++ b/tool/net/demo/script.lua @@ -0,0 +1,2 @@ +#!/usr/bin/redbean -i +print('hello world') diff --git a/tool/net/demo/unix-dir.lua b/tool/net/demo/unix-dir.lua new file mode 100644 index 000000000..c06008d74 --- /dev/null +++ b/tool/net/demo/unix-dir.lua @@ -0,0 +1,93 @@ +Write('\r\n') +Write('redbean\r\n') +Write('\r\n') + +Write('

    UNIX Directory Stream Demo

    \r\n') +Write('\r\n') +Write('\r\n') +Write('\r\n') +Write('\r\n') + +dir = '.' +for name, kind, ino, off in assert(unix.opendir(dir)) do + Write('\r\n') + + Write('
    name\r\n') +Write('type\r\n') +Write('ino\r\n') +Write('off\r\n') +Write('size\r\n') +Write('blocks\r\n') +Write('mode\r\n') +Write('uid\r\n') +Write('gid\r\n') +Write('dev\r\n') +Write('rdev\r\n') +Write('nlink\r\n') +Write('blksize\r\n') +Write('gen\r\n') +Write('flags\r\n') +Write('birthtim\r\n') +Write('mtim\r\n') +Write('atim\r\n') +Write('ctim\r\n') +Write('
    ') + Write(EscapeHtml(name)) + if kind == unix.DT_DIR then + Write('/') + end + Write('\r\n') + + Write('') + if kind == unix.DT_REG then Write('DT_REG') + elseif kind == unix.DT_DIR then Write('DT_DIR') + elseif kind == unix.DT_FIFO then Write('DT_FIFO') + elseif kind == unix.DT_CHR then Write('DT_CHR') + elseif kind == unix.DT_BLK then Write('DT_BLK') + elseif kind == unix.DT_LNK then Write('DT_LNK') + elseif kind == unix.DT_SOCK then Write('DT_SOCK') + else Write('DT_UNKNOWN') + end + Write('\r\n') + + Write('%d\r\n' % {ino}) + Write('%d\r\n' % {off}) + + st,err = unix.stat(dir..'/'..name, unix.AT_SYMLINK_NOFOLLOW) + if st then + + Write('%d\r\n' % {st:size()}) + Write('%d\r\n' % {st:blocks()}) + Write('%.7o\r\n' % {st:mode()}) + Write('%d\r\n' % {st:uid()}) + Write('%d\r\n' % {st:gid()}) + Write('%d\r\n' % {st:dev()}) + Write('%d,%d\r\n' % {unix.major(st:rdev()), unix.minor(st:rdev())}) + Write('%d\r\n' % {st:nlink()}) + Write('%d\r\n' % {st:blksize()}) + Write('%d\r\n' % {st:gen()}) + Write('%#x\r\n' % {st:flags()}) + + function WriteTime(unixsec,nanos) + year,mon,mday,hour,min,sec,gmtoffsec = unix.localtime(unixsec) + Write('%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.9d%+.2d%.2d\r\n' % { + year, mon, mday, hour, min, sec, nanos, + gmtoffsec / (60 * 60), math.abs(gmtoffsec) % 60}) + end + + WriteTime(st:birthtim()) + WriteTime(st:mtim()) + WriteTime(st:atim()) + WriteTime(st:ctim()) + + else + Write('%s\r\n' % {err}) + end + +end +Write('
    \r\n') diff --git a/tool/net/demo/unix-info.lua b/tool/net/demo/unix-info.lua index b772d069b..42cc5d26c 100644 --- a/tool/net/demo/unix-info.lua +++ b/tool/net/demo/unix-info.lua @@ -26,7 +26,7 @@ sid, errno = unix.getsid(0) if sid then Write('
    %d\r\n' % {sid}) else - Write('
    %s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
    %s\r\n' % {tostring(errno)}) end Write('
    unix.gethostname()\r\n') @@ -54,7 +54,7 @@ function PrintResourceLimit(name, id) end Write('\r\n') else - Write('
    %s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
    %s\r\n' % {EscapeHtml(tostring(errno))}) end end PrintResourceLimit('RLIMIT_AS', unix.RLIMIT_AS) @@ -77,13 +77,13 @@ if ifs then Write('%s %s/%d
    \r\n' % {EscapeHtml(ifs[i].name), FormatIp(ifs[i].ip), cidr}) end else - Write('%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('%s\r\n' % {EscapeHtml(tostring(errno))}) end enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_DEBUG) Write('
    unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_DEBUG)\r\n') if errno then - Write('
    %s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
    %s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
    %s\r\n' % {enabled}) end @@ -91,7 +91,7 @@ end enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_ACCEPTCONN) Write('
    unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_ACCEPTCONN)\r\n') if errno then - Write('
    %s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
    %s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
    %s\r\n' % {enabled}) end @@ -99,7 +99,7 @@ end enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEADDR) Write('
    unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEADDR)\r\n') if errno then - Write('
    %s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
    %s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
    %s\r\n' % {enabled}) end @@ -107,7 +107,7 @@ end enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEPORT) Write('
    unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEPORT)\r\n') if errno then - Write('
    %s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
    %s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
    %s\r\n' % {enabled}) end @@ -115,7 +115,7 @@ end enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_KEEPALIVE) Write('
    unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_KEEPALIVE)\r\n') if errno then - Write('
    %s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
    %s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
    %s\r\n' % {enabled}) end @@ -123,7 +123,7 @@ end enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_NODELAY) Write('
    unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_NODELAY)\r\n') if errno then - Write('
    %s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
    %s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
    %s\r\n' % {enabled}) end @@ -131,7 +131,7 @@ end secs, errno, micros = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVTIMEO) Write('
    unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVTIMEO)\r\n') if errno then - Write('
    %s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
    %s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
    %d sec %d µs\r\n' % {secs, micros}) end @@ -139,7 +139,7 @@ end secs, errno, micros = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDTIMEO) Write('
    unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDTIMEO)\r\n') if errno then - Write('
    %s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
    %s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
    %d sec %d µs\r\n' % {secs, micros}) end @@ -147,7 +147,7 @@ end enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_DONTROUTE) Write('
    unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_DONTROUTE)\r\n') if errno then - Write('
    %s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
    %s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
    %s\r\n' % {enabled}) end @@ -155,7 +155,7 @@ end bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDBUF) Write('
    unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDBUF)\r\n') if errno then - Write('
    %s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
    %s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
    %d\r\n' % {bytes}) end @@ -163,7 +163,7 @@ end bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVBUF) Write('
    unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVBUF)\r\n') if errno then - Write('
    %s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
    %s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
    %d\r\n' % {bytes}) end @@ -171,7 +171,7 @@ end bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_FASTOPEN) Write('
    unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_FASTOPEN)\r\n') if errno then - Write('
    %s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
    %s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
    %d\r\n' % {bytes}) end @@ -179,7 +179,7 @@ end enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_BROADCAST) Write('
    unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_BROADCAST)\r\n') if errno then - Write('
    %s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
    %s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
    %s\r\n' % {enabled}) end @@ -187,7 +187,7 @@ end enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_CORK) Write('
    unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_CORK)\r\n') if errno then - Write('
    %s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
    %s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
    %s\r\n' % {enabled}) end @@ -195,7 +195,7 @@ end enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_QUICKACK) Write('
    unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_QUICKACK)\r\n') if errno then - Write('
    %s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
    %s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
    %s\r\n' % {enabled}) end @@ -203,7 +203,7 @@ end enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_DEFER_ACCEPT) Write('
    unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_DEFER_ACCEPT)\r\n') if errno then - Write('
    %s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
    %s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
    %s\r\n' % {enabled}) end @@ -211,7 +211,7 @@ end enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_FASTOPEN_CONNECT) Write('
    unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_FASTOPEN_CONNECT)\r\n') if errno then - Write('
    %s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
    %s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
    %s\r\n' % {enabled}) end @@ -219,7 +219,7 @@ end bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDLOWAT) Write('
    unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDLOWAT)\r\n') if errno then - Write('
    %s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
    %s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
    %d\r\n' % {bytes}) end @@ -227,7 +227,7 @@ end bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVLOWAT) Write('
    unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVLOWAT)\r\n') if errno then - Write('
    %s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
    %s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
    %d\r\n' % {bytes}) end @@ -235,7 +235,7 @@ end bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPCNT) Write('
    unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPCNT)\r\n') if errno then - Write('
    %s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
    %s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
    %d\r\n' % {bytes}) end @@ -243,7 +243,7 @@ end bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_MAXSEG) Write('
    unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_MAXSEG)\r\n') if errno then - Write('
    %s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
    %s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
    %d\r\n' % {bytes}) end @@ -251,7 +251,7 @@ end bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_SYNCNT) Write('
    unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_SYNCNT)\r\n') if errno then - Write('
    %s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
    %s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
    %d\r\n' % {bytes}) end @@ -259,7 +259,7 @@ end bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_NOTSENT_LOWAT) Write('
    unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_NOTSENT_LOWAT)\r\n') if errno then - Write('
    %s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
    %s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
    %d\r\n' % {bytes}) end @@ -267,7 +267,7 @@ end bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_WINDOW_CLAMP) Write('
    unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_WINDOW_CLAMP)\r\n') if errno then - Write('
    %s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
    %s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
    %d\r\n' % {bytes}) end @@ -275,7 +275,7 @@ end bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPIDLE) Write('
    unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPIDLE)\r\n') if errno then - Write('
    %s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
    %s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
    %d\r\n' % {bytes}) end @@ -283,7 +283,7 @@ end bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPINTVL) Write('
    unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPINTVL)\r\n') if errno then - Write('
    %s\r\n' % {EscapeHtml(unix.strerrno(errno))}) + Write('
    %s\r\n' % {EscapeHtml(tostring(errno))}) else Write('
    %d\r\n' % {bytes}) end diff --git a/tool/net/demo/unix-raise.lua b/tool/net/demo/unix-raise.lua new file mode 100755 index 000000000..acb36ca96 --- /dev/null +++ b/tool/net/demo/unix-raise.lua @@ -0,0 +1,11 @@ +#!/home/jart/bin/redbean -i +assert(unix.sigaction(unix.SIGUSR1, function(sig) + gotsigusr1 = true +end)) +gotsigusr1 = false +assert(unix.raise(unix.SIGUSR1)) +if gotsigusr1 then + print('hooray the signal was delivered') +else + print('oh no some other signal was handled') +end diff --git a/tool/net/demo/unix-rawsocket.lua b/tool/net/demo/unix-rawsocket.lua index 38be17135..f0f9aaf8f 100644 --- a/tool/net/demo/unix-rawsocket.lua +++ b/tool/net/demo/unix-rawsocket.lua @@ -1,4 +1,4 @@ -local unix = require "unix" +local unix = require 'unix' local function main() if GetMethod() ~= 'GET' and GetMethod() ~= 'HEAD' then @@ -45,7 +45,7 @@ local function main() Write(EncodeBase64(LoadAsset('/redbean.png'))) Write('">\r\n') Write('redbean unix demo\r\n') - Write(' %s\r\n' % {unix.strerrno(errno)}) + Write(' %s\r\n' % {EscapeHtml(tostring(errno))}) Write('

\r\n') Write([[

@@ -63,7 +63,6 @@ local function main() unix.close(fd) return end - -- if pid is zero then we're the child -- turn into a daemon unix.umask(0) @@ -106,34 +105,6 @@ local function main() unix.write(fd, 'became an autonomous daemon reparented on your init!\r\n') unix.write(fd, '

\r\n') - unix.write(fd, '

listing of current directory

\r\n') - dir, err = unix.opendir('.') - if dir then - unix.write(fd, '
    \r\n') - while true do - name, errno, kind, ino, off = dir:read() - if not name then - break - end - unix.write(fd, '
  • ') - unix.write(fd, EscapeHtml(VisualizeControlCodes(name))) - if kind == unix.DT_DIR then - unix.write(fd, '/') - else - st, err = unix.stat(name) - if st then - unix.write(fd, ' (%d bytes)' % {st:size()}) - end - end - unix.write(fd, '\r\n') - end - unix.write(fd, '
\r\n') - else - unix.write(fd, '

\r\n') - unix.write(fd, 'failed: %s\r\n' % {EscapeHtml(VisualizeControlCodes(unix:strerror(err)))}) - unix.write(fd, '

\r\n') - end - -- terminate unix.close(fd) unix.exit(0) diff --git a/tool/net/demo/unix-subprocess.lua b/tool/net/demo/unix-subprocess.lua index ae387d945..5d060a42b 100644 --- a/tool/net/demo/unix-subprocess.lua +++ b/tool/net/demo/unix-subprocess.lua @@ -9,55 +9,48 @@ function main() end syscall = 'commandv' ls = assert(unix.commandv(cmd)) - if ls then - syscall = 'pipe' - reader, writer, errno = unix.pipe() - if reader then - -- oldint = assert(unix.sigaction(unix.SIGINT, unix.SIG_IGN)) - -- oldquit = assert(unix.sigaction(unix.SIGQUIT, unix.SIG_IGN)) - -- oldmask = assert(unix.sigprocmask(unix.SIG_BLOCK, (1 << (unix.SIGCHLD - 1)))) - syscall = 'fork' - child, errno = unix.fork() - if child then - if child == 0 then - unix.close(1) - unix.dup(writer) - unix.close(writer) - unix.close(reader) - -- unix.sigaction(unix.SIGINT, oldint) - -- unix.sigaction(unix.SIGQUIT, oldquit) - -- unix.sigprocmask(unix.SIG_SETMASK, oldmask) - unix.execve(ls, {ls, '-Shal'}) - unix.exit(127) + syscall = 'pipe' + reader, writer = assert(unix.pipe(unix.O_CLOEXEC)) + oldint = assert(unix.sigaction(unix.SIGINT, unix.SIG_IGN)) + oldquit = assert(unix.sigaction(unix.SIGQUIT, unix.SIG_IGN)) + oldmask = assert(unix.sigprocmask(unix.SIG_BLOCK, unix.Sigset(unix.SIGCHLD))) + syscall = 'fork' + child = assert(unix.fork()) + if child == 0 then + unix.close(0) + unix.open("/dev/null", unix.O_RDONLY) + unix.close(1) + unix.dup(writer) + unix.close(2) + unix.open("/dev/null", unix.O_RDONLY) + unix.sigaction(unix.SIGINT, oldint) + unix.sigaction(unix.SIGQUIT, oldquit) + unix.sigprocmask(unix.SIG_SETMASK, oldmask) + unix.execve(ls, {ls, '-Shal'}) + unix.exit(127) + else + unix.close(writer) + SetStatus(200) + SetHeader('Content-Type', 'text/plain') + while true do + data, err = unix.read(reader) + if data then + if data ~= '' then + Write(data) else - unix.close(writer) - SetStatus(200) - SetHeader('Content-Type', 'text/plain') - while true do - data, errno = unix.read(reader) - if data then - if data ~= '' then - Write(data) - else - break - end - elseif errno ~= unix.EINTR then - Log(kLogWarn, 'read() failed: %s' % {unix.strerror(errno)}) - break - end - end - unix.close(reader) - unix.wait(-1) - -- unix.sigaction(unix.SIGINT, oldint) - -- unix.sigaction(unix.SIGQUIT, oldquit) - -- unix.sigprocmask(unix.SIG_SETMASK, oldmask) - return + break end + elseif err:errno() ~= unix.EINTR then + Log(kLogWarn, 'read() failed: %s' % {tostring(err)}) + break end end + unix.close(reader) + unix.wait(-1) + unix.sigaction(unix.SIGINT, oldint) + unix.sigaction(unix.SIGQUIT, oldquit) + unix.sigprocmask(unix.SIG_SETMASK, oldmask) + return end - SetStatus(200) - SetHeader('Content-Type', 'text/plain') - Write('error %s calling %s()' % {unix.strerrno(errno), syscall}) end main() diff --git a/tool/net/demo/unix-webserver.lua b/tool/net/demo/unix-webserver.lua index 69c4ef444..b6837aae3 100644 --- a/tool/net/demo/unix-webserver.lua +++ b/tool/net/demo/unix-webserver.lua @@ -45,7 +45,7 @@ function main() server = unix.socket() unix.bind(server, ifs[i].ip) unix.listen(server) - ip, errno, port = unix.getsockname(server) + ip, port = assert(unix.getsockname(server)) addr = '%s:%d' % {FormatIp(ip), port} url = 'http://%s' % {addr} Log(kLogInfo, 'listening on %s' % {addr}) @@ -65,7 +65,7 @@ function main() if fd == mainfd then data, errno = unix.read(mainfd) if not data then - Log(kLogInfo, 'got %s from parent client' % {unix.strerrno(errno)}) + Log(kLogInfo, 'got %s from parent client' % {tostring(errno)}) -- prevent redbean core from writing a response unix.exit(1) end @@ -79,8 +79,7 @@ function main() unix.write(mainfd, data) elseif servers[fd] then unix.write(mainfd, 'preparing to accept from %d
\r\n' % {fd}) - client, errno, clientip, clientport = unix.accept(fd) - unix.write(mainfd, 'preparing to accept from %d
\r\n' % {fd}) + client, clientip, clientport = assert(unix.accept(fd)) addr = '%s:%d' % {FormatIp(clientip), clientport} addrs[client] = addr unix.write(mainfd, 'got client %s
\r\n' % {addr}) diff --git a/tool/net/help.txt b/tool/net/help.txt index 9a1c60ce4..85470e687 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -77,7 +77,6 @@ FLAGS --strace enables system call tracing --ftrace enables function call tracing - KEYBOARD CTRL-D EXIT @@ -126,6 +125,7 @@ KEYBOARD ALT-\ SQUEEZE ADJACENT WHITESPACE PROTIP REMAP CAPS LOCK TO CTRL +──────────────────────────────────────────────────────────────────────────────── USAGE @@ -234,37 +234,7 @@ USAGE inside the binary. redbean also respects your privacy and won't phone home because your computer is its home. - -REPL - - Your redbean displays a Read-Eval-Print-Loop that lets you modify the - state of the main server process while your server is running. Any - changes will propagate into forked clients. - - Your REPL is displayed only when redbean is run as a non-daemon in a - UNIX terminal or the Windows 10 command prompt or powershell. Since - the REPL is a Lua REPL it's not included in a redbean-static builds. - - redbean uses the same keyboard shortcuts as GNU Readline and Emacs. - Some of its keyboard commands (listed in a previous section) were - inspired by Paredit. - - A history of your commands is saved to `~/.redbean_history`. - - If you love the redbean repl and want to use it as your language - interpreter then you can pass the `-i` flag to put redbean into - interpreter mode. - - redbean.com -i binarytrees.lua 15 - - In this mode redbean won't start a web server and instead functions - like the `lua` command. The first command line argument becomes the - script you want to run. If you don't supply a script, then the repl - without a web server is displayed. - - This can be useful for testing, since the redbean extensions and - modules for the Lua language are still made available. - +──────────────────────────────────────────────────────────────────────────────── SECURITY @@ -283,6 +253,7 @@ SECURITY See http://redbean.dev for further details. +──────────────────────────────────────────────────────────────────────────────── LUA SERVER PAGES @@ -323,6 +294,88 @@ LUA SERVER PAGES one-time operations like importing modules. Then, as requests roll in, isolated processes are cloned from the blueprint you created. +──────────────────────────────────────────────────────────────────────────────── + +REPL + + Your redbean displays a Read-Eval-Print-Loop that lets you modify the + state of the main server process while your server is running. Any + changes will propagate into forked clients. + + Your REPL is displayed only when redbean is run as a non-daemon in a + UNIX terminal or the Windows 10 command prompt or PowerShell. Since + the REPL is a Lua REPL it's not included in a redbean-static builds. + + redbean uses the same keyboard shortcuts as GNU Readline and Emacs. + Some of its keyboard commands (listed in a previous section) were + inspired by Paredit. + + A history of your commands is saved to `~/.redbean_history`. + + If you love the redbean repl and want to use it as your language + interpreter then you can pass the `-i` flag to put redbean into + interpreter mode. + + redbean.com -i binarytrees.lua 15 + + In this mode redbean won't start a web server and instead functions + like the `lua` command. The first command line argument becomes the + script you want to run. If you don't supply a script, then the repl + without a web server is displayed. This is useful for testing since + redbean extensions and modules for the Lua language, are still made + available. You can also write redbean scripts with shebang lines: + + #!/usr/bin/redbean -i + print('hello world') + + However operating systems like Linux usually require that script + interperters be in the local executable format. You can "assimilate" + and install your redbean using the following commands: + + zip -d redbean.com .ape # remove the ape header + ./redbean.com -h >/dev/null # assimilate the binary + sudo cp redbean.com /usr/bin/redbean + + By following the above steps, redbean can be installed systemwide for + multiple user accounts. It's also possible to chmod the binary to have + setuid privileges, provided it's configured to drop privileges in the + most appropriate manner; see the UNIX section for further details. + +──────────────────────────────────────────────────────────────────────────────── + +GLOBALS + + arg: array[str] + + Array of command line arguments, excluding those parsed by + getopt() in the C code, which stops parsing at the first + non-hyphenated arg. In some cases you can use the magic -- + argument to delimit C from Lua arguments. + + For example, if you launch your redbean as follows: + + redbean.com -v arg1 arg2 + + Then your `/.init.lua` file will have the `arg` array like: + + arg[-1] = '/usr/bin/redbean.com' + arg[ 0] = '/zip/.init.lua' + arg[ 1] = 'arg1' + arg[ 2] = 'arg2' + + If you launch redbean in interpreter mode (rather than web + server) mode, then an invocation like this: + + ./redbean.com -i script.lua arg1 arg2 + + Would have an `arg` array like this: + + arg[-1] = './redbean.com' + arg[ 0] = 'script.lua' + arg[ 1] = 'arg1' + arg[ 2] = 'arg2' + +──────────────────────────────────────────────────────────────────────────────── SPECIAL PATHS @@ -369,39 +422,7 @@ SPECIAL PATHS If you enable HTTPS client verification then redbean will check that HTTPS clients (a) have a certificate and (b) it was signed. - -GLOBALS - - arg: array[str] - - Array of command line arguments, excluding those parsed by - getopt() in the C code, which stops parsing at the first - non-hyphenated arg. In some cases you can use the magic -- - argument to delimit C from Lua arguments. - - For example, if you launch your redbean as follows: - - redbean.com -v arg1 arg2 - - Then your `/.init.lua` file will have the `arg` array like: - - arg[-1] = '/usr/bin/redbean.com' - arg[ 0] = '/zip/.init.lua' - arg[ 1] = 'arg1' - arg[ 2] = 'arg2' - - If you launch redbean in interpreter mode (rather than web - server) mode, then an invocation like this: - - ./redbean.com -i script.lua arg1 arg2 - - Would have an `arg` array like this: - - arg[-1] = './redbean.com' - arg[ 0] = 'script.lua' - arg[ 1] = 'arg1' - arg[ 2] = 'arg2' - +──────────────────────────────────────────────────────────────────────────────── HOOKS @@ -450,6 +471,7 @@ HOOKS process once _exit() is ready to be called. This won't be called in uniprocess mode. +──────────────────────────────────────────────────────────────────────────────── FUNCTIONS @@ -1196,6 +1218,9 @@ FUNCTIONS Returns 64-bit hardware random integer from RDSEED instruction, with automatic fallback to RDRND and getrandom() if not available. + GetCpuCount() → int + Returns CPU core count or 0 if it couldn't be determined. + GetCpuCore() → int Returns 0-indexed CPU core on which process is currently scheduled. @@ -1216,6 +1241,7 @@ FUNCTIONS the density of information. Cryptographic random should be in the ballpark of 7.9 whereas plaintext will be more like 4.5. +──────────────────────────────────────────────────────────────────────────────── CONSTANTS @@ -1239,6 +1265,7 @@ CONSTANTS Logging anything at this level will result in a backtrace and process exit. +──────────────────────────────────────────────────────────────────────────────── LSQLITE3 MODULE @@ -1275,6 +1302,7 @@ LSQLITE3 MODULE we provide an APE build of the SQLite shell which you can use to administrate your redbean database. See the sqlite3.com download above. +──────────────────────────────────────────────────────────────────────────────── RE MODULE @@ -1344,6 +1372,7 @@ RE MODULE end of the line. This flag may only be used with re.search and regex_t*:search. +──────────────────────────────────────────────────────────────────────────────── MAXMIND MODULE @@ -1365,13 +1394,16 @@ MAXMIND MODULE For further details, please see maxmind.lua in redbean-demo.com. +──────────────────────────────────────────────────────────────────────────────── UNIX MODULE This module exposes the low-level UNIX system call interface. This module works on all supported platforms, including Windows NT. - unix.open(path:str, flags:int[, mode:int]) → fd:int, unix.Errno + unix.open(path:str, flags:int[, mode:int[, dirfd:int]]) + ├─→ fd:int + └─→ nil, unix.Errno Opens file. @@ -1404,6 +1436,7 @@ UNIX MODULE - `O_VERIFY` it's complicated (zero on non-FreeBSD) - `O_SHLOCK` it's complicated (zero on non-BSD) - `O_EXLOCK` it's complicated (zero on non-BSD) + - `O_NOATIME` don't record access time (zero on non-Linux) - `O_RANDOM` hint random access intent (zero on non-Windows) - `O_SEQUENTIAL` hint sequential access intent (zero on non-Windows) - `O_COMPRESSED` ask fs to abstract compression (zero on non-Windows) @@ -1424,26 +1457,42 @@ UNIX MODULE already. If it does exist then `nil` is returned along with `errno` set to `EEXIST`. - unix.close(fd:int) → ok:bool, unix.Errno + `dirfd` defaults to to `unix.AT_FDCWD` and may optionally be set to + a directory file descriptor to which `path` is relative. + + Returns `ENOENT` if `path` doesn't exist. + + Returns `ENOTDIR` if `path` contained a directory component that + wasn't a directory. + + unix.close(fd:int) + ├─→ true + └─→ nil, unix.Errno Closes file descriptor. - unix.read(fd:int[, bufsiz:int, offset:int]) → data:str, unix.Errno + unix.read(fd:int[, bufsiz:str[, offset:int]]) + ├─→ data:str + └─→ nil, unix.Errno Reads from file descriptor. - unix.write(fd:int, data[, offset:int]) → wrote:int, unix.Errno + unix.write(fd:int, data:str[, offset:int]) + ├─→ wrotebytes:int + └─→ nil, unix.Errno Writes to file descriptor. - unix.exit([exitcode]) → ⊥ + unix.exit([exitcode:int]) + └─→ ⊥ Invokes `_Exit(exitcode)` on the process. This will immediately halt the current process. Memory will be freed. File descriptors will be closed. Any open connections it owns will be reset. This function never returns. - unix.environ() → {str, ...} + unix.environ() + └─→ {str,...} Returns raw environment variables. @@ -1463,12 +1512,38 @@ UNIX MODULE command prompt inserts multiple environment variables with empty string as keys, for its internal bookkeeping. - unix.fork() → childpid|0:int, unix.Errno + unix.fork() + ├─┬─→ 0 + │ └─→ childpid:int + └─→ nil, unix.Errno - Creates a new process mitosis style. This returns twice. The - parent process gets the nonzero pid. The child gets zero. + Creates a new process mitosis style. - unix.commandv(prog:str) → path:str, unix.Errno + This system call returns twice. The parent process gets the nonzero + pid. The child gets zero. + + Here's some benchmarks for fork() performance across platforms: + + Linux 5.4 fork l: 97,200𝑐 31,395𝑛𝑠 [metal] + FreeBSD 12 fork l: 236,089𝑐 78,841𝑛𝑠 [vmware] + Darwin 20.6 fork l: 295,325𝑐 81,738𝑛𝑠 [metal] + NetBSD 9 fork l: 5,832,027𝑐 1,947,899𝑛𝑠 [vmware] + OpenBSD 6.8 fork l: 13,241,940𝑐 4,422,103𝑛𝑠 [vmware] + Windows10 fork l: 18,802,239𝑐 6,360,271𝑛𝑠 [metal] + + One of the benefits of using fork() is it creates an isolation + barrier between the different parts of your app. This can lead to + enhanced reliability and security. For example, redbean uses fork so + it can wipe your ssl keys from memory before handing over control to + request handlers that process untrusted input. It also ensures that + if your Lua app crashes, it won't take down the server as a whole. + Hence it should come as no surprise that fork() would go slower on + operating systems that have more security features. So depending on + your use case, you can choose the operating system that suits you. + + unix.commandv(prog:str) + ├─→ path:str + └─→ nil, unix.Errno Performs `$PATH` lookup of executable. @@ -1483,7 +1558,8 @@ UNIX MODULE `prog` contains slashes then it's not path searched either and will be returned if it exists. - unix.execve(prog:str[, args:List<*>[, env:List<*>]]) → false, unix.Errno + unix.execve(prog:str[, args:List<*>, env:List<*>]) + └─→ nil, unix.Errno Exits current process, replacing it with a new instance of the specified program. `prog` needs to be an absolute path, see @@ -1516,7 +1592,9 @@ UNIX MODULE `EAGAIN` is returned if you've enforced a max number of processes using `setrlimit(RLIMIT_NPROC)`. - unix.dup(oldfd:int[, newfd:int[, flags:int]]) → newfd:int, unix.Errno + unix.dup(oldfd:int[, newfd:int[, flags:int]]) + ├─→ newfd:int + └─→ nil, unix.Errno Duplicates file descriptor. @@ -1527,20 +1605,31 @@ UNIX MODULE `flags` can have `O_CLOEXEC` which means the returned file descriptors will be automatically closed upon execve(). - unix.pipe([flags:int]) → reader:int, unix.Errno, writer:int + unix.pipe([flags:int]) + ├─→ reader:int, writer:int + └─→ nil, unix.Errno Creates fifo which enables communication between processes. - Returns two file descriptors: one for reading and one for - writing. `flags` can have `O_CLOEXEC`. On error, `reader` and - `writer` will be `nil` and `errno` will be set to non-nil. + + `flags` can have any of + + - `O_CLOEXEC`: Automatically close file descriptor upon execve() + + - `O_NONBLOCK`: Request `EAGAIN` be raised rather than blocking. + + - `O_DIRECT`: Enable packet mode w/ atomic reads and writes, so long + as they're no larger than `PIPE_BUF` (guaranteed to be 512+ bytes) + with support limited to Linux, Windows NT, FreeBSD, and NetBSD. + + Returns two file descriptors: one for reading and one for writing. Here's an example of how pipe(), fork(), dup(), etc. may be used to serve an HTTP response containing the output of a subprocess. local unix = require "unix" - ls = unix.commandv("ls") - reader, writer = unix.pipe() - if unix.fork() == 0 then + ls = assert(unix.commandv("ls")) + reader, writer = assert(unix.pipe()) + if assert(unix.fork()) == 0 then unix.close(1) unix.dup(writer) unix.close(writer) @@ -1551,19 +1640,25 @@ UNIX MODULE unix.close(writer) SetHeader('Content-Type', 'text/plain') while true do - data = unix.read(reader) - if data ~= "" then - Write(data) - else + data, err = unix.read(reader) + if data then + if data ~= "" then + Write(data) + else + break + end + elseif err:errno() ~= EINTR then + Log(kLogWarn, tostring(err)) break end end - unix.close(reader) - unix.wait() + assert(unix.close(reader)) + assert(unix.wait()) end unix.wait([pid:int, options:int]) - → pid:int, unix.Errno, wstatus:int + ├─→ pid:int, wstatus:int + └─→ nil, unix.Errno Waits for subprocess to terminate. @@ -1586,52 +1681,90 @@ UNIX MODULE -- wait for zombies -- traditional technique for SIGCHLD handlers while true do - pid, wstatus, errno = unix.wait(-1, unix.WNOHANG) + pid, status = unix.wait(-1, unix.WNOHANG) if pid then - if unix.WIFEXITED(wstatus) then + if unix.WIFEXITED(status) then print('child', pid, 'exited with', - unix.WEXITSTATUS(wstatus)) - elseif unix.WIFSIGNALED(wstatus) then + unix.WEXITSTATUS(status)) + elseif unix.WIFSIGNALED(status) then print('child', pid, 'crashed with', - unix.strsignal(unix.WTERMSIG(wstatus))) + unix.strsignal(unix.WTERMSIG(status))) end - elseif errno == unix.ECHILD then - print('no more zombies') + elseif status:errno() == unix.ECHILD then + Log(kLogDebug, 'no more zombies') break - elseif errno == unix.ECHILD then - print('wait failed', unix.strerror(errno)) + else + Log(kLogWarn, tostring(err)) break end end - unix.getpid() → pid:int + unix.getpid() + └─→ pid:int Returns process id of current process. This function does not fail. - unix.getppid() → pid:int + unix.getppid() + └─→ pid:int Returns process id of parent process. This function does not fail. - unix.kill(pid, sig) → ok:bool, unix.Errno + unix.kill(pid:int, sig:int) + ├─→ true + └─→ nil, unix.Errno - Returns process id of current process. + Sends signal to process(es). - unix.raise(sig) → rc:int, unix.Errno + The impact of this action can be terminating the process, or + interrupting it to request something happen. + + `pid` can be: + + - `pid > 0` signals one process by id + - `== 0` signals all processes in current process group + - `-1` signals all processes possible (except init) + - `< -1` signals all processes in -pid process group + + `sig` can be: + + - `0` checks both if pid exists and we can signal it + - `SIGINT` sends ctrl-c keyboard interrupt + - `SIGQUIT` sends backtrace and exit signal + - `SIGTERM` sends shutdown signal + - etc. + + Windows NT only supports the kill() signals required by the ANSI C89 + standard, which are `SIGINT` and `SIGQUIT`. All other signals on the + Windows platform that are sent to another process via kill() will be + treated like `SIGKILL`. + + unix.raise(sig:int) + ├─→ rc:int + └─→ nil, unix.Errno Triggers signal in current process. + This is pretty much the same as `kill(getpid(), sig)`. - unix.access(path:str, how) → ok:bool, unix.Errno + unix.access(path:str, how:int[, flags:int[, dirfd:int]]) + ├─→ true + └─→ nil, unix.Errno Checks if effective user of current process has permission to access file. `how` can be `R_OK`, `W_OK`, `X_OK`, or `F_OK` to check for read, write, execute, and existence respectively. - unix.mkdir(path:str, mode) → ok:bool, unix.Errno + `flags` may have any of: + + - `AT_SYMLINK_NOFOLLOW`: do not follow symbolic links. + + unix.mkdir(path:str[, mode:int[, dirfd:int]]) + ├─→ true + └─→ nil, unix.Errno Makes directory. @@ -1654,7 +1787,9 @@ UNIX MODULE Fails with `ENAMETOOLONG` if the path is too long. - unix.makedirs(path:str, mode) → ok:bool, unix.Errno + unix.makedirs(path:str[, mode:int]) + ├─→ true + └─→ nil, unix.Errno Makes directories. @@ -1665,48 +1800,106 @@ UNIX MODULE Unlike mkdir() this convenience wrapper will automatically create parent parent directories as needed. - unix.chdir(path:str) → ok:bool, unix.Errno + unix.chdir(path:str) + ├─→ true + └─→ nil, unix.Errno Changes current directory to `path`. - unix.unlink(path:str) → ok:bool, unix.Errno + unix.unlink(path:str[, dirfd:int]) + ├─→ true + └─→ nil, unix.Errno Removes file at `path`. - unix.rmdir(path:str) → ok:bool, unix.Errno + If `path` refers to a symbolic link, the link is removed. + + Returns `EISDIR` if `path` refers to a directory. See rmdir(). + + unix.rmdir(path:str[, dirfd:int]) + ├─→ true + └─→ nil, unix.Errno Removes empty directory at `path`. - unix.rename(oldpath:str, newpath:str) → ok:bool, unix.Errno + Returns `ENOTDIR` if `path` isn't a directory, or a path component + in `path` exists yet wasn't a directory. + + unix.rename(oldpath:str, newpath:str[, olddirfd:int, newdirfd:int]) + ├─→ true + └─→ nil, unix.Errno Renames file or directory. - unix.link(existingpath:str, newpath:str) → ok:bool, unix.Errno + unix.link(existingpath:str, newpath:str[, flags:int[, olddirfd, newdirfd]]) + ├─→ true + └─→ nil, unix.Errno Creates hard link, so your underlying inode has two names. - unix.symlink(target:str, linkpath:str) → ok:bool, unix.Errno + unix.symlink(target:str, linkpath:str[, newdirfd:int]) + ├─→ true + └─→ nil, unix.Errno - Creates soft link, or a symbolic link. + Creates symbolic link. - unix.realpath(filename:str) → abspath:str, unix.Errno + On Windows NT a symbolic link is called a "reparse point" and can + only be created from an administrator account. Your redbean will + automatically request the appropriate permissions. + + unix.readlink(path:str[, dirfd:int]) + ├─→ content:str + └─→ nil, unix.Errno + + Reads contents of symbolic link. + + Note that broken links are supported on all platforms. A symbolic + link can contain just about anything. It's important to not assume + that `content` will be a valid filename. + + On Windows NT, this function transliterates `\` to `/` and + furthermore prefixes `//?/` to WIN32 DOS-style absolute paths, + thereby assisting with simple absolute filename checks in addition + to enabling one to exceed the traditional 260 character limit. + + unix.realpath(path:str) + ├─→ path:str + └─→ nil, unix.Errno Returns absolute path of filename, with `.` and `..` components removed, and symlinks will be resolved. - unix.chown(path:str, uid, gid) → ok:bool, unix.Errno + unix.chown(path:str, uid:int, gid:int[, flags:int[, dirfd:int]]) + ├─→ true + └─→ nil, unix.Errno Changes user and gorup on file. - unix.chmod(path:str, mode) → ok:bool, unix.Errno + Returns `ENOSYS` on Windows NT. + + unix.chmod(path:str, mode:int[, flags:int[, dirfd:int]]) + ├─→ true + └─→ nil, unix.Errno Changes mode bits on file. - unix.getcwd() → path:str, unix.Errno + On Windows NT the chmod system call only changes the read-only + status of a file. + + unix.getcwd() + ├─→ path:str + └─→ nil, unix.Errno Returns current working directory. - unix.fcntl(fd:int, cmd:int[, arg:int]) → rc:int, unix.Errno + On Windows NT, this function transliterates `\` to `/` and + furthermore prefixes `//?/` to WIN32 DOS-style absolute paths, + thereby assisting with simple absolute filename checks in addition + to enabling one to exceed the traditional 260 character limit. + + unix.fcntl(fd:int, cmd:int, ...) + ├─→ ... + └─→ nil, unix.Errno Manipulates file descriptor. @@ -1714,30 +1907,42 @@ UNIX MODULE lets you query and/or change the status of file descriptors. For example, it's possible using this to change `FD_CLOEXEC`. - POSIX advisory locks can be controlled by setting `cmd` to - `F_UNLCK`, `F_RDLCK`, `F_WRLCK`, `F_SETLK`, or `F_SETLKW`. + [work in progress] POSIX advisory locks can be controlled by setting + `cmd` to `F_UNLCK`, `F_RDLCK`, `F_WRLCK`, `F_SETLK`, or `F_SETLKW`. - unix.getsid(pid:int) → sid:int, unix.Errno + unix.getsid(pid:int) + ├─→ sid:int + └─→ nil, unix.Errno Gets session id. - unix.getpgrp() → pgid:int, unix.Errno + unix.getpgrp() + ├─→ pgid:int + └─→ nil, unix.Errno Gets process group id. - unix.setpgrp() → pgid:int, unix.Errno + unix.setpgrp() + ├─→ pgid:int + └─→ nil, unix.Errno Sets process group id. This is the same as `setpgid(0,0)`. - unix.setpgid(pid:int, pgid:int) → pgid:int, unix.Errno + unix.setpgid(pid:int, pgid:int) + ├─→ true + └─→ nil, unix.Errno Sets process group id the modern way. - unix.getpgid(pid) → pgid:int, unix.Errno + unix.getpgid(pid:int) + ├─→ pgid:int + └─→ nil, unix.Errno Gets process group id the modern wayp. - unix.setsid() → sid:int, unix.Errno + unix.setsid() + ├─→ sid:int + └─→ nil, unix.Errno Sets session id. @@ -1745,7 +1950,8 @@ UNIX MODULE Fails with `ENOSYS` on Windows NT. - unix.getuid() → uid:int + unix.getuid() + └─→ uid:int Gets real user id. @@ -1754,7 +1960,8 @@ UNIX MODULE This function does not fail. - unix.getgid() → gid:int + unix.getgid() + └─→ gid:int Sets real group id. @@ -1762,7 +1969,8 @@ UNIX MODULE This function does not fail. - unix.geteuid() → uid:int + unix.geteuid() + └─→ uid:int Gets effective user id. @@ -1774,7 +1982,8 @@ UNIX MODULE This function does not fail. - unix.getegid() → gid:int + unix.getegid() + └─→ gid:int Gets effective group id. @@ -1782,13 +1991,17 @@ UNIX MODULE This function does not fail. - unix.chroot(path:str) → ok:bool, unix.Errno + unix.chroot(path:str) + ├─→ true + └─→ nil, unix.Errno Changes root directory. Returns `ENOSYS` on Windows NT. - unix.setuid(uid:int) → ok:bool, unix.Errno + unix.setuid(uid:int) + ├─→ true + └─→ nil, unix.Errno Sets user id. @@ -1797,26 +2010,18 @@ UNIX MODULE `-G` and `-U` flags, you can replicate that behavior in the Lua processes you spawn as follows: - errno = unix.setgid(1000) -- check your /etc/groups - if errno then - Log(kLogFatal, setgid() failed: %s' % {unix.strerror(errno)}) - end - errno = unix.setuid(1000) -- check your /etc/passwd - if errno then - Log(kLogFatal, setuid() failed: %s' % {unix.strerror(errno)}) - end + ok, err = unix.setgid(1000) -- check your /etc/groups + if not ok then Log(kLogFatal, tostring(err)) end + ok, err = unix.setuid(1000) -- check your /etc/passwd + if not ok then Log(kLogFatal, tostring(err)) end If your goal is to relinquish privileges because redbean is a setuid binary, then things are more straightforward: - errno = unix.setgid(unix.getgid()) - if errno then - Log(kLogFatal, setgid() failed: %s' % {unix.strerror(errno)}) - end - errno = unix.setuid(unix.getuid()) - if errno then - Log(kLogFatal, setuid() failed: %s' % {unix.strerror(errno)}) - end + ok, err = unix.setgid(unix.getgid()) + if not ok then Log(kLogFatal, tostring(err)) end + ok, err = unix.setuid(unix.getuid()) + if not ok then Log(kLogFatal, tostring(err)) end See also the setresuid() function and be sure to refer to your local system manual about the subtleties of changing user id in a way that @@ -1824,13 +2029,17 @@ UNIX MODULE Returns `ENOSYS` on Windows NT if `uid` isn't `getuid()`. - unix.setgid(gid:int) → ok:bool, unix.Errno + unix.setgid(gid:int) + ├─→ true + └─→ nil, unix.Errno Sets group id. Returns `ENOSYS` on Windows NT if `gid` isn't `getgid()`. - unix.setresuid(real:int, effective:int, saved:int) → ok:bool, unix.Errno + unix.setresuid(real:int, effective:int, saved:int) + ├─→ true + └─→ nil, unix.Errno Sets real, effective, and saved user ids. @@ -1839,7 +2048,9 @@ UNIX MODULE Returns `ENOSYS` on Windows NT. Returns `ENOSYS` on Macintosh and NetBSD if `saved` isn't -1. - unix.setresgid(real:int, effective:int, saved:int) → ok:bool, unix.Errno + unix.setresgid(real:int, effective:int, saved:int) + ├─→ true + └─→ nil, unix.Errno Sets real, effective, and saved group ids. @@ -1848,7 +2059,8 @@ UNIX MODULE Returns `ENOSYS` on Windows NT. Returns `ENOSYS` on Macintosh and NetBSD if `saved` isn't -1. - unix.umask(mask:int) → oldmask:int + unix.umask(newmask:int) + └─→ oldmask:int Sets file permission mask and returns the old one. @@ -1880,7 +2092,9 @@ UNIX MODULE This function currently works on Linux, Windows, and NetBSD. On WIN32 it uses the ReportEvent() facility. - unix.clock_gettime([clock:int]) → seconds:int, unix.Errno, nanos:int + unix.clock_gettime([clock:int]) + ├─→ seconds:int, nanos:int + └─→ nil, unix.Errno Returns nanosecond precision timestamp from the system. @@ -1901,19 +2115,30 @@ UNIX MODULE Returns `EINVAL` if clock isn't supported on platform. - unix.nanosleep(seconds:int[, nanos:int]) - → remseconds:int, unix.Errno, remnanos:int + This function only fails if `clock` is invalid. + + unix.nanosleep(seconds:int, nanos:int) + ├─→ remseconds:int, remnanos:int + └─→ nil, unix.Errno Sleeps with nanosecond precision. + Returns `EINTR` if a signal was received while waiting. + unix.sync() - unix.fsync(fd:int) → ok:bool, unix.Errno - unix.fdatasync(fd:int) → ok:bool, unix.Errno + unix.fsync(fd:int) + ├─→ true + └─→ nil, unix.Errno + unix.fdatasync(fd:int) + ├─→ true + └─→ nil, unix.Errno These functions are used to make programs slower by asking the operating system to flush data to the physical medium. - unix.lseek(fd:int, offset:int, whence:int) → newpos:int, unix.Errno + unix.lseek(fd:int, offset:int[, whence:int]) + ├─→ newposbytes:int + └─→ nil, unix.Errno Seeks to file position. @@ -1925,21 +2150,27 @@ UNIX MODULE Returns the new position relative to the start of the file. - unix.truncate(path:str[, length:int]) → ok:bool, unix.Errno + unix.truncate(path:str[, length:int]) + ├─→ true + └─→ nil, unix.Errno Reduces or extends underlying physical medium of file. If file was originally larger, content >length is lost. `length` defaults to zero. - unix.ftruncate(fd:int[, length:int]) → ok:bool, unix.Errno + unix.ftruncate(fd:int[, length:int]) + ├─→ true + └─→ nil, unix.Errno Reduces or extends underlying physical medium of open file. If file was originally larger, content >length is lost. `length` defaults to zero. - unix.socket([family:int[, type:int[, protocol:int]]]) → fd:int, unix.Errno + unix.socket([family:int[, type:int[, protocol:int]]]) + ├─→ fd:int + └─→ nil, unix.Errno `family` defaults to `AF_INET` and can be: @@ -1965,14 +2196,17 @@ UNIX MODULE `SOCK_CLOEXEC` may be bitwise or'd into `type`. unix.socketpair([family:int[, type:int[, protocol:int]]]) - → fd1:int, unix.Errno, fd2:int + ├─→ fd1:int, fd2:int + └─→ nil, unix.Errno `SOCK_CLOEXEC` may be or'd into type `family` defaults to `AF_INET` `type` defaults to `SOCK_STREAM` `protocol` defaults to `IPPROTO_TCP` - unix.bind(fd:int[, ip:uint32, port:uint16]) → ok:bool, unix.Errno + unix.bind(fd:int[, ip:uint32, port:uint16]) + ├─→ true + └─→ nil, unix.Errno Binds socket. @@ -1987,18 +2221,14 @@ UNIX MODULE is to listen on all interfaces with a kernel-assigned ephemeral port number, that can be retrieved and used as follows: - local sock = unix.socket() -- create ipv4 tcp socket - errno = unix.bind(sock) -- all interfaces ephemeral port - if errno then - Log(kLogWarn, bind() failed: %s' % {unix.strerror(errno)}) - return - end - ip, port = unix.getsockname(sock) + sock = assert(unix.socket()) -- create ipv4 tcp socket + assert(unix.bind(sock)) -- all interfaces ephemeral port + ip, port = assert(unix.getsockname(sock)) print("listening on ip", FormatIp(ip), "port", port) - unix.listen(sock) - unix.accept(sock) + assert(unix.listen(sock)) + assert(unix.accept(sock)) while true do - client, clientip, clientport = unix.accept(sock) + client, clientip, clientport = assert(unix.accept(sock)) print("got client ip", FormatIp(clientip), "port", clientport) unix.close(client) end @@ -2006,11 +2236,13 @@ UNIX MODULE Further note that calling `unix.bind(sock)` is equivalent to not calling bind() at all, since the above behavior is the default. - unix.siocgifconf() → {{name:str,ip:uint32,netmask:uint32}, ...}, unix.Errno + unix.siocgifconf() + ├─→ {{name:str,ip:uint32,netmask:uint32}, ...} + └─→ nil, unix.Errno Returns list of network adapter addresses. - unix.getsockopt(fd:int, level:int, optname:int) → ok:bool, unix.Errno, ... + unix.getsockopt(fd:int, level:int, optname:int) → ... unix.setsockopt(fd:int, level:int, optname:int, ...) → ok:bool, unix.Errno Tunes networking parameters. @@ -2018,44 +2250,87 @@ UNIX MODULE `level` and `optname` may be one of the following. The ellipses type signature above changes depending on which options are used. - - `SOL_SOCKET` + `SO_TYPE`: bool - - `SOL_SOCKET` + `SO_DEBUG`: bool - - `SOL_SOCKET` + `SO_ACCEPTCONN`: bool - - `SOL_SOCKET` + `SO_BROADCAST`: bool - - `SOL_SOCKET` + `SO_REUSEADDR`: bool - - `SOL_SOCKET` + `SO_REUSEPORT`: bool - - `SOL_SOCKET` + `SO_KEEPALIVE`: bool - - `SOL_SOCKET` + `SO_DONTROUTE`: bool - - `SOL_SOCKET` + `SO_SNDBUF`: int - - `SOL_SOCKET` + `SO_RCVBUF`: int - - `SOL_SOCKET` + `SO_RCVLOWAT`: int - - `SOL_SOCKET` + `SO_SNDLOWAT`: int - - `SOL_SOCKET` + `SO_RCVTIMEO`: seconds:int[, micros:int] - - `SOL_SOCKET` + `SO_SNDTIMEO`: seconds:int[, micros:int] - - `SOL_TCP` + `TCP_NODELAY`: bool - - `SOL_TCP` + `TCP_CORK`: bool - - `SOL_TCP` + `TCP_QUICKACK`: bool - - `SOL_TCP` + `TCP_FASTOPEN_CONNECT`: bool - - `SOL_TCP` + `TCP_DEFER_ACCEPT`: bool - - `SOL_TCP` + `TCP_KEEPIDLE`: seconds:int - - `SOL_TCP` + `TCP_KEEPINTVL`: seconds:int - - `SOL_TCP` + `TCP_FASTOPEN`: int - - `SOL_TCP` + `TCP_KEEPCNT`: int - - `SOL_TCP` + `TCP_MAXSEG`: int - - `SOL_TCP` + `TCP_SYNCNT`: int - - `SOL_TCP` + `TCP_NOTSENT_LOWAT`: int - - `SOL_TCP` + `TCP_WINDOW_CLAMP`: int - - `SOL_IP` + `IP_TOS`: int - - `SOL_IP` + `IP_MTU`: int - - `SOL_IP` + `IP_TTL`: int - - `SOL_IP` + `IP_HDRINCL`: bool + unix.getsockopt(fd:int, level:int, optname:int) + ├─→ value:int + └─→ nil, unix.Errno + unix.setsockopt(fd:int, level:int, optname:int, value:bool) + ├─→ true + └─→ nil, unix.Errno + + - `SOL_SOCKET` + `SO_TYPE` + - `SOL_SOCKET` + `SO_DEBUG` + - `SOL_SOCKET` + `SO_ACCEPTCONN` + - `SOL_SOCKET` + `SO_BROADCAST` + - `SOL_SOCKET` + `SO_REUSEADDR` + - `SOL_SOCKET` + `SO_REUSEPORT` + - `SOL_SOCKET` + `SO_KEEPALIVE` + - `SOL_SOCKET` + `SO_DONTROUTE` + - `SOL_TCP` + `TCP_NODELAY` + - `SOL_TCP` + `TCP_CORK` + - `SOL_TCP` + `TCP_QUICKACK` + - `SOL_TCP` + `TCP_FASTOPEN_CONNECT` + - `SOL_TCP` + `TCP_DEFER_ACCEPT` + - `SOL_IP` + `IP_HDRINCL` + + unix.getsockopt(fd:int, level:int, optname:int) + ├─→ value:int + └─→ nil, unix.Errno + unix.setsockopt(fd:int, level:int, optname:int, value:int) + ├─→ true + └─→ nil, unix.Errno + + - `SOL_SOCKET` + `SO_SNDBUF` + - `SOL_SOCKET` + `SO_RCVBUF` + - `SOL_SOCKET` + `SO_RCVLOWAT` + - `SOL_SOCKET` + `SO_SNDLOWAT` + - `SOL_TCP` + `TCP_KEEPIDLE` + - `SOL_TCP` + `TCP_KEEPINTVL` + - `SOL_TCP` + `TCP_FASTOPEN` + - `SOL_TCP` + `TCP_KEEPCNT` + - `SOL_TCP` + `TCP_MAXSEG` + - `SOL_TCP` + `TCP_SYNCNT` + - `SOL_TCP` + `TCP_NOTSENT_LOWAT` + - `SOL_TCP` + `TCP_WINDOW_CLAMP` + - `SOL_IP` + `IP_TOS` + - `SOL_IP` + `IP_MTU` + - `SOL_IP` + `IP_TTL` + + unix.getsockopt(fd:int, level:int, optname:int) + ├─→ secs:int, nsecs:int + └─→ nil, unix.Errno + unix.setsockopt(fd:int, level:int, optname:int, secs:int[, nanos:int]) + ├─→ true + └─→ nil, unix.Errno + + - `SOL_SOCKET` + `SO_RCVTIMEO`: If this option is specified then + your stream socket will have a read() / recv() timeout. If the + specified interval elapses without receiving data, then EAGAIN + shall be returned by read. If this option is used on listening + sockets, it'll be inherited by accepted sockets. Your redbean + already does this for GetClientFd() based on the `-t` flag. + + - `SOL_SOCKET` + `SO_SNDTIMEO`: This is the same as `SO_RCVTIMEO` + but it applies to the write() / send() functions. + + unix.getsockopt(fd:int, unix.SOL_SOCKET, unix.SO_LINGER) + ├─→ seconds:int, enabled:bool + └─→ nil, unix.Errno + unix.setsockopt(fd:int, unix.SOL_SOCKET, unix.SO_LINGER, secs:int, enabled:bool) + ├─→ true + └─→ nil, unix.Errno + + This `SO_LINGER` parameter can be used to make close() a blocking + call. Normally when the kernel returns immediately when it receives + close(). Sometimes it's desirable to have extra assurance on errors + happened, even if it comes at the cost of performance. Returns `EINVAL` if settings other than the above are used. Returns `ENOSYS` if setting isn't supported by the host o/s. - unix.poll({fd:int=events:int, ...}[, timeoutms:int]) - → {fd:int=revents:int, ...}, unix.Errno + unix.poll({[fd:int]=events:int, ...}[, timeoutms:int]) + ├─→ {[fd:int]=revents:int, ...} + └─→ nil, unix.Errno Checks for events on a set of file descriptors. @@ -2068,16 +2343,21 @@ UNIX MODULE then that means block as long as it takes until there's an event or an interrupt. If the timeout expires, an empty table is returned. - unix.gethostname() → host:str, unix.Errno + unix.gethostname() + ├─→ host:str + └─→ nil, unix.Errno Returns hostname of system. - unix.listen(fd:int[, backlog:int]) → ok:bool, unix.Errno + unix.listen(fd:int[, backlog:int]) + ├─→ true + └─→ nil, unix.Errno Begins listening for incoming connections on a socket. unix.accept(serverfd:int[, flags:int]) - → clientfd:int, unix.Errno, ip:uint32, port:uint16 + ├─→ clientfd:int, ip:uint32, port:uint16 + └─→ nil, unix.Errno Accepts new client socket descriptor for a listening tcp socket. @@ -2086,7 +2366,9 @@ UNIX MODULE - `SOCK_CLOEXEC` - `SOCK_NONBLOCK` - unix.connect(fd:int, ip:uint32, port:uint16) → ok:bool, unix.Errno + unix.connect(fd:int, ip:uint32, port:uint16) + ├─→ true + └─→ nil, unix.Errno Connects a TCP socket to a remote host. @@ -2094,15 +2376,21 @@ UNIX MODULE remembers the intended address so that send() or write() may be used rather than sendto(). - unix.getsockname(fd:int) → ip:uint32, unix.Errno, port:uint16 + unix.getsockname(fd:int) + ├─→ ip:uint32, port:uint16 + └─→ nil, unix.Errno Retrieves the local address of a socket. - unix.getpeername(fd:int) → ip:uint32, unix.Errno, port:uint16 + unix.getpeername(fd:int) + ├─→ ip:uint32, port:uint16 + └─→ nil, unix.Errno Retrieves the remote address of a socket. - unix.recv(fd:int[, bufsiz:int[, flags:int]]) → data:str, unix.Errno + unix.recv(fd:int[, bufsiz:int[, flags:int]]) + ├─→ data:str + └─→ nil, unix.Errno `flags` can have: @@ -2112,7 +2400,8 @@ UNIX MODULE - `MSG_OOB` unix.recvfrom(fd:int[, bufsiz:int[, flags:int]]) - → data:str, unix.Errno, ip:uint32, port:uint16 + ├─→ data:str, ip:uint32, port:uint16 + └─→ nil, unix.Errno `flags` can have: @@ -2121,27 +2410,50 @@ UNIX MODULE - `MSG_PEEK` - `MSG_OOB` - unix.send(fd:int, data:str[, flags:int]) → sent:int, unix.Errno + unix.send(fd:int, data:str[, flags:int]) + ├─→ sent:int + └─→ nil, unix.Errno This is the same as `write` except it has a `flags` argument that's intended for sockets. - `flags` can have `MSG_OOB`, `MSG_DONTROUTE`, or `MSG_NOSIGNAL`. + `flags` may have any of: - unix.sendto(fd:int, data:str, ip:int, port:int[, flags:int]) - → sent:int, unix.Errno + - `MSG_OOB` + - `MSG_DONTROUTE` + - `MSG_NOSIGNAL` + + unix.sendto(fd:int, data:str, ip:uint32, port:uint16[, flags:int]) + ├─→ sent:int + └─→ nil, unix.Errno This is useful for sending messages over UDP sockets to specific addresses. - `flags` can have `MSG_OOB`, `MSG_DONTROUTE`, or `MSG_NOSIGNAL`. + `flags` may have any of: - unix.shutdown(fd:int, how:int) → ok:bool, unix.Errno + - `MSG_OOB` + - `MSG_DONTROUTE` + - `MSG_NOSIGNAL` - Partially closes socket. `how` can be `SHUT_RD`, `SHUT_WR`, or - `SHUT_RDWR`. + unix.shutdown(fd:int, how:int) + ├─→ true + └─→ nil, unix.Errno - unix.sigprocmask(how:int[, mask:int]) → oldmask:int, unix.Errno + Partially closes socket. + + `how` is set to one of: + + - `SHUT_RD`: sends a tcp half close for reading + - `SHUT_WR`: sends a tcp half close for writing + - `SHUT_RDWR` + + This system call currently has issues on Macintosh, so portable code + should log rather than assert failures reported by shutdown(). + + unix.sigprocmask(how:int, newmask:unix.Sigset) + ├─→ oldmask:unix.Sigset + └─→ nil, unix.Errno Manipulates bitset of signals blocked by process. @@ -2151,38 +2463,101 @@ UNIX MODULE - `SIG_UNBLOCK`: removes bits in `mask` from set of blocked signals - `SIG_SETMASK`: replaces process signal mask with `mask` - `mask` is a word encoded bitset of signals. Valid signal numbers - start at 1 and vary between platforms. The most famous `SIGKILL` - can't be masked, but if it could, it's assigned the number `9` - across all platforms, so if you wanted to add it to a bitset, you - would say, `1 << 8` or in general terms `1 << (sig - 1)`. + `mask` is a unix.Sigset() object (see section below). - unix.sigaction(sig:int[, handler:func|int[, flags:int[, mask:int]]]) - → oldhandler:func|int, unix.Errno, flags:int, mask:int + For example, to temporarily block `SIGTERM` and `SIGINT` so critical + work won't be interrupted, sigprocmask() can be used as follows: - `handler` can be `SIG_IGN`, `SIG_DFL`, `intptr_t`, or a Lua - function. `sig` can be `SIGINT`, `SIGQUIT`, `SIGTERM`, etc. - `flags` can have `SA_RESTART`, `SA_RESETHAND`, etc. Example: + newmask = unix.Sigset(unix.SIGTERM) + oldmask = assert(unix.sigprocmask(unix.SIG_BLOCK, newmask)) + -- do something... + assert(unix.sigprocmask(unix.SIG_SETMASK, oldmask)) - unix = require "unix" - unix.sigaction(unix.SIGUSR1, function(sig) - print('got %s' % {unix.strsignal(sig)}) - end) - unix.sigprocmask(unix.SIG_SETMASK, -1) - unix.raise(unix.SIGUSR1) - unix.sigsuspend() + unix.sigaction(sig:int[, handler:func|int[, flags:int[, mask:unix.Sigset]]]) + ├─→ oldhandler:func|int, flags:int, mask:unix.Sigset + └─→ nil, unix.Errno + + `sig` can be one of: + + - `unix.SIGINT` + - `unix.SIGQUIT` + - `unix.SIGTERM` + - etc. + + `handler` can be: + + - Lua function + - `unix.SIG_IGN` + - `unix.SIG_DFL` + + `flags` can have: + + - `unix.SA_RESTART`: Enables BSD signal handling semantics. Normally + i/o entrypoints check for pending signals to deliver. If one gets + delivered during an i/o call, the normal behavior is to cancel the + i/o operation and return -1 with `EINTR` in errno. If you use the + `SA_RESTART` flag then that behavior changes, so that any function + that's been annotated with @restartable will not return `EINTR` + and will instead resume the i/o operation. This makes coding + easier but it can be an anti-pattern if not used carefully, since + poor usage can easily result in latency issues. It also requires + one to do more work in signal handlers, so special care needs to + be given to which C library functions are @asyncsignalsafe. + + - `unix.SA_RESETHAND`: Causes signal handler to be single-shot. This + means that, upon entry of delivery to a signal handler, it's reset + to the `SIG_DFL` handler automatically. You may use the alias + `SA_ONESHOT` for this flag, which means the same thing. + + - `unix.SA_NODEFER`: Disables the reentrancy safety check on your signal + handler. Normally that's a good thing, since for instance if your + `SIGSEGV` signal handler happens to segfault, you're going to want + your process to just crash rather than looping endlessly. But in + some cases it's desirable to use `SA_NODEFER` instead, such as at + times when you wish to `longjmp()` out of your signal handler and + back into your program. This is only safe to do across platforms + for non-crashing signals such as `SIGCHLD` and `SIGINT`. Crash + handlers should use Xed instead to recover execution, because on + Windows a `SIGSEGV` or `SIGTRAP` crash handler might happen on a + separate stack and/or a separate thread. You may use the alias + `SA_NOMASK` for this flag, which means the same thing. + + - `unix.SA_NOCLDWAIT`: Changes `SIGCHLD` so the zombie is gone and + you can't call wait() anymore; similar but may still deliver the + SIGCHLD. + + - `unix.SA_NOCLDSTOP`: Lets you set `SIGCHLD` handler that's only + notified on exit/termination and not notified on `SIGSTOP`, + `SIGTSTP`, `SIGTTIN`, `SIGTTOU`, or `SIGCONT`. + + Example: + + assert(unix.sigaction(unix.SIGUSR1, function(sig) + gotsigusr1 = true + end)) + gotsigusr1 = false + assert(unix.raise(unix.SIGUSR1)) + ok, err = unix.sigsuspend() + assert(err:errno == unix.EINTR) + if gotsigusr1 + print('hooray the signal was delivered') + else + print('oh no some other signal was handled') + end It's a good idea to not do too much work in a signal handler. - unix.sigsuspend([mask]) → false, unix.Errno + unix.sigsuspend([mask:Sigmask]) + └─→ nil, unix.Errno Waits for signal to be delivered. The signal mask is temporarily replaced with `mask` during this system call. `mask` specifies which signals should be blocked. - unix.setitimer(which[, intsec, intmicros, valsec, valmicros]) - → intsec, unix.Errno, intns, valsec, valns + unix.setitimer(which[, intervalsec, intns, valuesec, valuens]) + ├─→ intervalsec:int, intervalns:int, valuesec:int, valuens:int + └─→ nil, unix.Errno Causes `SIGALRM` signals to be generated at some point(s) in the future. The `which` parameter should be `ITIMER_REAL`. @@ -2190,11 +2565,11 @@ UNIX MODULE Here's an example of how to create a 400 ms interval timer: ticks = 0 - unix.sigaction(unix.SIGALRM, function(sig) + assert(unix.sigaction(unix.SIGALRM, function(sig) print('tick no. %d' % {ticks}) ticks = ticks + 1 - end) - unix.setitimer(unix.ITIMER_REAL, 0, 400000, 0, 400000) + end)) + assert(unix.setitimer(unix.ITIMER_REAL, 0, 400e6, 0, 400e6)) while true do unix.sigsuspend() end @@ -2204,29 +2579,23 @@ UNIX MODULE unix.sigaction(unix.SIGALRM, MyOnSigAlrm, unix.SA_RESETHAND) unix.setitimer(unix.ITIMER_REAL, 0, 0, 1, 0) - unix.strerrno(unix.Errno) → str - - Turns `errno` code into its symbolic name, e.g. `"EINTR"`. If - `errno` isn't known, this function returns nil. - - unix.strerdoc(unix.Errno) → str - - Turns `errno` code into a descriptive string. If `errno` isn't - known, this function returns nil. - - unix.strerror(unix.Errno) → str - - Turns `errno` code into longest string describing the error. This - includes the output of both strerrno() and strerror() as well as the - error number. On Windows it includes FormatMessage() output too. If - `errno` isn't known, this function still returns a string. - unix.strsignal(sig:int) → str - Turns platform-specific `sig` code into its name, e.g. - `strsignal(9)` always returns `"SIGKILL"`. + Turns platform-specific `sig` code into its symbolic name. - unix.setrlimit(resource:int, soft:int[, hard:int]) → ok:bool, unix.Errno + For example: + + >: unix.strsignal(9) + "SIGKILL" + >: unix.strsignal(unix.SIGKILL) + "SIGKILL" + + Please note that signal numbers are normally different across + supported platforms, and the constants should be preferred. + + unix.setrlimit(resource:int, soft:int[, hard:int]) + ├─→ true + └─→ nil, unix.Errno Changes resource limit. @@ -2257,21 +2626,69 @@ UNIX MODULE 127. On most platforms these limits are enforced by the kernel and as such are inherited by subprocesses. - unix.getrlimit(resource:int) → soft:int, unix.Errno, hard:int + `hard` defaults to whatever was specified in `soft`. + + unix.getrlimit(resource:int) + ├─→ soft:int, hard:int + └─→ nil, unix.Errno Returns information about resource limit. - unix.stat(x) → unix.Stat, Errno* + unix.stat(path:str[, flags:int[, dirfd:int]]) + ├─→ unix.Stat + └─→ nil, unix.Errno - Gets information about file or directory. `x` may be a file or - directory path string, or it may be a file descriptor int that - was made by open(). + Gets information about file or directory. - unix.opendir(path:str) → unix.Dir, Errno* + `flags` may have any of: + + - `AT_SYMLINK_NOFOLLOW`: do not follow symbolic links. + + `dirfd` defaults to to `unix.AT_FDCWD` and may optionally be set to + a directory file descriptor to which `path` is relative. + + unix.fstat(fd:int) + ├─→ unix.Stat + └─→ nil, unix.Errno + + Gets information about opened file descriptor. + + `fd` should be a file descriptor that was opened using + `unix.open(path, O_RDONLY|O_DIRECTORY)`. + + `flags` may have any of: + + - `AT_SYMLINK_NOFOLLOW`: do not follow symbolic links. + + `dirfd` defaults to to `unix.AT_FDCWD` and may optionally be set to + a directory file descriptor to which `path` is relative. + + A common use for fstat() is getting the size of a file. For example: + + fd = assert(unix.open("hello.txt", unix.O_RDONLY)) + st = assert(unix.fstat(fd)) + Log(kLogInfo, 'hello.txt is %d bytes in size' % {st:size()}) + unix.close(fd) + + unix.opendir(path:str) + ├─→ state:unix.Dir + └─→ nil, unix.Errno Opens directory for listing its contents. - unix.fdopendir(fd:int) → unix.Dir, Errno* + For example, to print a simple directory listing: + + Write('
    \r\n') + for name, kind, ino, off in assert(unix.opendir(dir)) do + if name ~= '.' and name ~= '..' then + Write('
  • %s\r\n' % {EscapeHtml(name)}) + end + end + Write('
\r\n') + + unix.fdopendir(fd:int) + ├─→ next:function, state:unix.Dir + └─→ nil, unix.Errno Opens directory for listing its contents, via an fd. @@ -2279,40 +2696,58 @@ UNIX MODULE returned unix.Dir ownership takes ownership of the file descriptor and will close it automatically when garbage collected. +──────────────────────────────────────────────────────────────────────────────── + UNIX DIR OBJECT unix.Dir objects are created by opendir() or fdopendir(). The following methods are available: - unix.Dir:close() → ok:bool, unix.Errno + unix.Dir:close() + ├─→ true + └─→ nil, unix.Errno - may be called multiple times - called by the garbage collector too + Closes directory stream object and associated its file descriptor. - unix.Dir:read() → name:str, unix.Errno, kind:int, ino:int, off:int + This is called automatically by the garbage collector. + + This may be called multiple times. + + unix.Dir:read() + ├─→ name:str, kind:int, ino:int, off:int + └─→ nil + + Reads entry from directory stream. Returns `nil` if there are no more entries. Or error, `nil` will be returned and `errno` will be non-nil. `kind` can be any of: + - `DT_REG`: file is a regular file + - `DT_DIR`: file is a directory + - `DT_BLK`: file is a block device + - `DT_LNK`: file is a symbolic link + - `DT_CHR`: file is a character device + - `DT_FIFO`: file is a named pipe + - `DT_SOCK`: file is a named socket - `DT_UNKNOWN` - - `DT_REG` - - `DT_DIR` - - `DT_BLK` - - `DT_LNK` - - `DT_CHR` - - `DT_FIFO` - - `DT_SOCK` - unix.Dir:fd() → fd:int, unix.Errno + Note: This function also serves as the `__call` metamethod, so that + unix.Dir objects may be used as a for loop iterator. + + unix.Dir:fd() + ├─→ fd:int + └─→ nil, unix.Errno Returns file descriptor of open directory object. Returns `EOPNOTSUPP` if using a `/zip/...` path. Returns `EOPNOTSUPP` if using Windows NT. - unix.Dir:tell() → offset:int + unix.Dir:tell() + ├─→ off:int + └─→ nil, unix.Errno Returns current arbitrary offset into stream. @@ -2320,16 +2755,20 @@ UNIX MODULE Resets stream back to beginning. +──────────────────────────────────────────────────────────────────────────────── + UNIX STAT OBJECT unix.Stat objects are created by stat() or fstat(). The following methods are available: - unix.Stat:size() → bytes:int + unix.Stat:size() + └─→ bytes:int Size of file in bytes. - unix.Stat:mode() → mode:int + unix.Stat:mode() + └─→ mode:int Contains file type and permissions. @@ -2346,339 +2785,691 @@ UNIX MODULE - `(st:mode() & 0170000) == 0120000` means symbolic link - `(st:mode() & 0170000) == 0140000` means socket - unix.Stat:atim() → secs:int, nanos:int - - Size of file in bytes. - - unix.Stat:uid() → int + unix.Stat:uid() + └─→ uid:int User ID of file owner. - unix.Stat:gid() → int + unix.Stat:gid() + └─→ gid:int Group ID of file owner. - unix.Stat:mtim() → secs:int, nanos:int + unix.Stat:birthtim() + └─→ unixts:int, nanos:int + + File birth time. + + This field should be accurate on Apple, Windows, and BSDs. On Linux + this is the mimimum of atim/mtim/ctim. On Windows NT nanos is only + accurate to hectonanoseconds. + + Here's an example of how you might print a file timestamp: + + st = assert(unix.stat('/etc/passwd')) + unixts, nanos = st:birthtim() + year,mon,mday,hour,min,sec,gmtoffsec = unix.localtime(unixts) + Write('%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.9d%+.2d%.2d % { + year, mon, mday, hour, min, sec, nanos, + gmtoffsec / (60 * 60), math.abs(gmtoffsec) % 60}) + + unix.Stat:mtim() + └─→ unixts:int, nanos:int Last modified time. - unix.Stat:birthtim() → secs:int, nanos:int + unix.Stat:atim() + └─→ unixts:int, nanos:int - Creation time. Note that on Linux this is the mimimum of - atom/mtim/ctim. + Last access time. - unix.Stat:ctim() → secs:int, nanos:int + Please note that file systems are sometimes mounted with `noatime` + out of concern for i/o performance. Linux also provides `O_NOATIME` + as an option for open(). - Complicated time. Means time file status was last changed on - UNIX. Means creation time on Windows. + On Windows NT this is the same as birth time. - unix.Stat:blocks() → int + unix.Stat:ctim() + └─→ unixts:int, nanos:int - Number of blocks used by storage medium. + Complicated time. - unix.Stat:blksize() → int + Means time file status was last changed on UNIX. - Block size is usually 4096 for file system files. + On Windows NT this is the same as birth time. - unix.Stat:dev() → int + unix.Stat:blocks() + └─→ count512:int - ID of device containing file. + Number of 512-byte blocks used by storage medium. - unix.Stat:ino() → int + This provides some indication of how much physical storage a file + actually consumes. For example, for small file systems, your system + might report this number as being 8, which means 4096 bytes. + + On Windows NT, if compression is enabled for a file, then this + number will reflect the size *after* compression. you can use: + + st = assert(unix.stat("moby.txt")) + print('file size is %d bytes' % {st:size()}) + print('file takes up %d bytes of space' % {st:blocks() * 512}) + if GetHostOs() == 'WINDOWS' and st:flags() & 0x800 then + print('thanks to file system compression') + end + + To tell whether or not compression is being used on a file, + + unix.Stat:blksize() + └─→ bytes:int + + Block size that underlying device uses. + + This field might be of assistance in computing optimal i/o sizes. + + Please note this field has no relationship to blocks, as the latter + is fixed at a 512 byte size. + + unix.Stat:ino() + └─→ inode:int Inode number. - unix.Stat:rdev() → int + This can be used to detect some other process used rename() to swap + out a file underneath you, so you can do a refresh. redbean does it + during each main process heartbeat for its own use cases. - Device ID (if special file) + On Windows NT this is set to NtByHandleFileInformation::FileIndex. + unix.Stat:dev() + └─→ dev:int + + ID of device containing file. + + On Windows NT this is set to + NtByHandleFileInformation::VolumeSerialNumber. + + unix.Stat:rdev() + └─→ rdev:int + + Information about device type. + + This value may be set to 0 or -1 for files that aren't devices, + depending on the operating system. unix.major() and unix.minor() + may be used to extract the device numbers. + +──────────────────────────────────────────────────────────────────────────────── + + UNIX SIGSET OBJECT + + unix.Sigset(sig:int, ...) + └─→ unix.Sigset + + Creates new signal bitset. + + unix.Sigset:add(sig:int) + + Adds signal to bitset. + + Invalid signal numbers are ignored. + + unix.Sigset:remove(sig:int) + + Removes signal from bitset. + + Invalid signal numbers are ignored. + + unix.Sigset:fill() + + Sets all bits in signal bitset to true. + + unix.Sigset:clear() + + Sets all bits in signal bitset to false. + + unix.Sigset:contains(sig:int) + └─→ bool + + Returns true if `sig` is member of signal bitset. + + unix.Sigset:__repr() + unix.Sigset:__tostring() + + Returns Lua code string that recreates object. + +──────────────────────────────────────────────────────────────────────────────── + + UNIX SIGNALS + + unix.SIGINT + Terminal CTRL-C keystroke. + + unix.SIGQUIT + Terminal CTRL-\ keystroke. + + unix.SIGHUP + Terminal hangup or daemon reload; auto-broadcasted to process group. + + unix.SIGILL + Illegal instruction. + + unix.SIGTRAP + INT3 instruction. + + unix.SIGABRT + Process aborted. + + unix.SIGBUS + Valid memory access that went beyond underlying end of file. + + unix.SIGFPE + Illegal math. + + unix.SIGKILL + Terminate with extreme prejudice. + + unix.SIGUSR1 + Do whatever you want. + + unix.SIGUSR2 + Do whatever you want. + + unix.SIGSEGV + Invalid memory access. + + unix.SIGPIPE + Write to closed file descriptor. + + unix.SIGALRM + Sent by setitimer(). + + unix.SIGTERM + Terminate. + + unix.SIGCHLD + Child process exited or terminated and is now a zombie (unless this + is SIG_IGN or SA_NOCLDWAIT) or child process stopped due to terminal + i/o or profiling/debugging (unless you used SA_NOCLDSTOP) + + unix.SIGCONT + Child process resumed from profiling/debugging. + + unix.SIGSTOP + Child process stopped due to profiling/debugging. + + unix.SIGTSTP + Terminal CTRL-Z keystroke. + + unix.SIGTTIN + Terminal input for background process. + + unix.SIGTTOU + Terminal output for background process. + + unix.SIGXCPU + CPU time limit exceeded. + + unix.SIGXFSZ + File size limit exceeded. + + unix.SIGVTALRM + Virtual alarm clock. + + unix.SIGPROF + Profiling timer expired. + + unix.SIGWINCH + Terminal resized. + + unix.SIGPWR + Not implemented in most community editions of system five. + +──────────────────────────────────────────────────────────────────────────────── + UNIX ERRORS - - `EINVAL`: Invalid argument. Raised by [pretty much everything]. + unix.EINVAL + Invalid argument. - - `ENOSYS`: System call not available on this platform. On Windows - this is raised by chroot(), setuid(), setgid(), getsid(), setsid(). + Raised by [pretty much everything]. - - `ENOENT`: no such file or directory. Raised by access(), - alloc_hugepages(), bind(), chdir(), chmod(), chown(), chroot(), - clock_getres(), execve(), opendir(), inotify_add_watch(), kcmp(), - link(), mkdir(), mknod(), msgget(), open(), readlink(), rename(), - rmdir(), semget(), shmget(), stat(), swapon(), symlink(), - truncate(), unlink(), utime(), utimensat(). + unix.ENOSYS + System call not available on this platform. On Windows this is - - `ENOTDIR`: Not a directory. This means that a directory component in - a supplied path *existed* but wasn't a directory. For example, if - you try to `open("foo/bar")` and `foo` is a regular file, then - `ENOTDIR` will be returned. Raised by open(), access(), chdir(), - chroot(), execve(), link(), mkdir(), mknod(), opendir(), readlink(), - rename(), rmdir(), stat(), symlink(), truncate(), unlink(), - utimensat(), bind(), chmod(), chown(), fcntl(), futimesat(), - inotify_add_watch(). + Raised by chroot(), setuid(), setgid(), getsid(), setsid(). - - `EINTR`: The greatest of all errnos; crucial for building real time - reliable software. Raised by accept(), clock_nanosleep(), close(), - connect(), dup(), fcntl(), flock(), getrandom(), nanosleep(), - open(), pause(), poll(), ptrace(), read(), recv(), select(), send(), - sigsuspend(), sigwaitinfo(), truncate(), wait(), write() + unix.ENOENT + No such file or directory. - - `EIO`: Raised by access() acct() chdir() chmod() chown() chroot() - close() copy_file_range() execve() fallocate() fsync() ioperm() - link() madvise() mbind() pciconfig_read() ptrace() read() readlink() - sendfile() statfs() symlink() sync_file_range() truncate() unlink() - write() + Raised by access(), bind(), chdir(), chmod(), chown(), chroot(), + clock_getres(), execve(), opendir(), inotify_add_watch(), link(), + mkdir(), mknod(), open(), readlink(), rename(), rmdir(), stat(), + swapon(), symlink(), truncate(), unlink(), utime(), utimensat(). - - `ENXIO`: No such device or address. Raised by lseek(), open(), - prctl() + unix.ENOTDIR + Not a directory. This means that a directory component in a supplied + path *existed* but wasn't a directory. For example, if you try to + `open("foo/bar")` and `foo` is a regular file, then `ENOTDIR` will + be returned. - - `E2BIG`: Argument list too long. Raised by execve(), msgop(), - sched_setattr(), semop() + Raised by open(), access(), chdir(), chroot(), execve(), link(), + mkdir(), mknod(), opendir(), readlink(), rename(), rmdir(), stat(), + symlink(), truncate(), unlink(), utimensat(), bind(), chmod(), + chown(), fcntl(), futimesat(), inotify_add_watch(). - - `ENOEXEC`: exec format error. Raised by execve(), kexec_load(), - uselib() + unix.EINTR + The greatest of all errnos; crucial for building real time reliable + software. - - `ECHILD`: no child process. Raised by wait(), waitpid(), waitid(), - wait3(), wait4() + Raised by accept(), clock_nanosleep(), close(), connect(), dup(), + fcntl(), flock(), getrandom(), nanosleep(), open(), pause(), poll(), + ptrace(), read(), recv(), select(), send(), sigsuspend(), + sigwaitinfo(), truncate(), wait(), write(). - - `ESRCH`: No such process. Raised by getpriority(), getrlimit(), - getsid(), ioprio_set(), kill(), setpgid(), tkill(), utimensat(), + unix.EIO + Raised by access(), acct(), chdir(), chmod(), chown(), chroot(), + close(), copy_file_range(), execve(), fallocate(), fsync(), + ioperm(), link(), madvise(), mbind(), pciconfig_read(), ptrace(), + read(), readlink(), sendfile(), statfs(), symlink(), + sync_file_range(), truncate(), unlink(), write(). - - `EBADF`: bad file descriptor; cf. EBADFD. Raised by accept(), - access(), bind(), chdir(), chmod(), chown(), close(), connect(), - copy_file_range(), dup(), fcntl(), flock(), fsync(), futimesat(), - opendir(), getpeername(), getsockname(), getsockopt(), - inotify_add_watch(), inotify_rm_watch(), ioctl(), kcmp(), - kexec_load(), link(), listen(), llseek(), lseek(), mkdir(), mknod(), - mmap(), open(), prctl(), read(), readahead(), readlink(), recv(), - rename(), select(), send(), shutdown(), splice(), stat(), symlink(), - sync(), sync_file_range(), timerfd_create(), truncate(), unlink(), + unix.ENXIO + No such device or address. + + Raised by lseek(), open(), prctl() + + unix.E2BIG + Argument list too long. + + Raised by execve(), sched_setattr(). + + unix.ENOEXEC + Exec format error. + + Raised by execve(), uselib(). + + unix.ECHILD + No child process. + + Raised by wait(), waitpid(), waitid(), wait3(), wait4(). + + unix.ESRCH + No such process. + + Raised by getpriority(), getrlimit(), getsid(), ioprio_set(), + kill(), setpgid(), tkill(), utimensat(), + + unix.EBADF + Bad file descriptor; cf. EBADFD. + + Raised by accept(), access(), bind(), chdir(), chmod(), chown(), + close(), connect(), copy_file_range(), dup(), fcntl(), flock(), + fsync(), futimesat(), opendir(), getpeername(), getsockname(), + getsockopt(), inotify_add_watch(), inotify_rm_watch(), ioctl(), + link(), listen(), llseek(), lseek(), mkdir(), mknod(), mmap(), + open(), prctl(), read(), readahead(), readlink(), recv(), rename(), + select(), send(), shutdown(), splice(), stat(), symlink(), sync(), + sync_file_range(), timerfd_create(), truncate(), unlink(), utimensat(), write(), - - `EAGAIN`: resource temporarily unavailable (e.g. SO_RCVTIMEO - expired, too many processes, too much memory locked, read or write - with O_NONBLOCK needs polling, etc.). Raised by accept(), connect(), - eventfd(), fcntl(), fork(), getrandom(), mincore(), mlock(), mmap(), - mremap(), msgop(), poll(), read(), select(), send(), setresuid(), - setreuid(), setuid(), sigwaitinfo(), splice(), tee(), - timer_create(), timerfd_create(), tkill(), write(), + unix.EAGAIN + Resource temporarily unavailable (e.g. SO_RCVTIMEO expired, too many + processes, too much memory locked, read or write with O_NONBLOCK + needs polling, etc.). - - `EPIPE`: Broken pipe. Returned by write(), send(). This happens when - you try to write data to a subprocess via a pipe() but the reader - end has already closed, possibly because the process died. Normally - i/o routines only return this if `SIGPIPE` doesn't kill the process. + Raised by accept(), connect(), fcntl(), fork(), getrandom(), + mincore(), mlock(), mmap(), mremap(), poll(), read(), select(), + send(), setresuid(), setreuid(), setuid(), sigwaitinfo(), splice(), + tee(), timer_create(), timerfd_create(), tkill(), write(), + + unix.EPIPE + Broken pipe. Returned by write(), send(). This happens when you try + to write data to a subprocess via a pipe() but the reader end has + already closed, possibly because the process died. Normally i/o + routines only return this if `SIGPIPE` doesn't kill the process. Unlike default UNIX programs, redbean currently ignores `SIGPIPE` by default, so this error code is a distinct possibility when pipes or sockets are being used. - - `ENAMETOOLONG`: Filename too long. Cosmopolitan Libc currently - defines `PATH_MAX` as 512 characters. On UNIX, that limit should - only apply to system call wrappers like realpath(). On Windows NT - it's observed by all system calls that accept a pathname. Raised by - access(), bind(), chdir(), chmod(), chown(), chroot(), execve(), - gethostname(), inotify_add_watch(), link(), mkdir(), mknod(), - open(), readlink(), rename(), rmdir(), stat(), symlink(), + unix.ENAMETOOLONG + Filename too long. Cosmopolitan Libc currently defines `PATH_MAX` as + 512 characters. On UNIX, that limit should only apply to system call + wrappers like realpath(). On Windows NT it's observed by all system + calls that accept a pathname. + + Raised by access(), bind(), chdir(), chmod(), chown(), chroot(), + execve(), gethostname(), inotify_add_watch(), link(), mkdir(), + mknod(), open(), readlink(), rename(), rmdir(), stat(), symlink(), truncate(), u unlink(), utimensat() - - `EACCES`: Permission denied. Raised by access(), bind(), bpf(), - chdir(), chmod(), chown(), chroot(), clock_getres(), connect(), - execve(), fcntl(), getpriority(), inotify_add_watch(), link(), - mkdir(), mknod(), mmap(), mprotect(), msgctl(), msgget(), msgop(), - open(), prctl(), ptrace(), readlink(), rename(), rmdir(), semget(), - send(), setpgid(), shmget(), socket(), stat(), symlink(), + unix.EACCES + Permission denied. + + Raised by access(), bind(), chdir(), chmod(), chown(), chroot(), + clock_getres(), connect(), execve(), fcntl(), getpriority(), + inotify_add_watch(), link(), mkdir(), mknod(), mmap(), mprotect(), + msgctl(), open(), prctl(), ptrace(), readlink(), rename(), rmdir(), + semget(), send(), setpgid(), socket(), stat(), symlink(), truncate(), unlink(), uselib(), utime(), utimensat(), - - `ENOMEM`: We require more vespene gas. Raised by access(), bind(), - chdir(), chmod(), chown(), chroot(), clone(), copy_file_range(), - create_module(), eventfd(), execve(), fanotify_init(), fork(), + unix.ENOMEM + We require more vespene gas. + + Raised by access(), bind(), chdir(), chmod(), chown(), chroot(), + clone(), copy_file_range(), execve(), fanotify_init(), fork(), getgroups(), getrlimit(), inotify_add_watch(), inotify_init(), - ioperm(), kexec_load(), link(), mbind(), memfd_create(), mincore(), - mkdir(), mknod(), mlock(), mmap(), mprotect(), mremap(), msgget(), - msgop(), msync(), open(), poll(), readlink(), recv(), rename(), - rmdir(), select(), semget(), send(), shmget(), sigaltstack(), + ioperm(), link(), mbind(), mincore(), mkdir(), mknod(), mlock(), + mmap(), mprotect(), mremap(), msync(), open(), poll(), readlink(), + recv(), rename(), rmdir(), select(), send(), sigaltstack(), splice(), stat(), subpage_prot(), swapon(), symlink(), sync_file_range(), tee(), timer_create(), timerfd_create(), unlink(). - - `EPERM`: Operation not permitted. Raised by accept(), chmod(), - chown(), chroot(), copy_file_range(), execve(), fallocate(), - fanotify_init(), fcntl(), futex(), get_robust_list(), - getdomainname(), getgroups(), gethostname(), getpriority(), - getrlimit(), getsid(), gettimeofday(), idle(), init_module(), - io_submit(), ioctl_console(), ioctl_ficlonerange(), - ioctl_fideduperange(), ioctl_ns(), ioctl_tty(), ioperm(), iopl(), - ioprio_set(), kcmp(), kexec_load(), keyctl(), kill(), link(), - lookup_dcookie(), madvise(), mbind(), membarrier(), migrate_pages(), - mkdir(), mknod(), mlock(), mmap(), mount(), move_pages(), msgctl(), - nice(), open(), open_by_handle_at(), pciconfig_read(), - perf_event_open(), pidfd_getfd(), pidfd_send_signal(), pivot_root(), - prctl(), process_vm_readv(), ptrace(), quotactl(), reboot(), - rename(), request_key(), rmdir(), rt_sigqueueinfo(), - sched_setaffinity(), sched_setattr(), sched_setparam(), - sched_setscheduler(), semctl(), seteuid(), setfsgid(), setfsuid(), - setgid(), setns(), setpgid(), setresuid(), setreuid(), setsid(), - setuid(), setup(), setxattr(), shmctl(), shmget(), sigaltstack(), + unix.EPERM + Operation not permitted. + + Raised by accept(), chmod(), chown(), chroot(), copy_file_range(), + execve(), fallocate(), fanotify_init(), fcntl(), futex(), + get_robust_list(), getdomainname(), getgroups(), gethostname(), + getpriority(), getrlimit(), getsid(), gettimeofday(), idle(), + init_module(), io_submit(), ioctl_console(), ioctl_ficlonerange(), + ioctl_fideduperange(), ioperm(), iopl(), ioprio_set(), keyctl(), + kill(), link(), lookup_dcookie(), madvise(), mbind(), membarrier(), + migrate_pages(), mkdir(), mknod(), mlock(), mmap(), mount(), + move_pages(), msgctl(), nice(), open(), open_by_handle_at(), + pciconfig_read(), perf_event_open(), pidfd_getfd(), + pidfd_send_signal(), pivot_root(), prctl(), process_vm_readv(), + ptrace(), quotactl(), reboot(), rename(), request_key(), rmdir(), + rt_sigqueueinfo(), sched_setaffinity(), sched_setattr(), + sched_setparam(), sched_setscheduler(), seteuid(), setfsgid(), + setfsuid(), setgid(), setns(), setpgid(), setresuid(), setreuid(), + setsid(), setuid(), setup(), setxattr(), sigaltstack(), spu_create(), stime(), swapon(), symlink(), syslog(), truncate(), unlink(), utime(), utimensat(), write() - - `ENOTBLK`: Block device required. Raised by umount(). + unix.ENOTBLK + Block device required. - - `EBUSY`: Device or resource busy. Raised by bdflush(), dup(), - fcntl(), msync(), prctl(), ptrace(), rename(), rmdir(). + Raised by umount(). - - `EEXIST`: File exists. Raised by bpf(), create_module(), - inotify_add_watch(), link(), mkdir(), mknod(), mmap(), msgget(), - open(), rename(), rmdir(), semget(), shmget(), symlink() + unix.EBUSY + Device or resource busy. - - `EXDEV`: Improper link. Raised by copy_file_range(), link(), - rename() + Raised by dup(), fcntl(), msync(), prctl(), ptrace(), rename(), + rmdir(). - - `ENODEV`: No such device. Raised by arch_prctl(), eventfd(), mmap(), - open(), prctl(), timerfd_create() + unix.EEXIST + File exists. - - `EISDIR`: Is a a directory. Raised by copy_file_range(), execve(), - open(), read(), rename(), truncate(), unlink(). + Raised by inotify_add_watch(), link(), mkdir(), mknod(), mmap(), + open(), rename(), rmdir(), symlink() - - `ENFILE`: Too many open files in system. Raised by accept(), - eventfd(), execve(), inotify_init(), memfd_create(), mmap(), open(), - pipe(), shmget(), socket(), socketpair(), swapon(), - timerfd_create(), uselib(), userfaultfd(). + unix.EXDEV + Improper link. - - `EMFILE`: Too many open files. Raised by accept(), dup(), eventfd(), - execve(), fanotify_init(), fcntl(), inotify_init(), memfd_create(), - open(), pipe(), socket(), socketpair(), timerfd_create(). + Raised by copy_file_range(), link(), rename(). - - `ENOTTY`: Inappropriate i/o control operation. Raised by ioctl(). + unix.ENODEV + No such device. + + Raised by arch_prctl(), mmap(), open(), prctl(), timerfd_create(). + + unix.EISDIR + Is a a directory. + + Raised by copy_file_range(), execve(), open(), read(), rename(), + truncate(), unlink(). + + unix.ENFILE + Too many open files in system. + + Raised by accept(), execve(), inotify_init(), mmap(), open(), + pipe(), socket(), socketpair(), swapon(), timerfd_create(), + uselib(), userfaultfd(). + + unix.EMFILE + Too many open files. + + Raised by accept(), dup(), execve(), fanotify_init(), fcntl(), + inotify_init(), open(), pipe(), socket(), socketpair(), + timerfd_create(). + + unix.ENOTTY + Inappropriate i/o control operation. + + Raised by ioctl(). + + unix.ETXTBSY + Won't open executable that's executing in write mode. - - `ETXTBSY`: Won't open executable that's executing in write mode. Raised by access(), copy_file_range(), execve(), mmap(), open(), truncate(). - - `EFBIG`: File too large. Raised by copy_file_range(), open(), - truncate(), write(). + unix.EFBIG + File too large. - - `ENOSPC`: No space left on device. Raised by copy_file_range(), - fsync(), inotify_add_watch(), link(), mkdir(), mknod(), msgget(), - open(), rename(), semget(), shmget(), symlink(), sync_file_range(), + Raised by copy_file_range(), open(), truncate(), write(). + + unix.ENOSPC + No space left on device. + + Raised by copy_file_range(), fsync(), inotify_add_watch(), link(), + mkdir(), mknod(), open(), rename(), symlink(), sync_file_range(), write(). - - `EDQUOT`: Disk quota exceeded. Raised by link(), mkdir(), mknod(), - open(), rename(), symlink(), write() + unix.EDQUOT + Disk quota exceeded. - - `ESPIPE`: Invalid seek. Raised by lseek(), splice(), - sync_file_range(). - - - `EROFS`: Read-only filesystem. Raised by access(), bind(), chmod(), - chown(), link(), mkdir(), mknod(), open(), rename(), rmdir(), - symlink(), truncate(), unlink(), utime(), utimensat() - - - `EMLINK`: Too many links; raised by link(), mkdir(), rename() - - - `ERANGE`: Result too large. Raised by prctl(), semop(). - - - `EDEADLK`: Resource deadlock avoided. Raised by fcntl(). - - - `ENOLCK`: No locks available. Raised by fcntl(), flock(). - - - `ENOTEMPTY`: Directory not empty. Raised by rmdir(). - - - `ELOOP`: Too many levels of symbolic links. Raised by access(), - bind(), chdir(), chmod(), chown(), chroot(), execve(), link(), - mkdir(), mknod(), open(), readlink(), rename(), rmdir(), stat(), - symlink(), truncate(), unlink(), utimensat(). - - - `ENOMSG`: Raised by msgop(). - - - `EIDRM`: Identifier removed. Raised by msgctl(), msgget(), msgop(), - shmget(). - - - `ETIME`: Timer expired; timer expired. Raised by connect(). - - - `EPROTO`: Raised by accept(), connect(), socket(), socketpair(). - - - `EOVERFLOW`: Raised by copy_file_range(), fanotify_init(), lseek(), - mmap(), open(), stat(), statfs() - - - `ENOTSOCK`: Not a socket. Raised by accept(), bind(), connect(), - getpeername(), getsockname(), getsockopt(), listen(), recv(), - send(), shutdown(). - - - `EDESTADDRREQ`: Destination address required. Raised by send(), + Raised by link(), mkdir(), mknod(), open(), rename(), symlink(), write(). - - `EMSGSIZE`: Message too long. Raised by send(). + unix.ESPIPE + Invalid seek. - - `EPROTOTYPE`: Protocol wrong type for socket. Raised by connect(). + Raised by lseek(), splice(), sync_file_range(). - - `ENOPROTOOPT`: Protocol not available. Raised by getsockopt(), - accept(). + unix.EROFS + Read-only filesystem. - - `EPROTONOSUPPORT`: Protocol not supported. Raised by socket(), + Raised by access(), bind(), chmod(), chown(), link(), mkdir(), + mknod(), open(), rename(), rmdir(), symlink(), truncate(), unlink(), + utime(), utimensat(). + + unix.EMLINK + Too many links; + + raised by link(), mkdir(), rename(). + + unix.ERANGE + Result too large. + + Raised by prctl(). + + unix.EDEADLK + Resource deadlock avoided. + + Raised by fcntl(). + + unix.ENOLCK + No locks available. + + Raised by fcntl(), flock(). + + unix.ENOTEMPTY + Directory not empty. Raised by rmdir(). + + unix.ELOOP + Too many levels of symbolic links. Raised by access(), bind(), + chdir(), chmod(), chown(), chroot(), execve(), link(), mkdir(), + mknod(), open(), readlink(), rename(), rmdir(), stat(), symlink(), + truncate(), unlink(), utimensat(). + + unix.ENOMSG + Raised by msgop(). + + unix.EIDRM + Identifier removed. + + Raised by msgctl(). + + unix.ETIME + Timer expired; timer expired. + + Raised by connect(). + + unix.EPROTO + Raised by accept(), connect(), socket(), socketpair(). + + unix.EOVERFLOW + Raised by copy_file_range(), fanotify_init(), lseek(), mmap(), + open(), stat(), statfs() + + unix.ENOTSOCK + Not a socket. + + Raised by accept(), bind(), connect(), getpeername(), getsockname(), + getsockopt(), listen(), recv(), send(), shutdown(). + + unix.EDESTADDRREQ + Destination address required. + + Raised by send(), write(). + + unix.EMSGSIZE + Message too long. + + Raised by send(). + + unix.EPROTOTYPE + Protocol wrong type for socket. + + Raised by connect(). + + unix.ENOPROTOOPT + Protocol not available. + + Raised by getsockopt(), accept(). + + unix.EPROTONOSUPPORT + Protocol not supported. + + Raised by socket(), socketpair(). + + unix.ESOCKTNOSUPPORT + Socket type not supported. + + unix.ENOTSUP + Operation not supported. + + Raised by chmod(), clock_getres(), clock_nanosleep(), + timer_create(). + + unix.EOPNOTSUPP + Socket operation not supported. + + Raised by accept(), listen(), mmap(), prctl(), readv(), send(), socketpair(). - - `ESOCKTNOSUPPORT`: Socket type not supported. + unix.EPFNOSUPPORT + Protocol family not supported. - - `ENOTSUP`: Operation not supported. Raised by chmod(), - clock_getres(), clock_nanosleep(), timer_create() + unix.EAFNOSUPPORT + Address family not supported. - - `EOPNOTSUPP`: Socket operation not supported. Raised by accept(), - listen(), mmap(), prctl(), readv(), send(), socketpair(), + Raised by connect(), socket(), socketpair() - - `EPFNOSUPPORT`: protocol family not supported + unix.EADDRINUSE + Address already in use. - - `EAFNOSUPPORT`: address family not supported. Raised by connect(), - socket(), socketpair() + Raised by bind(), connect(), listen() - - `EADDRINUSE`: address already in use. Raised by bind(), connect(), - listen() + unix.EADDRNOTAVAIL + Address not available. - - `EADDRNOTAVAIL`: address not available. Raised by bind(), connect(). + Raised by bind(), connect(). - - `ENETDOWN`: network is down; ; WSAENETDOWN. Raised by accept() + unix.ENETDOWN + Network is down. - - `ENETUNREACH`: host is unreachable; ; WSAENETUNREACH. Raised by - accept(), connect() + Raised by accept() - - `ENETRESET`: connection reset by network + unix.ENETUNREACH + Host is unreachable. - - `ECONNABORTED`: connection reset before accept. Raised by accept() + Raised by accept(), connect() - - `ECONNRESET`: connection reset by client. Raised by send(), + unix.ENETRESET + Connection reset by network. - - `ENOBUFS`: no buffer space available; raised by getpeername(), - getsockname(), send(), + unix.ECONNABORTED + Connection reset before accept. - - `EISCONN`: socket is connected. Raised by connect(), send(). + Raised by accept(). - - `ENOTCONN`: socket is not connected. Raised by getpeername(), - recv(), send(), shutdown(), + unix.ECONNRESET + Connection reset by client. - - `ESHUTDOWN`: cannot send after transport endpoint shutdown; note - that shutdown write is an `EPIPE` + Raised by send(). - - `ETOOMANYREFS`: too many references: cannot splice. Raised by - sendmsg(), + unix.ENOBUFS + No buffer space available; - - `ETIMEDOUT`: connection timed out; ; WSAETIMEDOUT; raised by - connect(), + raised by getpeername(), getsockname(), send(). - - `ECONNREFUSED`: system-imposed limit on the number of threads was - encountered.; WSAECONNREFUSED. Raised by connect(), listen(), recv() + unix.EISCONN + Socket is connected. - - `EHOSTDOWN`: Host is down. Raised by accept() + Raised by connect(), send(). - - `EHOSTUNREACH`: Host is unreachable. Raised by accept() + unix.ENOTCONN + Socket is not connected. - - `EALREADY`: Connection already in progress. Raised by connect(), - send() + Raised by getpeername(), recv(), send(), shutdown(). - - `ENODATA`: No message is available in xsi stream or named pipe is - being closed; no data available; barely in posix; returned by ioctl; - very close in spirit to EPIPE? + unix.ESHUTDOWN + Cannot send after transport endpoint shutdown; note that shutdown + write is an `EPIPE`. + unix.ETOOMANYREFS + Too many references: cannot splice. + + Raised by sendmsg(). + + unix.ETIMEDOUT + Connection timed out; ; WSAETIMEDOUT; + + raised by connect(). + + unix.ECONNREFUSED + System-imposed limit on the number of threads was encountered. + + Raised by connect(), listen(), recv() + + unix.EHOSTDOWN + Host is down. + + Raised by accept() + + unix.EHOSTUNREACH + Host is unreachable. + + Raised by accept() + + unix.EALREADY + Connection already in progress. + + Raised by connect(), send() + + unix.ENODATA + No message is available in xsi stream or named pipe is being closed; + no data available; barely in posix; returned by ioctl; very close in + spirit to EPIPE? + +──────────────────────────────────────────────────────────────────────────────── LUA ENHANCEMENTS We've made some enhancements to the Lua language that should make it - more comfortable for C/C++ and Python developers. Some of these + more comfortable for C/C++ and Python developers. Some of these - redbean supports a printf modulus operator, like Python. For example, you can say `"hello %s" % {"world"}` instead of @@ -2691,6 +3482,7 @@ LUA ENHANCEMENTS - redbean supports the GNU syntax for the ASCII ESC character in string literals. For example, `"\e"` is the same as `"\x1b"`. +──────────────────────────────────────────────────────────────────────────────── SEE ALSO diff --git a/tool/net/lfuncs.c b/tool/net/lfuncs.c index d1be7df68..d828985ba 100644 --- a/tool/net/lfuncs.c +++ b/tool/net/lfuncs.c @@ -33,6 +33,7 @@ #include "libc/nexgen32e/rdtscp.h" #include "libc/rand/rand.h" #include "libc/runtime/gc.internal.h" +#include "libc/runtime/sysconf.h" #include "libc/sock/sock.h" #include "libc/sysv/consts/af.h" #include "libc/sysv/consts/rusage.h" @@ -82,6 +83,11 @@ int LuaGetCpuCore(lua_State *L) { return 1; } +int LuaGetCpuCount(lua_State *L) { + lua_pushinteger(L, GetCpuCount()); + return 1; +} + int LuaGetLogLevel(lua_State *L) { lua_pushinteger(L, __log_level); return 1; diff --git a/tool/net/lfuncs.h b/tool/net/lfuncs.h index b731d8a03..991ec9eea 100644 --- a/tool/net/lfuncs.h +++ b/tool/net/lfuncs.h @@ -35,6 +35,7 @@ int LuaEscapeUser(lua_State *); int LuaFormatHttpDateTime(lua_State *); int LuaFormatIp(lua_State *); int LuaGetCpuCore(lua_State *); +int LuaGetCpuCount(lua_State *); int LuaGetCpuNode(lua_State *); int LuaGetCryptoHash(lua_State *); int LuaGetHostOs(lua_State *); diff --git a/tool/net/lunix.c b/tool/net/lunix.c index cc3b47d10..f1b1fc567 100644 --- a/tool/net/lunix.c +++ b/tool/net/lunix.c @@ -17,10 +17,13 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" +#include "libc/bits/bits.h" #include "libc/calls/calls.h" #include "libc/calls/ioctl.h" +#include "libc/calls/makedev.h" #include "libc/calls/sigbits.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/struct/bpf.h" #include "libc/calls/struct/dirent.h" #include "libc/calls/struct/itimerval.h" #include "libc/calls/struct/siginfo.h" @@ -34,14 +37,18 @@ #include "libc/fmt/fmt.h" #include "libc/fmt/magnumstrs.internal.h" #include "libc/intrin/kprintf.h" -#include "libc/log/check.h" #include "libc/log/log.h" #include "libc/macros.internal.h" #include "libc/mem/fmt.h" #include "libc/mem/mem.h" +#include "libc/nt/runtime.h" +#include "libc/runtime/clktck.h" +#include "libc/runtime/memtrack.internal.h" #include "libc/runtime/runtime.h" #include "libc/sock/sock.h" #include "libc/sock/syslog.h" +#include "libc/stdio/append.internal.h" +#include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/sysv/consts/af.h" #include "libc/sysv/consts/at.h" @@ -52,6 +59,7 @@ #include "libc/sysv/consts/ip.h" #include "libc/sysv/consts/ipproto.h" #include "libc/sysv/consts/itimer.h" +#include "libc/sysv/consts/limits.h" #include "libc/sysv/consts/log.h" #include "libc/sysv/consts/msg.h" #include "libc/sysv/consts/nr.h" @@ -70,10 +78,12 @@ #include "libc/sysv/consts/tcp.h" #include "libc/sysv/consts/w.h" #include "libc/sysv/errfuns.h" +#include "libc/time/struct/tm.h" #include "libc/time/time.h" #include "libc/x/x.h" #include "third_party/lua/cosmo.h" #include "third_party/lua/lauxlib.h" +#include "third_party/lua/lgc.h" #include "third_party/lua/lua.h" #include "third_party/lua/luaconf.h" #include "tool/net/luacheck.h" @@ -95,11 +105,44 @@ struct UnixStat { struct UnixErrno { int refs; - int errno; + uint16_t errno; + uint16_t winerr; + const char *call; }; static lua_State *GL; +static void *LuaUnixRealloc(lua_State *L, void *p, size_t n) { + void *p2; + if ((p2 = realloc(p, n))) { + return p2; + } + if (IsLegalSize(n)) { + luaC_fullgc(L, 1); + p2 = realloc(p, n); + } + return p2; +} + +static void *LuaUnixAllocRaw(lua_State *L, size_t n) { + return LuaUnixRealloc(L, 0, n); +} + +static void *LuaUnixAlloc(lua_State *L, size_t n) { + void *p; + if ((p = LuaUnixAllocRaw(L, n))) { + bzero(p, n); + } + return p; +} + +static void LuaPushSigset(lua_State *L, struct sigset set) { + struct sigset *sp; + sp = lua_newuserdatauv(L, sizeof(*sp), 1); + luaL_setmetatable(L, "unix.Sigset"); + *sp = set; +} + static void LuaSetIntField(lua_State *L, const char *k, lua_Integer v) { lua_pushinteger(L, v); lua_setfield(L, -2, k); @@ -110,45 +153,42 @@ static dontinline int ReturnInteger(lua_State *L, lua_Integer x) { return 1; } +static dontinline int ReturnBoolean(lua_State *L, int x) { + lua_pushboolean(L, !!x); + return 1; +} + static dontinline int ReturnString(lua_State *L, const char *x) { lua_pushstring(L, x); return 1; } -static void LuaUnixPushErrno(lua_State *L, int err) { +static void LuaUnixPushErrno(lua_State *L, const char *sc, int uerr, int werr) { struct UnixErrno *ue, **uep; - ue = xcalloc(1, sizeof(struct UnixErrno)); + ue = LuaUnixAlloc(L, sizeof(*ue)); ue->refs = 1; - ue->errno = err; + ue->call = sc; + ue->errno = uerr; + ue->winerr = werr; uep = lua_newuserdatauv(L, sizeof(*uep), 1); luaL_setmetatable(L, "unix.Errno"); *uep = ue; } -static dontinline int SysretErrnoImpl(lua_State *L, int olderr, bool usebool) { - int i, newerr = errno; - if (!IsTiny() && !(0 < newerr && newerr < (!IsWindows() ? 4096 : 65536))) { - WARNF("errno should not be %d", newerr); +static dontinline int SysretErrno(lua_State *L, const char *call, int olderr) { + int i, unixerr, winerr; + unixerr = errno; + winerr = GetLastError(); + if (!IsTiny() && !(0 < unixerr && unixerr < (!IsWindows() ? 4096 : 65536))) { + WARNF("errno should not be %d", unixerr); } - if (usebool) { - lua_pushboolean(L, false); - } else { - lua_pushnil(L); - } - LuaUnixPushErrno(L, newerr); + lua_pushnil(L); + LuaUnixPushErrno(L, call, unixerr, winerr); errno = olderr; return 2; } -static dontinline int SysretErrnoNil(lua_State *L, int olderr) { - return SysretErrnoImpl(L, olderr, false); -} - -static dontinline int SysretErrnoBool(lua_State *L, int olderr) { - return SysretErrnoImpl(L, olderr, true); -} - -static int SysretBool(lua_State *L, int rc, int olderr) { +static int SysretBool(lua_State *L, const char *call, int olderr, int rc) { if (!IsTiny() && (rc != 0 && rc != -1)) { WARNF("syscall supposed to return 0 / -1 but got %d", rc); } @@ -156,11 +196,12 @@ static int SysretBool(lua_State *L, int rc, int olderr) { lua_pushboolean(L, true); return 1; } else { - return SysretErrnoBool(L, olderr); + return SysretErrno(L, call, olderr); } } -static int SysretInteger(lua_State *L, int64_t rc, int olderr) { +static int SysretInteger(lua_State *L, const char *call, int olderr, + int64_t rc) { if (rc != -1) { if (!IsTiny() && olderr != errno) { WARNF("errno unexpectedly changed %d → %d", olderr, errno); @@ -168,7 +209,7 @@ static int SysretInteger(lua_State *L, int64_t rc, int olderr) { lua_pushinteger(L, rc); return 1; } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, call, olderr); } } @@ -196,7 +237,7 @@ static char **ConvertLuaArrayToStringList(lua_State *L, int i) { lua_len(L, i); n = lua_tointeger(L, -1); lua_pop(L, 1); - if ((p = calloc(n + 1, sizeof(*p)))) { + if ((p = LuaUnixAllocRaw(L, (n + 1) * sizeof(*p)))) { for (j = 1; j <= n; ++j) { lua_geti(L, i, j); s = strdup(lua_tostring(L, -1)); @@ -209,6 +250,7 @@ static char **ConvertLuaArrayToStringList(lua_State *L, int i) { break; } } + p[j - 1] = 0; } return p; } @@ -216,130 +258,203 @@ static char **ConvertLuaArrayToStringList(lua_State *L, int i) { //////////////////////////////////////////////////////////////////////////////// // System Calls -static dontinline int LuaUnixGetid(lua_State *L, int f(void)) { - return ReturnInteger(L, f()); -} - -// unix.getpid() → pid:int -static int LuaUnixGetpid(lua_State *L) { - return LuaUnixGetid(L, getpid); -} - -// unix.getppid() → pid:int -static int LuaUnixGetppid(lua_State *L) { - return LuaUnixGetid(L, getppid); -} - -// unix.getuid() → uid:int -static int LuaUnixGetuid(lua_State *L) { - return LuaUnixGetid(L, getuid); -} - -// unix.getgid() → gid:int -static int LuaUnixGetgid(lua_State *L) { - return LuaUnixGetid(L, getgid); -} - -// unix.geteuid() → uid:int -static int LuaUnixGeteuid(lua_State *L) { - return LuaUnixGetid(L, geteuid); -} - -// unix.getegid() → gid:int -static int LuaUnixGetegid(lua_State *L) { - return LuaUnixGetid(L, getegid); -} - -// unix.umask(mask:int) → oldmask:int -static int LuaUnixUmask(lua_State *L) { - return ReturnInteger(L, umask(luaL_checkinteger(L, 1))); -} - -// unix.exit([exitcode:int]) → ⊥ +// unix.exit([exitcode:int]) +// └─→ ⊥ static wontreturn int LuaUnixExit(lua_State *L) { _Exit(luaL_optinteger(L, 1, 0)); } -// unix.access(path:str, how:int) → ok:bool, unix.Errno -// how can be: R_OK, W_OK, X_OK, F_OK -static int LuaUnixAccess(lua_State *L) { - int olderr = errno; - return SysretBool(L, access(luaL_checkstring(L, 1), luaL_checkinteger(L, 2)), - olderr); +static dontinline int LuaUnixGetid(lua_State *L, int f(void)) { + return ReturnInteger(L, f()); } -// unix.mkdir(path:str[, mode:int]) → ok:bool, unix.Errno -// mode should be octal +// unix.getpid() +// └─→ pid:int +static int LuaUnixGetpid(lua_State *L) { + return LuaUnixGetid(L, getpid); +} + +// unix.getppid() +// └─→ pid:int +static int LuaUnixGetppid(lua_State *L) { + return LuaUnixGetid(L, getppid); +} + +// unix.getuid() +// └─→ uid:int +static int LuaUnixGetuid(lua_State *L) { + return LuaUnixGetid(L, getuid); +} + +// unix.getgid() +// └─→ gid:int +static int LuaUnixGetgid(lua_State *L) { + return LuaUnixGetid(L, getgid); +} + +// unix.geteuid() +// └─→ uid:int +static int LuaUnixGeteuid(lua_State *L) { + return LuaUnixGetid(L, geteuid); +} + +// unix.getegid() +// └─→ gid:int +static int LuaUnixGetegid(lua_State *L) { + return LuaUnixGetid(L, getegid); +} + +// unix.umask(newmask:int) +// └─→ oldmask:int +static int LuaUnixUmask(lua_State *L) { + return ReturnInteger(L, umask(luaL_checkinteger(L, 1))); +} + +// unix.access(path:str, how:int[, flags:int[, dirfd:int]]) +// ├─→ true +// └─→ nil, unix.Errno +static int LuaUnixAccess(lua_State *L) { + int olderr = errno; + return SysretBool( + L, "access", olderr, + faccessat(luaL_optinteger(L, 3, AT_FDCWD), luaL_checkstring(L, 1), + luaL_checkinteger(L, 2), luaL_optinteger(L, 4, 0))); +} + +// unix.mkdir(path:str[, mode:int[, dirfd:int]]) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixMkdir(lua_State *L) { int olderr = errno; return SysretBool( - L, mkdir(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0755)), olderr); + L, "mkdir", olderr, + mkdirat(luaL_optinteger(L, 3, AT_FDCWD), luaL_checkstring(L, 1), + luaL_optinteger(L, 2, 0755))); } -// unix.makedirs(path:str[, mode:int]) → ok:bool, unix.Errno -// mode should be octal +// unix.makedirs(path:str[, mode:int]) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixMakedirs(lua_State *L) { int olderr = errno; return SysretBool( - L, makedirs(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0755)), olderr); + L, "makedirs", olderr, + makedirs(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0755))); } -// unix.chdir(path:str) → ok:bool, unix.Errno +// unix.chdir(path:str) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixChdir(lua_State *L) { int olderr = errno; - return SysretBool(L, chdir(luaL_checkstring(L, 1)), olderr); + return SysretBool(L, "chdir", olderr, chdir(luaL_checkstring(L, 1))); } -// unix.unlink(path:str) → ok:bool, unix.Errno +// unix.unlink(path:str[, dirfd:int]) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixUnlink(lua_State *L) { int olderr = errno; - return SysretBool(L, unlink(luaL_checkstring(L, 1)), olderr); + return SysretBool( + L, "unlink", olderr, + unlinkat(luaL_optinteger(L, 2, AT_FDCWD), luaL_checkstring(L, 1), 0)); } -// unix.rmdir(path:str) → ok:bool, unix.Errno +// unix.rmdir(path:str[, dirfd:int]) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixRmdir(lua_State *L) { int olderr = errno; - return SysretBool(L, rmdir(luaL_checkstring(L, 1)), olderr); + return SysretBool(L, "rmdir", olderr, + unlinkat(luaL_optinteger(L, 2, AT_FDCWD), + luaL_checkstring(L, 1), AT_REMOVEDIR)); } -// unix.rename(oldpath:str, newpath:str) → ok:bool, unix.Errno +// unix.rename(oldpath:str, newpath:str[, olddirfd:int, newdirfd:int]) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixRename(lua_State *L) { int olderr = errno; - return SysretBool(L, rename(luaL_checkstring(L, 1), luaL_checkstring(L, 2)), - olderr); + return SysretBool( + L, "rename", olderr, + renameat(luaL_optinteger(L, 3, AT_FDCWD), luaL_checkstring(L, 1), + luaL_optinteger(L, 4, AT_FDCWD), luaL_checkstring(L, 2))); } -// unix.link(existingpath:str, newpath:str) → ok:bool, unix.Errno +// unix.link(existingpath:str, newpath:str[, flags:int[, olddirfd, newdirfd]]) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixLink(lua_State *L) { int olderr = errno; - return SysretBool(L, link(luaL_checkstring(L, 1), luaL_checkstring(L, 2)), - olderr); + return SysretBool( + L, "link", olderr, + linkat(luaL_optinteger(L, 4, AT_FDCWD), luaL_checkstring(L, 1), + luaL_optinteger(L, 5, AT_FDCWD), luaL_checkstring(L, 2), + luaL_optinteger(L, 3, 0))); } -// unix.symlink(target:str, linkpath:str) → ok:bool, unix.Errno +// unix.symlink(target:str, linkpath:str[, newdirfd:int]) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixSymlink(lua_State *L) { int olderr = errno; - return SysretBool(L, symlink(luaL_checkstring(L, 1), luaL_checkstring(L, 2)), - olderr); + return SysretBool( + L, "symlink", olderr, + symlinkat(luaL_checkstring(L, 1), luaL_optinteger(L, 3, AT_FDCWD), + luaL_checkstring(L, 2))); } -// unix.chown(path:str, uid:int, gid:int) → ok:bool, unix.Errno +// unix.chown(path:str, uid:int, gid:int[, flags:int[, dirfd:int]]) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixChown(lua_State *L) { int olderr = errno; - return SysretBool(L, - chown(luaL_checkstring(L, 1), luaL_checkinteger(L, 2), - luaL_checkinteger(L, 3)), - olderr); + return SysretBool( + L, "chown", olderr, + fchownat(luaL_optinteger(L, 5, AT_FDCWD), luaL_checkstring(L, 1), + luaL_checkinteger(L, 2), luaL_checkinteger(L, 3), + luaL_optinteger(L, 4, 0))); } -// unix.chmod(path:str, mode:int) → ok:bool, unix.Errno +// unix.chmod(path:str, mode:int[, flags:int[, dirfd:int]]) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixChmod(lua_State *L) { int olderr = errno; - return SysretBool(L, chmod(luaL_checkstring(L, 1), luaL_checkinteger(L, 2)), - olderr); + return SysretBool( + L, "chmod", olderr, + fchmodat(luaL_optinteger(L, 4, AT_FDCWD), luaL_checkstring(L, 1), + luaL_checkinteger(L, 2), luaL_optinteger(L, 3, 0))); } -// unix.getcwd() → path:str, unix.Errno +// unix.readlink(path:str[, dirfd:int]) +// ├─→ content:str +// └─→ nil, unix.Errno +static int LuaUnixReadlink(lua_State *L) { + char *buf; + ssize_t rc; + int olderr = errno; + size_t got, bufsiz = 8192; + if ((buf = LuaUnixAllocRaw(L, bufsiz))) { + if ((rc = readlinkat(luaL_optinteger(L, 2, AT_FDCWD), + luaL_checkstring(L, 1), buf, bufsiz)) != -1) { + got = rc; + if (got < bufsiz) { + lua_pushlstring(L, buf, got); + free(buf); + return 1; + } else { + enametoolong(); + } + } + free(buf); + } + return SysretErrno(L, "readlink", olderr); +} + +// unix.getcwd() +// ├─→ path:str +// └─→ nil, unix.Errno static int LuaUnixGetcwd(lua_State *L) { int olderr; char *path; @@ -349,17 +464,21 @@ static int LuaUnixGetcwd(lua_State *L) { free(path); return 1; } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "getcwd", olderr); } } -// unix.fork() → childpid|0:int, unix.Errno +// unix.fork() +// ├─┬─→ 0 +// │ └─→ childpid:int +// └─→ nil, unix.Errno static int LuaUnixFork(lua_State *L) { int olderr = errno; - return SysretInteger(L, fork(), olderr); + return SysretInteger(L, "fork", olderr, fork()); } -// unix.environ() → {str,...} +// unix.environ() +// └─→ {str,...} static int LuaUnixEnviron(lua_State *L) { int i; char **e; @@ -371,7 +490,8 @@ static int LuaUnixEnviron(lua_State *L) { return 1; } -// unix.execve(prog:str[, args:List<*>, env:List<*>]) → false, unix.Errno +// unix.execve(prog:str[, args:List<*>, env:List<*>]) +// └─→ nil, unix.Errno static int LuaUnixExecve(lua_State *L) { int olderr; const char *prog; @@ -386,14 +506,14 @@ static int LuaUnixExecve(lua_State *L) { freeme2 = envp; } else { FreeStringList(argv); - return SysretErrnoBool(L, olderr); + return SysretErrno(L, "execve", olderr); } } else { envp = environ; freeme2 = 0; } } else { - return SysretErrnoBool(L, olderr); + return SysretErrno(L, "execve", olderr); } } else { ezargs[0] = prog; @@ -406,31 +526,35 @@ static int LuaUnixExecve(lua_State *L) { execve(prog, argv, envp); FreeStringList(freeme1); FreeStringList(freeme2); - return SysretErrnoBool(L, olderr); + return SysretErrno(L, "execve", olderr); } -// unix.commandv(prog:str) → path:str, unix.Errno +// unix.commandv(prog:str) +// ├─→ path:str +// └─→ nil, unix.Errno static int LuaUnixCommandv(lua_State *L) { int olderr; const char *prog; char *pathbuf, *resolved; olderr = errno; prog = luaL_checkstring(L, 1); - if ((pathbuf = malloc(PATH_MAX + 1))) { + if ((pathbuf = LuaUnixAllocRaw(L, PATH_MAX + 1))) { if ((resolved = commandv(prog, pathbuf, PATH_MAX + 1))) { lua_pushstring(L, resolved); free(pathbuf); return 1; } else { free(pathbuf); - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "commandv", olderr); } } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "commandv", olderr); } } -// unix.realpath(path:str) → path:str, unix.Errno +// unix.realpath(path:str) +// ├─→ path:str +// └─→ nil, unix.Errno static int LuaUnixRealpath(lua_State *L) { char *resolved; int olderr; @@ -442,7 +566,7 @@ static int LuaUnixRealpath(lua_State *L) { free(resolved); return 1; } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "realpath", olderr); } } @@ -452,24 +576,29 @@ static int LuaUnixSyslog(lua_State *L) { return 0; } -// unix.chroot(path:str) → ok:bool, unix.Errno +// unix.chroot(path:str) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixChroot(lua_State *L) { int olderr = errno; - return SysretBool(L, chroot(luaL_checkstring(L, 1)), olderr); + return SysretBool(L, "chroot", olderr, chroot(luaL_checkstring(L, 1))); } -// unix.setrlimit(resource:int, soft:int[, hard:int]) → ok:bool, unix.Errno +// unix.setrlimit(resource:int, soft:int[, hard:int]) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixSetrlimit(lua_State *L) { int olderr = errno; int64_t soft = luaL_checkinteger(L, 2); return SysretBool( - L, + L, "setrlimit", olderr, setrlimit(luaL_checkinteger(L, 1), - &(struct rlimit){soft, luaL_optinteger(L, 3, soft)}), - olderr); + &(struct rlimit){soft, luaL_optinteger(L, 3, soft)})); } -// unix.getrlimit(resource:int) → soft:int, unix.Errno, hard:int +// unix.getrlimit(resource:int) +// ├─→ soft:int, hard:int +// └─→ nil, unix.Errno static int LuaUnixGetrlimit(lua_State *L) { struct rlimit rlim; int rc, olderr, resource; @@ -477,51 +606,58 @@ static int LuaUnixGetrlimit(lua_State *L) { resource = luaL_checkinteger(L, 1); if (!getrlimit(resource, &rlim)) { lua_pushinteger(L, rlim.rlim_cur < RLIM_INFINITY ? rlim.rlim_cur : -1); - lua_pushnil(L); lua_pushinteger(L, rlim.rlim_max < RLIM_INFINITY ? rlim.rlim_max : -1); return 3; } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "getrlimit", olderr); } } -// unix.kill(pid:int, sig:int) → ok:bool, unix.Errno +// unix.kill(pid:int, sig:int) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixKill(lua_State *L) { int olderr = errno; - return SysretBool(L, kill(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2)), - olderr); + return SysretBool(L, "kill", olderr, + kill(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2))); } -// unix.raise(sig:int) → rc:int, unix.Errno +// unix.raise(sig:int) +// ├─→ rc:int +// └─→ nil, unix.Errno static int LuaUnixRaise(lua_State *L) { int olderr = errno; - return SysretInteger(L, raise(luaL_checkinteger(L, 1)), olderr); + return SysretInteger(L, "raise", olderr, raise(luaL_checkinteger(L, 1))); } -// unix.wait([pid:int, options:int]) → pid:int, unix.Errno, wstatus:int +// unix.wait([pid:int, options:int]) +// ├─→ pid:int, wstatus:int +// └─→ nil, unix.Errno static int LuaUnixWait(lua_State *L) { int pid, wstatus, olderr = errno; if ((pid = wait4(luaL_optinteger(L, 1, -1), &wstatus, luaL_optinteger(L, 2, 0), 0)) != -1) { lua_pushinteger(L, pid); - lua_pushnil(L); lua_pushinteger(L, wstatus); return 3; } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "wait", olderr); } } -// unix.fcntl(fd:int, cmd:int[, arg:int]) → rc:int, unix.Errno +// unix.fcntl(fd:int, cmd:int[, arg:int]) +// ├─→ true, ... +// └─→ nil, unix.Errno static int LuaUnixFcntl(lua_State *L) { int olderr = errno; - return SysretInteger(L, - fcntl(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2), - luaL_optinteger(L, 3, 0)), - olderr); + return SysretBool(L, "fcntl", olderr, + fcntl(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2), + luaL_optinteger(L, 3, 0))); } -// unix.dup(oldfd:int[, newfd:int[, flags:int]]) → newfd:int, unix.Errno +// unix.dup(oldfd:int[, newfd:int[, flags:int]]) +// ├─→ newfd:int +// └─→ nil, unix.Errno static int LuaUnixDup(lua_State *L) { int rc, oldfd, newfd, flags, olderr; olderr = errno; @@ -533,111 +669,133 @@ static int LuaUnixDup(lua_State *L) { } else { rc = dup3(oldfd, newfd, flags); } - return SysretInteger(L, rc, olderr); + return SysretInteger(L, "dup", olderr, rc); } -// unix.pipe([flags:int]) → reader:int, unix.Errno, writer:int +// unix.pipe([flags:int]) +// ├─→ reader:int, writer:int +// └─→ nil, unix.Errno static int LuaUnixPipe(lua_State *L) { int pipefd[2], olderr = errno; if (!pipe2(pipefd, luaL_optinteger(L, 1, 0))) { lua_pushinteger(L, pipefd[0]); - lua_pushnil(L); lua_pushinteger(L, pipefd[1]); return 2; } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "pipe", olderr); } } -// unix.getsid(pid) → sid:int, unix.Errno +// unix.getsid(pid:int) +// ├─→ sid:int +// └─→ nil, unix.Errno static int LuaUnixGetsid(lua_State *L) { int olderr = errno; - return SysretInteger(L, getsid(luaL_checkinteger(L, 1)), olderr); + return SysretInteger(L, "getsid", olderr, getsid(luaL_checkinteger(L, 1))); } -static dontinline int LuaUnixRc0(lua_State *L, int f(void)) { +static dontinline int LuaUnixRc0(lua_State *L, const char *call, int f(void)) { int olderr = errno; - return SysretInteger(L, f(), olderr); + return SysretInteger(L, call, olderr, f()); } -// unix.getpgrp() → pgid:int, unix.Errno +// unix.getpgrp() +// ├─→ pgid:int +// └─→ nil, unix.Errno static int LuaUnixGetpgrp(lua_State *L) { - return LuaUnixRc0(L, getpgrp); + return LuaUnixRc0(L, "getpgrp", getpgrp); } -// unix.setpgrp() → pgid:int, unix.Errno +// unix.setpgrp() +// ├─→ pgid:int +// └─→ nil, unix.Errno static int LuaUnixSetpgrp(lua_State *L) { - return LuaUnixRc0(L, setpgrp); + return LuaUnixRc0(L, "setpgrp", setpgrp); } -// unix.setsid() → sid:int, unix.Errno +// unix.setsid() +// ├─→ sid:int +// └─→ nil, unix.Errno static int LuaUnixSetsid(lua_State *L) { - return LuaUnixRc0(L, setsid); + return LuaUnixRc0(L, "setsid", setsid); } -// unix.getpgid(pid:int) → pgid:int, unix.Errno +// unix.getpgid(pid:int) +// ├─→ pgid:int +// └─→ nil, unix.Errno static int LuaUnixGetpgid(lua_State *L) { int olderr = errno; - return SysretInteger(L, getpgid(luaL_checkinteger(L, 1)), olderr); + return SysretInteger(L, "getpgid", olderr, getpgid(luaL_checkinteger(L, 1))); } -// unix.setpgid(pid:int, pgid:int) → pgid:int, unix.Errno +// unix.setpgid(pid:int, pgid:int) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixSetpgid(lua_State *L) { int olderr = errno; - return SysretInteger( - L, setpgid(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2)), olderr); + return SysretBool(L, "setpgid", olderr, + setpgid(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2))); } -static dontinline int LuaUnixSetid(lua_State *L, int f(int)) { +static dontinline int LuaUnixSetid(lua_State *L, const char *call, int f(int)) { int olderr = errno; - return SysretBool(L, f(luaL_checkinteger(L, 1)), olderr); + return SysretBool(L, call, olderr, f(luaL_checkinteger(L, 1))); } -// unix.setuid(uid:int) → ok:bool, unix.Errno +// unix.setuid(uid:int) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixSetuid(lua_State *L) { - return LuaUnixSetid(L, setuid); + return LuaUnixSetid(L, "setuid", setuid); } -// unix.setgid(gid:int) → ok:bool, unix.Errno +// unix.setgid(gid:int) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixSetgid(lua_State *L) { - return LuaUnixSetid(L, setgid); + return LuaUnixSetid(L, "setgid", setgid); } -static dontinline int LuaUnixSetresid(lua_State *L, +static dontinline int LuaUnixSetresid(lua_State *L, const char *call, int f(uint32_t, uint32_t, uint32_t)) { int olderr = errno; - return SysretBool(L, + return SysretBool(L, call, olderr, f(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2), - luaL_checkinteger(L, 3)), - olderr); + luaL_checkinteger(L, 3))); } -// unix.setresuid(real:int, effective:int, saved:int) → ok:bool, unix.Errno +// unix.setresuid(real:int, effective:int, saved:int) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixSetresuid(lua_State *L) { - return LuaUnixSetresid(L, setresuid); + return LuaUnixSetresid(L, "setresuid", setresuid); } -// unix.setresgid(real:int, effective:int, saved:int) → ok:bool, unix.Errno +// unix.setresgid(real:int, effective:int, saved:int) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixSetresgid(lua_State *L) { - return LuaUnixSetresid(L, setresgid); + return LuaUnixSetresid(L, "setresgid", setresgid); } -// unix.clock_gettime([clock:int]) → seconds:int, unix.Errno, nanos:int +// unix.clock_gettime([clock:int]) +// ├─→ seconds:int, nanos:int +// └─→ nil, unix.Errno static int LuaUnixGettime(lua_State *L) { struct timespec ts; int rc, olderr = errno; if (!clock_gettime(luaL_optinteger(L, 1, CLOCK_REALTIME), &ts)) { lua_pushinteger(L, ts.tv_sec); - lua_pushnil(L); lua_pushinteger(L, ts.tv_nsec); - return 3; + return 2; } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "clock_gettime", olderr); } } // unix.nanosleep(seconds:int, nanos:int) -// → remseconds:int, unix.Errno, remnanos:int +// ├─→ remseconds:int, remnanos:int +// └─→ nil, unix.Errno static int LuaUnixNanosleep(lua_State *L) { int olderr = errno; struct timespec req, rem; @@ -645,11 +803,10 @@ static int LuaUnixNanosleep(lua_State *L) { req.tv_nsec = luaL_optinteger(L, 2, 0); if (!nanosleep(&req, &rem)) { lua_pushinteger(L, rem.tv_sec); - lua_pushnil(L); lua_pushinteger(L, rem.tv_nsec); - return 3; + return 2; } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "nanosleep", olderr); } } @@ -659,68 +816,85 @@ static int LuaUnixSync(lua_State *L) { return 0; } -// unix.fsync(fd:int) → ok:bool, unix.Errno +// unix.fsync(fd:int) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixFsync(lua_State *L) { int olderr = errno; - return SysretBool(L, fsync(luaL_checkinteger(L, 1)), olderr); + return SysretBool(L, "fsync", olderr, fsync(luaL_checkinteger(L, 1))); } -// unix.fdatasync(fd:int) → ok:bool, unix.Errno +// unix.fdatasync(fd:int) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixFdatasync(lua_State *L) { int olderr = errno; - return SysretBool(L, fdatasync(luaL_checkinteger(L, 1)), olderr); + return SysretBool(L, "fdatasync", olderr, fdatasync(luaL_checkinteger(L, 1))); } -// unix.open(path:str, flags:int[, mode:int]) → fd:int, unix.Errno +// unix.open(path:str, flags:int[, mode:int[, dirfd:int]]) +// ├─→ fd:int +// └─→ nil, unix.Errno static int LuaUnixOpen(lua_State *L) { int olderr = errno; - return SysretInteger(L, - open(luaL_checkstring(L, 1), luaL_checkinteger(L, 2), - luaL_optinteger(L, 3, 0)), - olderr); + return SysretInteger( + L, "open", olderr, + openat(luaL_optinteger(L, 4, AT_FDCWD), luaL_checkstring(L, 1), + luaL_checkinteger(L, 2), luaL_optinteger(L, 3, 0))); } -// unix.close(fd:int) → ok:bool, unix.Errno +// unix.close(fd:int) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixClose(lua_State *L) { int olderr = errno; - return SysretBool(L, close(luaL_checkinteger(L, 1)), olderr); + return SysretBool(L, "close", olderr, close(luaL_checkinteger(L, 1))); } -// unix.lseek(fd:int, offset:int[, whence:int]) → newpos:int, unix.Errno +// unix.lseek(fd:int, offset:int[, whence:int]) +// ├─→ newposbytes:int +// └─→ nil, unix.Errno static int LuaUnixLseek(lua_State *L) { int olderr = errno; - return SysretInteger(L, + return SysretInteger(L, "lseek", olderr, lseek(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2), - luaL_optinteger(L, 3, SEEK_SET)), - olderr); + luaL_optinteger(L, 3, SEEK_SET))); } -// unix.truncate(path:str[, length:int]) → ok:bool, unix.Errno +// unix.truncate(path:str[, length:int]) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixTruncate(lua_State *L) { int olderr = errno; - return SysretBool( - L, truncate(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0)), olderr); + return SysretBool(L, "truncate", olderr, + truncate(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0))); } -// unix.ftruncate(fd:int[, length:int]) → ok:bool, unix.Errno +// unix.ftruncate(fd:int[, length:int]) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixFtruncate(lua_State *L) { int olderr = errno; return SysretBool( - L, ftruncate(luaL_checkinteger(L, 1), luaL_optinteger(L, 2, 0)), olderr); + L, "ftruncate", olderr, + ftruncate(luaL_checkinteger(L, 1), luaL_optinteger(L, 2, 0))); } -// unix.read(fd:int[, bufsiz:str, offset:int]) → data:str, unix.Errno +// unix.read(fd:int[, bufsiz:str[, offset:int]]) +// ├─→ data:str +// └─→ nil, unix.Errno static int LuaUnixRead(lua_State *L) { char *buf; size_t got; + ssize_t rc; int fd, olderr; - int64_t rc, bufsiz, offset; + lua_Integer bufsiz, offset; olderr = errno; fd = luaL_checkinteger(L, 1); bufsiz = luaL_optinteger(L, 2, BUFSIZ); offset = luaL_optinteger(L, 3, -1); bufsiz = MIN(bufsiz, 0x7ffff000); - if ((buf = malloc(bufsiz))) { + if ((buf = LuaUnixAllocRaw(L, bufsiz))) { if (offset == -1) { rc = read(fd, buf, bufsiz); } else { @@ -733,19 +907,22 @@ static int LuaUnixRead(lua_State *L) { return 1; } else { free(buf); - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "read", olderr); } } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "read", olderr); } } -// unix.write(fd:int, data:str[, offset:int]) → rc:int, unix.Errno +// unix.write(fd:int, data:str[, offset:int]) +// ├─→ wrotebytes:int +// └─→ nil, unix.Errno static int LuaUnixWrite(lua_State *L) { + ssize_t rc; size_t size; int fd, olderr; const char *data; - int64_t rc, offset; + lua_Integer offset; olderr = errno; fd = luaL_checkinteger(L, 1); data = luaL_checklstring(L, 2, &size); @@ -756,7 +933,7 @@ static int LuaUnixWrite(lua_State *L) { } else { rc = pwrite(fd, data, size, offset); } - return SysretInteger(L, rc, olderr); + return SysretInteger(L, "write", olderr, rc); } static int ReturnStat(lua_State *L, struct UnixStat *ust) { @@ -768,72 +945,40 @@ static int ReturnStat(lua_State *L, struct UnixStat *ust) { return 1; } -// unix.stat(path:str) → unix.Stat, unix.Errno +// unix.stat(path:str[, flags:int[, dirfd:int]]) +// ├─→ unix.Stat +// └─→ nil, unix.Errno static int LuaUnixStat(lua_State *L) { const char *path; - int olderr = errno; struct UnixStat *ust; + int dirfd, flags, olderr = errno; path = luaL_checkstring(L, 1); - if ((ust = malloc(sizeof(*ust)))) { - if (!stat(path, &ust->st)) { + flags = luaL_optinteger(L, 2, 0); + dirfd = luaL_optinteger(L, 3, AT_FDCWD); + if ((ust = LuaUnixAllocRaw(L, sizeof(*ust)))) { + if (!fstatat(dirfd, path, &ust->st, flags)) { return ReturnStat(L, ust); } free(ust); } - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "stat", olderr); } -// unix.fstat(fd:int) → unix.Stat, unix.Errno +// unix.fstat(fd:int) +// ├─→ unix.Stat +// └─→ nil, unix.Errno static int LuaUnixFstat(lua_State *L) { int fd, olderr = errno; struct UnixStat *ust; olderr = errno; fd = luaL_checkinteger(L, 1); - if ((ust = malloc(sizeof(*ust)))) { + if ((ust = LuaUnixAllocRaw(L, sizeof(*ust)))) { if (!fstat(fd, &ust->st)) { return ReturnStat(L, ust); } free(ust); } - return SysretErrnoNil(L, olderr); -} - -static int ReturnDir(lua_State *L, struct UnixDir *udir) { - struct UnixDir **udirp; - udir->refs = 1; - udirp = lua_newuserdatauv(L, sizeof(*udirp), 1); - luaL_setmetatable(L, "unix.Dir"); - *udirp = udir; - return 1; -} - -// unix.opendir(path:str) → unix.Dir, unix.Errno -static int LuaUnixOpendir(lua_State *L) { - int olderr = errno; - const char *path; - struct UnixDir *udir; - path = luaL_checkstring(L, 1); - if ((udir = calloc(1, sizeof(*udir)))) { - if ((udir->dir = opendir(path))) { - return ReturnDir(L, udir); - } - free(udir); - } - return SysretErrnoNil(L, olderr); -} - -// unix.fdopendir(fd:int) → unix.Dir, unix.Errno -static int LuaUnixFdopendir(lua_State *L) { - int fd, olderr = errno; - struct UnixDir *udir; - fd = luaL_checkinteger(L, 1); - if ((udir = calloc(1, sizeof(*udir)))) { - if ((udir->dir = fdopendir(fd))) { - return ReturnDir(L, udir); - } - free(udir); - } - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "fstat", olderr); } static bool IsSockoptBool(int l, int x) { @@ -893,157 +1038,167 @@ static bool IsSockoptTimeval(int l, int x) { } } -// unix.setsockopt(fd:int, level:int, optname:int, ...) -// → ok:bool, unix.Errno static int LuaUnixSetsockopt(lua_State *L) { + void *optval; struct linger l; + uint32_t optsize; struct timeval tv; - int rc, fd, level, optname, optval, olderr = errno; + int rc, fd, level, optname, optint, olderr = errno; fd = luaL_checkinteger(L, 1); level = luaL_checkinteger(L, 2); optname = luaL_checkinteger(L, 3); if (IsSockoptBool(level, optname)) { - optval = lua_toboolean(L, 4); - return SysretBool( - L, setsockopt(fd, level, optname, &optval, sizeof(optval)), olderr); + // unix.setsockopt(fd:int, level:int, optname:int, value:bool) + // ├─→ true + // └─→ nil, unix.Errno + optint = lua_toboolean(L, 4); + optval = &optint; + optsize = sizeof(optint); } else if (IsSockoptInt(level, optname)) { - optval = luaL_checkinteger(L, 4); - return SysretBool( - L, setsockopt(fd, level, optname, &optval, sizeof(optval)), olderr); + // unix.setsockopt(fd:int, level:int, optname:int, value:int) + // ├─→ true + // └─→ nil, unix.Errno + optint = luaL_checkinteger(L, 4); + optval = &optint; + optsize = sizeof(optint); } else if (IsSockoptTimeval(level, optname)) { + // unix.setsockopt(fd:int, level:int, optname:int, secs:int[, nanos:int]) + // ├─→ true + // └─→ nil, unix.Errno tv.tv_sec = luaL_checkinteger(L, 4); - tv.tv_usec = luaL_optinteger(L, 5, 0); - return SysretBool(L, setsockopt(fd, level, optname, &tv, sizeof(tv)), - olderr); + optval = &tv; + optsize = sizeof(tv); } else if (level == SOL_SOCKET && optname == SO_LINGER) { - l.l_onoff = lua_toboolean(L, 4); - l.l_linger = luaL_checkinteger(L, 5); - return SysretBool(L, setsockopt(fd, level, optname, &l, sizeof(l)), olderr); + // unix.setsockopt(fd:int, level:int, optname:int, secs:int, enabled:bool) + // ├─→ true + // └─→ nil, unix.Errno + l.l_linger = luaL_checkinteger(L, 4); + l.l_onoff = lua_toboolean(L, 5); + optval = &l; + optsize = sizeof(l); } else { einval(); - return SysretErrnoBool(L, olderr); + return SysretErrno(L, "setsockopt", olderr); } + return SysretBool(L, "setsockopt", olderr, + setsockopt(fd, level, optname, optval, optsize)); } static int LuaUnixGetsockopt(lua_State *L) { + uint32_t size; struct linger l; struct timeval tv; - uint32_t lsize, tvsize, optvalsize; int rc, fd, level, optname, optval, olderr = errno; fd = luaL_checkinteger(L, 1); level = luaL_checkinteger(L, 2); optname = luaL_checkinteger(L, 3); - if (IsSockoptBool(level, optname)) { + if (IsSockoptBool(level, optname) || IsSockoptInt(level, optname)) { // unix.getsockopt(fd:int, level:int, optname:int) - // → bool, unix.Errno - optvalsize = sizeof(optval); - if (getsockopt(fd, level, optname, &optval, &optvalsize) != -1) { - CheckOptvalsize(L, sizeof(optval), optvalsize); - lua_pushboolean(L, optval); - return 1; - } else { - return SysretErrnoNil(L, olderr); - } - } else if (IsSockoptInt(level, optname)) { - optvalsize = sizeof(optval); - if (getsockopt(fd, level, optname, &optval, &optvalsize) != -1) { - CheckOptvalsize(L, sizeof(optval), optvalsize); + // ├─→ value:int + // └─→ nil, unix.Errno + size = sizeof(optval); + if (getsockopt(fd, level, optname, &optval, &size) != -1) { + CheckOptvalsize(L, sizeof(optval), size); lua_pushinteger(L, optval); return 1; - } else { - return SysretErrnoNil(L, olderr); } } else if (IsSockoptTimeval(level, optname)) { - tvsize = sizeof(tv); - if (getsockopt(fd, level, optname, &tv, &tvsize) != -1) { - CheckOptvalsize(L, sizeof(tv), tvsize); + // unix.getsockopt(fd:int, level:int, optname:int) + // ├─→ secs:int, nsecs:int + // └─→ nil, unix.Errno + size = sizeof(tv); + if (getsockopt(fd, level, optname, &tv, &size) != -1) { + CheckOptvalsize(L, sizeof(tv), size); lua_pushinteger(L, tv.tv_sec); - lua_pushnil(L); - lua_pushinteger(L, tv.tv_usec); - return 3; - } else { - return SysretErrnoNil(L, olderr); + lua_pushinteger(L, tv.tv_usec * 1000); + return 2; } } else if (level == SOL_SOCKET && optname == SO_LINGER) { - lsize = sizeof(l); - if (getsockopt(fd, level, optname, &l, &lsize) != -1) { - CheckOptvalsize(L, sizeof(l), lsize); - lua_pushinteger(L, l.l_onoff); - lua_pushnil(L); + // unix.getsockopt(fd:int, unix.SOL_SOCKET, unix.SO_LINGER) + // ├─→ seconds:int, enabled:bool + // └─→ nil, unix.Errno + size = sizeof(l); + if (getsockopt(fd, level, optname, &l, &size) != -1) { + CheckOptvalsize(L, sizeof(l), size); lua_pushinteger(L, l.l_linger); - return 3; - } else { - return SysretErrnoNil(L, olderr); + lua_pushboolean(L, !!l.l_onoff); + return 1; } } else { einval(); - return SysretErrnoNil(L, olderr); } + return SysretErrno(L, "getsockopt", olderr); } -// unix.socket([family:int[, type:int[, protocol:int]]]) → fd:int[, -// unix.Errno] +// unix.socket([family:int[, type:int[, protocol:int]]]) +// ├─→ fd:int +// └─→ nil, unix.Errno static int LuaUnixSocket(lua_State *L) { int olderr = errno; return SysretInteger( - L, + L, "socket", olderr, socket(luaL_optinteger(L, 1, AF_INET), luaL_optinteger(L, 2, SOCK_STREAM), - luaL_optinteger(L, 3, IPPROTO_TCP)), - olderr); + luaL_optinteger(L, 3, IPPROTO_TCP))); } // unix.socketpair([family:int[, type:int[, protocol:int]]]) -// → fd1:int, unix.Errno, fd2:int +// ├─→ fd1:int, fd2:int +// └─→ nil, unix.Errno static int LuaUnixSocketpair(lua_State *L) { int sv[2], olderr = errno; if (!socketpair(luaL_optinteger(L, 1, AF_INET), luaL_optinteger(L, 2, SOCK_STREAM), luaL_optinteger(L, 3, IPPROTO_TCP), sv)) { lua_pushinteger(L, sv[0]); - lua_pushnil(L); lua_pushinteger(L, sv[1]); return 2; } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "socketpair", olderr); } } -// unix.bind(fd:int[, ip:uint32, port:uint16]) → ok:bool, unix.Errno +// unix.bind(fd:int[, ip:uint32, port:uint16]) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixBind(lua_State *L) { int olderr = errno; - return SysretBool(L, + return SysretBool(L, "bind", olderr, bind(luaL_checkinteger(L, 1), &(struct sockaddr_in){ .sin_family = AF_INET, .sin_addr.s_addr = htonl(luaL_optinteger(L, 2, 0)), .sin_port = htons(luaL_optinteger(L, 3, 0)), }, - sizeof(struct sockaddr_in)), - olderr); + sizeof(struct sockaddr_in))); } -// unix.connect(fd:int, ip:uint32, port:uint16) → ok:bool, unix.Errno +// unix.connect(fd:int, ip:uint32, port:uint16) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixConnect(lua_State *L) { int olderr = errno; return SysretBool( - L, + L, "connect", olderr, connect(luaL_checkinteger(L, 1), &(struct sockaddr_in){ .sin_addr.s_addr = htonl(luaL_checkinteger(L, 2)), .sin_port = htons(luaL_checkinteger(L, 3)), }, - sizeof(struct sockaddr_in)), - olderr); + sizeof(struct sockaddr_in))); } -// unix.listen(fd:int[, backlog:int]) → ok:bool, unix.Errno +// unix.listen(fd:int[, backlog:int]) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixListen(lua_State *L) { int olderr = errno; - return SysretBool( - L, listen(luaL_checkinteger(L, 1), luaL_optinteger(L, 2, 10)), olderr); + return SysretBool(L, "listen", olderr, + listen(luaL_checkinteger(L, 1), luaL_optinteger(L, 2, 10))); } -// unix.getsockname(fd:int) → ip:uint32, unix.Errno, port:uint16 +// unix.getsockname(fd:int) +// ├─→ ip:uint32, port:uint16 +// └─→ nil, unix.Errno static int LuaUnixGetsockname(lua_State *L) { int fd, olderr; uint32_t addrsize; @@ -1053,15 +1208,16 @@ static int LuaUnixGetsockname(lua_State *L) { fd = luaL_checkinteger(L, 1); if (!getsockname(fd, &sa, &addrsize)) { lua_pushinteger(L, ntohl(sa.sin_addr.s_addr)); - lua_pushnil(L); lua_pushinteger(L, ntohs(sa.sin_port)); - return 3; + return 2; } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "getsockname", olderr); } } -// unix.getpeername(fd:int) → ip:uint32, unix.Errno, port:uint16 +// unix.getpeername(fd:int) +// ├─→ ip:uint32, port:uint16 +// └─→ nil, unix.Errno static int LuaUnixGetpeername(lua_State *L) { int fd, olderr; uint32_t addrsize; @@ -1071,16 +1227,16 @@ static int LuaUnixGetpeername(lua_State *L) { fd = luaL_checkinteger(L, 1); if (!getpeername(fd, &sa, &addrsize)) { lua_pushinteger(L, ntohl(sa.sin_addr.s_addr)); - lua_pushnil(L); lua_pushinteger(L, ntohs(sa.sin_port)); - return 3; + return 2; } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "getpeername", olderr); } } // unix.siocgifconf() -// → {{name:str,ip:uint32,netmask:uint32}, ...}, unix.Errno +// ├─→ {{name:str,ip:uint32,netmask:uint32}, ...} +// └─→ nil, unix.Errno static int LuaUnixSiocgifconf(lua_State *L) { size_t n; char *data; @@ -1088,19 +1244,19 @@ static int LuaUnixSiocgifconf(lua_State *L) { struct ifreq *ifr; struct ifconf conf; olderr = errno; - if (!(data = malloc((n = 4096)))) { - return SysretErrnoNil(L, olderr); + if (!(data = LuaUnixAllocRaw(L, (n = 4096)))) { + return SysretErrno(L, "siocgifconf", olderr); } if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)) == -1) { free(data); - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "siocgifconf", olderr); } conf.ifc_buf = data; conf.ifc_len = n; if (ioctl(fd, SIOCGIFCONF, &conf) == -1) { close(fd); free(data); - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "siocgifconf", olderr); } lua_newtable(L); i = 0; @@ -1127,7 +1283,9 @@ static int LuaUnixSiocgifconf(lua_State *L) { return 1; } -// unix.gethostname() → host:str, unix.Errno +// unix.gethostname() +// ├─→ host:str +// └─→ nil, unix.Errno static int LuaUnixGethostname(lua_State *L) { int rc, olderr; char buf[DNS_NAME_MAX + 1]; @@ -1138,15 +1296,14 @@ static int LuaUnixGethostname(lua_State *L) { return 1; } else { enomem(); - return SysretErrnoNil(L, olderr); } - } else { - return SysretErrnoNil(L, olderr); } + return SysretErrno(L, "gethostname", olderr); } // unix.accept(serverfd:int[, flags:int]) -// → clientfd:int, unix.Errno, ip:uint32, port:uint16 +// ├─→ clientfd:int, ip:uint32, port:uint16 +// └─→ nil, unix.Errno static int LuaUnixAccept(lua_State *L) { uint32_t addrsize; struct sockaddr_in sa; @@ -1158,28 +1315,37 @@ static int LuaUnixAccept(lua_State *L) { clientfd = accept4(serverfd, &sa, &addrsize, flags); if (clientfd != -1) { lua_pushinteger(L, clientfd); - lua_pushnil(L); lua_pushinteger(L, ntohl(sa.sin_addr.s_addr)); lua_pushinteger(L, ntohs(sa.sin_port)); - return 4; + return 3; } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "accept", olderr); } } -// unix.poll({fd:int=events:int, ...}[, timeoutms:int]) -// → {fd:int=revents:int, ...}, unix.Errno +// unix.poll({[fd:int]=events:int, ...}[, timeoutms:int]) +// ├─→ {[fd:int]=revents:int, ...} +// └─→ nil, unix.Errno static int LuaUnixPoll(lua_State *L) { size_t nfds; - struct pollfd *fds; + struct pollfd *fds, *fds2; int i, fd, olderr, events, timeoutms; + olderr = errno; luaL_checktype(L, 1, LUA_TTABLE); lua_pushnil(L); for (fds = 0, nfds = 0; lua_next(L, 1);) { if (lua_isinteger(L, -2)) { - fds = xrealloc(fds, ++nfds * sizeof(*fds)); - fds[nfds - 1].fd = lua_tointeger(L, -2); - fds[nfds - 1].events = lua_tointeger(L, -1); + if ((fds2 = LuaUnixRealloc(L, fds, (nfds + 1) * sizeof(*fds)))) { + fds2[nfds].fd = lua_tointeger(L, -2); + fds2[nfds].events = lua_tointeger(L, -1); + fds = fds2; + ++nfds; + } else { + free(fds); + return SysretErrno(L, "poll", olderr); + } + } else { + // ignore non-integer key } lua_pop(L, 1); } @@ -1197,56 +1363,60 @@ static int LuaUnixPoll(lua_State *L) { return 1; } else { free(fds); - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "poll", olderr); } } -// unix.recvfrom(fd:int[, bufsiz:str[, flags:int]]) -// → data:str, unix.Errno, ip:uint32, port:uint16 +// unix.recvfrom(fd:int[, bufsiz:int[, flags:int]]) +// ├─→ data:str, ip:uint32, port:uint16 +// └─→ nil, unix.Errno static int LuaUnixRecvfrom(lua_State *L) { char *buf; size_t got; ssize_t rc; uint32_t addrsize; + lua_Integer bufsiz; struct sockaddr_in sa; - int fd, flags, bufsiz, olderr; + int fd, flags, olderr; olderr = errno; addrsize = sizeof(sa); fd = luaL_checkinteger(L, 1); bufsiz = luaL_optinteger(L, 2, 1500); flags = luaL_optinteger(L, 3, 0); bufsiz = MIN(bufsiz, 0x7ffff000); - if ((buf = malloc(bufsiz))) { + if ((buf = LuaUnixAllocRaw(L, bufsiz))) { rc = recvfrom(fd, buf, bufsiz, flags, &sa, &addrsize); if (rc != -1) { got = rc; lua_pushlstring(L, buf, got); - lua_pushnil(L); lua_pushinteger(L, ntohl(sa.sin_addr.s_addr)); lua_pushinteger(L, ntohs(sa.sin_port)); free(buf); - return 4; + return 3; } else { free(buf); - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "recvfrom", olderr); } } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "recvfrom", olderr); } } -// unix.recv(fd:int[, bufsiz:int[, flags:int]]) → data:str, unix.Errno +// unix.recv(fd:int[, bufsiz:int[, flags:int]]) +// ├─→ data:str +// └─→ nil, unix.Errno static int LuaUnixRecv(lua_State *L) { char *buf; size_t got; ssize_t rc; - int fd, flags, bufsiz, olderr, pushed; + lua_Integer bufsiz; + int fd, flags, olderr, pushed; olderr = errno; fd = luaL_checkinteger(L, 1); bufsiz = luaL_optinteger(L, 2, 1500); flags = luaL_optinteger(L, 3, 0); bufsiz = MIN(bufsiz, 0x7ffff000); - if ((buf = malloc(bufsiz))) { + if ((buf = LuaUnixAllocRaw(L, bufsiz))) { rc = recv(fd, buf, bufsiz, flags); if (rc != -1) { got = rc; @@ -1255,34 +1425,39 @@ static int LuaUnixRecv(lua_State *L) { return 1; } else { free(buf); - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "recv", olderr); } } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "recv", olderr); } } -// unix.send(fd:int, data:str[, flags:int]) → sent:int, unix.Errno +// unix.send(fd:int, data:str[, flags:int]) +// ├─→ sent:int +// └─→ nil, unix.Errno static int LuaUnixSend(lua_State *L) { char *data; ssize_t rc; size_t sent, size; - int fd, flags, bufsiz, olderr; + lua_Integer bufsiz; + int fd, flags, olderr; olderr = errno; fd = luaL_checkinteger(L, 1); data = luaL_checklstring(L, 2, &size); size = MIN(size, 0x7ffff000); flags = luaL_optinteger(L, 5, 0); - return SysretInteger(L, send(fd, data, size, flags), olderr); + return SysretInteger(L, "send", olderr, send(fd, data, size, flags)); } // unix.sendto(fd:int, data:str, ip:uint32, port:uint16[, flags:int]) -// → sent:int, unix.Errno +// ├─→ sent:int +// └─→ nil, unix.Errno static int LuaUnixSendto(lua_State *L) { char *data; size_t sent, size; + lua_Integer bufsiz; + int fd, flags, olderr; struct sockaddr_in sa; - int fd, flags, bufsiz, olderr; olderr = errno; bzero(&sa, sizeof(sa)); fd = luaL_checkinteger(L, 1); @@ -1291,70 +1466,64 @@ static int LuaUnixSendto(lua_State *L) { sa.sin_addr.s_addr = htonl(luaL_checkinteger(L, 3)); sa.sin_port = htons(luaL_checkinteger(L, 4)); flags = luaL_optinteger(L, 5, 0); - return SysretInteger(L, sendto(fd, data, size, flags, &sa, sizeof(sa)), - olderr); + return SysretInteger(L, "sendto", olderr, + sendto(fd, data, size, flags, &sa, sizeof(sa))); } -// unix.shutdown(fd:int, how:int) → ok:bool, unix.Errno +// unix.shutdown(fd:int, how:int) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixShutdown(lua_State *L) { int olderr = errno; - return SysretBool( - L, shutdown(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2)), olderr); + return SysretBool(L, "shutdown", olderr, + shutdown(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2))); } -// unix.sigprocmask(how:int[, mask:int]) → oldmask:int, unix.Errno +// unix.sigprocmask(how:int, newmask:unix.Sigset) +// ├─→ oldmask:unix.Sigset +// └─→ nil, unix.Errno static int LuaUnixSigprocmask(lua_State *L) { uint64_t imask; - int how, olderr; - sigset_t mask, oldmask, *maskptr; + int i, n, how, olderr; + struct sigset *newmask, oldmask; olderr = errno; how = luaL_checkinteger(L, 1); - if (lua_isnoneornil(L, 2)) { - // if mask isn't passed then we're querying - maskptr = 0; - } else { - maskptr = &mask; - imask = luaL_checkinteger(L, 2); - bzero(&mask, sizeof(mask)); - if (how == SIG_SETMASK) { - sigprocmask(how, 0, &mask); - } - mask.__bits[0] = imask; - } - if (!sigprocmask(how, maskptr, &oldmask)) { - lua_pushinteger(L, oldmask.__bits[0]); + newmask = luaL_checkudata(L, 2, "unix.Sigset"); + if (!sigprocmask(how, newmask, &oldmask)) { + LuaPushSigset(L, oldmask); return 1; } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "sigprocmask", olderr); } } static void LuaUnixOnSignal(int sig, siginfo_t *si, ucontext_t *ctx) { + int type; + lua_State *L = GL; + struct sigset ss, os; STRACE("LuaUnixOnSignal(%G)", sig); - lua_getglobal(GL, "__signal_handlers"); - CHECK_EQ(LUA_TFUNCTION, lua_geti(GL, -1, sig)); - lua_remove(GL, -2); - lua_pushinteger(GL, sig); - if (lua_pcall(GL, 1, 0, 0) != LUA_OK) { - ERRORF("(lua) %s failed: %s", strsignal(sig), lua_tostring(GL, -1)); - lua_pop(GL, 1); // pop error + lua_getglobal(L, "__signal_handlers"); + type = lua_rawgeti(L, -1, sig); + lua_remove(L, -2); // pop __signal_handlers + if (type == LUA_TFUNCTION) { + lua_pushinteger(L, sig); + if (lua_pcall(L, 1, 0, 0) != LUA_OK) { + sigfillset(&ss); + sigprocmask(SIG_BLOCK, &ss, &os); + ERRORF("(lua) %s failed: %s", strsignal(sig), lua_tostring(L, -1)); + sigprocmask(SIG_SETMASK, &os, 0); + lua_pop(L, 1); // pop error + } + } else { + lua_pop(L, 1); // pop handler } } -// unix.sigaction(sig:int[, handler:func|int[, flags:int[, mask:int]]]) -// → oldhandler:func|int, unix.Errno, flags:int, mask:int -// -// unix = require "unix" -// unix.sigaction(unix.SIGUSR1, function(sig) -// print(string.format("got %s", unix.strsignal(sig))) -// end) -// unix.sigprocmask(unix.SIG_SETMASK, -1) -// unix.raise(unix.SIGUSR1) -// unix.sigsuspend() -// -// handler can be SIG_IGN, SIG_DFL, intptr_t, or a Lua function -// sig can be SIGINT, SIGQUIT, SIGTERM, SIGUSR1, etc. +// unix.sigaction(sig:int[, handler:func|int[, flags:int[, mask:unix.Sigset]]]) +// ├─→ oldhandler:func|int, flags:int, mask:unix.Sigset +// └─→ nil, unix.Errno static int LuaUnixSigaction(lua_State *L) { + struct sigset *mask; int i, n, sig, olderr; struct sigaction sa, oldsa, *saptr; saptr = &sa; @@ -1394,14 +1563,18 @@ static int LuaUnixSigaction(lua_State *L) { unreachable; } sa.sa_flags = luaL_optinteger(L, 3, SA_RESTART); - sa.sa_mask.__bits[0] |= luaL_optinteger(L, 4, 0); + if (!lua_isnoneornil(L, 4)) { + mask = luaL_checkudata(L, 4, "unix.Sigset"); + sa.sa_mask.__bits[0] |= mask->__bits[0]; + sa.sa_mask.__bits[1] |= mask->__bits[1]; + } if (!sigaction(sig, saptr, &oldsa)) { lua_getglobal(L, "__signal_handlers"); // push the old handler result to stack. if the global lua handler // table has a real function, then we prefer to return that. if it's // absent or a raw integer value, then we're better off returning // what the kernel gave us in &oldsa. - if (lua_geti(L, -1, sig) != LUA_TFUNCTION) { + if (lua_rawgeti(L, -1, sig) != LUA_TFUNCTION) { lua_pop(L, 1); lua_pushinteger(L, (intptr_t)oldsa.sa_handler); } @@ -1412,29 +1585,36 @@ static int LuaUnixSigaction(lua_State *L) { } else { lua_pushnil(L); } - lua_seti(L, -3, sig); + lua_rawseti(L, -3, sig); } // remove the signal handler table from stack lua_remove(L, -2); - // finish pushing the last 3/4 results - lua_pushnil(L); + // finish pushing the last 2/3 results lua_pushinteger(L, oldsa.sa_flags); - lua_pushinteger(L, oldsa.sa_mask.__bits[0]); - return 4; + LuaPushSigset(L, oldsa.sa_mask); + return 3; } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "sigaction", olderr); } } -// unix.sigsuspend([mask]) → false, unix.Errno +// unix.sigsuspend([mask:Sigmask]) +// └─→ nil, unix.Errno static int LuaUnixSigsuspend(lua_State *L) { int olderr = errno; - sigsuspend(&(struct sigset){{luaL_optinteger(L, 1, 0)}}); - return SysretErrnoBool(L, olderr); + struct sigset *set; + if (!lua_isnoneornil(L, 1)) { + set = luaL_checkudata(L, 1, "unix.Sigset"); + } else { + set = 0; + } + sigsuspend(set); + return SysretErrno(L, "sigsuspend", olderr); } -// unix.setitimer(which[, intsec, intmicros, valsec, valmicros]) -// → intsec:int, unix.Errno, intns:int, valsec:int, valns:int +// unix.setitimer(which[, intervalsec, intns, valuesec, valuens]) +// ├─→ intervalsec:int, intervalns:int, valuesec:int, valuens:int +// └─→ nil, unix.Errno static int LuaUnixSetitimer(lua_State *L) { int which, olderr; struct itimerval it, oldit, *itptr; @@ -1443,21 +1623,20 @@ static int LuaUnixSetitimer(lua_State *L) { if (!lua_isnoneornil(L, 2)) { itptr = ⁢ it.it_interval.tv_sec = luaL_optinteger(L, 2, 0); - it.it_interval.tv_usec = luaL_optinteger(L, 3, 0); + it.it_interval.tv_usec = luaL_optinteger(L, 3, 0) / 1000; it.it_value.tv_sec = luaL_optinteger(L, 4, 0); - it.it_value.tv_usec = luaL_optinteger(L, 5, 0); + it.it_value.tv_usec = luaL_optinteger(L, 5, 0) / 1000; } else { itptr = 0; } if (!setitimer(which, itptr, &oldit)) { lua_pushinteger(L, oldit.it_interval.tv_sec); - lua_pushnil(L); - lua_pushinteger(L, oldit.it_interval.tv_usec); + lua_pushinteger(L, oldit.it_interval.tv_usec * 1000); lua_pushinteger(L, oldit.it_value.tv_sec); - lua_pushinteger(L, oldit.it_value.tv_usec); - return 5; + lua_pushinteger(L, oldit.it_value.tv_usec * 1000); + return 4; } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "setitimer", olderr); } } @@ -1465,46 +1644,104 @@ static dontinline int LuaUnixStr(lua_State *L, char *f(int)) { return ReturnString(L, f(luaL_checkinteger(L, 1))); } -// unix.strerdoc(errno) → str +// unix.strerdoc(errno:int) +// └─→ mediummessage:str static int LuaUnixStrerdoc(lua_State *L) { return LuaUnixStr(L, strerdoc); } -// unix.strsignal(sig) → str +// unix.strsignal(sig:int) +// └─→ symbol:str static int LuaUnixStrsignal(lua_State *L) { return LuaUnixStr(L, strsignal); } -// unix.strerror(errno) → str -static int LuaUnixStrerror(lua_State *L) { - return LuaUnixStr(L, strerror); -} - -// unix.strerrno(errno) → str|nil +// unix.strerrno(errno:int) +// └─→ symbol:int static int LuaUnixStrerrno(lua_State *L) { return LuaUnixStr(L, strerrno); } -// unix.WIFEXITED(wstatus) → int -static int LuaUnixWifexited(lua_State *L) { - return ReturnInteger(L, WIFEXITED(luaL_checkinteger(L, 1))); +// unix.strerror(errno:int) +// └─→ longmessage:str +static int LuaUnixStrerror(lua_State *L) { + return LuaUnixStr(L, strerror); } -// unix.WEXITSTATUS(wstatus) → int +// unix.WIFEXITED(wstatus) +// └─→ bool +static int LuaUnixWifexited(lua_State *L) { + return ReturnBoolean(L, WIFEXITED(luaL_checkinteger(L, 1))); +} + +// unix.WEXITSTATUS(wstatus) +// └─→ exitcode:uint8 static int LuaUnixWexitstatus(lua_State *L) { return ReturnInteger(L, WEXITSTATUS(luaL_checkinteger(L, 1))); } -// unix.WIFSIGNALED(wstatus) → int +// unix.WIFSIGNALED(wstatus) +// └─→ bool static int LuaUnixWifsignaled(lua_State *L) { - return ReturnInteger(L, WIFSIGNALED(luaL_checkinteger(L, 1))); + return ReturnBoolean(L, WIFSIGNALED(luaL_checkinteger(L, 1))); } -// unix.WTERMSIG(wstatus) → int +// unix.WTERMSIG(wstatus) +// └─→ sig:uint8 static int LuaUnixWtermsig(lua_State *L) { return ReturnInteger(L, WTERMSIG(luaL_checkinteger(L, 1))); } +static dontinline int LuaUnixTime(lua_State *L, const char *call, + struct tm *f(const time_t *, struct tm *)) { + int64_t ts; + struct tm tm; + int olderr = errno; + ts = luaL_checkinteger(L, 1); + if (f(&ts, &tm)) { + lua_pushinteger(L, tm.tm_year + 1900); + lua_pushinteger(L, tm.tm_mon + 1); // 1 ≤ mon ≤ 12 + lua_pushinteger(L, tm.tm_mday); // 1 ≤ mday ≤ 31 + lua_pushinteger(L, tm.tm_hour); // 0 ≤ hour ≤ 23 + lua_pushinteger(L, tm.tm_min); // 0 ≤ min ≤ 59 + lua_pushinteger(L, tm.tm_sec); // 0 ≤ sec ≤ 60 + lua_pushinteger(L, tm.tm_gmtoff); // ±93600 seconds + lua_pushinteger(L, tm.tm_wday); // 0 ≤ wday ≤ 6 + lua_pushinteger(L, tm.tm_yday); // 0 ≤ yday ≤ 365 + lua_pushinteger(L, tm.tm_isdst); // daylight savings + lua_pushstring(L, tm.tm_zone); + return 11; + } else { + return SysretErrno(L, call, olderr); + } +} + +// unix.gmtime(unixsecs:int) +// ├─→ year,mon,mday,hour,min,sec,gmtoffsec,wday,yday,dst:int,zone:str +// └─→ nil,unix.Errno +static int LuaUnixGmtime(lua_State *L) { + return LuaUnixTime(L, "gmtime", gmtime_r); +} + +// unix.localtime(unixts:int) +// ├─→ year,mon,mday,hour,min,sec,gmtoffsec,wday,yday,dst:int,zone:str +// └─→ nil,unix.Errno +static int LuaUnixLocaltime(lua_State *L) { + return LuaUnixTime(L, "localtime", localtime_r); +} + +// unix.major(rdev:int) +// └─→ major:int +static int LuaUnixMajor(lua_State *L) { + return ReturnInteger(L, major(luaL_checkinteger(L, 1))); +} + +// unix.minor(rdev:int) +// └─→ minor:int +static int LuaUnixMinor(lua_State *L) { + return ReturnInteger(L, minor(luaL_checkinteger(L, 1))); +} + //////////////////////////////////////////////////////////////////////////////// // unix.Stat object @@ -1514,42 +1751,62 @@ static dontinline struct stat *GetUnixStat(lua_State *L) { return &(*ust)->st; } +// unix.Stat:size() +// └─→ bytes:int static int LuaUnixStatSize(lua_State *L) { return ReturnInteger(L, GetUnixStat(L)->st_size); } +// unix.Stat:mode() +// └─→ mode:int static int LuaUnixStatMode(lua_State *L) { return ReturnInteger(L, GetUnixStat(L)->st_mode); } +// unix.Stat:dev() +// └─→ dev:int static int LuaUnixStatDev(lua_State *L) { return ReturnInteger(L, GetUnixStat(L)->st_dev); } +// unix.Stat:ino() +// └─→ inodeint static int LuaUnixStatIno(lua_State *L) { return ReturnInteger(L, GetUnixStat(L)->st_ino); } +// unix.Stat:nlink() +// └─→ count:int static int LuaUnixStatNlink(lua_State *L) { return ReturnInteger(L, GetUnixStat(L)->st_nlink); } +// unix.Stat:rdev() +// └─→ rdev:int static int LuaUnixStatRdev(lua_State *L) { return ReturnInteger(L, GetUnixStat(L)->st_rdev); } +// unix.Stat:uid() +// └─→ uid:int static int LuaUnixStatUid(lua_State *L) { return ReturnInteger(L, GetUnixStat(L)->st_uid); } +// unix.Stat:gid() +// └─→ gid:int static int LuaUnixStatGid(lua_State *L) { return ReturnInteger(L, GetUnixStat(L)->st_gid); } +// unix.Stat:blocks() +// └─→ count:int static int LuaUnixStatBlocks(lua_State *L) { return ReturnInteger(L, GetUnixStat(L)->st_blocks); } +// unix.Stat:blksize() +// └─→ bytes:int static int LuaUnixStatBlksize(lua_State *L) { return ReturnInteger(L, GetUnixStat(L)->st_blksize); } @@ -1560,22 +1817,42 @@ static dontinline int UnixStatTim(lua_State *L, struct timespec *ts) { return 2; } +// unix.Stat:atim() +// └─→ unixts:int, nanos:int static int LuaUnixStatAtim(lua_State *L) { return UnixStatTim(L, &GetUnixStat(L)->st_atim); } +// unix.Stat:mtim() +// └─→ unixts:int, nanos:int static int LuaUnixStatMtim(lua_State *L) { return UnixStatTim(L, &GetUnixStat(L)->st_mtim); } +// unix.Stat:ctim() +// └─→ unixts:int, nanos:int static int LuaUnixStatCtim(lua_State *L) { return UnixStatTim(L, &GetUnixStat(L)->st_ctim); } +// unix.Stat:birthtim() +// └─→ unixts:int, nanos:int static int LuaUnixStatBirthtim(lua_State *L) { return UnixStatTim(L, &GetUnixStat(L)->st_birthtim); } +// unix.Stat:gen() +// └─→ gen:int [xnu/bsd] +static int LuaUnixStatGen(lua_State *L) { + return ReturnInteger(L, GetUnixStat(L)->st_gen); +} + +// unix.Stat:flags() +// └─→ flags:int [xnu/bsd] +static int LuaUnixStatFlags(lua_State *L) { + return ReturnInteger(L, GetUnixStat(L)->st_flags); +} + static void FreeUnixStat(struct UnixStat *stat) { if (!--stat->refs) { free(stat); @@ -1613,6 +1890,8 @@ static const luaL_Reg kLuaUnixStatMeth[] = { {"rdev", LuaUnixStatRdev}, // {"size", LuaUnixStatSize}, // {"uid", LuaUnixStatUid}, // + {"flags", LuaUnixStatFlags}, // + {"gen", LuaUnixStatGen}, // {0}, // }; @@ -1635,32 +1914,37 @@ static void LuaUnixStatObj(lua_State *L) { // unix.Errno object static dontinline struct UnixErrno *GetUnixErrno(lua_State *L) { - struct UnixErrno **ue; - ue = luaL_checkudata(L, 1, "unix.Errno"); - return *ue; + struct UnixErrno **ep; + ep = luaL_checkudata(L, 1, "unix.Errno"); + return *ep; +} + +static int LuaUnixErrnoErrno(lua_State *L) { + return ReturnInteger(L, GetUnixErrno(L)->errno); +} + +static int LuaUnixErrnoWinerr(lua_State *L) { + return ReturnInteger(L, GetUnixErrno(L)->winerr); } static int LuaUnixErrnoName(lua_State *L) { - struct UnixErrno *e = GetUnixErrno(L); - lua_pushstring(L, strerrno(e->errno)); - return 1; + return ReturnString(L, strerrno(GetUnixErrno(L)->errno)); } static int LuaUnixErrnoDoc(lua_State *L) { - struct UnixErrno *e = GetUnixErrno(L); - lua_pushstring(L, strerdoc(e->errno)); - return 1; -} - -static int LuaUnixErrnoError(lua_State *L) { - struct UnixErrno *e = GetUnixErrno(L); - lua_pushstring(L, strerror(e->errno)); - return 1; + return ReturnString(L, strerdoc(GetUnixErrno(L)->errno)); } static int LuaUnixErrnoToString(lua_State *L) { - struct UnixErrno *e = GetUnixErrno(L); - lua_pushfstring(L, "error: system call failed: %s", strerror(e->errno)); + char msg[256]; + struct UnixErrno *e; + e = GetUnixErrno(L); + if (e->call) { + strerror_wr(e->errno, e->winerr, msg, sizeof(msg)); + lua_pushfstring(L, "%s() failed: %s", e->call, msg); + } else { + lua_pushstring(L, strerrno(e->errno)); + } return 1; } @@ -1681,10 +1965,12 @@ static int LuaUnixErrnoGc(lua_State *L) { } static const luaL_Reg kLuaUnixErrnoMeth[] = { - {"error", LuaUnixErrnoError}, // - {"name", LuaUnixErrnoName}, // - {"doc", LuaUnixErrnoDoc}, // - {0}, // + {"strerror", LuaUnixErrnoToString}, // + {"errno", LuaUnixErrnoErrno}, // + {"winerr", LuaUnixErrnoWinerr}, // + {"name", LuaUnixErrnoName}, // + {"doc", LuaUnixErrnoDoc}, // + {0}, // }; static const luaL_Reg kLuaUnixErrnoMeta[] = { @@ -1702,6 +1988,128 @@ static void LuaUnixErrnoObj(lua_State *L) { lua_pop(L, 1); } +//////////////////////////////////////////////////////////////////////////////// +// unix.Sigset object + +// unix.Sigset(sig:int, ...) +// └─→ unix.Sigset +static int LuaUnixSigset(lua_State *L) { + int i, n; + lua_Integer sig; + struct sigset set; + sigemptyset(&set); + n = lua_gettop(L); + for (i = 1; i <= n; ++i) { + sig = luaL_checkinteger(L, i); + if (1 <= sig && sig <= NSIG) { + set.__bits[(sig - 1) >> 6] |= 1ull << ((sig - 1) & 63); + } + } + LuaPushSigset(L, set); + return 1; +} + +// unix.Sigset:add(sig:int) +static int LuaUnixSigsetAdd(lua_State *L) { + lua_Integer sig; + struct sigset *set; + set = luaL_checkudata(L, 1, "unix.Sigset"); + sig = luaL_checkinteger(L, 2); + if (1 <= sig && sig <= NSIG) { + set->__bits[(sig - 1) >> 6] |= 1ull << ((sig - 1) & 63); + } + return 0; +} + +// unix.Sigset:remove(sig:int) +static int LuaUnixSigsetRemove(lua_State *L) { + lua_Integer sig; + struct sigset *set; + set = luaL_checkudata(L, 1, "unix.Sigset"); + sig = luaL_checkinteger(L, 2); + if (1 <= sig && sig <= NSIG) { + set->__bits[(sig - 1) >> 6] &= ~(1ull << ((sig - 1) & 63)); + } + return 0; +} + +// unix.Sigset:fill() +static int LuaUnixSigsetFill(lua_State *L) { + struct sigset *set; + set = luaL_checkudata(L, 1, "unix.Sigset"); + memset(set, -1, sizeof(*set)); + return 0; +} + +// unix.Sigset:clear() +static int LuaUnixSigsetClear(lua_State *L) { + struct sigset *set; + set = luaL_checkudata(L, 1, "unix.Sigset"); + bzero(set, sizeof(*set)); + return 0; +} + +// unix.Sigset:contains(sig:int) +// └─→ bool +static int LuaUnixSigsetContains(lua_State *L) { + lua_Integer sig; + struct sigset *set; + set = luaL_checkudata(L, 1, "unix.Sigset"); + sig = luaL_checkinteger(L, 2); + return ReturnBoolean( + L, (1 <= sig && sig <= NSIG) + ? !!(set->__bits[(sig - 1) >> 6] & (1ull << ((sig - 1) & 63))) + : false); +} + +static int LuaUnixSigsetTostring(lua_State *L) { + char *b = 0; + int sig, first; + struct sigset *ss; + ss = luaL_checkudata(L, 1, "unix.Sigset"); + appends(&b, "unix.Sigset"); + appendw(&b, '('); + for (sig = first = 1; sig <= NSIG; ++sig) { + if (sigismember(ss, sig) == 1) { + if (!first) { + appendw(&b, READ16LE(", ")); + } else { + first = 0; + } + appendw(&b, READ64LE("unix.\0\0")); + appends(&b, strsignal(sig)); + } + } + appendw(&b, ')'); + lua_pushlstring(L, b, appendz(b).i); + free(b); + return 1; +} + +static const luaL_Reg kLuaUnixSigsetMeth[] = { + {"add", LuaUnixSigsetAdd}, // + {"fill", LuaUnixSigsetFill}, // + {"clear", LuaUnixSigsetClear}, // + {"remove", LuaUnixSigsetRemove}, // + {"contains", LuaUnixSigsetContains}, // + {0}, // +}; + +static const luaL_Reg kLuaUnixSigsetMeta[] = { + {"__tostring", LuaUnixSigsetTostring}, // + {"__repr", LuaUnixSigsetTostring}, // + {0}, // +}; + +static void LuaUnixSigsetObj(lua_State *L) { + luaL_newmetatable(L, "unix.Sigset"); + luaL_setfuncs(L, kLuaUnixSigsetMeta, 0); + luaL_newlibtable(L, kLuaUnixSigsetMeth); + luaL_setfuncs(L, kLuaUnixSigsetMeth, 0); + lua_setfield(L, -2, "__index"); + lua_pop(L, 1); +} + //////////////////////////////////////////////////////////////////////////////// // unix.Dir object @@ -1719,44 +2127,53 @@ static DIR *GetDirOrDie(lua_State *L) { } static int FreeUnixDir(struct UnixDir *dir) { + int rc; if (--dir->refs) return 0; - return closedir(dir->dir); + rc = closedir(dir->dir); + free(dir); + return rc; } -// unix.Dir:close() → ok:bool, unix.Errno -// may be called multiple times -// called by the garbage collector too +// unix.Dir:close() +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixDirClose(lua_State *L) { int rc, olderr; struct UnixDir **udir; udir = GetUnixDirSelf(L); - if (!*udir) return 0; - olderr = 0; - rc = FreeUnixDir(*udir); - *udir = 0; - return SysretBool(L, rc, olderr); -} - -// unix.Dir:read() → name:str, unix.Errno, kind:int, ino:int, off:int -static int LuaUnixDirRead(lua_State *L) { - int olderr = errno; - struct dirent *ent; - errno = 0; - if ((ent = readdir(GetDirOrDie(L)))) { - lua_pushlstring(L, ent->d_name, strnlen(ent->d_name, sizeof(ent->d_name))); - lua_pushnil(L); - lua_pushinteger(L, ent->d_type); - lua_pushinteger(L, ent->d_ino); - lua_pushinteger(L, ent->d_off); - return 5; - } else if (!ent && !errno) { - return 0; // end of listing + if (*udir) { + olderr = 0; + rc = FreeUnixDir(*udir); + *udir = 0; + return SysretBool(L, "closedir", olderr, rc); } else { - return SysretErrnoNil(L, olderr); + lua_pushboolean(L, true); + return 1; } } -// unix.Dir:fd() → fd:int, unix.Errno +// unix.Dir:read() +// ├─→ name:str, kind:int, ino:int, off:int +// └─→ nil +static int LuaUnixDirRead(lua_State *L) { + struct dirent *ent; + if ((ent = readdir(GetDirOrDie(L)))) { + lua_pushlstring(L, ent->d_name, strnlen(ent->d_name, sizeof(ent->d_name))); + lua_pushinteger(L, ent->d_type); + lua_pushinteger(L, ent->d_ino); + lua_pushinteger(L, ent->d_off); + return 4; + } else { + // end of directory stream condition + // we make the assumption getdents() won't fail + lua_pushnil(L); + return 1; + } +} + +// unix.Dir:fd() +// ├─→ fd:int +// └─→ nil, unix.Errno static int LuaUnixDirFd(lua_State *L) { int fd, olderr; olderr = errno; @@ -1765,16 +2182,16 @@ static int LuaUnixDirFd(lua_State *L) { lua_pushinteger(L, fd); return 1; } else { - return SysretErrnoNil(L, olderr); + return SysretErrno(L, "dirfd", olderr); } } -// unix.Dir:tell() → off:int +// unix.Dir:tell() +// ├─→ off:int +// └─→ nil, unix.Errno static int LuaUnixDirTell(lua_State *L) { - long off; - off = telldir(GetDirOrDie(L)); - lua_pushinteger(L, off); - return 1; + int olderr = errno; + return SysretInteger(L, "telldir", olderr, telldir(GetDirOrDie(L))); } // unix.Dir:rewind() @@ -1783,6 +2200,48 @@ static int LuaUnixDirRewind(lua_State *L) { return 0; } +static int ReturnDir(lua_State *L, struct UnixDir *udir) { + struct UnixDir **udirp; + udir->refs = 1; + udirp = lua_newuserdatauv(L, sizeof(*udirp), 1); + luaL_setmetatable(L, "unix.Dir"); + *udirp = udir; + return 1; +} + +// unix.opendir(path:str) +// ├─→ state:unix.Dir +// └─→ nil, unix.Errno +static int LuaUnixOpendir(lua_State *L) { + int olderr = errno; + const char *path; + struct UnixDir *udir; + path = luaL_checkstring(L, 1); + if ((udir = LuaUnixAlloc(L, sizeof(*udir)))) { + if ((udir->dir = opendir(path))) { + return ReturnDir(L, udir); + } + free(udir); + } + return SysretErrno(L, "opendir", olderr); +} + +// unix.fdopendir(fd:int) +// ├─→ next:function, state:unix.Dir +// └─→ nil, unix.Errno +static int LuaUnixFdopendir(lua_State *L) { + int fd, olderr = errno; + struct UnixDir *udir; + fd = luaL_checkinteger(L, 1); + if ((udir = LuaUnixAlloc(L, sizeof(*udir)))) { + if ((udir->dir = fdopendir(fd))) { + return ReturnDir(L, udir); + } + free(udir); + } + return SysretErrno(L, "fdopendir", olderr); +} + static const luaL_Reg kLuaUnixDirMeth[] = { {"close", LuaUnixDirClose}, // {"read", LuaUnixDirRead}, // @@ -1793,8 +2252,9 @@ static const luaL_Reg kLuaUnixDirMeth[] = { }; static const luaL_Reg kLuaUnixDirMeta[] = { - {"__gc", LuaUnixDirClose}, // - {0}, // + {"__call", LuaUnixDirRead}, // + {"__gc", LuaUnixDirClose}, // + {0}, // }; static void LuaUnixDirObj(lua_State *L) { @@ -1810,6 +2270,7 @@ static void LuaUnixDirObj(lua_State *L) { // UNIX module static const luaL_Reg kLuaUnix[] = { + {"Sigset", LuaUnixSigset}, // creates signal bitmask {"exit", LuaUnixExit}, // exit w/o atexit {"stat", LuaUnixStat}, // get file info from path {"fstat", LuaUnixFstat}, // get file info from fd @@ -1823,6 +2284,7 @@ static const luaL_Reg kLuaUnix[] = { {"chdir", LuaUnixChdir}, // change directory {"chown", LuaUnixChown}, // change owner of file {"chmod", LuaUnixChmod}, // change mode of file + {"readlink", LuaUnixReadlink}, // reads symbolic link {"getcwd", LuaUnixGetcwd}, // get current directory {"fork", LuaUnixFork}, // make child process via mitosis {"execve", LuaUnixExecve}, // replace process with program @@ -1893,6 +2355,10 @@ static const luaL_Reg kLuaUnix[] = { {"sigprocmask", LuaUnixSigprocmask}, // change signal mask {"sigsuspend", LuaUnixSigsuspend}, // wait for signal {"setitimer", LuaUnixSetitimer}, // set alarm clock + {"gmtime", LuaUnixGmtime}, // destructure unix timestamp + {"localtime", LuaUnixLocaltime}, // localize unix timestamp + {"major", LuaUnixMajor}, // extract device info + {"minor", LuaUnixMinor}, // extract device info {"strerror", LuaUnixStrerror}, // turn errno into string {"strerrno", LuaUnixStrerrno}, // turn errno into string {"strerdoc", LuaUnixStrerdoc}, // turn errno into string @@ -1917,9 +2383,10 @@ static void LoadMagnums(lua_State *L, struct MagnumStr *ms, const char *pfx) { int LuaUnix(lua_State *L) { GL = L; luaL_newlib(L, kLuaUnix); + LuaUnixSigsetObj(L); + LuaUnixErrnoObj(L); LuaUnixStatObj(L); LuaUnixDirObj(L); - LuaUnixErrnoObj(L); lua_newtable(L); lua_setglobal(L, "__signal_handlers"); @@ -2008,7 +2475,7 @@ int LuaUnix(lua_State *L) { LuaSetIntField(L, "DT_FIFO", DT_FIFO); LuaSetIntField(L, "DT_SOCK", DT_SOCK); - // readdir() type + // poll() flags LuaSetIntField(L, "POLLIN", POLLIN); LuaSetIntField(L, "POLLPRI", POLLPRI); LuaSetIntField(L, "POLLOUT", POLLOUT); @@ -2023,6 +2490,7 @@ int LuaUnix(lua_State *L) { // i/o options LuaSetIntField(L, "AT_FDCWD", AT_FDCWD); + LuaSetIntField(L, "AT_EACCESS", AT_EACCESS); LuaSetIntField(L, "AT_SYMLINK_NOFOLLOW", AT_SYMLINK_NOFOLLOW); // sigprocmask() handlers @@ -2055,5 +2523,21 @@ int LuaUnix(lua_State *L) { LuaSetIntField(L, "SOL_TCP", SOL_TCP); LuaSetIntField(L, "SOL_UDP", SOL_UDP); + // sigaction() flags + LuaSetIntField(L, "SA_RESTART", SA_RESTART); + LuaSetIntField(L, "SA_RESETHAND", SA_RESETHAND); + LuaSetIntField(L, "SA_NODEFER", SA_NODEFER); + LuaSetIntField(L, "SA_NOCLDWAIT", SA_NOCLDWAIT); + LuaSetIntField(L, "SA_NOCLDSTOP", SA_NOCLDSTOP); + + LuaSetIntField(L, "NSIG", NSIG); + LuaSetIntField(L, "BUFSIZ", BUFSIZ); + LuaSetIntField(L, "ARG_MAX", ARG_MAX); + LuaSetIntField(L, "CLK_TCK", CLK_TCK); + LuaSetIntField(L, "PATH_MAX", PATH_MAX); + LuaSetIntField(L, "OPEN_MAX", OPEN_MAX); + LuaSetIntField(L, "PIPE_BUF", PIPE_BUF); + LuaSetIntField(L, "CHILD_MAX", CHILD_MAX); + return 1; } diff --git a/tool/net/net.mk b/tool/net/net.mk index b5d763938..5a0dc7c88 100644 --- a/tool/net/net.mk +++ b/tool/net/net.mk @@ -175,6 +175,7 @@ o/$(MODE)/tool/net/demo/sql.lua.zip.o \ o/$(MODE)/tool/net/demo/unix-rawsocket.lua.zip.o \ o/$(MODE)/tool/net/demo/unix-subprocess.lua.zip.o \ o/$(MODE)/tool/net/demo/unix-webserver.lua.zip.o \ +o/$(MODE)/tool/net/demo/unix-dir.lua.zip.o \ o/$(MODE)/tool/net/demo/unix-info.lua.zip.o \ o/$(MODE)/tool/net/demo/fetch.lua.zip.o \ o/$(MODE)/tool/net/demo/hello.lua.zip.o \ @@ -221,6 +222,7 @@ o/$(MODE)/tool/net/redbean-demo.com.dbg: \ o/$(MODE)/tool/net/demo/unix-rawsocket.lua.zip.o \ o/$(MODE)/tool/net/demo/unix-subprocess.lua.zip.o \ o/$(MODE)/tool/net/demo/unix-webserver.lua.zip.o \ + o/$(MODE)/tool/net/demo/unix-dir.lua.zip.o \ o/$(MODE)/tool/net/demo/unix-info.lua.zip.o \ o/$(MODE)/tool/net/demo/fetch.lua.zip.o \ o/$(MODE)/tool/net/demo/hello.lua.zip.o \ diff --git a/tool/net/redbean.c b/tool/net/redbean.c index b8413236e..dd53ed025 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -63,6 +63,7 @@ #include "libc/runtime/directmap.internal.h" #include "libc/runtime/gc.h" #include "libc/runtime/gc.internal.h" +#include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" #include "libc/runtime/stack.h" #include "libc/runtime/symbols.internal.h" @@ -196,6 +197,8 @@ STATIC_YOINK("zip_uri_support"); // puncts not used: !"#$%&'()*+,-./;<=>@[\]^_`{|}~ #define GETOPTS "BSVZabdfghijkmsuvzA:C:D:F:G:H:K:L:M:P:R:T:U:c:e:l:p:r:t:" +extern unsigned long long __kbirth; + static const uint8_t kGzipHeader[] = { 0x1F, // MAGNUM 0x8B, // MAGNUM @@ -4956,6 +4959,7 @@ static const luaL_Reg kLuaFuncs[] = { {"GetComment", LuaGetAssetComment}, // {"GetCookie", LuaGetCookie}, // {"GetCpuCore", LuaGetCpuCore}, // + {"GetCpuCount", LuaGetCpuCount}, // {"GetCpuNode", LuaGetCpuNode}, // {"GetCryptoHash", LuaGetCryptoHash}, // {"GetDate", LuaGetDate}, // @@ -5192,6 +5196,7 @@ static void LuaPrint(lua_State *L) { static void LuaInterpreter(lua_State *L) { int i, n, sig, status; const char *script; + if (funtrace) ftrace_install(); if (optind < __argc) { script = __argv[optind]; if (!strcmp(script, "-")) script = 0; @@ -5201,7 +5206,11 @@ static void LuaInterpreter(lua_State *L) { luaL_checkstack(L, n + 3, "too many script args"); for (i = 1; i <= n; i++) lua_rawgeti(L, -i, i); lua_remove(L, -i); // remove arg table from stack + if (funtrace) ++g_ftrace; + if (systrace) ++__strace; status = lua_runchunk(L, n, LUA_MULTRET); + if (systrace) --__strace; + if (funtrace) --g_ftrace; } lua_report(L, status); } else { @@ -5225,7 +5234,9 @@ static void LuaInterpreter(lua_State *L) { exit(1); } if (status == LUA_OK) { + if (funtrace) ++g_ftrace; status = lua_runchunk(GL, 0, LUA_MULTRET); + if (funtrace) --g_ftrace; } if (status == LUA_OK) { LuaPrint(GL); @@ -6358,7 +6369,6 @@ static int HandleConnection(size_t i) { connectionclose = false; if (!IsTiny()) { if (systrace) { - extern unsigned long long __kbirth; __strace = 1; __kbirth = rdtsc(); } @@ -6474,18 +6484,20 @@ static void RestoreApe(void) { if (endswith(zpath, ".com.dbg")) return; if ((a = GetAssetZip("/.ape", 5)) && (p = LoadAsset(a, &n))) { close(zfd); - if ((zfd = OpenExecutable()) == -1 || WRITE(zfd, p, n) == -1) + if ((zfd = OpenExecutable()) == -1 || WRITE(zfd, p, n) == -1) { WARNF("(srvr) can't restore .ape"); + } free(p); } else { - INFOF("(srvr) /.ape not found"); + DEBUGF("(srvr) /.ape not found"); } } static int HandleReadline(void) { int status; + lua_State *L = GL; for (;;) { - status = lua_loadline(GL); + status = lua_loadline(L); if (status < 0) { if (status == -1) { OnTerm(SIGHUP); // eof @@ -6508,12 +6520,12 @@ static int HandleReadline(void) { linenoiseDisableRawMode(); LUA_REPL_LOCK; if (status == LUA_OK) { - status = lua_runchunk(GL, 0, LUA_MULTRET); + status = lua_runchunk(L, 0, LUA_MULTRET); } if (status == LUA_OK) { - LuaPrint(GL); + LuaPrint(L); } else { - lua_report(GL, status); + lua_report(L, status); } LUA_REPL_UNLOCK; if (lua_repl_isterminal) { @@ -6683,26 +6695,28 @@ static int EventLoop(int ms) { } static void ReplEventLoop(void) { + lua_State *L = GL; DEBUGF("ReplEventLoop()"); polls[0].fd = 0; lua_repl_completions_callback = HandleCompletions; - lua_initrepl(GL, "redbean"); + lua_initrepl(L, "redbean"); if (lua_repl_isterminal) { linenoiseEnableRawMode(0); } EventLoop(100); linenoiseDisableRawMode(); lua_freerepl(); - lua_settop(GL, 0); // clear stack + lua_settop(L, 0); // clear stack polls[0].fd = -1; } static uint32_t WindowsReplThread(void *arg) { int sig; + lua_State *L = GL; DEBUGF("WindowsReplThread()"); lua_repl_blocking = true; lua_repl_completions_callback = HandleCompletions; - lua_initrepl(GL, "redbean"); + lua_initrepl(L, "redbean"); if (lua_repl_isterminal) { linenoiseEnableRawMode(0); } @@ -6714,7 +6728,7 @@ static uint32_t WindowsReplThread(void *arg) { linenoiseDisableRawMode(); lua_freerepl(); LUA_REPL_LOCK; - lua_settop(GL, 0); // clear stack + lua_settop(L, 0); // clear stack LUA_REPL_UNLOCK; if ((sig = linenoiseGetInterrupt())) { raise(sig); @@ -6896,7 +6910,7 @@ void RedBean(int argc, char *argv[]) { (shared = mmap(NULL, ROUNDUP(sizeof(struct Shared), FRAMESIZE), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0))); - zpath = program_executable_name; + zpath = GetProgramExecutableName(); CHECK_NE(-1, (zfd = open(zpath, O_RDONLY))); CHECK_NE(-1, fstat(zfd, &zst)); OpenZip(true); From 6a145a92628d0cf17d437806d2c86a350f17cfcf Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Wed, 27 Apr 2022 05:39:39 -0700 Subject: [PATCH 07/18] Make improvements - Add hierarchical auto-completion to redbean's repl - Fetch latest localtime() and strftime() from Eggert - Shave a few milliseconds off redbean start latency - Fix redbean repl with multi-line statements - Make the Lua unix module code more elegant - Harden Lua data structure serialization --- examples/examples.mk | 2 +- libc/calls/kopenflags.S | 57 +- libc/calls/reservefd.c | 12 +- libc/calls/uname.c | 2 + libc/calls/weirdtypes.h | 20 + libc/fmt/strerror_wr.greg.c | 6 +- libc/runtime/getsymboltable.greg.c | 15 +- libc/str/longsort.c | 4 + libc/str/strchrnul.c | 2 + libc/sysv/consts.sh | 3 - libc/sysv/consts/exit.h | 13 +- libc/time/asctime.c | 137 +- libc/time/asctime_r.c | 40 - libc/time/ctime.c | 30 +- libc/time/ctime_r.c | 26 +- libc/time/difftime.c | 78 +- libc/time/localtime.c | 2454 ++++++++++++++------------- libc/time/strftime.c | 881 ++++++---- libc/time/time.mk | 4 + libc/time/tz.internal.h | 542 ++++++ libc/time/tzfile.internal.h | 125 +- third_party/chibicc/chibicc.mk | 2 +- third_party/linenoise/linenoise.c | 15 +- third_party/lua/cosmo.h | 4 +- third_party/lua/ldo.c | 3 +- third_party/lua/lrepl.c | 73 +- third_party/lua/ltests.c | 2 +- third_party/lua/lua.main.c | 2 - third_party/lua/lua.mk | 2 +- third_party/lua/luaencodejsondata.c | 14 +- third_party/lua/luaencodeluadata.c | 118 +- third_party/lua/luaformatstack.c | 2 +- third_party/make/make.mk | 2 +- third_party/python/python.mk | 2 +- third_party/quickjs/quickjs.mk | 2 +- third_party/sqlite3/sqlite3.mk | 2 +- tool/build/build.mk | 2 +- tool/net/help.txt | 61 +- tool/net/lunix.c | 130 +- tool/net/net.mk | 17 +- tool/net/redbean.c | 14 +- tool/plinko/plinko.mk | 2 +- tool/viz/viz.mk | 4 +- usr/share/zoneinfo/Anchorage | Bin 0 -> 977 bytes 44 files changed, 2987 insertions(+), 1941 deletions(-) delete mode 100644 libc/time/asctime_r.c create mode 100644 libc/time/tz.internal.h create mode 100644 usr/share/zoneinfo/Anchorage diff --git a/examples/examples.mk b/examples/examples.mk index 0f6973377..a76e23ebd 100644 --- a/examples/examples.mk +++ b/examples/examples.mk @@ -140,7 +140,7 @@ o/$(MODE)/examples/nesemu1.com: \ @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \ -o o/$(MODE)/examples/.nesemu1/.symtab $< - @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ + @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ o/$(MODE)/examples/.nesemu1/.symtab o/$(MODE)/examples/hello.com.dbg: \ diff --git a/libc/calls/kopenflags.S b/libc/calls/kopenflags.S index 9ced691a0..a0749a5af 100644 --- a/libc/calls/kopenflags.S +++ b/libc/calls/kopenflags.S @@ -31,34 +31,35 @@ .align 4 .underrun kOpenFlags: - .e O_RDWR,"RDWR" // order matters - .e O_RDONLY,"RDONLY" // - .e O_WRONLY,"WRONLY" // - .e O_ACCMODE,"ACCMODE" // mask of prev three - .e O_CREAT,"CREAT" // - .e O_EXCL,"EXCL" // - .e O_TRUNC,"TRUNC" // - .e O_CLOEXEC,"CLOEXEC" // - .e O_DIRECT,"DIRECT" // no-op on xnu/openbsd - .e O_APPEND,"APPEND" // weird on nt - .e O_TMPFILE,"TMPFILE" // linux, windows - .e O_NOFOLLOW,"NOFOLLOW" // unix - .e O_SYNC,"SYNC" // unix - .e O_ASYNC,"ASYNC" // unix - .e O_NOCTTY,"NOCTTY" // unix - .e O_NOATIME,"NOATIME" // linux - .e O_EXEC,"EXEC" // free/openbsd - .e O_SEARCH,"SEARCH" // free/netbsd - .e O_DSYNC,"DSYNC" // linux/xnu/open/netbsd - .e O_RSYNC,"RSYNC" // linux/open/netbsd - .e O_PATH,"PATH" // linux - .e O_VERIFY,"VERIFY" // freebsd - .e O_SHLOCK,"SHLOCK" // bsd - .e O_EXLOCK,"EXLOCK" // bsd - .e O_RANDOM,"RANDOM" // windows - .e O_SEQUENTIAL,"SEQUENTIAL" // windows - .e O_COMPRESSED,"COMPRESSED" // windows - .e O_INDEXED,"INDEXED" // windows + .e O_RDWR,"RDWR" // order matters + .e O_RDONLY,"RDONLY" // + .e O_WRONLY,"WRONLY" // + .e O_ACCMODE,"ACCMODE" // mask of prev three + .e O_CREAT,"CREAT" // + .e O_EXCL,"EXCL" // + .e O_TRUNC,"TRUNC" // + .e O_CLOEXEC,"CLOEXEC" // + .e O_NONBLOCK,"NONBLOCK" // + .e O_DIRECT,"DIRECT" // no-op on xnu/openbsd + .e O_APPEND,"APPEND" // weird on nt + .e O_TMPFILE,"TMPFILE" // linux, windows + .e O_NOFOLLOW,"NOFOLLOW" // unix + .e O_SYNC,"SYNC" // unix + .e O_ASYNC,"ASYNC" // unix + .e O_NOCTTY,"NOCTTY" // unix + .e O_NOATIME,"NOATIME" // linux + .e O_EXEC,"EXEC" // free/openbsd + .e O_SEARCH,"SEARCH" // free/netbsd + .e O_DSYNC,"DSYNC" // linux/xnu/open/netbsd + .e O_RSYNC,"RSYNC" // linux/open/netbsd + .e O_PATH,"PATH" // linux + .e O_VERIFY,"VERIFY" // freebsd + .e O_SHLOCK,"SHLOCK" // bsd + .e O_EXLOCK,"EXLOCK" // bsd + .e O_RANDOM,"RANDOM" // windows + .e O_SEQUENTIAL,"SEQUENTIAL" // windows + .e O_COMPRESSED,"COMPRESSED" // windows + .e O_INDEXED,"INDEXED" // windows .long MAGNUM_TERMINATOR .endobj kOpenFlags,globl,hidden .overrun diff --git a/libc/calls/reservefd.c b/libc/calls/reservefd.c index 1937d2e08..a68a56e7c 100644 --- a/libc/calls/reservefd.c +++ b/libc/calls/reservefd.c @@ -88,9 +88,9 @@ int __reservefd(int start) { /** * Closes non-stdio file descriptors to free dynamic memory. */ -static void __freefds(void) { +static void FreeFds(void) { int i; - NTTRACE("__freefds()"); + NTTRACE("FreeFds()"); for (i = 3; i < g_fds.n; ++i) { if (g_fds.p[i].kind) { close(i); @@ -104,10 +104,10 @@ static void __freefds(void) { } } -static textstartup void __freefds_init(void) { - atexit(__freefds); +static textstartup void FreeFdsInit(void) { + atexit(FreeFds); } -const void *const __freefds_ctor[] initarray = { - __freefds_init, +const void *const FreeFdsCtor[] initarray = { + FreeFdsInit, }; diff --git a/libc/calls/uname.c b/libc/calls/uname.c index b96686840..fa187db12 100644 --- a/libc/calls/uname.c +++ b/libc/calls/uname.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/weaken.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" @@ -23,6 +24,7 @@ #include "libc/dce.h" #include "libc/fmt/itoa.h" #include "libc/intrin/asan.internal.h" +#include "libc/log/log.h" #include "libc/nt/enum/computernameformat.h" #include "libc/nt/struct/teb.h" #include "libc/runtime/runtime.h" diff --git a/libc/calls/weirdtypes.h b/libc/calls/weirdtypes.h index 4b0cb8a65..de7973ae1 100644 --- a/libc/calls/weirdtypes.h +++ b/libc/calls/weirdtypes.h @@ -48,6 +48,26 @@ typedef __UINT_FAST32_TYPE__ uint_fast32_t; typedef __INT_FAST64_TYPE__ int_fast64_t; typedef __UINT_FAST64_TYPE__ uint_fast64_t; +#define TIME_T_MAX __INT64_MAX__ +#define UINT_FAST64_MAX __UINT_FAST64_MAX__ +#define UINT_FAST8_MAX __UINT_FAST8_MAX__ +#define INT_FAST32_MAX __INT_FAST32_MAX__ +#define INT_FAST16_MAX __INT_FAST16_MAX__ +#define UINT_FAST32_MAX __UINT_FAST32_MAX__ +#define INT_FAST8_MAX __INT_FAST8_MAX__ +#define INT_FAST64_MAX __INT_FAST64_MAX__ +#define UINT_FAST16_MAX __UINT_FAST16_MAX__ + +#define TIME_T_MIN (-TIME_T_MAX - 1) +#define UINT_FAST64_MIN (-UINT_FAST64_MAX - 1) +#define UINT_FAST8_MIN (-UINT_FAST8_MAX - 1) +#define INT_FAST32_MIN (-INT_FAST32_MAX - 1) +#define INT_FAST16_MIN (-INT_FAST16_MAX - 1) +#define UINT_FAST32_MIN (-UINT_FAST32_MAX - 1) +#define INT_FAST8_MIN (-INT_FAST8_MAX - 1) +#define INT_FAST64_MIN (-INT_FAST64_MAX - 1) +#define UINT_FAST16_MIN (-UINT_FAST16_MAX - 1) + #define atomic_bool _Atomic(_Bool) #define atomic_bool32 atomic_int_fast32_t #define atomic_char _Atomic(char) diff --git a/libc/fmt/strerror_wr.greg.c b/libc/fmt/strerror_wr.greg.c index 93b8616c4..8fad73863 100644 --- a/libc/fmt/strerror_wr.greg.c +++ b/libc/fmt/strerror_wr.greg.c @@ -44,17 +44,17 @@ int strerror_wr(int err, uint32_t winerr, char *buf, size_t size) { if (size > 1) *buf++ = c; if (size) *buf = 0; } else if (!IsWindows() || err == winerr || !winerr) { - ksnprintf(buf, size, "%s:%d:%s", sym, err, msg); + ksnprintf(buf, size, "%s/%d/%s", sym, err, msg); } else { if ((n = FormatMessage( kNtFormatMessageFromSystem | kNtFormatMessageIgnoreInserts, 0, winerr, MAKELANGID(kNtLangNeutral, kNtSublangDefault), winmsg, ARRAYLEN(winmsg), 0))) { while ((n && winmsg[n - 1] <= ' ') || winmsg[n - 1] == '.') --n; - ksnprintf(buf, size, "%s:%d:%s:%d:%.*hs", sym, err, msg, winerr, n, + ksnprintf(buf, size, "%s/%d/%s/%d/%.*hs", sym, err, msg, winerr, n, winmsg); } else { - ksnprintf(buf, size, "%s:%d:%s:%d", sym, err, msg, winerr); + ksnprintf(buf, size, "%s/%d/%s/%d", sym, err, msg, winerr); } } return 0; diff --git a/libc/runtime/getsymboltable.greg.c b/libc/runtime/getsymboltable.greg.c index 584170d26..60af4749a 100644 --- a/libc/runtime/getsymboltable.greg.c +++ b/libc/runtime/getsymboltable.greg.c @@ -54,7 +54,7 @@ static ssize_t FindSymtabInZip(struct Zipos *zipos) { * @note This code can't depend on dlmalloc() */ static struct SymbolTable *GetSymbolTableFromZip(struct Zipos *zipos) { - ssize_t cf, lf; + ssize_t rc, cf, lf; size_t size, size2; struct DeflateState ds; struct SymbolTable *res = 0; @@ -67,14 +67,16 @@ static struct SymbolTable *GetSymbolTableFromZip(struct Zipos *zipos) { case kZipCompressionNone: memcpy(res, (void *)ZIP_LFILE_CONTENT(zipos->map + lf), size); break; +#if 0 case kZipCompressionDeflate: - if (undeflate(res, size, (void *)ZIP_LFILE_CONTENT(zipos->map + lf), - GetZipLfileCompressedSize(zipos->map + lf), - &ds) == -1) { + rc = undeflate(res, size, (void *)ZIP_LFILE_CONTENT(zipos->map + lf), + GetZipLfileCompressedSize(zipos->map + lf), &ds); + if (rc == -1) { munmap(res, size2); res = 0; } break; +#endif default: munmap(res, size2); res = 0; @@ -115,11 +117,8 @@ static struct SymbolTable *GetSymbolTableFromElf(void) { * @return symbol table, or NULL w/ errno on first call */ struct SymbolTable *GetSymbolTable(void) { - int ft, st; struct Zipos *z; if (!g_symtab && !__isworker) { - ft = g_ftrace, g_ftrace = 0; - st = __strace, __strace = 0; if (weaken(__zipos_get) && (z = weaken(__zipos_get)())) { if ((g_symtab = GetSymbolTableFromZip(z))) { g_symtab->names = @@ -131,8 +130,6 @@ struct SymbolTable *GetSymbolTable(void) { if (!g_symtab) { g_symtab = GetSymbolTableFromElf(); } - g_ftrace = ft; - __strace = st; } return g_symtab; } diff --git a/libc/str/longsort.c b/libc/str/longsort.c index bdf420b81..fcbb9e366 100644 --- a/libc/str/longsort.c +++ b/libc/str/longsort.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/strace.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/macros.internal.h" @@ -80,4 +81,7 @@ void longsort(long *x, size_t n) { longsort_pure(x, n, t); } } + if (n > 1000) { + STRACE("longsort(%p, %'zu)", x, n); + } } diff --git a/libc/str/strchrnul.c b/libc/str/strchrnul.c index 981b9aaa6..3a3125442 100644 --- a/libc/str/strchrnul.c +++ b/libc/str/strchrnul.c @@ -53,6 +53,8 @@ noasan static inline const char *strchrnul_sse(const char *s, unsigned char c) { /** * Returns pointer to first instance of character. * + * If c is not found then a pointer to the nul byte is returned. + * * @param s is a NUL-terminated string * @param c is masked with 255 as byte to search for * @return pointer to first instance of c, or pointer to diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index ec140561e..7cd16c0b2 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -1110,9 +1110,6 @@ syscon pf PF_VSOCK 40 0 0 0 0 0 syscon pf PF_WANPIPE 25 0 0 0 0 0 syscon pf PF_X25 9 0 0 0 0 0 -syscon exit EXIT_SUCCESS 0 0 0 0 0 0 # consensus -syscon exit EXIT_FAILURE 1 1 1 1 1 1 # consensus - # Eric Allman's exit() codes # # - Broadly supported style guideline; diff --git a/libc/sysv/consts/exit.h b/libc/sysv/consts/exit.h index b980539d0..70e67d83f 100644 --- a/libc/sysv/consts/exit.h +++ b/libc/sysv/consts/exit.h @@ -1,16 +1,7 @@ #ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS_EXIT_H_ #define COSMOPOLITAN_LIBC_SYSV_CONSTS_EXIT_H_ -#include "libc/runtime/symbolic.h" -#define EXIT_FAILURE SYMBOLIC(EXIT_FAILURE) -#define EXIT_SUCCESS SYMBOLIC(EXIT_SUCCESS) +#define EXIT_FAILURE 1 +#define EXIT_SUCCESS 0 -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -extern const long EXIT_FAILURE; -extern const long EXIT_SUCCESS; - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_EXIT_H_ */ diff --git a/libc/time/asctime.c b/libc/time/asctime.c index b17b44e6f..c076e59f1 100644 --- a/libc/time/asctime.c +++ b/libc/time/asctime.c @@ -1,31 +1,114 @@ -/*-*- 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 │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/fmt.h" #include "libc/time/time.h" +#include "libc/time/tz.internal.h" +// clang-format off +/* asctime and asctime_r a la POSIX and ISO C, except pad years before 1000. */ -static char g_asctime_buf[64]; +/* +** This file is in the public domain, so clarified as of +** 1996-06-05 by Arthur David Olson. +*/ -/** - * Converts date time to string. - * - * @return date time string in statically allocated buffer - * @see asctime_r for reentrant version - */ -char *asctime(const struct tm *date) { - return asctime_r(date, g_asctime_buf); +/* +** Avoid the temptation to punt entirely to strftime; +** the output of strftime is supposed to be locale specific +** whereas the output of asctime is supposed to be constant. +*/ + +/* +** Some systems only handle "%.2d"; others only handle "%02d"; +** "%02.2d" makes (most) everybody happy. +** At least some versions of gcc warn about the %02.2d; +** we conditionalize below to avoid the warning. +*/ +/* +** All years associated with 32-bit time_t values are exactly four digits long; +** some years associated with 64-bit time_t values are not. +** Vintage programs are coded for years that are always four digits long +** and may assume that the newline always lands in the same place. +** For years that are less than four digits, we pad the output with +** leading zeroes to get the newline in the traditional place. +** The -4 ensures that we get four characters of output even if +** we call a strftime variant that produces fewer characters for some years. +** The ISO C and POSIX standards prohibit padding the year, +** but many implementations pad anyway; most likely the standards are buggy. +*/ +#ifdef __GNUC__ +#define ASCTIME_FMT "%s %s%3d %2.2d:%2.2d:%2.2d %-4s\n" +#else /* !defined __GNUC__ */ +#define ASCTIME_FMT "%s %s%3d %02.2d:%02.2d:%02.2d %-4s\n" +#endif /* !defined __GNUC__ */ +/* +** For years that are more than four digits we put extra spaces before the year +** so that code trying to overwrite the newline won't end up overwriting +** a digit within a year and truncating the year (operating on the assumption +** that no output is better than wrong output). +*/ +#ifdef __GNUC__ +#define ASCTIME_FMT_B "%s %s%3d %2.2d:%2.2d:%2.2d %s\n" +#else /* !defined __GNUC__ */ +#define ASCTIME_FMT_B "%s %s%3d %02.2d:%02.2d:%02.2d %s\n" +#endif /* !defined __GNUC__ */ + +#define STD_ASCTIME_BUF_SIZE 26 +/* +** Big enough for something such as +** ??? ???-2147483648 -2147483648:-2147483648:-2147483648 -2147483648\n +** (two three-character abbreviations, five strings denoting integers, +** seven explicit spaces, two explicit colons, a newline, +** and a trailing NUL byte). +** The values above are for systems where an int is 32 bits and are provided +** as an example; the define below calculates the maximum for the system at +** hand. +*/ +#define MAX_ASCTIME_BUF_SIZE (2*3+5*INT_STRLEN_MAXIMUM(int)+7+2+1+1) + +static char buf_asctime[MAX_ASCTIME_BUF_SIZE]; + +char * +asctime_r(register const struct tm *timeptr, char *buf) +{ + register const char * wn; + register const char * mn; + char year[INT_STRLEN_MAXIMUM(int) + 2]; + char result[MAX_ASCTIME_BUF_SIZE]; + + if (timeptr == NULL) { + errno = EINVAL; + return strcpy(buf, "??? ??? ?? ??:??:?? ????\n"); + } + if (timeptr->tm_wday < 0 || timeptr->tm_wday >= DAYSPERWEEK) + wn = "???"; + else wn = kWeekdayNameShort[timeptr->tm_wday]; + if (timeptr->tm_mon < 0 || timeptr->tm_mon >= MONSPERYEAR) + mn = "???"; + else mn = kMonthNameShort[timeptr->tm_mon]; + /* + ** Use strftime's %Y to generate the year, to avoid overflow problems + ** when computing timeptr->tm_year + TM_YEAR_BASE. + ** Assume that strftime is unaffected by other out-of-range members + ** (e.g., timeptr->tm_mday) when processing "%Y". + */ + strftime(year, sizeof year, "%Y", timeptr); + /* + ** We avoid using snprintf since it's not available on all systems. + */ + (sprintf)(result, + ((strlen(year) <= 4) ? ASCTIME_FMT : ASCTIME_FMT_B), + wn, mn, + timeptr->tm_mday, timeptr->tm_hour, + timeptr->tm_min, timeptr->tm_sec, + year); + if (strlen(result) < STD_ASCTIME_BUF_SIZE || buf == buf_asctime) + return strcpy(buf, result); + else { + errno = EOVERFLOW; + return NULL; + } +} + +char * +asctime(register const struct tm *timeptr) +{ + return asctime_r(timeptr, buf_asctime); } diff --git a/libc/time/asctime_r.c b/libc/time/asctime_r.c deleted file mode 100644 index fda926f26..000000000 --- a/libc/time/asctime_r.c +++ /dev/null @@ -1,40 +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 │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/fmt/fmt.h" -#include "libc/time/struct/tm.h" -#include "libc/time/time.h" - -static unsigned clip(unsigned index, unsigned count) { - return index < count ? index : 0; -} - -/** - * Converts date time to string. - * - * @param buf needs to have 64 bytes - * @return pointer to buf - * @see asctime_r for reentrant version - */ -char *asctime_r(const struct tm *date, char buf[hasatleast 64]) { - (snprintf)(buf, 64, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n", - kWeekdayNameShort[clip(date->tm_wday, 7)], - kMonthNameShort[clip(date->tm_mon, 12)], date->tm_mday, - date->tm_hour, date->tm_min, date->tm_sec, 1900 + date->tm_year); - return buf; -} diff --git a/libc/time/ctime.c b/libc/time/ctime.c index e0e34ebfb..feedb7676 100644 --- a/libc/time/ctime.c +++ b/libc/time/ctime.c @@ -1,21 +1,13 @@ -/*-*- 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 │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/weirdtypes.h" #include "libc/time/time.h" -char *ctime(const int64_t *timep) { return asctime(localtime(timep)); } +char *ctime(const time_t *timep) { + /* + ** Section 4.12.3.2 of X3.159-1989 requires that + ** The ctime function converts the calendar time pointed to by timer + ** to local time in the form of a string. It is equivalent to + ** asctime(localtime(timer)) + */ + struct tm *tmp = localtime(timep); + return tmp ? asctime(tmp) : NULL; +} diff --git a/libc/time/ctime_r.c b/libc/time/ctime_r.c index ddfd57b52..c830a081c 100644 --- a/libc/time/ctime_r.c +++ b/libc/time/ctime_r.c @@ -1,25 +1,9 @@ -/*-*- 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 │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/weirdtypes.h" #include "libc/time/struct/tm.h" #include "libc/time/time.h" -char *ctime_r(const int64_t *timep, char buf[hasatleast 64]) { - struct tm date[1]; - return asctime_r(localtime_r(timep, date), buf); +char *ctime_r(const time_t *timep, char *buf) { + struct tm mytm; + struct tm *tmp = localtime_r(timep, &mytm); + return tmp ? asctime_r(tmp, buf) : NULL; } diff --git a/libc/time/difftime.c b/libc/time/difftime.c index 435401775..00f1daa00 100644 --- a/libc/time/difftime.c +++ b/libc/time/difftime.c @@ -1,21 +1,61 @@ -/*-*- 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 │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ +/*-*- mode:c; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ +│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/time/time.h" +#include "libc/time/tz.internal.h" +// clang-format off +/* Return the difference between two timestamps. */ -double difftime(int64_t x, int64_t y) { return x - y; } +/* +** This file is in the public domain, so clarified as of +** 1996-06-05 by Arthur David Olson. +*/ + +/* Return -X as a double. Using this avoids casting to 'double'. */ +static inline double +dminus(double x) +{ + return -x; +} + +double +difftime(time_t time1, time_t time0) +{ + /* + ** If double is large enough, simply convert and subtract + ** (assuming that the larger type has more precision). + */ + if (sizeof(time_t) < sizeof(double)) { + double t1 = time1, t0 = time0; + return t1 - t0; + } + + /* + ** The difference of two unsigned values can't overflow + ** if the minuend is greater than or equal to the subtrahend. + */ + if (!TYPE_SIGNED(time_t)) + return time0 <= time1 ? time1 - time0 : dminus(time0 - time1); + + /* Use uintmax_t if wide enough. */ + if (sizeof(time_t) <= sizeof(uintmax_t)) { + uintmax_t t1 = time1, t0 = time0; + return time0 <= time1 ? t1 - t0 : dminus(t0 - t1); + } + + /* + ** Handle cases where both time1 and time0 have the same sign + ** (meaning that their difference cannot overflow). + */ + if ((time1 < 0) == (time0 < 0)) + return time1 - time0; + + /* + ** The values have opposite signs and uintmax_t is too narrow. + ** This suffers from double rounding; attempt to lessen that + ** by using long double temporaries. + */ + { + long double t1 = time1, t0 = time0; + return t1 - t0; + } +} diff --git a/libc/time/localtime.c b/libc/time/localtime.c index a79d0330c..68ac75a34 100644 --- a/libc/time/localtime.c +++ b/libc/time/localtime.c @@ -1,35 +1,18 @@ /*-*- mode:c; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ │vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/initializer.internal.h" +#define LOCALTIME_IMPLEMENTATION #include "libc/calls/calls.h" -#include "libc/macros.internal.h" -#include "libc/math.h" -#include "libc/mem/mem.h" -#include "libc/nexgen32e/nexgen32e.h" -#include "libc/runtime/runtime.h" +#include "libc/intrin/spinlock.h" #include "libc/str/str.h" #include "libc/sysv/consts/o.h" -#include "libc/time/struct/tm.h" #include "libc/time/time.h" +#include "libc/time/tz.internal.h" #include "libc/time/tzfile.internal.h" -#define ALL_STATE - -#define time_t int64_t -#define int_fast64_t int64_t -#define int_fast32_t int32_t -#define GRANDPARENTED "local time zone must be set" -#define AVGSECSPERYEAR 31556952L -#define SECSPERREPEAT \ - ((int_fast64_t)YEARSPERREPEAT * (int_fast64_t)AVGSECSPERYEAR) -#define SECSPERREPEAT_BITS 34 /* ceil(log2(SECSPERREPEAT)) */ -#define YEARSPERREPEAT 400 /* years before a Gregorian repeat */ -#define TM_ZONE tm_zone -#define INITIALIZE(x) x = 0 - STATIC_YOINK("zip_uri_support"); STATIC_YOINK("usr/share/zoneinfo/"); +STATIC_YOINK("usr/share/zoneinfo/Anchorage"); STATIC_YOINK("usr/share/zoneinfo/Beijing"); STATIC_YOINK("usr/share/zoneinfo/Berlin"); STATIC_YOINK("usr/share/zoneinfo/Boulder"); @@ -41,26 +24,31 @@ 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"); STATIC_YOINK("usr/share/zoneinfo/UTC"); -/* clang-format off */ +// clang-format off +/* Convert timestamp from time_t to struct tm. */ /* ** This file is in the public domain, so clarified as of ** 1996-06-05 by Arthur David Olson. */ -/* #ifndef lint */ -/* #ifndef NOID */ -/* static char elsieid[] = "@(#)localtime.c 8.3"; */ -/* #endif /\* !defined NOID *\/ */ -/* #endif /\* !defined lint *\/ */ - /* ** Leap second handling from Bradley White. ** POSIX-style TZ environment variable handling from Guy Harris. */ +_Alignas(64) static char locallock; + +static int lock(void) { + _spinlock(&locallock); + return 0; +} + +static void unlock(void) { + _spunlock(&locallock); +} + #ifndef TZ_ABBR_MAX_LEN #define TZ_ABBR_MAX_LEN 16 #endif /* !defined TZ_ABBR_MAX_LEN */ @@ -98,36 +86,44 @@ STATIC_YOINK("usr/share/zoneinfo/UTC"); #endif /* !defined WILDABBR */ static const char wildabbr[] = WILDABBR; -static char wildabbr2[sizeof(WILDABBR)]; -static const char gmt[] = "UTC"; +static const char gmt[] = "GMT"; /* ** The DST rules to use if TZ has no rules and we can't load TZDEFRULES. -** We default to US rules as of 1999-08-17. -** POSIX 1003.1 section 8.1.1 says that the default DST rules are -** implementation dependent; for historical reasons, US rules are a -** common default. +** Default to US rules as of 2017-05-07. +** POSIX does not specify the default DST rules; +** for historical reasons, US rules are a common default. */ #ifndef TZDEFRULESTRING -#define TZDEFRULESTRING ",M4.1.0,M10.5.0" -#endif /* !defined TZDEFDST */ +#define TZDEFRULESTRING ",M3.2.0,M11.1.0" +#endif struct ttinfo { /* time type information */ - long tt_gmtoff; /* UTC offset in seconds */ - int tt_isdst; /* used to set tm_isdst */ - int tt_abbrind; /* abbreviation list index */ - int tt_ttisstd; /* TRUE if transition is std time */ - int tt_ttisgmt; /* TRUE if transition is UTC */ + int_fast32_t tt_utoff; /* UT offset in seconds */ + bool tt_isdst; /* used to set tm_isdst */ + int tt_desigidx; /* abbreviation list index */ + bool tt_ttisstd; /* transition is std time */ + bool tt_ttisut; /* transition is UT */ }; struct lsinfo { /* leap second information */ time_t ls_trans; /* transition time */ - long ls_corr; /* correction to apply */ + int_fast32_t ls_corr; /* correction to apply */ }; +#define SMALLEST(a, b) (((a) < (b)) ? (a) : (b)) #define BIGGEST(a, b) (((a) > (b)) ? (a) : (b)) +/* This abbreviation means local time is unspecified. */ +static char const UNSPEC[] = "-00"; + +/* How many extra bytes are needed at the end of struct state's chars array. + This needs to be at least 1 for null termination in case the input + data isn't properly terminated, and it also needs to be big enough + for ttunspecified to work without crashing. */ +enum { CHARS_EXTRA = BIGGEST(sizeof UNSPEC, 2) - 1 }; + #ifdef TZNAME_MAX #define MY_TZNAME_MAX TZNAME_MAX #endif /* defined TZNAME_MAX */ @@ -140,63 +136,46 @@ struct state { int timecnt; int typecnt; int charcnt; - int goback; - int goahead; + bool goback; + bool goahead; time_t ats[TZ_MAX_TIMES]; unsigned char types[TZ_MAX_TIMES]; struct ttinfo ttis[TZ_MAX_TYPES]; - char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof gmt), + char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + CHARS_EXTRA, + sizeof gmt), (2 * (MY_TZNAME_MAX + 1)))]; struct lsinfo lsis[TZ_MAX_LEAPS]; + + /* The time type to use for early times or if no transitions. + It is always zero for recent tzdb releases. + It might be nonzero for data from tzdb 2018e or earlier. */ + int defaulttype; +}; + +enum r_type { + JULIAN_DAY, /* Jn = Julian day */ + DAY_OF_YEAR, /* n = day of year */ + MONTH_NTH_DAY_OF_WEEK /* Mm.n.d = month, week, day of week */ }; struct rule { - int r_type; /* type of rule--see below */ + enum r_type r_type; /* type of rule */ int r_day; /* day number of rule */ int r_week; /* week number of rule */ int r_mon; /* month number of rule */ - int32_t r_time; /* transition time of rule */ + int_fast32_t r_time; /* transition time of rule */ }; -#define JULIAN_DAY 0 /* Jn - Julian day */ -#define DAY_OF_YEAR 1 /* n - day of year */ -#define MONTH_NTH_DAY_OF_WEEK 2 /* Mm.n.d - month, week, day of week */ - -/* -** Prototypes for static functions. -*/ - -static int32_t detzcode(const char *); -static time_t detzcode64(const char *); -static int differ_by_repeat(time_t, time_t); -static const char * getzname(const char *); -static const char * getqzname(const char *, const int); -static const char * getnum(const char *, int *, int, int); -static const char * getsecs(const char *, int32_t *); -static const char * getoffset(const char *, int32_t *); -static const char * getrule(const char *, struct rule *); -static void gmtload(struct state *); -static struct tm * gmtsub(const time_t *, int32_t, struct tm *); -static struct tm * localsub(const time_t *, int32_t, struct tm *); -static int increment_overflow(int *, int); -static int leaps_thru_end_of(int); -static int normalize_overflow(int *, int *, int); -static void settzname(void); -static time_t time1(struct tm *, struct tm * (*)(const time_t *, - int32_t, struct tm *), - int32_t); -static time_t time2(struct tm *, struct tm *(*)(const time_t *, - int32_t, struct tm *), - int32_t, int *); -static time_t time2sub(struct tm *, struct tm *(*)(const time_t *, - int32_t, struct tm*), - int32_t, int *, int); -static struct tm * timesub(const time_t *, int32_t, - const struct state *, struct tm *); -static int tmcomp(const struct tm *, const struct tm *); -static time_t transtime(time_t, int, const struct rule *, int32_t); -static int tzload(const char *, struct state *, int); -static int tzparse(const char *, struct state *, int); +static struct tm *gmtsub(struct state const *, time_t const *, int_fast32_t, + struct tm *); +static bool increment_overflow(int *, int); +static bool increment_overflow_time(time_t *, int_fast32_t); +static int_fast32_t leapcorr(struct state const *, time_t); +static bool normalize_overflow32(int_fast32_t *, int *, int); +static struct tm *timesub(time_t const *, int_fast32_t, struct state const *, + struct tm *); +static bool typesequiv(struct state const *, int, int); +static bool tzparse(char const *, struct state *, struct state *); #ifdef ALL_STATE static struct state * lclptr; @@ -216,18 +195,6 @@ static struct state gmtmem; static char lcl_TZname[TZ_STRLEN_MAX + 1]; static int lcl_is_set; -static int gmt_is_set; - -char * tzname[2] /* = { */ -/* wildabbr, */ -/* wildabbr */ -/* } */; - -INITIALIZER(400, _init_localtime, { - memcpy(wildabbr2, wildabbr, sizeof(WILDABBR)); - tzname[0] = wildabbr2; - tzname[1] = wildabbr2; -}) /* ** Section 4.12.3 of X3.159-1989 requires that @@ -239,87 +206,145 @@ INITIALIZER(400, _init_localtime, { static struct tm tm; -#ifdef USG_COMPAT -time_t timezone; +#if 2 <= HAVE_TZNAME + TZ_TIME_T +char * tzname[2] = { + (char *) wildabbr, + (char *) wildabbr +}; +#endif +#if 2 <= USG_COMPAT + TZ_TIME_T +long timezone; int daylight; -#endif /* defined USG_COMPAT */ +#endif +#if 2 <= ALTZONE + TZ_TIME_T +long altzone; +#endif -#ifdef ALTZONE -time_t altzone; -#endif /* defined ALTZONE */ +/* Initialize *S to a value based on UTOFF, ISDST, and DESIGIDX. */ +static void +init_ttinfo(struct ttinfo *s, int_fast32_t utoff, bool isdst, int desigidx) +{ + s->tt_utoff = utoff; + s->tt_isdst = isdst; + s->tt_desigidx = desigidx; + s->tt_ttisstd = false; + s->tt_ttisut = false; +} -static int32_t -detzcode( - const char * const codep -) { - register int32_t result; +/* Return true if SP's time type I does not specify local time. */ +static bool +ttunspecified(struct state const *sp, int i) +{ + char const *abbr = &sp->chars[sp->ttis[i].tt_desigidx]; + /* memcmp is likely faster than strcmp, and is safe due to CHARS_EXTRA. */ + return memcmp(abbr, UNSPEC, sizeof UNSPEC) == 0; +} + +static int_fast32_t +detzcode(const char *const codep) +{ + register int_fast32_t result; register int i; - result = (codep[0] & 0x80) ? ~0L : 0; - for (i = 0; i < 4; ++i) - result = ((unsigned)result << 8) | (codep[i] & 0xff); + int_fast32_t one = 1; + int_fast32_t halfmaxval = one << (32 - 2); + int_fast32_t maxval = halfmaxval - 1 + halfmaxval; + int_fast32_t minval = -1 - maxval; + + result = codep[0] & 0x7f; + for (i = 1; i < 4; ++i) + result = (result << 8) | (codep[i] & 0xff); + + if (codep[0] & 0x80) { + /* Do two's-complement negation even on non-two's-complement machines. + If the result would be minval - 1, return minval. */ + result -= !TWOS_COMPLEMENT(int_fast32_t) && result != 0; + result += minval; + } return result; } -static time_t -detzcode64( - const char * const codep -) { - register time_t result; - register int i; - result = (codep[0] & 0x80) ? (~(int_fast64_t) 0) : 0; - for (i = 0; i < 8; ++i) - result = result * 256 + (codep[i] & 0xff); +static int_fast64_t +detzcode64(const char *const codep) +{ + register int_fast64_t result; + register int i; + int_fast64_t one = 1; + int_fast64_t halfmaxval = one << (64 - 2); + int_fast64_t maxval = halfmaxval - 1 + halfmaxval; + int_fast64_t minval = -TWOS_COMPLEMENT(int_fast64_t) - maxval; + + result = codep[0] & 0x7f; + for (i = 1; i < 8; ++i) + result = (result << 8) | (codep[i] & 0xff); + + if (codep[0] & 0x80) { + /* Do two's-complement negation even on non-two's-complement machines. + If the result would be minval - 1, return minval. */ + result -= !TWOS_COMPLEMENT(int_fast64_t) && result != 0; + result += minval; + } return result; } +static void +update_tzname_etc(struct state const *sp, struct ttinfo const *ttisp) +{ +#if HAVE_TZNAME + tzname[ttisp->tt_isdst] = (char *) &sp->chars[ttisp->tt_desigidx]; +#endif +#if USG_COMPAT + if (!ttisp->tt_isdst) + timezone = - ttisp->tt_utoff; +#endif +#if ALTZONE + if (ttisp->tt_isdst) + altzone = - ttisp->tt_utoff; +#endif +} + static void settzname(void) { - register struct state * sp; - register int i; - sp = lclptr; - tzname[0] = wildabbr2; - tzname[1] = wildabbr2; -#ifdef USG_COMPAT + register struct state * const sp = lclptr; + register int i; + +#if HAVE_TZNAME + tzname[0] = tzname[1] = (char *) (sp ? wildabbr : gmt); +#endif +#if USG_COMPAT daylight = 0; timezone = 0; -#endif /* defined USG_COMPAT */ -#ifdef ALTZONE +#endif +#if ALTZONE altzone = 0; -#endif /* defined ALTZONE */ -#ifdef ALL_STATE +#endif if (sp == NULL) { - tzname[0] = tzname[1] = gmt; return; } -#endif /* defined ALL_STATE */ + /* + ** And to get the latest time zone abbreviations into tzname. . . + */ for (i = 0; i < sp->typecnt; ++i) { register const struct ttinfo * const ttisp = &sp->ttis[i]; - tzname[ttisp->tt_isdst] = - &sp->chars[ttisp->tt_abbrind]; -#ifdef USG_COMPAT - if (ttisp->tt_isdst) - daylight = 1; - if (i == 0 || !ttisp->tt_isdst) - timezone = -(ttisp->tt_gmtoff); -#endif /* defined USG_COMPAT */ -#ifdef ALTZONE - if (i == 0 || ttisp->tt_isdst) - altzone = -(ttisp->tt_gmtoff); -#endif /* defined ALTZONE */ + update_tzname_etc(sp, ttisp); } - /* - ** And to get the latest zone names into tzname. . . - */ for (i = 0; i < sp->timecnt; ++i) { register const struct ttinfo * const ttisp = &sp->ttis[ sp->types[i]]; - tzname[ttisp->tt_isdst] = - &sp->chars[ttisp->tt_abbrind]; + update_tzname_etc(sp, ttisp); +#if USG_COMPAT + if (ttisp->tt_isdst) + daylight = 1; +#endif } +} + +static void +scrub_abbrs(struct state *sp) +{ + int i; /* - ** Finally, scrub the abbreviations. ** First, replace bogus characters. */ for (i = 0; i < sp->charcnt; ++i) @@ -330,330 +355,521 @@ settzname(void) */ for (i = 0; i < sp->typecnt; ++i) { register const struct ttinfo * const ttisp = &sp->ttis[i]; - register char * cp = &sp->chars[ttisp->tt_abbrind]; + char *cp = &sp->chars[ttisp->tt_desigidx]; + if (strlen(cp) > TZ_ABBR_MAX_LEN && strcmp(cp, GRANDPARENTED) != 0) *(cp + TZ_ABBR_MAX_LEN) = '\0'; } } -forceinline int -differ_by_repeat( - const time_t t1, - const time_t t0 -) { - if (TYPE_INTEGRAL(time_t) && - TYPE_BIT(time_t) - TYPE_SIGNED(time_t) < SECSPERREPEAT_BITS) - return 0; - return (t1 - t0) == SECSPERREPEAT; -} +/* Input buffer for data read from a compiled tz file. */ +union input_buffer { + /* The first part of the buffer, interpreted as a header. */ + struct tzhead tzhead; -forceinline int -cmpstr( - 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); -} + /* The entire buffer. */ + char buf[2 * sizeof(struct tzhead) + 2 * sizeof(struct state) + + 4 * TZ_MAX_TIMES]; +}; +/* TZDIR with a trailing '/' rather than a trailing '\0'. */ +static char const tzdirslash[sizeof TZDIR] = TZDIR "/"; + +/* Local storage needed for 'tzloadbody'. */ +union local_storage { + /* The results of analyzing the file's contents after it is opened. */ + struct file_analysis { + /* The input buffer. */ + union input_buffer u; + + /* A temporary state used for parsing a TZ string in the file. */ + struct state st; + } u; + + /* The file name to be opened. */ + char fullname[BIGGEST(sizeof(struct file_analysis), + sizeof tzdirslash + 1024)]; +}; + +/* Load tz data from the file named NAME into *SP. Read extended + format if DOEXTEND. Use *LSP for temporary storage. Return 0 on + success, an errno value on failure. */ static int -typesequiv( - const struct state *sp, - int a, - int b -) { - int result; +tzloadbody(char const *name, struct state *sp, bool doextend, + union local_storage *lsp) +{ + register int i; + register int fid; + register int stored; + register ssize_t nread; + register bool doaccess; + register union input_buffer *up = &lsp->u.u; + register int tzheadsize = sizeof(struct tzhead); + + sp->goback = sp->goahead = false; + + if (! name) { + name = TZDEFAULT; + if (! name) + return EINVAL; + } + + if (name[0] == ':') + ++name; +#ifdef SUPPRESS_TZDIR + /* Do not prepend TZDIR. This is intended for specialized + applications only, due to its security implications. */ + doaccess = true; +#else + doaccess = name[0] == '/'; +#endif + if (!doaccess) { + char const *dot; + size_t namelen = strlen(name); + if (sizeof lsp->fullname - sizeof tzdirslash <= namelen) + return ENAMETOOLONG; + + /* Create a string "TZDIR/NAME". Using sprintf here + would pull in stdio (and would fail if the + resulting string length exceeded INT_MAX!). */ + memcpy(lsp->fullname, tzdirslash, sizeof tzdirslash); + strcpy(lsp->fullname + sizeof tzdirslash, name); + + /* Set doaccess if NAME contains a ".." file name + component, as such a name could read a file outside + the TZDIR virtual subtree. */ + for (dot = name; (dot = strchr(dot, '.')); dot++) + if ((dot == name || dot[-1] == '/') && dot[1] == '.' + && (dot[2] == '/' || !dot[2])) { + doaccess = true; + break; + } + + name = lsp->fullname; + } + if (doaccess && access(name, R_OK) != 0) + return errno; + fid = open(name, O_RDONLY); + if (fid < 0) + return errno; + + nread = read(fid, up->buf, sizeof up->buf); + if (nread < tzheadsize) { + int err = nread < 0 ? errno : EINVAL; + close(fid); + return err; + } + if (close(fid) < 0) + return errno; + for (stored = 4; stored <= 8; stored *= 2) { + char version = up->tzhead.tzh_version[0]; + bool skip_datablock = stored == 4 && version; + int_fast32_t datablock_size; + int_fast32_t ttisstdcnt = detzcode(up->tzhead.tzh_ttisstdcnt); + int_fast32_t ttisutcnt = detzcode(up->tzhead.tzh_ttisutcnt); + int_fast64_t prevtr = -1; + int_fast32_t prevcorr = 0; + int_fast32_t leapcnt = detzcode(up->tzhead.tzh_leapcnt); + int_fast32_t timecnt = detzcode(up->tzhead.tzh_timecnt); + int_fast32_t typecnt = detzcode(up->tzhead.tzh_typecnt); + int_fast32_t charcnt = detzcode(up->tzhead.tzh_charcnt); + char const *p = up->buf + tzheadsize; + /* Although tzfile(5) currently requires typecnt to be nonzero, + support future formats that may allow zero typecnt + in files that have a TZ string and no transitions. */ + if (! (0 <= leapcnt && leapcnt < TZ_MAX_LEAPS + && 0 <= typecnt && typecnt < TZ_MAX_TYPES + && 0 <= timecnt && timecnt < TZ_MAX_TIMES + && 0 <= charcnt && charcnt < TZ_MAX_CHARS + && 0 <= ttisstdcnt && ttisstdcnt < TZ_MAX_TYPES + && 0 <= ttisutcnt && ttisutcnt < TZ_MAX_TYPES)) + return EINVAL; + datablock_size + = (timecnt * stored /* ats */ + + timecnt /* types */ + + typecnt * 6 /* ttinfos */ + + charcnt /* chars */ + + leapcnt * (stored + 4) /* lsinfos */ + + ttisstdcnt /* ttisstds */ + + ttisutcnt); /* ttisuts */ + if (nread < tzheadsize + datablock_size) + return EINVAL; + if (skip_datablock) + p += datablock_size; + else { + if (! ((ttisstdcnt == typecnt || ttisstdcnt == 0) + && (ttisutcnt == typecnt || ttisutcnt == 0))) + return EINVAL; + + sp->leapcnt = leapcnt; + sp->timecnt = timecnt; + sp->typecnt = typecnt; + sp->charcnt = charcnt; + + /* Read transitions, discarding those out of time_t range. + But pretend the last transition before TIME_T_MIN + occurred at TIME_T_MIN. */ + timecnt = 0; + for (i = 0; i < sp->timecnt; ++i) { + int_fast64_t at + = stored == 4 ? detzcode(p) : detzcode64(p); + sp->types[i] = at <= TIME_T_MAX; + if (sp->types[i]) { + time_t attime + = ((TYPE_SIGNED(time_t) ? at < TIME_T_MIN : at < 0) + ? TIME_T_MIN : at); + if (timecnt && attime <= sp->ats[timecnt - 1]) { + if (attime < sp->ats[timecnt - 1]) + return EINVAL; + sp->types[i - 1] = 0; + timecnt--; + } + sp->ats[timecnt++] = attime; + } + p += stored; + } + + timecnt = 0; + for (i = 0; i < sp->timecnt; ++i) { + unsigned char typ = *p++; + if (sp->typecnt <= typ) + return EINVAL; + if (sp->types[i]) + sp->types[timecnt++] = typ; + } + sp->timecnt = timecnt; + for (i = 0; i < sp->typecnt; ++i) { + register struct ttinfo * ttisp; + unsigned char isdst, desigidx; + + ttisp = &sp->ttis[i]; + ttisp->tt_utoff = detzcode(p); + p += 4; + isdst = *p++; + if (! (isdst < 2)) + return EINVAL; + ttisp->tt_isdst = isdst; + desigidx = *p++; + if (! (desigidx < sp->charcnt)) + return EINVAL; + ttisp->tt_desigidx = desigidx; + } + for (i = 0; i < sp->charcnt; ++i) + sp->chars[i] = *p++; + /* Ensure '\0'-terminated, and make it safe to call + ttunspecified later. */ + memset(&sp->chars[i], 0, CHARS_EXTRA); + + /* Read leap seconds, discarding those out of time_t range. */ + leapcnt = 0; + for (i = 0; i < sp->leapcnt; ++i) { + int_fast64_t tr = stored == 4 ? detzcode(p) : detzcode64(p); + int_fast32_t corr = detzcode(p + stored); + p += stored + 4; + + /* Leap seconds cannot occur before the Epoch, + or out of order. */ + if (tr <= prevtr) + return EINVAL; + + /* To avoid other botches in this code, each leap second's + correction must differ from the previous one's by 1 + second or less, except that the first correction can be + any value; these requirements are more generous than + RFC 8536, to allow future RFC extensions. */ + if (! (i == 0 + || (prevcorr < corr + ? corr == prevcorr + 1 + : (corr == prevcorr + || corr == prevcorr - 1)))) + return EINVAL; + prevtr = tr; + prevcorr = corr; + + if (tr <= TIME_T_MAX) { + sp->lsis[leapcnt].ls_trans = tr; + sp->lsis[leapcnt].ls_corr = corr; + leapcnt++; + } + } + sp->leapcnt = leapcnt; + + for (i = 0; i < sp->typecnt; ++i) { + register struct ttinfo * ttisp; + + ttisp = &sp->ttis[i]; + if (ttisstdcnt == 0) + ttisp->tt_ttisstd = false; + else { + if (*p != true && *p != false) + return EINVAL; + ttisp->tt_ttisstd = *p++; + } + } + for (i = 0; i < sp->typecnt; ++i) { + register struct ttinfo * ttisp; + + ttisp = &sp->ttis[i]; + if (ttisutcnt == 0) + ttisp->tt_ttisut = false; + else { + if (*p != true && *p != false) + return EINVAL; + ttisp->tt_ttisut = *p++; + } + } + } + + nread -= p - up->buf; + memmove(up->buf, p, nread); + + /* If this is an old file, we're done. */ + if (!version) + break; + } + if (doextend && nread > 2 && + up->buf[0] == '\n' && up->buf[nread - 1] == '\n' && + sp->typecnt + 2 <= TZ_MAX_TYPES) { + struct state *ts = &lsp->u.st; + + up->buf[nread - 1] = '\0'; + if (tzparse(&up->buf[1], ts, sp)) { + + /* Attempt to reuse existing abbreviations. + Without this, America/Anchorage would be right on + the edge after 2037 when TZ_MAX_CHARS is 50, as + sp->charcnt equals 40 (for LMT AST AWT APT AHST + AHDT YST AKDT AKST) and ts->charcnt equals 10 + (for AKST AKDT). Reusing means sp->charcnt can + stay 40 in this example. */ + int gotabbr = 0; + int charcnt = sp->charcnt; + for (i = 0; i < ts->typecnt; i++) { + char *tsabbr = ts->chars + ts->ttis[i].tt_desigidx; + int j; + for (j = 0; j < charcnt; j++) + if (strcmp(sp->chars + j, tsabbr) == 0) { + ts->ttis[i].tt_desigidx = j; + gotabbr++; + break; + } + if (! (j < charcnt)) { + int tsabbrlen = strlen(tsabbr); + if (j + tsabbrlen < TZ_MAX_CHARS) { + strcpy(sp->chars + j, tsabbr); + charcnt = j + tsabbrlen + 1; + ts->ttis[i].tt_desigidx = j; + gotabbr++; + } + } + } + if (gotabbr == ts->typecnt) { + sp->charcnt = charcnt; + + /* Ignore any trailing, no-op transitions generated + by zic as they don't help here and can run afoul + of bugs in zic 2016j or earlier. */ + while (1 < sp->timecnt + && (sp->types[sp->timecnt - 1] + == sp->types[sp->timecnt - 2])) + sp->timecnt--; + + for (i = 0; + i < ts->timecnt && sp->timecnt < TZ_MAX_TIMES; + i++) { + time_t t = ts->ats[i]; + if (increment_overflow_time(&t, leapcorr(sp, t)) + || (0 < sp->timecnt + && t <= sp->ats[sp->timecnt - 1])) + continue; + sp->ats[sp->timecnt] = t; + sp->types[sp->timecnt] = (sp->typecnt + + ts->types[i]); + sp->timecnt++; + } + for (i = 0; i < ts->typecnt; i++) + sp->ttis[sp->typecnt++] = ts->ttis[i]; + } + } + } + if (sp->typecnt == 0) + return EINVAL; + if (sp->timecnt > 1) { + if (sp->ats[0] <= TIME_T_MAX - SECSPERREPEAT) { + time_t repeatat = sp->ats[0] + SECSPERREPEAT; + int repeattype = sp->types[0]; + for (i = 1; i < sp->timecnt; ++i) + if (sp->ats[i] == repeatat + && typesequiv(sp, sp->types[i], repeattype)) { + sp->goback = true; + break; + } + } + if (TIME_T_MIN + SECSPERREPEAT <= sp->ats[sp->timecnt - 1]) { + time_t repeatat = sp->ats[sp->timecnt - 1] - SECSPERREPEAT; + int repeattype = sp->types[sp->timecnt - 1]; + for (i = sp->timecnt - 2; i >= 0; --i) + if (sp->ats[i] == repeatat + && typesequiv(sp, sp->types[i], repeattype)) { + sp->goahead = true; + break; + } + } + } + + /* Infer sp->defaulttype from the data. Although this default + type is always zero for data from recent tzdb releases, + things are trickier for data from tzdb 2018e or earlier. + + The first set of heuristics work around bugs in 32-bit data + generated by tzdb 2013c or earlier. The workaround is for + zones like Australia/Macquarie where timestamps before the + first transition have a time type that is not the earliest + standard-time type. See: + https://mm.icann.org/pipermail/tz/2013-May/019368.html */ + /* + ** If type 0 does not specify local time, or is unused in transitions, + ** it's the type to use for early times. + */ + for (i = 0; i < sp->timecnt; ++i) + if (sp->types[i] == 0) + break; + i = i < sp->timecnt && ! ttunspecified(sp, 0) ? -1 : 0; + /* + ** Absent the above, + ** if there are transition times + ** and the first transition is to a daylight time + ** find the standard type less than and closest to + ** the type of the first transition. + */ + if (i < 0 && sp->timecnt > 0 && sp->ttis[sp->types[0]].tt_isdst) { + i = sp->types[0]; + while (--i >= 0) + if (!sp->ttis[i].tt_isdst) + break; + } + /* The next heuristics are for data generated by tzdb 2018e or + earlier, for zones like EST5EDT where the first transition + is to DST. */ + /* + ** If no result yet, find the first standard type. + ** If there is none, punt to type zero. + */ + if (i < 0) { + i = 0; + while (sp->ttis[i].tt_isdst) + if (++i >= sp->typecnt) { + i = 0; + break; + } + } + /* A simple 'sp->defaulttype = 0;' would suffice here if we + didn't have to worry about 2018e-or-earlier data. Even + simpler would be to remove the defaulttype member and just + use 0 in its place. */ + sp->defaulttype = i; + + return 0; +} + +/* Load tz data from the file named NAME into *SP. Read extended + format if DOEXTEND. Return 0 on success, an errno value on failure. */ +static int +tzload(char const *name, struct state *sp, bool doextend) +{ +#ifdef ALL_STATE + union local_storage *lsp = malloc(sizeof *lsp); + if (!lsp) { + return HAVE_MALLOC_ERRNO ? errno : ENOMEM; + } else { + int err = tzloadbody(name, sp, doextend, lsp); + free(lsp); + return err; + } +#else + int i; + volatile char *p; + volatile unsigned x; + union local_storage ls; /* 70+ kilobytes */ + p = (char *)&ls; + for (x = i = 0; i < sizeof(ls); i += 4096) { + x += p[i]; /* make sure tzdata doesn't smash the stack */ + } + return tzloadbody(name, sp, doextend, &ls); +#endif +} + +static bool +typesequiv(const struct state *sp, int a, int b) +{ + register bool result; + if (sp == NULL || - a < 0 || a >= sp->typecnt || - b < 0 || b >= sp->typecnt) - result = FALSE; + a < 0 || a >= sp->typecnt || + b < 0 || b >= sp->typecnt) + result = false; else { - const struct ttinfo * ap = &sp->ttis[a]; - const struct ttinfo * bp = &sp->ttis[b]; - result = ap->tt_gmtoff == bp->tt_gmtoff && - ap->tt_isdst == bp->tt_isdst && - ap->tt_ttisstd == bp->tt_ttisstd && - ap->tt_ttisgmt == bp->tt_ttisgmt && - cmpstr(&sp->chars[ap->tt_abbrind], - &sp->chars[bp->tt_abbrind]) == 0; + register const struct ttinfo * ap = &sp->ttis[a]; + register const struct ttinfo * bp = &sp->ttis[b]; + result = (ap->tt_utoff == bp->tt_utoff + && ap->tt_isdst == bp->tt_isdst + && ap->tt_ttisstd == bp->tt_ttisstd + && ap->tt_ttisut == bp->tt_ttisut + && (strcmp(&sp->chars[ap->tt_desigidx], + &sp->chars[bp->tt_desigidx]) + == 0)); } return result; } -static int -tzload( - const char * name, - struct state * const sp, - const int doextend -) { - register const char * p; - register int i; - register int fid; - register int stored; - register int nread; - union { - struct tzhead tzhead; - char buf[2 * sizeof(struct tzhead) + - 2 * sizeof *sp + - 4 * TZ_MAX_TIMES]; - } * up; - char fullname[PATH_MAX+1]; - - up = calloc(1, sizeof *up); - if (up == NULL) - return -1; - - sp->goback = sp->goahead = FALSE; - if (name != NULL /* && issetugid() != 0 */) { - if ((name[0] == ':' && (strchr(name, '/'))) || - name[0] == '/' || strchr(name, '.')) - name = NULL; - } - if (name == NULL && (name = TZDEFAULT) == NULL) - goto oops; - - if (name[0] == ':') - ++name; - if (name[0] != '/') { - if ((p = TZDIR) == NULL) - goto oops; - if ((strlen(p) + strlen(name) + 1) >= sizeof fullname) - goto oops; - strlcpy(fullname, p, sizeof fullname); - strlcat(fullname, "/", sizeof fullname); - strlcat(fullname, name, sizeof fullname); - name = fullname; - } - if ((fid = open(name, O_RDONLY)) == -1) - goto oops; - - nread = read(fid, up->buf, sizeof up->buf); - if (close(fid) < 0 || nread <= 0) - goto oops; - for (stored = 4; stored <= 8; stored *= 2) { - int ttisstdcnt; - int ttisgmtcnt; - - ttisstdcnt = (int) detzcode(up->tzhead.tzh_ttisstdcnt); - ttisgmtcnt = (int) detzcode(up->tzhead.tzh_ttisgmtcnt); - sp->leapcnt = (int) detzcode(up->tzhead.tzh_leapcnt); - sp->timecnt = (int) detzcode(up->tzhead.tzh_timecnt); - sp->typecnt = (int) detzcode(up->tzhead.tzh_typecnt); - sp->charcnt = (int) detzcode(up->tzhead.tzh_charcnt); - p = up->tzhead.tzh_charcnt + sizeof up->tzhead.tzh_charcnt; - if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS || - sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES || - sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES || - sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS || - (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) || - (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0)) - goto oops; - if (nread - (p - up->buf) < - sp->timecnt * stored + /* ats */ - sp->timecnt + /* types */ - sp->typecnt * 6 + /* ttinfos */ - sp->charcnt + /* chars */ - sp->leapcnt * (stored + 4) + /* lsinfos */ - ttisstdcnt + /* ttisstds */ - ttisgmtcnt) /* ttisgmts */ - goto oops; - for (i = 0; i < sp->timecnt; ++i) { - sp->ats[i] = (stored == 4) ? - detzcode(p) : detzcode64(p); - p += stored; - } - for (i = 0; i < sp->timecnt; ++i) { - sp->types[i] = (unsigned char) *p++; - if (sp->types[i] >= sp->typecnt) - goto oops; - } - for (i = 0; i < sp->typecnt; ++i) { - struct ttinfo * ttisp; - - ttisp = &sp->ttis[i]; - ttisp->tt_gmtoff = detzcode(p); - p += 4; - ttisp->tt_isdst = (unsigned char) *p++; - if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1) - goto oops; - ttisp->tt_abbrind = (unsigned char) *p++; - if (ttisp->tt_abbrind < 0 || - ttisp->tt_abbrind > sp->charcnt) - goto oops; - } - for (i = 0; i < sp->charcnt; ++i) - sp->chars[i] = *p++; - sp->chars[i] = '\0'; /* ensure '\0' at end */ - for (i = 0; i < sp->leapcnt; ++i) { - struct lsinfo * lsisp; - - lsisp = &sp->lsis[i]; - lsisp->ls_trans = (stored == 4) ? - detzcode(p) : detzcode64(p); - p += stored; - lsisp->ls_corr = detzcode(p); - p += 4; - } - for (i = 0; i < sp->typecnt; ++i) { - struct ttinfo * ttisp; - - ttisp = &sp->ttis[i]; - if (ttisstdcnt == 0) - ttisp->tt_ttisstd = FALSE; - else { - ttisp->tt_ttisstd = *p++; - if (ttisp->tt_ttisstd != TRUE && - ttisp->tt_ttisstd != FALSE) - goto oops; - } - } - for (i = 0; i < sp->typecnt; ++i) { - struct ttinfo * ttisp; - - ttisp = &sp->ttis[i]; - if (ttisgmtcnt == 0) - ttisp->tt_ttisgmt = FALSE; - else { - ttisp->tt_ttisgmt = *p++; - if (ttisp->tt_ttisgmt != TRUE && - ttisp->tt_ttisgmt != FALSE) - goto oops; - } - } - /* - ** Out-of-sort ats should mean we're running on a - ** signed time_t system but using a data file with - ** unsigned values (or vice versa). - */ - for (i = 0; i < sp->timecnt - 2; ++i) - if (sp->ats[i] > sp->ats[i + 1]) { - ++i; - /* - ** Ignore the end (easy). - */ - sp->timecnt = i; - break; - } - /* - ** If this is an old file, we're done. - */ - if (up->tzhead.tzh_version[0] == '\0') - break; - nread -= p - up->buf; - for (i = 0; i < nread; ++i) - up->buf[i] = p[i]; - /* - ** If this is a narrow integer time_t system, we're done. - */ - if (stored >= sizeof(time_t)) - break; - } - if (doextend && nread > 2 && - up->buf[0] == '\n' && up->buf[nread - 1] == '\n' && - sp->typecnt + 2 <= TZ_MAX_TYPES) { - struct state *ts; - int result; - ts = calloc(1, sizeof(struct state)); - if (!ts) abort(); - up->buf[nread - 1] = '\0'; - result = tzparse(&up->buf[1], ts, FALSE); - if (result == 0 && ts->typecnt == 2 && - sp->charcnt + ts->charcnt <= TZ_MAX_CHARS) { - for (i = 0; i < 2; ++i) - ts->ttis[i].tt_abbrind += - sp->charcnt; - for (i = 0; i < ts->charcnt; ++i) - sp->chars[sp->charcnt++] = - ts->chars[i]; - i = 0; - while (i < ts->timecnt && - ts->ats[i] <= - sp->ats[sp->timecnt - 1]) - ++i; - while (i < ts->timecnt && - sp->timecnt < TZ_MAX_TIMES) { - sp->ats[sp->timecnt] = - ts->ats[i]; - sp->types[sp->timecnt] = - sp->typecnt + - ts->types[i]; - ++sp->timecnt; - ++i; - } - sp->ttis[sp->typecnt++] = ts->ttis[0]; - sp->ttis[sp->typecnt++] = ts->ttis[1]; - } - free(ts); - } - if (sp->timecnt > 1) { - for (i = 1; i < sp->timecnt; ++i) { - if (typesequiv(sp, sp->types[i], sp->types[0]) && - differ_by_repeat(sp->ats[i], sp->ats[0])) { - sp->goback = TRUE; - break; - } - } - for (i = sp->timecnt - 2; i >= 0; --i) { - if (typesequiv(sp, sp->types[sp->timecnt - 1], - sp->types[i]) && - differ_by_repeat(sp->ats[sp->timecnt - 1], - sp->ats[i])) { - sp->goahead = TRUE; - break; - } - } - } - free(up); - return 0; -oops: - free(up); - return -1; -} - -static const unsigned char kMonthLengths[2][MONSPERYEAR] = { +static const int mon_lengths[2][MONSPERYEAR] = { { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } }; -static const int kYearLengths[2] = { +static const int year_lengths[2] = { DAYSPERNYEAR, DAYSPERLYEAR }; +/* Is C an ASCII digit? */ +static inline bool +is_digit(char c) +{ + return '0' <= c && c <= '9'; +} + /* -** Given a pointer into a time zone string, scan until a character that is not -** a valid character in a zone name is found. Return a pointer to that -** character. +** Given a pointer into a timezone string, scan until a character that is not +** a valid character in a time zone abbreviation is found. +** Return a pointer to that character. */ -static const char * -getzname( - const char * strp -) { - char c; - while ((c = *strp) != '\0' && !isdigit(c) && c != ',' && c != '-' && - c != '+') { - ++strp; - } +static nosideeffect const char * +getzname(register const char *strp) +{ + register char c; + + while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' && + c != '+') + ++strp; return strp; } /* -** Given a pointer into an extended time zone string, scan until the ending -** delimiter of the zone name is located. Return a pointer to the delimiter. +** Given a pointer into an extended timezone string, scan until the ending +** delimiter of the time zone abbreviation is located. +** Return a pointer to the delimiter. ** ** As with getzname above, the legal character set is actually quite ** restricted, with other characters producing undefined results. ** We don't do any checking here; checking is done later in common-case code. */ -static const char * -getqzname( - const char * strp, - const int delim -) { - register int c; +static nosideeffect const char * +getqzname(register const char *strp, const int delim) +{ + register int c; while ((c = *strp) != '\0' && c != delim) ++strp; @@ -661,23 +877,19 @@ getqzname( } /* -** Given a pointer into a time zone string, extract a number from that string. +** Given a pointer into a timezone string, extract a number from that string. ** Check that the number is within a specified range; if it is not, return ** NULL. ** Otherwise, return a pointer to the first character not part of the number. */ static const char * -getnum( - const char * strp, - int * const nump, - const int min, - const int max -) { - register char c; - register int num; +getnum(register const char *strp, int *const nump, const int min, const int max) +{ + register char c; + register int num; - if (strp == NULL || !isdigit(c = *strp)) + if (strp == NULL || !is_digit(c = *strp)) return NULL; num = 0; do { @@ -685,7 +897,7 @@ getnum( if (num > max) return NULL; /* illegal value */ c = *++strp; - } while (isdigit(c)); + } while (is_digit(c)); if (num < min) return NULL; /* illegal value */ *nump = num; @@ -693,7 +905,7 @@ getnum( } /* -** Given a pointer into a time zone string, extract a number of seconds, +** Given a pointer into a timezone string, extract a number of seconds, ** in hh[:mm[:ss]] form, from the string. ** If any error occurs, return NULL. ** Otherwise, return a pointer to the first character not part of the number @@ -701,21 +913,21 @@ getnum( */ static const char * -getsecs( - const char * strp, - int32_t * const secsp -) { - int num; +getsecs(register const char *strp, int_fast32_t *const secsp) +{ + int num; + int_fast32_t secsperhour = SECSPERHOUR; + /* - ** `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like + ** 'HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like ** "M10.4.6/26", which does not conform to Posix, ** but which specifies the equivalent of - ** ``02:00 on the first Sunday on or after 23 Oct''. + ** "02:00 on the first Sunday on or after 23 Oct". */ strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1); if (strp == NULL) return NULL; - *secsp = num * (int32_t) SECSPERHOUR; + *secsp = num * secsperhour; if (*strp == ':') { ++strp; strp = getnum(strp, &num, 0, MINSPERHOUR - 1); @@ -724,7 +936,7 @@ getsecs( *secsp += num * SECSPERMIN; if (*strp == ':') { ++strp; - /* `SECSPERMIN' allows for leap seconds. */ + /* 'SECSPERMIN' allows for leap seconds. */ strp = getnum(strp, &num, 0, SECSPERMIN); if (strp == NULL) return NULL; @@ -735,20 +947,19 @@ getsecs( } /* -** Given a pointer into a time zone string, extract an offset, in +** Given a pointer into a timezone string, extract an offset, in ** [+-]hh[:mm[:ss]] form, from the string. ** If any error occurs, return NULL. ** Otherwise, return a pointer to the first character not part of the time. */ static const char * -getoffset( - const char * strp, - int32_t * const offsetp -) { - register int neg = 0; +getoffset(register const char *strp, int_fast32_t *const offsetp) +{ + register bool neg = false; + if (*strp == '-') { - neg = 1; + neg = true; ++strp; } else if (*strp == '+') ++strp; @@ -761,17 +972,15 @@ getoffset( } /* -** Given a pointer into a time zone string, extract a rule in the form +** Given a pointer into a timezone string, extract a rule in the form ** date[/time]. See POSIX section 8 for the format of "date" and "time". ** If a valid rule is not found, return NULL. ** Otherwise, return a pointer to the first character not part of the rule. */ static const char * -getrule( - const char * strp, - struct rule * const rulep -) { +getrule(const char *strp, register struct rule *const rulep) +{ if (*strp == 'J') { /* ** Julian day. @@ -796,7 +1005,7 @@ getrule( if (*strp++ != '.') return NULL; strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1); - } else if (isdigit(*strp)) { + } else if (is_digit(*strp)) { /* ** Day of year. */ @@ -810,30 +1019,25 @@ getrule( ** Time specified. */ ++strp; - strp = getsecs(strp, &rulep->r_time); + strp = getoffset(strp, &rulep->r_time); } else rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */ return strp; } /* -** Given the Epoch-relative time of January 1, 00:00:00 UTC, in a year, the -** year, a rule, and the offset from UTC at the time that rule takes effect, -** calculate the Epoch-relative time that rule takes effect. +** Given a year, a rule, and the offset from UT at the time that rule takes +** effect, calculate the year-relative time that rule takes effect. */ -static time_t -transtime( - const time_t janfirst, - const int year, - 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; +static int_fast32_t +transtime(const int year, register const struct rule *const rulep, + const int_fast32_t offset) +{ + register bool leapyear; + register int_fast32_t value; + register int i; + int d, m1, yy0, yy1, yy2, dow; - INITIALIZE(value); leapyear = isleap(year); switch (rulep->r_type) { @@ -845,7 +1049,7 @@ transtime( ** add SECSPERDAY times the day number-1 to the time of ** January 1, midnight, to get the day. */ - value = janfirst + (rulep->r_day - 1) * SECSPERDAY; + value = (rulep->r_day - 1) * SECSPERDAY; if (leapyear && rulep->r_day >= 60) value += SECSPERDAY; break; @@ -856,16 +1060,13 @@ transtime( ** Just add SECSPERDAY times the day number to the time of ** January 1, midnight, to get the day. */ - value = janfirst + rulep->r_day * SECSPERDAY; + value = rulep->r_day * SECSPERDAY; break; case MONTH_NTH_DAY_OF_WEEK: /* ** Mm.n.d - nth "dth day" of month m. */ - value = janfirst; - for (i = 0; i < rulep->r_mon - 1; ++i) - value += kMonthLengths[leapyear][i] * SECSPERDAY; /* ** Use Zeller's Congruence to get day-of-week of first day of @@ -890,7 +1091,7 @@ transtime( d += DAYSPERWEEK; for (i = 1; i < rulep->r_week; ++i) { if (d + DAYSPERWEEK >= - kMonthLengths[leapyear][rulep->r_mon - 1]) + mon_lengths[leapyear][rulep->r_mon - 1]) break; d += DAYSPERWEEK; } @@ -898,15 +1099,19 @@ transtime( /* ** "d" is the day-of-month (zero-origin) of the day we want. */ - value += d * SECSPERDAY; + value = d * SECSPERDAY; + for (i = 0; i < rulep->r_mon - 1; ++i) + value += mon_lengths[leapyear][i] * SECSPERDAY; break; + + default: UNREACHABLE(); } /* - ** "value" is the Epoch-relative time of 00:00:00 UTC on the day in - ** question. To get the Epoch-relative time of the specified local + ** "value" is the year-relative time of 00:00:00 UT on the day in + ** question. To get the year-relative time of the specified local ** time on that day, add the transition time and the current offset - ** from UTC. + ** from UT. */ return value + rulep->r_time + offset; } @@ -916,142 +1121,194 @@ transtime( ** appropriate. */ -static int -tzparse( - const char * name, - struct state * const sp, - const int lastditch -) { - const char * stdname; - const char * dstname; - size_t stdlen; - size_t dstlen; - int32_t stdoffset; - int32_t dstoffset; - register time_t * atp; - register unsigned char *typep; - register char * cp; - register int load_result; +static bool +tzparse(const char *name, struct state *sp, struct state *basep) +{ + const char * stdname; + const char * dstname; + size_t stdlen; + size_t dstlen; + size_t charcnt; + int_fast32_t stdoffset; + int_fast32_t dstoffset; + register char * cp; + register bool load_ok; + time_t atlo = TIME_T_MIN, leaplo = TIME_T_MIN; - INITIALIZE(dstname); stdname = name; - if (lastditch) { - stdlen = strlen(name); /* length of standard zone name */ - name += stdlen; - if (stdlen >= sizeof sp->chars) - stdlen = (sizeof sp->chars) - 1; - stdoffset = 0; + if (*name == '<') { + name++; + stdname = name; + name = getqzname(name, '>'); + if (*name != '>') + return false; + stdlen = name - stdname; + name++; } else { - if (*name == '<') { - name++; - stdname = name; - name = getqzname(name, '>'); - if (*name != '>') - return (-1); - stdlen = name - stdname; - name++; - } else { - name = getzname(name); - stdlen = name - stdname; - } - if (*name == '\0') - return -1; - name = getoffset(name, &stdoffset); - if (name == NULL) - return -1; + name = getzname(name); + stdlen = name - stdname; } - load_result = tzload(TZDEFRULES, sp, FALSE); - if (load_result != 0) - sp->leapcnt = 0; /* so, we're off a little */ - sp->timecnt = 0; + if (!stdlen) + return false; + name = getoffset(name, &stdoffset); + if (name == NULL) + return false; + charcnt = stdlen + 1; + if (sizeof sp->chars < charcnt) + return false; + if (basep) { + if (0 < basep->timecnt) + atlo = basep->ats[basep->timecnt - 1]; + load_ok = false; + sp->leapcnt = basep->leapcnt; + memcpy(sp->lsis, basep->lsis, sp->leapcnt * sizeof *sp->lsis); + } else { + load_ok = tzload(TZDEFRULES, sp, false) == 0; + if (!load_ok) + sp->leapcnt = 0; /* So, we're off a little. */ + } + if (0 < sp->leapcnt) + leaplo = sp->lsis[sp->leapcnt - 1].ls_trans; if (*name != '\0') { if (*name == '<') { dstname = ++name; name = getqzname(name, '>'); if (*name != '>') - return -1; + return false; dstlen = name - dstname; name++; } else { dstname = name; name = getzname(name); - dstlen = name - dstname; /* length of DST zone name */ + dstlen = name - dstname; /* length of DST abbr. */ } + if (!dstlen) + return false; + charcnt += dstlen + 1; + if (sizeof sp->chars < charcnt) + return false; if (*name != '\0' && *name != ',' && *name != ';') { name = getoffset(name, &dstoffset); if (name == NULL) - return -1; + return false; } else dstoffset = stdoffset - SECSPERHOUR; - if (*name == '\0' && load_result != 0) + if (*name == '\0' && !load_ok) name = TZDEFRULESTRING; if (*name == ',' || *name == ';') { struct rule start; struct rule end; register int year; - register time_t janfirst; - time_t starttime; - time_t endtime; + register int timecnt; + time_t janfirst; + int_fast32_t janoffset = 0; + int yearbeg, yearlim; ++name; if ((name = getrule(name, &start)) == NULL) - return -1; + return false; if (*name++ != ',') - return -1; + return false; if ((name = getrule(name, &end)) == NULL) - return -1; + return false; if (*name != '\0') - return -1; + return false; sp->typecnt = 2; /* standard time and DST */ /* ** Two transitions per year, from EPOCH_YEAR forward. */ - sp->ttis[0].tt_gmtoff = -dstoffset; - sp->ttis[0].tt_isdst = 1; - sp->ttis[0].tt_abbrind = stdlen + 1; - sp->ttis[1].tt_gmtoff = -stdoffset; - sp->ttis[1].tt_isdst = 0; - sp->ttis[1].tt_abbrind = 0; - atp = sp->ats; - typep = sp->types; + init_ttinfo(&sp->ttis[0], -stdoffset, false, 0); + init_ttinfo(&sp->ttis[1], -dstoffset, true, stdlen + 1); + sp->defaulttype = 0; + timecnt = 0; janfirst = 0; - for (year = EPOCH_YEAR; - sp->timecnt + 2 <= TZ_MAX_TIMES; - ++year) { - time_t newfirst; + yearbeg = EPOCH_YEAR; - starttime = transtime(janfirst, year, &start, - stdoffset); - endtime = transtime(janfirst, year, &end, - dstoffset); - if (starttime > endtime) { - *atp++ = endtime; - *typep++ = 1; /* DST ends */ - *atp++ = starttime; - *typep++ = 0; /* DST begins */ - } else { - *atp++ = starttime; - *typep++ = 0; /* DST begins */ - *atp++ = endtime; - *typep++ = 1; /* DST ends */ - } - sp->timecnt += 2; - newfirst = janfirst; - newfirst += kYearLengths[isleap(year)] * - SECSPERDAY; - if (newfirst <= janfirst) - break; - janfirst = newfirst; + do { + int_fast32_t yearsecs + = year_lengths[isleap(yearbeg - 1)] * SECSPERDAY; + yearbeg--; + if (increment_overflow_time(&janfirst, -yearsecs)) { + janoffset = -yearsecs; + break; + } + } while (atlo < janfirst + && EPOCH_YEAR - YEARSPERREPEAT / 2 < yearbeg); + + while (true) { + int_fast32_t yearsecs + = year_lengths[isleap(yearbeg)] * SECSPERDAY; + int yearbeg1 = yearbeg; + time_t janfirst1 = janfirst; + if (increment_overflow_time(&janfirst1, yearsecs) + || increment_overflow(&yearbeg1, 1) + || atlo <= janfirst1) + break; + yearbeg = yearbeg1; + janfirst = janfirst1; } + + yearlim = yearbeg; + if (increment_overflow(&yearlim, YEARSPERREPEAT + 1)) + yearlim = INT_MAX; + for (year = yearbeg; year < yearlim; year++) { + int_fast32_t + starttime = transtime(year, &start, stdoffset), + endtime = transtime(year, &end, dstoffset); + int_fast32_t + yearsecs = (year_lengths[isleap(year)] + * SECSPERDAY); + bool reversed = endtime < starttime; + if (reversed) { + int_fast32_t swap = starttime; + starttime = endtime; + endtime = swap; + } + if (reversed + || (starttime < endtime + && endtime - starttime < yearsecs)) { + if (TZ_MAX_TIMES - 2 < timecnt) + break; + sp->ats[timecnt] = janfirst; + if (! increment_overflow_time + (&sp->ats[timecnt], + janoffset + starttime) + && atlo <= sp->ats[timecnt]) + sp->types[timecnt++] = !reversed; + sp->ats[timecnt] = janfirst; + if (! increment_overflow_time + (&sp->ats[timecnt], + janoffset + endtime) + && atlo <= sp->ats[timecnt]) { + sp->types[timecnt++] = reversed; + } + } + if (endtime < leaplo) { + yearlim = year; + if (increment_overflow(&yearlim, + YEARSPERREPEAT + 1)) + yearlim = INT_MAX; + } + if (increment_overflow_time + (&janfirst, janoffset + yearsecs)) + break; + janoffset = 0; + } + sp->timecnt = timecnt; + if (! timecnt) { + sp->ttis[0] = sp->ttis[1]; + sp->typecnt = 1; /* Perpetual DST. */ + } else if (YEARSPERREPEAT < year - yearbeg) + sp->goback = sp->goahead = true; } else { - register int32_t theirstdoffset; - register int32_t theirdstoffset; - register int32_t theiroffset; - register int isdst; + register int_fast32_t theirstdoffset; + register int_fast32_t theirdstoffset; + register int_fast32_t theiroffset; + register bool isdst; register int i; register int j; if (*name != '\0') - return -1; + return false; /* ** Initial values of theirstdoffset and theirdstoffset. */ @@ -1060,7 +1317,7 @@ tzparse( j = sp->types[i]; if (!sp->ttis[j].tt_isdst) { theirstdoffset = - -sp->ttis[j].tt_gmtoff; + - sp->ttis[j].tt_utoff; break; } } @@ -1069,15 +1326,14 @@ tzparse( j = sp->types[i]; if (sp->ttis[j].tt_isdst) { theirdstoffset = - -sp->ttis[j].tt_gmtoff; + - sp->ttis[j].tt_utoff; break; } } /* ** Initially we're assumed to be in standard time. */ - isdst = FALSE; - theiroffset = theirstdoffset; + isdst = false; /* ** Now juggle transition times and types ** tracking offsets as you do. @@ -1085,16 +1341,17 @@ tzparse( for (i = 0; i < sp->timecnt; ++i) { j = sp->types[i]; sp->types[i] = sp->ttis[j].tt_isdst; - if (sp->ttis[j].tt_ttisgmt) { + if (sp->ttis[j].tt_ttisut) { /* No adjustment to transition time */ } else { /* - ** If summer time is in effect, and the - ** transition time was not specified as - ** standard time, add the summer time - ** offset to the transition time; - ** otherwise, add the standard time - ** offset to the transition time. + ** If daylight saving time is in + ** effect, and the transition time was + ** not specified as standard time, add + ** the daylight saving time offset to + ** the transition time; otherwise, add + ** the standard time offset to the + ** transition time. */ /* ** Transitions from DST to DDST @@ -1110,206 +1367,191 @@ tzparse( theirstdoffset; } } - theiroffset = -sp->ttis[j].tt_gmtoff; + theiroffset = -sp->ttis[j].tt_utoff; if (sp->ttis[j].tt_isdst) theirdstoffset = theiroffset; else theirstdoffset = theiroffset; } /* ** Finally, fill in ttis. - ** ttisstd and ttisgmt need not be handled. */ - sp->ttis[0].tt_gmtoff = -stdoffset; - sp->ttis[0].tt_isdst = FALSE; - sp->ttis[0].tt_abbrind = 0; - sp->ttis[1].tt_gmtoff = -dstoffset; - sp->ttis[1].tt_isdst = TRUE; - sp->ttis[1].tt_abbrind = stdlen + 1; + init_ttinfo(&sp->ttis[0], -stdoffset, false, 0); + init_ttinfo(&sp->ttis[1], -dstoffset, true, stdlen + 1); sp->typecnt = 2; + sp->defaulttype = 0; } } else { dstlen = 0; sp->typecnt = 1; /* only standard time */ sp->timecnt = 0; - sp->ttis[0].tt_gmtoff = -stdoffset; - sp->ttis[0].tt_isdst = 0; - sp->ttis[0].tt_abbrind = 0; + init_ttinfo(&sp->ttis[0], -stdoffset, false, 0); + sp->defaulttype = 0; } - sp->charcnt = stdlen + 1; - if (dstlen != 0) - sp->charcnt += dstlen + 1; - if ((size_t) sp->charcnt > sizeof sp->chars) - return -1; + sp->charcnt = charcnt; cp = sp->chars; - (void) strncpy(cp, stdname, stdlen); + memcpy(cp, stdname, stdlen); cp += stdlen; *cp++ = '\0'; if (dstlen != 0) { - (void) strncpy(cp, dstname, dstlen); + memcpy(cp, dstname, dstlen); *(cp + dstlen) = '\0'; } - return 0; + return true; } static void -gmtload( - struct state * const sp -) { - if (tzload(gmt, sp, TRUE) != 0) - (void) tzparse(gmt, sp, TRUE); +gmtload(struct state *const sp) +{ + if (tzload(gmt, sp, true) != 0) + tzparse("GMT0", sp, NULL); } -#ifndef STD_INSPIRED -/* -** A non-static declaration of tzsetwall in a system header file -** may cause a warning about this upcoming static declaration... -*/ -static -#endif /* !defined STD_INSPIRED */ -void -tzsetwall(void) +/* Initialize *SP to a value appropriate for the TZ setting NAME. + Return 0 on success, an errno value on failure. */ +static int +zoneinit(struct state *sp, char const *name) { - if (lcl_is_set < 0) - return; - lcl_is_set = -1; -#ifdef ALL_STATE - if (lclptr == NULL) { - if ((lclptr = malloc(sizeof(*lclptr)))) { - __cxa_atexit(free, lclptr, 0); - } else { - settzname(); /* all we can do */ - return; - } + if (name && ! name[0]) { + /* + ** User wants it fast rather than right. + */ + sp->leapcnt = 0; /* so, we're off a little */ + sp->timecnt = 0; + sp->typecnt = 0; + sp->charcnt = 0; + sp->goback = sp->goahead = false; + init_ttinfo(&sp->ttis[0], 0, false, 0); + strcpy(sp->chars, gmt); + sp->defaulttype = 0; + return 0; + } else { + int err = tzload(name, sp, true); + if (err != 0 && name && name[0] != ':' && tzparse(name, sp, NULL)) + err = 0; + if (err == 0) + scrub_abbrs(sp); + return err; } +} + +static void +tzset_unlocked(void) +{ + char const *name = getenv("TZ"); + struct state *sp = lclptr; + int lcl = name ? strlen(name) < sizeof lcl_TZname : -1; + if (lcl < 0 + ? lcl_is_set < 0 + : 0 < lcl_is_set && strcmp(lcl_TZname, name) == 0) + return; +#ifdef ALL_STATE + if (! sp) + lclptr = sp = malloc(sizeof *lclptr); #endif /* defined ALL_STATE */ - if (tzload((char *) NULL, lclptr, TRUE) != 0) - gmtload(lclptr); + if (sp) { + if (zoneinit(sp, name) != 0) + zoneinit(sp, ""); + if (0 < lcl) + strcpy(lcl_TZname, name); + } settzname(); + lcl_is_set = lcl; } void tzset(void) { - register const char * name = NULL; - /* static char buf[PROP_VALUE_MAX]; */ - - name = getenv("TZ"); - - /* // try the "persist.sys.timezone" system property first */ - /* if (name == NULL && __system_property_get("persist.sys.timezone", buf) > 0) */ - /* name = buf; */ - - if (name == NULL) { - tzsetwall(); + if (lock() != 0) return; - } + tzset_unlocked(); + unlock(); +} - if (lcl_is_set > 0 && strcmp(lcl_TZname, name) == 0) +static void +gmtcheck(void) +{ + static bool gmt_is_set; + if (lock() != 0) return; - lcl_is_set = strlen(name) < sizeof lcl_TZname; - if (lcl_is_set) - (void) strcpy(lcl_TZname, name); - + if (! gmt_is_set) { #ifdef ALL_STATE - if (lclptr == NULL) { - if ((lclptr = malloc(sizeof(*lclptr)))) { - __cxa_atexit(free, lclptr, 0); - } else { - settzname(); /* all we can do */ - return; - } + gmtptr = malloc(sizeof *gmtptr); +#endif + if (gmtptr) + gmtload(gmtptr); + gmt_is_set = true; } -#endif /* defined ALL_STATE */ - if (*name == '\0') { - /* - ** User wants it fast rather than right. - */ - lclptr->leapcnt = 0; /* so, we're off a little */ - lclptr->timecnt = 0; - lclptr->typecnt = 0; - lclptr->ttis[0].tt_isdst = 0; - lclptr->ttis[0].tt_gmtoff = 0; - lclptr->ttis[0].tt_abbrind = 0; - (void) strcpy(lclptr->chars, gmt); - } else if (tzload(name, lclptr, TRUE) != 0) - if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0) - (void) gmtload(lclptr); - settzname(); + unlock(); } /* ** The easy way to behave "as if no library function calls" localtime -** is to not call it--so we drop its guts into "localsub", which can be -** freely called. (And no, the PANS doesn't require the above behavior-- +** is to not call it, so we drop its guts into "localsub", which can be +** freely called. (And no, the PANS doesn't require the above behavior, ** but it *is* desirable.) ** -** The unused offset argument is for the benefit of mktime variants. +** If successful and SETNAME is nonzero, +** set the applicable parts of tzname, timezone and altzone; +** however, it's OK to omit this step if the timezone is POSIX-compatible, +** since in that case tzset should have already done this step correctly. +** SETNAME's type is int_fast32_t for compatibility with gmtsub, +** but it is actually a boolean and its value should be 0 or 1. */ /*ARGSUSED*/ static struct tm * -localsub( - const time_t * const timep, - const int32_t offset, - struct tm * const tmp -) { - register struct state * sp; +localsub(struct state const *sp, time_t const *timep, int_fast32_t setname, + struct tm *const tmp) +{ register const struct ttinfo * ttisp; register int i; register struct tm * result; const time_t t = *timep; - sp = lclptr; -#ifdef ALL_STATE - if (sp == NULL) - return gmtsub(timep, offset, tmp); -#endif /* defined ALL_STATE */ + if (sp == NULL) { + /* Don't bother to set tzname etc.; tzset has already done it. */ + return gmtsub(gmtptr, timep, 0, tmp); + } if ((sp->goback && t < sp->ats[0]) || (sp->goahead && t > sp->ats[sp->timecnt - 1])) { - time_t newt = t; + time_t newt; register time_t seconds; - register time_t tcycles; - register int_fast64_t icycles; + register time_t years; if (t < sp->ats[0]) seconds = sp->ats[0] - t; else seconds = t - sp->ats[sp->timecnt - 1]; --seconds; - tcycles = seconds / YEARSPERREPEAT / AVGSECSPERYEAR; - ++tcycles; - icycles = tcycles; - if (tcycles - icycles >= 1 || icycles - tcycles >= 1) - return NULL; - seconds = icycles; - seconds *= YEARSPERREPEAT; - seconds *= AVGSECSPERYEAR; + + /* Beware integer overflow, as SECONDS might + be close to the maximum time_t. */ + years = seconds / SECSPERREPEAT * YEARSPERREPEAT; + seconds = years * AVGSECSPERYEAR; + years += YEARSPERREPEAT; if (t < sp->ats[0]) - newt += seconds; - else newt -= seconds; + newt = t + seconds + SECSPERREPEAT; + else + newt = t - seconds - SECSPERREPEAT; + if (newt < sp->ats[0] || newt > sp->ats[sp->timecnt - 1]) return NULL; /* "cannot happen" */ - result = localsub(&newt, offset, tmp); - if (result == tmp) { - register time_t newy; + result = localsub(sp, &newt, setname, tmp); + if (result) { + register int_fast64_t newy; - newy = tmp->tm_year; + newy = result->tm_year; if (t < sp->ats[0]) - newy -= icycles * YEARSPERREPEAT; - else newy += icycles * YEARSPERREPEAT; - tmp->tm_year = newy; - if (tmp->tm_year != newy) + newy -= years; + else newy += years; + if (! (INT_MIN <= newy && newy <= INT_MAX)) return NULL; + result->tm_year = newy; } return result; } if (sp->timecnt == 0 || t < sp->ats[0]) { - i = 0; - while (sp->ttis[i].tt_isdst) - if (++i >= sp->typecnt) { - i = 0; - break; - } + i = sp->defaulttype; } else { register int lo = 1; register int hi = sp->timecnt; @@ -1321,43 +1563,50 @@ localsub( hi = mid; else lo = mid + 1; } - i = (int) sp->types[lo - 1]; + i = sp->types[lo - 1]; } ttisp = &sp->ttis[i]; /* ** To get (wrong) behavior that's compatible with System V Release 2.0 ** you'd replace the statement below with - ** t += ttisp->tt_gmtoff; + ** t += ttisp->tt_utoff; ** timesub(&t, 0L, sp, tmp); */ - result = timesub(&t, ttisp->tt_gmtoff, sp, tmp); - tmp->tm_isdst = ttisp->tt_isdst; - tzname[tmp->tm_isdst] = &sp->chars[ttisp->tt_abbrind]; -#ifdef TM_ZONE - tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind]; -#endif /* defined TM_ZONE */ + result = timesub(&t, ttisp->tt_utoff, sp, tmp); + if (result) { + result->tm_isdst = ttisp->tt_isdst; + result->tm_zone = (char *) &sp->chars[ttisp->tt_desigidx]; + if (setname) + update_tzname_etc(sp, ttisp); + } return result; } -struct tm * -localtime( - const time_t * const timep -) { - tzset(); - return localsub(timep, 0L, &tm); +static struct tm * +localtime_tzset(time_t const *timep, struct tm *tmp, bool setname) +{ + int err = lock(); + if (err) { + errno = err; + return NULL; + } + if (setname || !lcl_is_set) + tzset_unlocked(); + tmp = localsub(lclptr, timep, setname, tmp); + unlock(); + return tmp; } -/* -** Re-entrant version of localtime. -*/ +struct tm * +localtime(const time_t *timep) +{ + return localtime_tzset(timep, &tm, true); +} struct tm * -localtime_r( - const time_t * const timep, - struct tm * tmp -) { - tzset(); - return localsub(timep, 0L, tmp); +localtime_r(const time_t *timep, struct tm *tmp) +{ + return localtime_tzset(timep, tmp, false); } /* @@ -1365,249 +1614,164 @@ localtime_r( */ static struct tm * -gmtsub( - const time_t * const timep, - const int32_t offset, - struct tm * const tmp -) { +gmtsub(struct state const *sp, time_t const *timep, int_fast32_t offset, + struct tm *tmp) +{ register struct tm * result; - if (!gmt_is_set) { - gmt_is_set = TRUE; -#ifdef ALL_STATE - if (!gmtptr) { - gmtptr = malloc(sizeof(*gmtptr)); - __cxa_atexit(free, gmtptr, 0); - } - if (gmtptr) -#endif /* defined ALL_STATE */ - gmtload(gmtptr); - } + result = timesub(timep, offset, gmtptr, tmp); -#ifdef TM_ZONE /* ** Could get fancy here and deliver something such as - ** "UTC+xxxx" or "UTC-xxxx" if offset is non-zero, + ** "+xx" or "-xx" if offset is non-zero, ** but this is no time for a treasure hunt. */ - if (offset != 0) - tmp->TM_ZONE = wildabbr; - else { -#ifdef ALL_STATE - if (gmtptr == NULL) - tmp->TM_ZONE = gmt; - else tmp->TM_ZONE = gmtptr->chars; -#endif /* defined ALL_STATE */ -#ifndef ALL_STATE - tmp->TM_ZONE = gmtptr->chars; -#endif /* State Farm */ - } -#endif /* defined TM_ZONE */ + tmp->tm_zone = ((char *) + (offset ? wildabbr : gmtptr ? gmtptr->chars : gmt)); return result; } -struct tm * -gmtime( - const time_t * const timep -) { - return gmtsub(timep, 0L, &tm); -} - /* * Re-entrant version of gmtime. */ struct tm * -gmtime_r( - const time_t * const timep, - struct tm * tmp -) { - return gmtsub(timep, 0L, tmp); +gmtime_r(const time_t *timep, struct tm *tmp) +{ + gmtcheck(); + return gmtsub(gmtptr, timep, 0, tmp); } -#ifdef STD_INSPIRED - struct tm * -offtime( - const time_t * const timep, - const int32_t offset -) { - return gmtsub(timep, offset, &tm); +gmtime(const time_t *timep) +{ + return gmtime_r(timep, &tm); } -#endif /* defined STD_INSPIRED */ - /* ** Return the number of leap years through the end of the given year ** where, to make the math easy, the answer for year zero is defined as zero. */ -pureconst optimizespeed static int -leaps_thru_end_of( - const int y -) { - return (y >= 0) ? (y / 4 - y / 100 + y / 400) : - -(leaps_thru_end_of(-(y + 1)) + 1); +static time_t +leaps_thru_end_of_nonneg(time_t y) +{ + return y / 4 - y / 100 + y / 400; +} + +static time_t +leaps_thru_end_of(time_t y) +{ + return (y < 0 + ? -1 - leaps_thru_end_of_nonneg(-1 - y) + : leaps_thru_end_of_nonneg(y)); } static struct tm * -timesub( - const time_t * const timep, - const int32_t offset, - const struct state * const sp, - struct tm * const tmp -) { - const struct lsinfo * lp; - time_t tdays; - int idays; /* unsigned would be so 2003 */ - long rem; /* ^wut */ - int y; - int leap; - long corr; - int hit; - int i; +timesub(const time_t *timep, int_fast32_t offset, + const struct state *sp, struct tm *tmp) +{ + register const struct lsinfo * lp; + register time_t tdays; + register const int * ip; + register int_fast32_t corr; + register int i; + int_fast32_t idays, rem, dayoff, dayrem; + time_t y; + + /* If less than SECSPERMIN, the number of seconds since the + most recent positive leap second; otherwise, do not add 1 + to localtime tm_sec because of leap seconds. */ + time_t secs_since_posleap = SECSPERMIN; corr = 0; - hit = 0; -#ifdef ALL_STATE i = (sp == NULL) ? 0 : sp->leapcnt; -#endif /* defined ALL_STATE */ -#ifndef ALL_STATE - i = sp->leapcnt; -#endif /* State Farm */ while (--i >= 0) { lp = &sp->lsis[i]; if (*timep >= lp->ls_trans) { - if (*timep == lp->ls_trans) { - hit = ((i == 0 && lp->ls_corr > 0) || - lp->ls_corr > sp->lsis[i - 1].ls_corr); - if (hit) - while (i > 0 && - sp->lsis[i].ls_trans == - sp->lsis[i - 1].ls_trans + 1 && - sp->lsis[i].ls_corr == - sp->lsis[i - 1].ls_corr + 1) { - ++hit; - --i; - } - } corr = lp->ls_corr; + if ((i == 0 ? 0 : lp[-1].ls_corr) < corr) + secs_since_posleap = *timep - lp->ls_trans; break; } } - y = EPOCH_YEAR; - tdays = *timep / SECSPERDAY; - rem = *timep - tdays * SECSPERDAY; - while (tdays < 0 || tdays >= kYearLengths[isleap(y)]) { - int newy; - register time_t tdelta; - register int idelta; - register int leapdays; - tdelta = tdays / DAYSPERLYEAR; - idelta = tdelta; - if (tdelta - idelta >= 1 || idelta - tdelta >= 1) - return NULL; - if (idelta == 0) - idelta = (tdays < 0) ? -1 : 1; - newy = y; - if (increment_overflow(&newy, idelta)) - return NULL; + /* Calculate the year, avoiding integer overflow even if + time_t is unsigned. */ + tdays = *timep / SECSPERDAY; + rem = *timep % SECSPERDAY; + rem += offset % SECSPERDAY - corr % SECSPERDAY + 3 * SECSPERDAY; + dayoff = offset / SECSPERDAY - corr / SECSPERDAY + rem / SECSPERDAY - 3; + rem %= SECSPERDAY; + /* y = (EPOCH_YEAR + + floor((tdays + dayoff) / DAYSPERREPEAT) * YEARSPERREPEAT), + sans overflow. But calculate against 1570 (EPOCH_YEAR - + YEARSPERREPEAT) instead of against 1970 so that things work + for localtime values before 1970 when time_t is unsigned. */ + dayrem = tdays % DAYSPERREPEAT; + dayrem += dayoff % DAYSPERREPEAT; + y = (EPOCH_YEAR - YEARSPERREPEAT + + ((1 + dayoff / DAYSPERREPEAT + dayrem / DAYSPERREPEAT + - ((dayrem % DAYSPERREPEAT) < 0) + + tdays / DAYSPERREPEAT) + * YEARSPERREPEAT)); + /* idays = (tdays + dayoff) mod DAYSPERREPEAT, sans overflow. */ + idays = tdays % DAYSPERREPEAT; + idays += dayoff % DAYSPERREPEAT + 2 * DAYSPERREPEAT; + idays %= DAYSPERREPEAT; + /* Increase Y and decrease IDAYS until IDAYS is in range for Y. */ + while (year_lengths[isleap(y)] <= idays) { + int tdelta = idays / DAYSPERLYEAR; + int_fast32_t ydelta = tdelta + !tdelta; + time_t newy = y + ydelta; + register int leapdays; leapdays = leaps_thru_end_of(newy - 1) - leaps_thru_end_of(y - 1); - tdays -= ((time_t) newy - y) * DAYSPERNYEAR; - tdays -= leapdays; + idays -= ydelta * DAYSPERNYEAR; + idays -= leapdays; y = newy; } - { - register long seconds; - seconds = tdays * SECSPERDAY + 0.5; - tdays = seconds / SECSPERDAY; - rem += seconds - tdays * SECSPERDAY; + if (!TYPE_SIGNED(time_t) && y < TM_YEAR_BASE) { + int signed_y = y; + tmp->tm_year = signed_y - TM_YEAR_BASE; + } else if ((!TYPE_SIGNED(time_t) || INT_MIN + TM_YEAR_BASE <= y) + && y - TM_YEAR_BASE <= INT_MAX) + tmp->tm_year = y - TM_YEAR_BASE; + else { + errno = EOVERFLOW; + return NULL; } - /* - ** Given the range, we can now fearlessly cast... - */ - idays = tdays; - rem += offset - corr; - while (rem < 0) { - rem += SECSPERDAY; - --idays; - } - while (rem >= SECSPERDAY) { - rem -= SECSPERDAY; - ++idays; - } - while (idays < 0) { - if (increment_overflow(&y, -1)) - return NULL; - idays += kYearLengths[isleap(y)]; - } - while (idays >= kYearLengths[isleap(y)]) { - idays -= kYearLengths[isleap(y)]; - if (increment_overflow(&y, 1)) - return NULL; - } - tmp->tm_year = y; - if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE)) - return NULL; tmp->tm_yday = idays; /* ** The "extra" mods below avoid overflow problems. */ - tmp->tm_wday = EPOCH_WDAY + - ((y - EPOCH_YEAR) % DAYSPERWEEK) * - (DAYSPERNYEAR % DAYSPERWEEK) + - leaps_thru_end_of(y - 1) - - leaps_thru_end_of(EPOCH_YEAR - 1) + - idays; + tmp->tm_wday = (TM_WDAY_BASE + + ((tmp->tm_year % DAYSPERWEEK) + * (DAYSPERNYEAR % DAYSPERWEEK)) + + leaps_thru_end_of(y - 1) + - leaps_thru_end_of(TM_YEAR_BASE - 1) + + idays); tmp->tm_wday %= DAYSPERWEEK; if (tmp->tm_wday < 0) tmp->tm_wday += DAYSPERWEEK; - tmp->tm_hour = (int) (rem / SECSPERHOUR); + tmp->tm_hour = rem / SECSPERHOUR; rem %= SECSPERHOUR; - tmp->tm_min = (int) (rem / SECSPERMIN); - /* - ** A positive leap second requires a special - ** representation. This uses "... ??:59:60" et seq. - */ - tmp->tm_sec = (int) (rem % SECSPERMIN) + hit; - leap = isleap(y); - for (tmp->tm_mon = 0; - idays >= kMonthLengths[leap][tmp->tm_mon]; - ++(tmp->tm_mon)) { - idays -= kMonthLengths[leap][tmp->tm_mon]; - } - tmp->tm_mday = (int) (idays + 1); + tmp->tm_min = rem / SECSPERMIN; + tmp->tm_sec = rem % SECSPERMIN; + + /* Use "... ??:??:60" at the end of the localtime minute containing + the second just before the positive leap second. */ + tmp->tm_sec += secs_since_posleap <= tmp->tm_sec; + + ip = mon_lengths[isleap(y)]; + for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; ++(tmp->tm_mon)) + idays -= ip[tmp->tm_mon]; + tmp->tm_mday = idays + 1; tmp->tm_isdst = 0; -#ifdef TM_GMTOFF - tmp->TM_GMTOFF = offset; -#endif /* defined TM_GMTOFF */ + tmp->tm_gmtoff = offset; return tmp; } -/* char * */ -/* ctime(timep) */ -/* const time_t * const timep; */ -/* { */ -/* /\* */ -/* ** Section 4.12.3.2 of X3.159-1989 requires that */ -/* ** The ctime function converts the calendar time pointed to by timer */ -/* ** to local time in the form of a string. It is equivalent to */ -/* ** asctime(localtime(timer)) */ -/* *\/ */ -/* return asctime(localtime(timep)); */ -/* } */ - -/* char * */ -/* ctime_r(timep, buf) */ -/* const time_t * const timep; */ -/* char * buf; */ -/* { */ -/* struct tm mytm; */ -/* return asctime_r(localtime_r(timep, &mytm), buf); */ -/* } */ - /* ** Adapted from code provided by Robert Elz, who writes: ** The "best" way to do mktime I think is based on an idea of Bob @@ -1622,31 +1786,58 @@ timesub( #endif /* !defined WRONG */ /* -** Simplified normalize logic courtesy Paul Eggert. +** Normalize logic courtesy Paul Eggert. */ -static inline int -increment_overflow( - int * number, - int delta -) { -#ifdef __GNUC__ - return __builtin_add_overflow(*number, delta, number); -#else - int number0; - number0 = *number; - *number += delta; - return (*number < number0) != (delta < 0); -#endif +static bool +increment_overflow(int *ip, int j) +{ + register int const i = *ip; + + /* + ** If i >= 0 there can only be overflow if i + j > INT_MAX + ** or if j > INT_MAX - i; given i >= 0, INT_MAX - i cannot overflow. + ** If i < 0 there can only be overflow if i + j < INT_MIN + ** or if j < INT_MIN - i; given i < 0, INT_MIN - i cannot overflow. + */ + if ((i >= 0) ? (j > INT_MAX - i) : (j < INT_MIN - i)) + return true; + *ip += j; + return false; } -static int -normalize_overflow( - int * const tensptr, - int * const unitsptr, - const int base -) { +static bool +increment_overflow32(int_fast32_t *const lp, int const m) +{ + register int_fast32_t const l = *lp; + + if ((l >= 0) ? (m > INT_FAST32_MAX - l) : (m < INT_FAST32_MIN - l)) + return true; + *lp += m; + return false; +} + +static bool +increment_overflow_time(time_t *tp, int_fast32_t j) +{ + /* + ** This is like + ** 'if (! (TIME_T_MIN <= *tp + j && *tp + j <= TIME_T_MAX)) ...', + ** except that it does the right thing even if *tp + j would overflow. + */ + if (! (j < 0 + ? (TYPE_SIGNED(time_t) ? TIME_T_MIN - j <= *tp : -1 - j < *tp) + : *tp <= TIME_T_MAX - j)) + return true; + *tp += j; + return false; +} + +static bool +normalize_overflow(int *const tensptr, int *const unitsptr, const int base) +{ register int tensdelta; + tensdelta = (*unitsptr >= 0) ? (*unitsptr / base) : (-1 - (-1 - *unitsptr) / base); @@ -1654,14 +1845,27 @@ normalize_overflow( return increment_overflow(tensptr, tensdelta); } +static bool +normalize_overflow32(int_fast32_t *tensptr, int *unitsptr, int base) +{ + register int tensdelta; + + tensdelta = (*unitsptr >= 0) ? + (*unitsptr / base) : + (-1 - (-1 - *unitsptr) / base); + *unitsptr -= tensdelta * base; + return increment_overflow32(tensptr, tensdelta); +} + static int -tmcomp( - const struct tm * const atmp, - const struct tm * const btmp -) { +tmcomp(register const struct tm *const atmp, + register const struct tm *const btmp) +{ register int result; - if ((result = (atmp->tm_year - btmp->tm_year)) == 0 && - (result = (atmp->tm_mon - btmp->tm_mon)) == 0 && + + if (atmp->tm_year != btmp->tm_year) + return atmp->tm_year < btmp->tm_year ? -1 : 1; + if ((result = (atmp->tm_mon - btmp->tm_mon)) == 0 && (result = (atmp->tm_mday - btmp->tm_mday)) == 0 && (result = (atmp->tm_hour - btmp->tm_hour)) == 0 && (result = (atmp->tm_min - btmp->tm_min)) == 0) @@ -1670,25 +1874,26 @@ tmcomp( } static time_t -time2sub( - struct tm * const tmp, - struct tm * (* const funcp)(const time_t*, int32_t, struct tm*), - const int32_t offset, - int * const okayp, - const int do_norm_secs -) { - register const struct state * sp; +time2sub(struct tm *const tmp, + struct tm *(*funcp)(struct state const *, time_t const *, + int_fast32_t, struct tm *), + struct state const *sp, + const int_fast32_t offset, + bool *okayp, + bool do_norm_secs) +{ register int dir; register int i, j; register int saved_seconds; - register long li; + register int_fast32_t li; register time_t lo; register time_t hi; - int32_t y; + int_fast32_t y; time_t newt; time_t t; struct tm yourtm, mytm; - *okayp = FALSE; + + *okayp = false; yourtm = *tmp; if (do_norm_secs) { if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec, @@ -1700,42 +1905,42 @@ time2sub( if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY)) return WRONG; y = yourtm.tm_year; - if (normalize_overflow(&y, &yourtm.tm_mon, MONSPERYEAR)) + if (normalize_overflow32(&y, &yourtm.tm_mon, MONSPERYEAR)) return WRONG; /* ** Turn y into an actual year number for now. ** It is converted back to an offset from TM_YEAR_BASE later. */ - if (increment_overflow(&y, TM_YEAR_BASE)) + if (increment_overflow32(&y, TM_YEAR_BASE)) return WRONG; while (yourtm.tm_mday <= 0) { - if (increment_overflow(&y, -1)) + if (increment_overflow32(&y, -1)) return WRONG; li = y + (1 < yourtm.tm_mon); - yourtm.tm_mday += kYearLengths[isleap(li)]; + yourtm.tm_mday += year_lengths[isleap(li)]; } while (yourtm.tm_mday > DAYSPERLYEAR) { li = y + (1 < yourtm.tm_mon); - yourtm.tm_mday -= kYearLengths[isleap(li)]; - if (increment_overflow(&y, 1)) + yourtm.tm_mday -= year_lengths[isleap(li)]; + if (increment_overflow32(&y, 1)) return WRONG; } for ( ; ; ) { - i = kMonthLengths[isleap(y)][yourtm.tm_mon]; + i = mon_lengths[isleap(y)][yourtm.tm_mon]; if (yourtm.tm_mday <= i) break; yourtm.tm_mday -= i; if (++yourtm.tm_mon >= MONSPERYEAR) { yourtm.tm_mon = 0; - if (increment_overflow(&y, 1)) + if (increment_overflow32(&y, 1)) return WRONG; } } - if (increment_overflow(&y, -TM_YEAR_BASE)) + if (increment_overflow32(&y, -TM_YEAR_BASE)) + return WRONG; + if (! (INT_MIN <= y && y <= INT_MAX)) return WRONG; yourtm.tm_year = y; - if (yourtm.tm_year != y) - return WRONG; if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN) saved_seconds = 0; else if (y + TM_YEAR_BASE < EPOCH_YEAR) { @@ -1758,27 +1963,15 @@ time2sub( /* ** Do a binary search (this works whatever time_t's type is). */ - if (!TYPE_SIGNED(time_t)) { - lo = 0; - hi = lo - 1; - } else if (!TYPE_INTEGRAL(time_t)) { - if (sizeof(time_t) > sizeof(float)) - hi = (time_t) DBL_MAX; - else hi = (time_t) FLT_MAX; - lo = -hi; - } else { - lo = 1; - for (i = 0; i < (int) TYPE_BIT(time_t) - 1; ++i) - lo *= 2; - hi = -(lo + 1); - } + lo = TIME_T_MIN; + hi = TIME_T_MAX; for ( ; ; ) { t = lo / 2 + hi / 2; if (t < lo) t = lo; else if (t > hi) t = hi; - if ((*funcp)(&t, offset, &mytm) == NULL) { + if (! funcp(sp, &t, offset, &mytm)) { /* ** Assume that t is too extreme to be represented in ** a struct tm; arrange things so that it is less @@ -1788,14 +1981,14 @@ time2sub( } else dir = tmcomp(&mytm, &yourtm); if (dir != 0) { if (t == lo) { - ++t; - if (t <= lo) + if (t == TIME_T_MAX) return WRONG; + ++t; ++lo; } else if (t == hi) { - --t; - if (t >= hi) + if (t == TIME_T_MIN) return WRONG; + --t; --hi; } if (lo > hi) @@ -1805,6 +1998,35 @@ time2sub( else lo = t; continue; } +#if defined TM_GMTOFF && ! UNINIT_TRAP + if (mytm.TM_GMTOFF != yourtm.TM_GMTOFF + && (yourtm.TM_GMTOFF < 0 + ? (-SECSPERDAY <= yourtm.TM_GMTOFF + && (mytm.TM_GMTOFF <= + (SMALLEST(INT_FAST32_MAX, LONG_MAX) + + yourtm.TM_GMTOFF))) + : (yourtm.TM_GMTOFF <= SECSPERDAY + && ((BIGGEST(INT_FAST32_MIN, LONG_MIN) + + yourtm.TM_GMTOFF) + <= mytm.TM_GMTOFF)))) { + /* MYTM matches YOURTM except with the wrong UT offset. + YOURTM.TM_GMTOFF is plausible, so try it instead. + It's OK if YOURTM.TM_GMTOFF contains uninitialized data, + since the guess gets checked. */ + time_t altt = t; + int_fast32_t diff = mytm.TM_GMTOFF - yourtm.TM_GMTOFF; + if (!increment_overflow_time(&altt, diff)) { + struct tm alttm; + if (funcp(sp, &altt, offset, &alttm) + && alttm.tm_isdst == mytm.tm_isdst + && alttm.TM_GMTOFF == yourtm.TM_GMTOFF + && tmcomp(&alttm, &yourtm) == 0) { + t = altt; + mytm = alttm; + } + } + } +#endif if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst) break; /* @@ -1813,25 +2035,19 @@ time2sub( ** It's okay to guess wrong since the guess ** gets checked. */ - /* - ** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's. - */ - sp = (const struct state *) - (((void *) funcp == (void *) localsub) ? - lclptr : gmtptr); -#ifdef ALL_STATE if (sp == NULL) return WRONG; -#endif /* defined ALL_STATE */ for (i = sp->typecnt - 1; i >= 0; --i) { if (sp->ttis[i].tt_isdst != yourtm.tm_isdst) continue; for (j = sp->typecnt - 1; j >= 0; --j) { if (sp->ttis[j].tt_isdst == yourtm.tm_isdst) continue; - newt = t + sp->ttis[j].tt_gmtoff - - sp->ttis[i].tt_gmtoff; - if ((*funcp)(&newt, offset, &mytm) == NULL) + if (ttunspecified(sp, j)) + continue; + newt = (t + sp->ttis[j].tt_utoff + - sp->ttis[i].tt_utoff); + if (! funcp(sp, &newt, offset, &mytm)) continue; if (tmcomp(&mytm, &yourtm) != 0) continue; @@ -1851,57 +2067,62 @@ label: if ((newt < t) != (saved_seconds < 0)) return WRONG; t = newt; - if ((*funcp)(&t, offset, tmp)) - *okayp = TRUE; + if (funcp(sp, &t, offset, tmp)) + *okayp = true; return t; } static time_t -time2( - struct tm * const tmp, - struct tm * (* const funcp)(const time_t*, int32_t, struct tm*), - const int32_t offset, - int * const okayp -) { - time_t t; +time2(struct tm * const tmp, + struct tm *(*funcp)(struct state const *, time_t const *, + int_fast32_t, struct tm *), + struct state const *sp, + const int_fast32_t offset, + bool *okayp) +{ + time_t t; + /* ** First try without normalization of seconds ** (in case tm_sec contains a value associated with a leap second). ** If that fails, try with normalization of seconds. */ - t = time2sub(tmp, funcp, offset, okayp, FALSE); - return *okayp ? t : time2sub(tmp, funcp, offset, okayp, TRUE); + t = time2sub(tmp, funcp, sp, offset, okayp, false); + return *okayp ? t : time2sub(tmp, funcp, sp, offset, okayp, true); } static time_t -time1( - struct tm * const tmp, - struct tm * (* const funcp)(const time_t *, int32_t, struct tm *), - const int32_t offset -) { +time1(struct tm *const tmp, + struct tm *(*funcp)(struct state const *, time_t const *, + int_fast32_t, struct tm *), + struct state const *sp, + const int_fast32_t offset) +{ register time_t t; - register const struct state * sp; register int samei, otheri; register int sameind, otherind; register int i; register int nseen; - int seen[TZ_MAX_TYPES]; - int types[TZ_MAX_TYPES]; - int okay; + char seen[TZ_MAX_TYPES]; + unsigned char types[TZ_MAX_TYPES]; + bool okay; + + if (tmp == NULL) { + errno = EINVAL; + return WRONG; + } if (tmp->tm_isdst > 1) tmp->tm_isdst = 1; - t = time2(tmp, funcp, offset, &okay); -#ifdef PCTS - /* - ** PCTS code courtesy Grant Sullivan. - */ + t = time2(tmp, funcp, sp, offset, &okay); if (okay) return t; if (tmp->tm_isdst < 0) +#ifdef PCTS + /* + ** POSIX Conformance Test Suite code courtesy Grant Sullivan. + */ tmp->tm_isdst = 0; /* reset to std and try again */ -#endif /* defined PCTS */ -#ifndef PCTS - if (okay || tmp->tm_isdst < 0) +#else return t; #endif /* !defined PCTS */ /* @@ -1910,21 +2131,14 @@ time1( ** We try to divine the type they started from and adjust to the ** type they need. */ - /* - ** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's. - */ - sp = (const struct state *) (((void *) funcp == (void *) localsub) ? - lclptr : gmtptr); -#ifdef ALL_STATE if (sp == NULL) return WRONG; -#endif /* defined ALL_STATE */ for (i = 0; i < sp->typecnt; ++i) - seen[i] = FALSE; + seen[i] = false; nseen = 0; for (i = sp->timecnt - 1; i >= 0; --i) - if (!seen[sp->types[i]]) { - seen[sp->types[i]] = TRUE; + if (!seen[sp->types[i]] && !ttunspecified(sp, sp->types[i])) { + seen[sp->types[i]] = true; types[nseen++] = sp->types[i]; } for (sameind = 0; sameind < nseen; ++sameind) { @@ -1935,117 +2149,57 @@ time1( otheri = types[otherind]; if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst) continue; - tmp->tm_sec += sp->ttis[otheri].tt_gmtoff - - sp->ttis[samei].tt_gmtoff; + tmp->tm_sec += (sp->ttis[otheri].tt_utoff + - sp->ttis[samei].tt_utoff); tmp->tm_isdst = !tmp->tm_isdst; - t = time2(tmp, funcp, offset, &okay); + t = time2(tmp, funcp, sp, offset, &okay); if (okay) return t; - tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff - - sp->ttis[samei].tt_gmtoff; + tmp->tm_sec -= (sp->ttis[otheri].tt_utoff + - sp->ttis[samei].tt_utoff); tmp->tm_isdst = !tmp->tm_isdst; } } return WRONG; } -time_t -mktime( - struct tm * const tmp -) { - tzset(); - return time1(tmp, localsub, 0L); +static time_t +mktime_tzname(struct state *sp, struct tm *tmp, bool setname) +{ + if (sp) + return time1(tmp, localsub, sp, setname); + else { + gmtcheck(); + return time1(tmp, gmtsub, gmtptr, 0); + } } time_t -timelocal( - struct tm * const tmp -) { - tmp->tm_isdst = -1; /* in case it wasn't initialized */ - return mktime(tmp); +mktime(struct tm *tmp) +{ + time_t t; + int err = lock(); + if (err) { + errno = err; + return -1; + } + tzset_unlocked(); + t = mktime_tzname(lclptr, tmp, true); + unlock(); + return t; } -time_t -timegm( - struct tm * const tmp -) { - tmp->tm_isdst = 0; - return time1(tmp, gmtsub, 0L); -} - -time_t -timeoff( - struct tm * const tmp, - const long offset -) { - tmp->tm_isdst = 0; - return time1(tmp, gmtsub, offset); -} - -/* -** IEEE Std 1003.1-1988 (POSIX) legislates that 536457599 -** shall correspond to "Wed Dec 31 23:59:59 UTC 1986", which -** is not the case if we are accounting for leap seconds. -** So, we provide the following conversion routines for use -** when exchanging timestamps with POSIX conforming systems. -*/ - -static long -leapcorr( - time_t * timep -) { - register struct state * sp; - register struct lsinfo * lp; +static int_fast32_t +leapcorr(struct state const *sp, time_t t) +{ + register struct lsinfo const * lp; register int i; - sp = lclptr; i = sp->leapcnt; while (--i >= 0) { lp = &sp->lsis[i]; - if (*timep >= lp->ls_trans) + if (t >= lp->ls_trans) return lp->ls_corr; } return 0; } - -pureconst time_t -time2posix( - time_t t -) { - tzset(); - return t - leapcorr(&t); -} - -pureconst time_t -posix2time( - time_t t -) { - time_t x; - time_t y; - - tzset(); - /* - ** For a positive leap second hit, the result - ** is not unique. For a negative leap second - ** hit, the corresponding time doesn't exist, - ** so we return an adjacent second. - */ - x = t + leapcorr(&t); - y = x - leapcorr(&x); - if (y < t) { - do { - x++; - y = x - leapcorr(&x); - } while (y < t); - if (t != y) - return x - 1; - } else if (y > t) { - do { - --x; - y = x - leapcorr(&x); - } while (y > t); - if (t != y) - return x + 1; - } - return x; -} diff --git a/libc/time/strftime.c b/libc/time/strftime.c index f49462291..41d16607f 100644 --- a/libc/time/strftime.c +++ b/libc/time/strftime.c @@ -1,5 +1,5 @@ -/*-*- 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│ +/*-*- mode:c; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ +│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ │ Copyright (c) 1989 The Regents of the University of California. │ │ All rights reserved. │ @@ -16,354 +16,529 @@ │ 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/fmt/itoa.h" -#include "libc/macros.internal.h" -#include "libc/nexgen32e/nexgen32e.h" -#include "libc/time/struct/tm.h" +#include "libc/inttypes.h" +#include "libc/stdio/stdio.h" #include "libc/time/time.h" -#include "libc/time/tzfile.internal.h" +#include "libc/time/tz.internal.h" +#include "libc/unicode/locale.h" +// clang-format off +// converts broken-down timestamp to string + +#define DIVISOR 100 asm(".ident\t\"\\n\\n\ strftime (BSD-3)\\n\ Copyright 1989 The Regents of the University of California\""); asm(".include \"libc/disclaimer.inc\""); -static char *strftime_add(char *p, const char *pe, const char *str) { - while (p < pe && (*p = *str++)) ++p; - return p; +/* +** Based on the UCB version with the copyright notice appearing above. +** +** This is ANSIish only when "multibyte character == plain character". +*/ + +struct lc_time_T { + const char * mon[MONSPERYEAR]; + const char * month[MONSPERYEAR]; + const char * wday[DAYSPERWEEK]; + const char * weekday[DAYSPERWEEK]; + const char * X_fmt; + const char * x_fmt; + const char * c_fmt; + const char * am; + const char * pm; + const char * date_fmt; +}; + +#define Locale (&C_time_locale) + +static const struct lc_time_T C_time_locale = { + { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }, { + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December" + }, { + "Sun", "Mon", "Tue", "Wed", + "Thu", "Fri", "Sat" + }, { + "Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday" + }, + + /* X_fmt */ + "%H:%M:%S", + + /* + ** x_fmt + ** C99 and later require this format. + ** Using just numbers (as here) makes Quakers happier; + ** it's also compatible with SVR4. + */ + "%m/%d/%y", + + /* + ** c_fmt + ** C99 and later require this format. + ** Previously this code used "%D %X", but we now conform to C99. + ** Note that + ** "%a %b %d %H:%M:%S %Y" + ** is used by Solaris 2.3. + */ + "%a %b %e %T %Y", + + /* am */ + "AM", + + /* pm */ + "PM", + + /* date_fmt */ + "%a %b %e %H:%M:%S %Z %Y" +}; + +enum warn { IN_NONE, IN_SOME, IN_THIS, IN_ALL }; + +#ifndef YEAR_2000_NAME +#define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS" +#endif /* !defined YEAR_2000_NAME */ + +static char * +_add(const char *str, char *pt, const char *ptlim) +{ + while (pt < ptlim && (*pt = *str++) != '\0') + ++pt; + return pt; } -static char *strftime_conv(char *p, const char *pe, int n, const char *format) { - char buf[22]; - (snprintf)(buf, sizeof(buf), format, n); - return strftime_add(p, pe, buf); +static char * +_conv(int n, const char *format, char *pt, const char *ptlim) +{ + char buf[INT_STRLEN_MAXIMUM(int) + 1]; + (sprintf)(buf, format, n); + return _add(buf, pt, ptlim); } -static char *strftime_secs(char *p, const char *pe, const struct tm *t) { - char ibuf[21]; - struct tm tmp; - int64_t s; - tmp = *t; /* Make a copy, mktime(3) modifies the tm struct. */ - s = mktime(&tmp); - FormatInt64(ibuf, s); - return strftime_add(p, pe, ibuf); +/* +** POSIX and the C Standard are unclear or inconsistent about +** what %C and %y do if the year is negative or exceeds 9999. +** Use the convention that %C concatenated with %y yields the +** same output as %Y, and that %Y contains at least 4 bytes, +** with more only if necessary. +*/ + +static char * +_yconv(int a, int b, bool convert_top, bool convert_yy, + char *pt, const char *ptlim) +{ + register int lead; + register int trail; + + trail = a % DIVISOR + b % DIVISOR; + lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR; + trail %= DIVISOR; + if (trail < 0 && lead > 0) { + trail += DIVISOR; + --lead; + } else if (lead < 0 && trail > 0) { + trail -= DIVISOR; + ++lead; + } + if (convert_top) { + if (lead == 0 && trail < 0) + pt = _add("-0", pt, ptlim); + else pt = _conv(lead, "%02d", pt, ptlim); + } + if (convert_yy) + pt = _conv(((trail < 0) ? -trail : trail), "%02d", pt, ptlim); + return pt; } -static char *strftime_timefmt(char *p, const char *pe, const char *format, - const struct tm *t) { - int i; - long diff; - char const *sign; - /* size_t z1, z2, z3; */ - for (; *format; ++format) { - if (*format == '%') { - label: - switch (*++format) { - case '\0': - --format; - break; - case 'A': - p = strftime_add(p, pe, - (t->tm_wday < 0 || t->tm_wday > 6) - ? "?" - : kWeekdayName[t->tm_wday]); - continue; - case 'a': - p = strftime_add(p, pe, - (t->tm_wday < 0 || t->tm_wday > 6) - ? "?" - : kWeekdayNameShort[t->tm_wday]); - continue; - case 'B': - p = strftime_add( - p, pe, - (t->tm_mon < 0 || t->tm_mon > 11) ? "?" : kMonthName[t->tm_mon]); - continue; - case 'b': - case 'h': - p = strftime_add(p, pe, - (t->tm_mon < 0 || t->tm_mon > 11) - ? "?" - : kMonthNameShort[t->tm_mon]); - continue; - case 'c': - p = strftime_timefmt(p, pe, "%D %X", t); - continue; - case 'C': - /* - ** %C used to do a... - ** strftime_timefmt("%a %b %e %X %Y", t); - ** ...whereas now POSIX 1003.2 calls for - ** something completely different. - ** (ado, 5/24/93) - */ - p = strftime_conv(p, pe, div100int64(t->tm_year + TM_YEAR_BASE), - "%02d"); - continue; - case 'D': - p = strftime_timefmt(p, pe, "%m/%d/%y", t); - continue; - case 'x': - /* - ** Version 3.0 of strftime from Arnold Robbins - ** (arnold@skeeve.atl.ga.us) does the - ** equivalent of... - ** strftime_timefmt("%a %b %e %Y"); - ** ...for %x; since the X3J11 C language - ** standard calls for "date, using locale's - ** date format," anything goes. Using just - ** numbers (as here) makes Quakers happier. - ** Word from Paul Eggert (eggert@twinsun.com) - ** is that %Y-%m-%d is the ISO standard date - ** format, specified in ISO 2014 and later - ** ISO 8601:1988, with a summary available in - ** pub/doc/ISO/english/ISO8601.ps.Z on - ** ftp.uni-erlangen.de. - ** (ado, 5/30/93) - */ - p = strftime_timefmt(p, pe, "%m/%d/%y", t); - continue; - case 'd': - p = strftime_conv(p, pe, t->tm_mday, "%02d"); - continue; - case 'E': - case 'O': - /* - ** POSIX locale extensions, a la - ** Arnold Robbins' strftime version 3.0. - ** The sequences - ** %Ec %EC %Ex %Ey %EY - ** %Od %oe %OH %OI %Om %OM - ** %OS %Ou %OU %OV %Ow %OW %Oy - ** are supposed to provide alternate - ** representations. - ** (ado, 5/24/93) - */ - goto label; - case 'e': - p = strftime_conv(p, pe, t->tm_mday, "%2d"); - continue; - case 'H': - p = strftime_conv(p, pe, t->tm_hour, "%02d"); - continue; - case 'I': - p = strftime_conv(p, pe, (t->tm_hour % 12) ? (t->tm_hour % 12) : 12, - "%02d"); - continue; - case 'j': - p = strftime_conv(p, pe, t->tm_yday + 1, "%03d"); - continue; - case 'k': - /* - ** This used to be... - ** strftime_conv(t->tm_hour % 12 ? - ** t->tm_hour % 12 : 12, 2, ' '); - ** ...and has been changed to the below to - ** match SunOS 4.1.1 and Arnold Robbins' - ** strftime version 3.0. That is, "%k" and - ** "%l" have been swapped. - ** (ado, 5/24/93) - */ - p = strftime_conv(p, pe, t->tm_hour, "%2d"); - continue; +static char * +_fmt(const char *format, const struct tm *t, char *pt, + const char *ptlim, enum warn *warnp) +{ + for ( ; *format; ++format) { + if (*format == '%') { +label: + switch (*++format) { + case '\0': + --format; + break; + case 'A': + pt = _add((t->tm_wday < 0 || + t->tm_wday >= DAYSPERWEEK) ? + "?" : Locale->weekday[t->tm_wday], + pt, ptlim); + continue; + case 'a': + pt = _add((t->tm_wday < 0 || + t->tm_wday >= DAYSPERWEEK) ? + "?" : Locale->wday[t->tm_wday], + pt, ptlim); + continue; + case 'B': + pt = _add((t->tm_mon < 0 || + t->tm_mon >= MONSPERYEAR) ? + "?" : Locale->month[t->tm_mon], + pt, ptlim); + continue; + case 'b': + case 'h': + pt = _add((t->tm_mon < 0 || + t->tm_mon >= MONSPERYEAR) ? + "?" : Locale->mon[t->tm_mon], + pt, ptlim); + continue; + case 'C': + /* + ** %C used to do a... + ** _fmt("%a %b %e %X %Y", t); + ** ...whereas now POSIX 1003.2 calls for + ** something completely different. + ** (ado, 1993-05-24) + */ + pt = _yconv(t->tm_year, TM_YEAR_BASE, + true, false, pt, ptlim); + continue; + case 'c': + { + enum warn warn2 = IN_SOME; + + pt = _fmt(Locale->c_fmt, t, pt, ptlim, &warn2); + if (warn2 == IN_ALL) + warn2 = IN_THIS; + if (warn2 > *warnp) + *warnp = warn2; + } + continue; + case 'D': + pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp); + continue; + case 'd': + pt = _conv(t->tm_mday, "%02d", pt, ptlim); + continue; + case 'E': + case 'O': + /* + ** Locale modifiers of C99 and later. + ** The sequences + ** %Ec %EC %Ex %EX %Ey %EY + ** %Od %oe %OH %OI %Om %OM + ** %OS %Ou %OU %OV %Ow %OW %Oy + ** are supposed to provide alternative + ** representations. + */ + goto label; + case 'e': + pt = _conv(t->tm_mday, "%2d", pt, ptlim); + continue; + case 'F': + pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp); + continue; + case 'H': + pt = _conv(t->tm_hour, "%02d", pt, ptlim); + continue; + case 'I': + pt = _conv((t->tm_hour % 12) ? + (t->tm_hour % 12) : 12, + "%02d", pt, ptlim); + continue; + case 'j': + pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim); + continue; + case 'k': + /* + ** This used to be... + ** _conv(t->tm_hour % 12 ? + ** t->tm_hour % 12 : 12, 2, ' '); + ** ...and has been changed to the below to + ** match SunOS 4.1.1 and Arnold Robbins' + ** strftime version 3.0. That is, "%k" and + ** "%l" have been swapped. + ** (ado, 1993-05-24) + */ + pt = _conv(t->tm_hour, "%2d", pt, ptlim); + continue; #ifdef KITCHEN_SINK - case 'K': - /* - ** After all this time, still unclaimed! - */ - p = strftime_add(p, pe, "kitchen sink"); - continue; + case 'K': + /* + ** After all this time, still unclaimed! + */ + pt = _add("kitchen sink", pt, ptlim); + continue; #endif /* defined KITCHEN_SINK */ - case 'l': - /* - ** This used to be... - ** strftime_conv(t->tm_hour, 2, ' '); - ** ...and has been changed to the below to - ** match SunOS 4.1.1 and Arnold Robbin's - ** strftime version 3.0. That is, "%k" and - ** "%l" have been swapped. - ** (ado, 5/24/93) - */ - p = strftime_conv(p, pe, (t->tm_hour % 12) ? (t->tm_hour % 12) : 12, - "%2d"); - continue; - case 'M': - p = strftime_conv(p, pe, t->tm_min, "%02d"); - continue; - case 'm': - p = strftime_conv(p, pe, t->tm_mon + 1, "%02d"); - continue; - case 'n': - p = strftime_add(p, pe, "\n"); - continue; - case 'p': - p = strftime_add(p, pe, t->tm_hour >= 12 ? "PM" : "AM"); - continue; - case 'R': - p = strftime_timefmt(p, pe, "%H:%M", t); - continue; - case 'r': - p = strftime_timefmt(p, pe, "%I:%M:%S %p", t); - continue; - case 'S': - p = strftime_conv(p, pe, t->tm_sec, "%02d"); - continue; - case 's': - p = strftime_secs(p, pe, t); - continue; - case 'T': - case 'X': - p = strftime_timefmt(p, pe, "%H:%M:%S", t); - continue; - case 't': - p = strftime_add(p, pe, "\t"); - continue; - case 'U': - p = strftime_conv(p, pe, (t->tm_yday + 7 - t->tm_wday) / 7, "%02d"); - continue; - case 'u': - /* - ** From Arnold Robbins' strftime version 3.0: - ** "ISO 8601: Weekday as a decimal number - ** [1 (Monday) - 7]" - ** (ado, 5/24/93) - */ - p = strftime_conv(p, pe, (t->tm_wday == 0) ? 7 : t->tm_wday, "%d"); - continue; - case 'V': - /* - ** From Arnold Robbins' strftime version 3.0: - ** "the week number of the year (the first - ** Monday as the first day of week 1) as a - ** decimal number (01-53). The method for - ** determining the week number is as specified - ** by ISO 8601 (to wit: if the week containing - ** January 1 has four or more days in the new - ** year, then it is week 1, otherwise it is - ** week 53 of the previous year and the next - ** week is week 1)." - ** (ado, 5/24/93) - */ - /* - ** XXX--If January 1 falls on a Friday, - ** January 1-3 are part of week 53 of the - ** previous year. By analogy, if January - ** 1 falls on a Thursday, are December 29-31 - ** of the PREVIOUS year part of week 1??? - ** (ado 5/24/93) - ** - ** You are understood not to expect this. - */ - i = (t->tm_yday + 10 - (t->tm_wday ? (t->tm_wday - 1) : 6)) / 7; - if (i == 0) { - /* - ** What day of the week does - ** January 1 fall on? - */ - i = t->tm_wday - (t->tm_yday - 1); - /* - ** Fri Jan 1: 53 - ** Sun Jan 1: 52 - ** Sat Jan 1: 53 if previous - ** year a leap - ** year, else 52 - */ - if (i == TM_FRIDAY) - i = 53; - else if (i == TM_SUNDAY) - i = 52; - else - i = isleap(t->tm_year + TM_YEAR_BASE) ? 53 : 52; -#ifdef XPG4_1994_04_09 - /* - ** As of 4/9/94, though, - ** XPG4 calls for 53 - ** unconditionally. - */ - i = 53; -#endif /* defined XPG4_1994_04_09 */ - } - p = strftime_conv(p, pe, i, "%02d"); - continue; - case 'v': - /* - ** From Arnold Robbins' strftime version 3.0: - ** "date as dd-bbb-YYYY" - ** (ado, 5/24/93) - */ - p = strftime_timefmt(p, pe, "%e-%b-%Y", t); - continue; - case 'W': - p = strftime_conv( - p, pe, (t->tm_yday + 7 - (t->tm_wday ? (t->tm_wday - 1) : 6)) / 7, - "%02d"); - continue; - case 'w': - p = strftime_conv(p, pe, t->tm_wday, "%d"); - continue; - case 'y': - p = strftime_conv(p, pe, (t->tm_year + TM_YEAR_BASE) % 100, "%02d"); - continue; - case 'Y': - p = strftime_conv(p, pe, t->tm_year + TM_YEAR_BASE, "%04d"); - continue; - case 'Z': - if (t->tm_zone) { - p = strftime_add(p, pe, t->tm_zone); - } else { - if (t->tm_isdst == 0 || t->tm_isdst == 1) { - p = strftime_add(p, pe, tzname[t->tm_isdst]); - } else { - p = strftime_add(p, pe, "?"); - } - } - continue; - case 'z': - if (t->tm_isdst < 0) continue; -#ifdef TM_GMTOFF - diff = t->TM_GMTOFF; -#else /* !defined TM_GMTOFF */ - if (t->tm_isdst == 0) -#ifdef USG_COMPAT - diff = -timezone; -#else /* !defined USG_COMPAT */ - continue; -#endif /* !defined USG_COMPAT */ - else -#ifdef ALTZONE - diff = -altzone; -#else /* !defined ALTZONE */ - continue; -#endif /* !defined ALTZONE */ -#endif /* !defined TM_GMTOFF */ - if (diff < 0) { - sign = "-"; - diff = -diff; - } else { - sign = "+"; - } - p = strftime_add(p, pe, sign); - diff /= SECSPERMIN; - diff = (diff / MINSPERHOUR) * 100 + (diff % MINSPERHOUR); - p = strftime_conv(p, pe, diff, "%04d"); - continue; - case '%': - /* - * X311J/88-090 (4.12.3.5): if conversion char is - * undefined, behavior is undefined. Print out the - * character itself as printf(3) also does. - */ - default: - break; - } - } - if (p >= pe) break; - *p++ = *format; - } - return p; + case 'l': + /* + ** This used to be... + ** _conv(t->tm_hour, 2, ' '); + ** ...and has been changed to the below to + ** match SunOS 4.1.1 and Arnold Robbin's + ** strftime version 3.0. That is, "%k" and + ** "%l" have been swapped. + ** (ado, 1993-05-24) + */ + pt = _conv((t->tm_hour % 12) ? + (t->tm_hour % 12) : 12, + "%2d", pt, ptlim); + continue; + case 'M': + pt = _conv(t->tm_min, "%02d", pt, ptlim); + continue; + case 'm': + pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim); + continue; + case 'n': + pt = _add("\n", pt, ptlim); + continue; + case 'p': + pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ? + Locale->pm : + Locale->am, + pt, ptlim); + continue; + case 'R': + pt = _fmt("%H:%M", t, pt, ptlim, warnp); + continue; + case 'r': + pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp); + continue; + case 'S': + pt = _conv(t->tm_sec, "%02d", pt, ptlim); + continue; + case 's': + { + struct tm tm; + char buf[INT_STRLEN_MAXIMUM( + time_t) + 1]; + time_t mkt; + + tm = *t; + tm.tm_yday = -1; + mkt = mktime(&tm); + if (mkt == (time_t) -1) { + /* Fail unless this -1 represents + a valid time. */ + struct tm tm_1; + if (!localtime_r(&mkt, &tm_1)) + return NULL; + if (!(tm.tm_year == tm_1.tm_year + && tm.tm_yday == tm_1.tm_yday + && tm.tm_hour == tm_1.tm_hour + && tm.tm_min == tm_1.tm_min + && tm.tm_sec == tm_1.tm_sec)) + return NULL; + } + if (TYPE_SIGNED(time_t)) { + intmax_t n = mkt; + (sprintf)(buf, "%"PRIdMAX, n); + } else { + uintmax_t n = mkt; + (sprintf)(buf, "%"PRIuMAX, n); + } + pt = _add(buf, pt, ptlim); + } + continue; + case 'T': + pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp); + continue; + case 't': + pt = _add("\t", pt, ptlim); + continue; + case 'U': + pt = _conv((t->tm_yday + DAYSPERWEEK - + t->tm_wday) / DAYSPERWEEK, + "%02d", pt, ptlim); + continue; + case 'u': + /* + ** From Arnold Robbins' strftime version 3.0: + ** "ISO 8601: Weekday as a decimal number + ** [1 (Monday) - 7]" + ** (ado, 1993-05-24) + */ + pt = _conv((t->tm_wday == 0) ? + DAYSPERWEEK : t->tm_wday, + "%d", pt, ptlim); + continue; + case 'V': /* ISO 8601 week number */ + case 'G': /* ISO 8601 year (four digits) */ + case 'g': /* ISO 8601 year (two digits) */ +/* +** From Arnold Robbins' strftime version 3.0: "the week number of the +** year (the first Monday as the first day of week 1) as a decimal number +** (01-53)." +** (ado, 1993-05-24) +** +** From by Markus Kuhn: +** "Week 01 of a year is per definition the first week which has the +** Thursday in this year, which is equivalent to the week which contains +** the fourth day of January. In other words, the first week of a new year +** is the week which has the majority of its days in the new year. Week 01 +** might also contain days from the previous year and the week before week +** 01 of a year is the last week (52 or 53) of the previous year even if +** it contains days from the new year. A week starts with Monday (day 1) +** and ends with Sunday (day 7). For example, the first week of the year +** 1997 lasts from 1996-12-30 to 1997-01-05..." +** (ado, 1996-01-02) +*/ + { + int year; + int base; + int yday; + int wday; + int w; + + year = t->tm_year; + base = TM_YEAR_BASE; + yday = t->tm_yday; + wday = t->tm_wday; + for ( ; ; ) { + int len; + int bot; + int top; + + len = isleap_sum(year, base) ? + DAYSPERLYEAR : + DAYSPERNYEAR; + /* + ** What yday (-3 ... 3) does + ** the ISO year begin on? + */ + bot = ((yday + 11 - wday) % + DAYSPERWEEK) - 3; + /* + ** What yday does the NEXT + ** ISO year begin on? + */ + top = bot - + (len % DAYSPERWEEK); + if (top < -3) + top += DAYSPERWEEK; + top += len; + if (yday >= top) { + ++base; + w = 1; + break; + } + if (yday >= bot) { + w = 1 + ((yday - bot) / + DAYSPERWEEK); + break; + } + --base; + yday += isleap_sum(year, base) ? + DAYSPERLYEAR : + DAYSPERNYEAR; + } + if (*format == 'V') + pt = _conv(w, "%02d", + pt, ptlim); + else if (*format == 'g') { + *warnp = IN_ALL; + pt = _yconv(year, base, + false, true, + pt, ptlim); + } else pt = _yconv(year, base, + true, true, + pt, ptlim); + } + continue; + case 'v': + /* + ** From Arnold Robbins' strftime version 3.0: + ** "date as dd-bbb-YYYY" + ** (ado, 1993-05-24) + */ + pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp); + continue; + case 'W': + pt = _conv((t->tm_yday + DAYSPERWEEK - + (t->tm_wday ? + (t->tm_wday - 1) : + (DAYSPERWEEK - 1))) / DAYSPERWEEK, + "%02d", pt, ptlim); + continue; + case 'w': + pt = _conv(t->tm_wday, "%d", pt, ptlim); + continue; + case 'X': + pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp); + continue; + case 'x': + { + enum warn warn2 = IN_SOME; + + pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2); + if (warn2 == IN_ALL) + warn2 = IN_THIS; + if (warn2 > *warnp) + *warnp = warn2; + } + continue; + case 'y': + *warnp = IN_ALL; + pt = _yconv(t->tm_year, TM_YEAR_BASE, + false, true, + pt, ptlim); + continue; + case 'Y': + pt = _yconv(t->tm_year, TM_YEAR_BASE, + true, true, + pt, ptlim); + continue; + case 'Z': + pt = _add(t->tm_zone, pt, ptlim); + /* + ** C99 and later say that %Z must be + ** replaced by the empty string if the + ** time zone abbreviation is not + ** determinable. + */ + continue; + case 'z': + { + long diff; + char const * sign; + bool negative; + + diff = t->tm_gmtoff; + negative = diff < 0; + if (diff == 0) { + negative = t->tm_zone[0] == '-'; + } + if (negative) { + sign = "-"; + diff = -diff; + } else sign = "+"; + pt = _add(sign, pt, ptlim); + diff /= SECSPERMIN; + diff = (diff / MINSPERHOUR) * 100 + + (diff % MINSPERHOUR); + pt = _conv(diff, "%04d", pt, ptlim); + } + continue; + case '+': + pt = _fmt(Locale->date_fmt, t, pt, ptlim, + warnp); + continue; + case '%': + /* + ** X311J/88-090 (4.12.3.5): if conversion char is + ** undefined, behavior is undefined. Print out the + ** character itself as printf(3) also does. + */ + default: + break; + } + } + if (pt == ptlim) + break; + *pt++ = *format; + } + return pt; } /** @@ -379,14 +554,32 @@ static char *strftime_timefmt(char *p, const char *pe, const char *format, * * @return bytes copied excluding nul, or 0 on error */ -size_t strftime(char *s, size_t size, const char *f, const struct tm *t) { - char *p; - p = strftime_timefmt(s, s + size, f, t); - if (p < s + size) { - *p = '\0'; - return p - s; - } else { - s[size - 1] = '\0'; - return 0; - } +size_t +strftime(char *s, size_t maxsize, const char *format, const struct tm *t) +{ + char * p; + int saved_errno = errno; + enum warn warn = IN_NONE; + + tzset(); + p = _fmt(format, t, s, s + maxsize, &warn); + if (!p) { + errno = EOVERFLOW; + return 0; + } + if (p == s + maxsize) { + errno = ERANGE; + return 0; + } + *p = '\0'; + errno = saved_errno; + return p - s; +} + +size_t +strftime_l(char *s, size_t maxsize, char const *format, struct tm const *t, + locale_t locale) +{ + /* Just call strftime, as only the C locale is supported. */ + return strftime(s, maxsize, format, t); } diff --git a/libc/time/time.mk b/libc/time/time.mk index 412653371..0690eaa7c 100644 --- a/libc/time/time.mk +++ b/libc/time/time.mk @@ -67,6 +67,10 @@ o/$(MODE)/libc/time/strftime.o: \ -fdata-sections \ -ffunction-sections +o/$(MODE)/libc/time/localtime.o: \ + OVERRIDE_CPPFLAGS += \ + -DSTACK_FRAME_UNLIMITED + o/$(MODE)/libc/time/now.o: \ OVERRIDE_CFLAGS += \ -O3 diff --git a/libc/time/tz.internal.h b/libc/time/tz.internal.h new file mode 100644 index 000000000..5c4f2acaf --- /dev/null +++ b/libc/time/tz.internal.h @@ -0,0 +1,542 @@ +#ifndef COSMOPOLITAN_THIRD_PARTY_TZ_PRIVATE_H_ +#define COSMOPOLITAN_THIRD_PARTY_TZ_PRIVATE_H_ +#include "libc/calls/weirdtypes.h" +#include "libc/errno.h" +#include "libc/inttypes.h" +#include "libc/limits.h" +#include "libc/runtime/runtime.h" +#include "libc/sysv/consts/ok.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ +/* clang-format off */ +/* Private header for tzdb code. */ + +/* +** This file is in the public domain, so clarified as of +** 1996-06-05 by Arthur David Olson. +*/ + +/* +** This header is for use ONLY with the time conversion code. +** There is no guarantee that it will remain unchanged, +** or that it will remain at all. +** Do NOT copy it to any system include directory. +** Thank you! +*/ + +/* +** zdump has been made independent of the rest of the time +** conversion package to increase confidence in the verification it provides. +** You can use zdump to help in verifying other implementations. +** To do this, compile with -DUSE_LTZ=0 and link without the tz library. +*/ +#ifndef USE_LTZ +# define USE_LTZ 1 +#endif + +/* This string was in the Factory zone through version 2016f. */ +#define GRANDPARENTED "Local time zone must be set--see zic manual page" + +/* +** Defaults for preprocessor symbols. +** You can override these in your C compiler options, e.g. '-DHAVE_GETTEXT=1'. +*/ + +#ifndef HAVE_DECL_ASCTIME_R +#define HAVE_DECL_ASCTIME_R 1 +#endif + +#if !defined HAVE_GENERIC && defined __has_extension +# if __has_extension(c_generic_selections) +# define HAVE_GENERIC 1 +# else +# define HAVE_GENERIC 0 +# endif +#endif +/* _Generic is buggy in pre-4.9 GCC. */ +#if !defined HAVE_GENERIC && defined __GNUC__ +# define HAVE_GENERIC (4 < __GNUC__ + (9 <= __GNUC_MINOR__)) +#endif +#ifndef HAVE_GENERIC +# define HAVE_GENERIC (201112 <= __STDC_VERSION__) +#endif + +#ifndef HAVE_GETTEXT +#define HAVE_GETTEXT 0 +#endif /* !defined HAVE_GETTEXT */ + +#ifndef HAVE_INCOMPATIBLE_CTIME_R +#define HAVE_INCOMPATIBLE_CTIME_R 0 +#endif + +#ifndef HAVE_LINK +#define HAVE_LINK 1 +#endif /* !defined HAVE_LINK */ + +#ifndef HAVE_MALLOC_ERRNO +#define HAVE_MALLOC_ERRNO 1 +#endif + +#ifndef HAVE_POSIX_DECLS +#define HAVE_POSIX_DECLS 1 +#endif + +#ifndef HAVE_STRTOLL +#define HAVE_STRTOLL 1 +#endif + +#ifndef HAVE_SYMLINK +#define HAVE_SYMLINK 1 +#endif /* !defined HAVE_SYMLINK */ + +#if HAVE_INCOMPATIBLE_CTIME_R +#define asctime_r _incompatible_asctime_r +#define ctime_r _incompatible_ctime_r +#endif /* HAVE_INCOMPATIBLE_CTIME_R */ + +/* +** Nested includes +*/ + +/* Avoid clashes with NetBSD by renaming NetBSD's declarations. + If defining the 'timezone' variable, avoid a clash with FreeBSD's + 'timezone' function by renaming its declaration. */ +#define localtime_rz sys_localtime_rz +#define mktime_z sys_mktime_z +#define posix2time_z sys_posix2time_z +#define time2posix_z sys_time2posix_z +#if defined USG_COMPAT && USG_COMPAT == 2 +# define timezone sys_timezone +#endif +#define timezone_t sys_timezone_t +#define tzalloc sys_tzalloc +#define tzfree sys_tzfree +#undef localtime_rz +#undef mktime_z +#undef posix2time_z +#undef time2posix_z +#if defined USG_COMPAT && USG_COMPAT == 2 +# undef timezone +#endif +#undef timezone_t +#undef tzalloc +#undef tzfree + +#if HAVE_GETTEXT +#include +#endif /* HAVE_GETTEXT */ + +#ifndef HAVE_STRFTIME_L +# if _POSIX_VERSION < 200809 +# define HAVE_STRFTIME_L 0 +# else +# define HAVE_STRFTIME_L 1 +# endif +#endif + +#ifndef USG_COMPAT +# ifndef _XOPEN_VERSION +# define USG_COMPAT 0 +# else +# define USG_COMPAT 1 +# endif +#endif + +#ifndef HAVE_TZNAME +# if _POSIX_VERSION < 198808 && !USG_COMPAT +# define HAVE_TZNAME 0 +# else +# define HAVE_TZNAME 1 +# endif +#endif + +#ifndef ALTZONE +# if defined __sun || defined _M_XENIX +# define ALTZONE 1 +# else +# define ALTZONE 0 +# endif +#endif + +#ifndef R_OK +#define R_OK 4 +#endif /* !defined R_OK */ + +#if 3 <= __GNUC__ +# define ATTRIBUTE_FORMAT(spec) __attribute__((__format__ spec)) +#else +# define ATTRIBUTE_FORMAT(spec) /* empty */ +#endif + +/* +** Workarounds for compilers/systems. +*/ + +#ifndef EPOCH_LOCAL +# define EPOCH_LOCAL 0 +#endif +#ifndef EPOCH_OFFSET +# define EPOCH_OFFSET 0 +#endif + +/* +** Compile with -Dtime_tz=T to build the tz package with a private +** int64_t type equivalent to T rather than the system-supplied int64_t. +** This debugging feature can test unusual design decisions +** (e.g., int64_t wider than 'long', or unsigned int64_t) even on +** typical platforms. +*/ +#if defined time_tz || EPOCH_LOCAL || EPOCH_OFFSET != 0 +# define TZ_INT64_T 1 +#else +# define TZ_INT64_T 0 +#endif + +#if defined LOCALTIME_IMPLEMENTATION && TZ_INT64_T +static int64_t sys_time(int64_t *x) { return time(x); } +#endif + +#if TZ_INT64_T + +typedef time_tz tz_int64_t; + +# undef asctime +# define asctime tz_asctime +# undef asctime_r +# define asctime_r tz_asctime_r +# undef ctime +# define ctime tz_ctime +# undef ctime_r +# define ctime_r tz_ctime_r +# undef difftime +# define difftime tz_difftime +# undef gmtime +# define gmtime tz_gmtime +# undef gmtime_r +# define gmtime_r tz_gmtime_r +# undef localtime +# define localtime tz_localtime +# undef localtime_r +# define localtime_r tz_localtime_r +# undef localtime_rz +# define localtime_rz tz_localtime_rz +# undef mktime +# define mktime tz_mktime +# undef mktime_z +# define mktime_z tz_mktime_z +# undef offtime +# define offtime tz_offtime +# undef posix2time +# define posix2time tz_posix2time +# undef posix2time_z +# define posix2time_z tz_posix2time_z +# undef strftime +# define strftime tz_strftime +# undef time +# define time tz_time +# undef time2posix +# define time2posix tz_time2posix +# undef time2posix_z +# define time2posix_z tz_time2posix_z +# undef int64_t +# define int64_t tz_int64_t +# undef timegm +# define timegm tz_timegm +# undef timelocal +# define timelocal tz_timelocal +# undef timeoff +# define timeoff tz_timeoff +# undef tzalloc +# define tzalloc tz_tzalloc +# undef tzfree +# define tzfree tz_tzfree +# undef tzset +# define tzset tz_tzset +# if HAVE_STRFTIME_L +# undef strftime_l +# define strftime_l tz_strftime_l +# endif +# if HAVE_TZNAME +# undef tzname +# define tzname tz_tzname +# endif +# if USG_COMPAT +# undef daylight +# define daylight tz_daylight +# undef timezone +# define timezone tz_timezone +# endif +# if ALTZONE +# undef altzone +# define altzone tz_altzone +# endif + +char *asctime(struct tm const *); +char *asctime_r(struct tm const *restrict, char *restrict); +char *ctime(int64_t const *); +char *ctime_r(int64_t const *, char *); +double difftime(int64_t, int64_t) pureconst; +size_t strftime(char *restrict, size_t, char const *restrict, + struct tm const *restrict); +# if HAVE_STRFTIME_L +size_t strftime_l(char *restrict, size_t, char const *restrict, + struct tm const *restrict, locale_t); +# endif +struct tm *gmtime(int64_t const *); +struct tm *gmtime_r(int64_t const *restrict, struct tm *restrict); +struct tm *localtime(int64_t const *); +struct tm *localtime_r(int64_t const *restrict, struct tm *restrict); +int64_t mktime(struct tm *); +int64_t time(int64_t *); +void tzset(void); +#endif + +#if !HAVE_DECL_ASCTIME_R && !defined asctime_r +extern char *asctime_r(struct tm const *restrict, char *restrict); +#endif + +#ifndef HAVE_DECL_ENVIRON +# if defined environ || defined __USE_GNU +# define HAVE_DECL_ENVIRON 1 +# else +# define HAVE_DECL_ENVIRON 0 +# endif +#endif + +#if 2 <= HAVE_TZNAME + (TZ_INT64_T || !HAVE_POSIX_DECLS) +extern char *tzname[]; +#endif +#if 2 <= USG_COMPAT + (TZ_INT64_T || !HAVE_POSIX_DECLS) +extern long timezone; +extern int daylight; +#endif +#if 2 <= ALTZONE + (TZ_INT64_T || !HAVE_POSIX_DECLS) +extern long altzone; +#endif + +/* +** The STD_INSPIRED functions are similar, but most also need +** declarations if time_tz is defined. +*/ + +#ifdef STD_INSPIRED +# if TZ_INT64_T || !defined offtime +struct tm *offtime(int64_t const *, long); +# endif +# if TZ_INT64_T || !defined timegm +int64_t timegm(struct tm *); +# endif +# if TZ_INT64_T || !defined timelocal +int64_t timelocal(struct tm *); +# endif +# if TZ_INT64_T || !defined timeoff +int64_t timeoff(struct tm *, long); +# endif +# if TZ_INT64_T || !defined time2posix +int64_t time2posix(int64_t); +# endif +# if TZ_INT64_T || !defined posix2time +int64_t posix2time(int64_t); +# endif +#endif + +/* Infer TM_ZONE on systems where this information is known, but suppress + guessing if NO_TM_ZONE is defined. Similarly for TM_GMTOFF. */ +#define TM_GMTOFF tm_gmtoff +#define TM_ZONE tm_zone + +/* +** Define functions that are ABI compatible with NetBSD but have +** better prototypes. NetBSD 6.1.4 defines a pointer type timezone_t +** and labors under the misconception that 'const timezone_t' is a +** pointer to a constant. This use of 'const' is ineffective, so it +** is not done here. What we call 'struct state' NetBSD calls +** 'struct __state', but this is a private name so it doesn't matter. +*/ +#if NETBSD_INSPIRED +typedef struct state *timezone_t; +struct tm *localtime_rz(timezone_t restrict, int64_t const *restrict, + struct tm *restrict); +int64_t mktime_z(timezone_t restrict, struct tm *restrict); +timezone_t tzalloc(char const *); +void tzfree(timezone_t); +# ifdef STD_INSPIRED +# if TZ_INT64_T || !defined posix2time_z +int64_t posix2time_z(timezone_t, int64_t) nosideeffect; +# endif +# if TZ_INT64_T || !defined time2posix_z +int64_t time2posix_z(timezone_t, int64_t) nosideeffect; +# endif +# endif +#endif + +/* +** Finally, some convenience items. +*/ + +#define TYPE_BIT(type) (sizeof(type) * CHAR_BIT) +#define TYPE_SIGNED(type) (((type) -1) < 0) +#define TWOS_COMPLEMENT(t) ((t) ~ (t) 0 < 0) + +/* Max and min values of the integer type T, of which only the bottom + B bits are used, and where the highest-order used bit is considered + to be a sign bit if T is signed. */ +#define MAXVAL(t, b) \ + ((t) (((t) 1 << ((b) - 1 - TYPE_SIGNED(t))) \ + - 1 + ((t) 1 << ((b) - 1 - TYPE_SIGNED(t))))) +#define MINVAL(t, b) \ + ((t) (TYPE_SIGNED(t) ? - TWOS_COMPLEMENT(t) - MAXVAL(t, b) : 0)) + +/* The extreme time values, assuming no padding. */ +#define INT64_T_MIN_NO_PADDING MINVAL(int64_t, TYPE_BIT(int64_t)) +#define INT64_T_MAX_NO_PADDING MAXVAL(int64_t, TYPE_BIT(int64_t)) + +/* The extreme time values. These are macros, not constants, so that + any portability problems occur only when compiling .c files that use + the macros, which is safer for applications that need only zdump and zic. + This implementation assumes no padding if int64_t is signed and + either the compiler lacks support for _Generic or int64_t is not one + of the standard signed integer types. */ +#if HAVE_GENERIC +# define INT64_T_MIN \ + _Generic((int64_t) 0, \ + signed char: SCHAR_MIN, short: SHRT_MIN, \ + int: INT_MIN, long: LONG_MIN, long long: LLONG_MIN, \ + default: INT64_T_MIN_NO_PADDING) +# define INT64_T_MAX \ + (TYPE_SIGNED(int64_t) \ + ? _Generic((int64_t) 0, \ + signed char: SCHAR_MAX, short: SHRT_MAX, \ + int: INT_MAX, long: LONG_MAX, long long: LLONG_MAX, \ + default: INT64_T_MAX_NO_PADDING) \ + : (int64_t) -1) +#else +# define INT64_T_MIN INT64_T_MIN_NO_PADDING +# define INT64_T_MAX INT64_T_MAX_NO_PADDING +#endif + +/* +** 302 / 1000 is log10(2.0) rounded up. +** Subtract one for the sign bit if the type is signed; +** add one for integer division truncation; +** add one more for a minus sign if the type is signed. +*/ +#define INT_STRLEN_MAXIMUM(type) \ + ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + \ + 1 + TYPE_SIGNED(type)) + +/* +** INITIALIZE(x) +*/ + +#define INITIALIZE(x) ((x) = 0) + +/* Whether memory access must strictly follow the C standard. + If 0, it's OK to read uninitialized storage so long as the value is + not relied upon. Defining it to 0 lets mktime access parts of + struct tm that might be uninitialized, as a heuristic when the + standard doesn't say what to return and when tm_gmtoff can help + mktime likely infer a better value. */ +#ifndef UNINIT_TRAP +# define UNINIT_TRAP 0 +#endif + +#ifdef DEBUG +# define UNREACHABLE() abort() +#elif 4 < __GNUC__ + (5 <= __GNUC_MINOR__) +# define UNREACHABLE() __builtin_unreachable() +#elif defined __has_builtin +# if __has_builtin(__builtin_unreachable) +# define UNREACHABLE() __builtin_unreachable() +# endif +#endif +#ifndef UNREACHABLE +# define UNREACHABLE() ((void) 0) +#endif + +/* +** For the benefit of GNU folk... +** '_(MSGID)' uses the current locale's message library string for MSGID. +** The default is to use gettext if available, and use MSGID otherwise. +*/ + +#if HAVE_GETTEXT +#define _(msgid) gettext(msgid) +#else /* !HAVE_GETTEXT */ +#define _(msgid) msgid +#endif /* !HAVE_GETTEXT */ + +#if !defined TZ_DOMAIN && defined HAVE_GETTEXT +# define TZ_DOMAIN "tz" +#endif + +#if HAVE_INCOMPATIBLE_CTIME_R +#undef asctime_r +#undef ctime_r +char *asctime_r(struct tm const *, char *); +char *ctime_r(int64_t const *, char *); +#endif /* HAVE_INCOMPATIBLE_CTIME_R */ + +/* Handy macros that are independent of tzfile implementation. */ + +#define SECSPERMIN 60 +#define MINSPERHOUR 60 +#define HOURSPERDAY 24 +#define DAYSPERWEEK 7 +#define DAYSPERNYEAR 365 +#define DAYSPERLYEAR 366 +#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) +#define SECSPERDAY ((int_fast32_t) SECSPERHOUR * HOURSPERDAY) +#define MONSPERYEAR 12 + +#define YEARSPERREPEAT 400 /* years before a Gregorian repeat */ +#define DAYSPERREPEAT ((int_fast32_t) 400 * 365 + 100 - 4 + 1) +#define SECSPERREPEAT ((int_fast64_t) DAYSPERREPEAT * SECSPERDAY) +#define AVGSECSPERYEAR (SECSPERREPEAT / YEARSPERREPEAT) + +#define TM_SUNDAY 0 +#define TM_MONDAY 1 +#define TM_TUESDAY 2 +#define TM_WEDNESDAY 3 +#define TM_THURSDAY 4 +#define TM_FRIDAY 5 +#define TM_SATURDAY 6 + +#define TM_JANUARY 0 +#define TM_FEBRUARY 1 +#define TM_MARCH 2 +#define TM_APRIL 3 +#define TM_MAY 4 +#define TM_JUNE 5 +#define TM_JULY 6 +#define TM_AUGUST 7 +#define TM_SEPTEMBER 8 +#define TM_OCTOBER 9 +#define TM_NOVEMBER 10 +#define TM_DECEMBER 11 + +#define TM_YEAR_BASE 1900 +#define TM_WDAY_BASE TM_MONDAY + +#define EPOCH_YEAR 1970 +#define EPOCH_WDAY TM_THURSDAY + +#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) + +/* +** Since everything in isleap is modulo 400 (or a factor of 400), we know that +** isleap(y) == isleap(y % 400) +** and so +** isleap(a + b) == isleap((a + b) % 400) +** or +** isleap(a + b) == isleap(a % 400 + b % 400) +** This is true even if % means modulo rather than Fortran remainder +** (which is allowed by C89 but not by C99 or later). +** We use this to avoid addition overflow problems. +*/ + +#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400) + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_THIRD_PARTY_TZ_PRIVATE_H_ */ diff --git a/libc/time/tzfile.internal.h b/libc/time/tzfile.internal.h index cc2aa6067..bfef13de0 100644 --- a/libc/time/tzfile.internal.h +++ b/libc/time/tzfile.internal.h @@ -1,46 +1,56 @@ #ifndef TZFILE_H #define TZFILE_H - -#define TM_ZONE tm_zone -#define TM_GMTOFF tm_gmtoff +/* clang-format off */ +/* Layout and location of TZif files. */ /* ** This file is in the public domain, so clarified as of ** 1996-06-05 by Arthur David Olson. */ +/* +** This header is for use ONLY with the time conversion code. +** There is no guarantee that it will remain unchanged, +** or that it will remain at all. +** Do NOT copy it to any system include directory. +** Thank you! +*/ + /* ** Information about time zone files. */ #ifndef TZDIR -#define TZDIR "/zip/usr/share/zoneinfo" -#endif +#define TZDIR "/zip/usr/share/zoneinfo" /* Time zone object file directory */ +#endif /* !defined TZDIR */ #ifndef TZDEFAULT -#define TZDEFAULT "GST" -#endif +#define TZDEFAULT "GST" +#endif /* !defined TZDEFAULT */ #ifndef TZDEFRULES -#define TZDEFRULES "New_York" -#endif +#define TZDEFRULES "New_York" +#endif /* !defined TZDEFRULES */ + + +/* See Internet RFC 8536 for more details about the following format. */ /* ** Each file begins with. . . */ -#define TZ_MAGIC "TZif" +#define TZ_MAGIC "TZif" struct tzhead { - char tzh_magic[4]; /* TZ_MAGIC */ - char tzh_version[1]; /* '\0' or '2' or '3' as of 2013 */ - char tzh_reserved[15]; /* reserved; must be zero */ - char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */ - char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */ - char tzh_leapcnt[4]; /* coded number of leap seconds */ - char tzh_timecnt[4]; /* coded number of transition times */ - char tzh_typecnt[4]; /* coded number of local time types */ - char tzh_charcnt[4]; /* coded number of abbr. chars */ + char tzh_magic[4]; /* TZ_MAGIC */ + char tzh_version[1]; /* '\0' or '2'-'4' as of 2021 */ + char tzh_reserved[15]; /* reserved; must be zero */ + char tzh_ttisutcnt[4]; /* coded number of trans. time flags */ + char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */ + char tzh_leapcnt[4]; /* coded number of leap seconds */ + char tzh_timecnt[4]; /* coded number of transition times */ + char tzh_typecnt[4]; /* coded number of local time types */ + char tzh_charcnt[4]; /* coded number of abbr. chars */ }; /* @@ -58,14 +68,15 @@ struct tzhead { ** one (char [4]) total correction after above ** tzh_ttisstdcnt (char)s indexed by type; if 1, transition ** time is standard time, if 0, -** transition time is wall clock time -** if absent, transition times are -** assumed to be wall clock time -** tzh_ttisgmtcnt (char)s indexed by type; if 1, transition -** time is UT, if 0, -** transition time is local time -** if absent, transition times are +** transition time is local (wall clock) +** time; if absent, transition times are ** assumed to be local time +** tzh_ttisutcnt (char)s indexed by type; if 1, transition +** time is UT, if 0, transition time is +** local time; if absent, transition +** times are assumed to be local time. +** When this is 1, the corresponding +** std/wall indicator must also be 1. */ /* @@ -91,73 +102,21 @@ struct tzhead { */ #ifndef TZ_MAX_TIMES -#define TZ_MAX_TIMES 1200 +#define TZ_MAX_TIMES 2000 #endif /* !defined TZ_MAX_TIMES */ #ifndef TZ_MAX_TYPES /* This must be at least 17 for Europe/Samara and Europe/Vilnius. */ -#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */ +#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */ #endif /* !defined TZ_MAX_TYPES */ #ifndef TZ_MAX_CHARS -#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */ -/* (limited by what unsigned chars can hold) */ +#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */ + /* (limited by what unsigned chars can hold) */ #endif /* !defined TZ_MAX_CHARS */ #ifndef TZ_MAX_LEAPS -#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */ +#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */ #endif /* !defined TZ_MAX_LEAPS */ -#define SECSPERMIN 60 -#define MINSPERHOUR 60 -#define HOURSPERDAY 24 -#define DAYSPERWEEK 7 -#define DAYSPERNYEAR 365 -#define DAYSPERLYEAR 366 -#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) -#define SECSPERDAY ((int_fast32_t)SECSPERHOUR * HOURSPERDAY) -#define MONSPERYEAR 12 - -#define TM_SUNDAY 0 -#define TM_MONDAY 1 -#define TM_TUESDAY 2 -#define TM_WEDNESDAY 3 -#define TM_THURSDAY 4 -#define TM_FRIDAY 5 -#define TM_SATURDAY 6 - -#define TM_JANUARY 0 -#define TM_FEBRUARY 1 -#define TM_MARCH 2 -#define TM_APRIL 3 -#define TM_MAY 4 -#define TM_JUNE 5 -#define TM_JULY 6 -#define TM_AUGUST 7 -#define TM_SEPTEMBER 8 -#define TM_OCTOBER 9 -#define TM_NOVEMBER 10 -#define TM_DECEMBER 11 - -#define TM_YEAR_BASE 1900 - -#define EPOCH_YEAR 1970 -#define EPOCH_WDAY TM_THURSDAY - -#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) - -/* -** Since everything in isleap is modulo 400 (or a factor of 400), we know that -** isleap(y) == isleap(y % 400) -** and so -** isleap(a + b) == isleap((a + b) % 400) -** or -** isleap(a + b) == isleap(a % 400 + b % 400) -** This is true even if % means modulo rather than Fortran remainder -** (which is allowed by C89 but not C99). -** We use this to avoid addition overflow problems. -*/ - -#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400) - #endif /* !defined TZFILE_H */ diff --git a/third_party/chibicc/chibicc.mk b/third_party/chibicc/chibicc.mk index c77672dac..ec387ba89 100644 --- a/third_party/chibicc/chibicc.mk +++ b/third_party/chibicc/chibicc.mk @@ -116,7 +116,7 @@ o/$(MODE)/third_party/chibicc/chibicc.com: \ @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \ -o o/$(MODE)/third_party/chibicc/.chibicc/.symtab $< - @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ + @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ o/$(MODE)/third_party/chibicc/.chibicc/.symtab o/$(MODE)/third_party/chibicc/as.com.dbg: \ diff --git a/third_party/linenoise/linenoise.c b/third_party/linenoise/linenoise.c index 964ed1e24..2eac70d57 100644 --- a/third_party/linenoise/linenoise.c +++ b/third_party/linenoise/linenoise.c @@ -339,7 +339,7 @@ static int notwseparator(wint_t c) { } static int iswname(wint_t c) { - return !iswseparator(c) || c == '_' || c == '-' || c == '.'; + return !iswseparator(c) || c == '_' || c == '-' || c == '.' || c == ':'; } static int notwname(wint_t c) { @@ -1956,23 +1956,22 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf, // handle tab and tab-tab completion if (seq[0] == '\t' && completionCallback) { - size_t i, j, k, n, m, itemlen; + size_t i, n, m; // we know that the user pressed tab once rc = 0; linenoiseFreeCompletions(&l->lc); i = Backwards(l, l->pos, iswname); - j = l->pos; { - char *s = strndup(l->buf + i, j - i); + char *s = strndup(l->buf + i, l->pos - i); completionCallback(s, &l->lc); free(s); } m = GetCommonPrefixLength(&l->lc); - if (m > j - i || (m == j - i && l->lc.len == 1)) { + if (m > l->pos - i || (m == l->pos - i && l->lc.len == 1)) { // on common prefix (or single completion) we complete and return - n = i + m + (l->len - j); + n = i + m + (l->len - l->pos); if (linenoiseGrow(l, n + 1)) { - memmove(l->buf + i + m, l->buf + i + j, l->len - j + 1); + memmove(l->buf + i + m, l->buf + l->pos, l->len - l->pos + 1); memcpy(l->buf + i, l->lc.cvec[0], m); l->pos = i + m; l->len = n; @@ -1994,7 +1993,7 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf, if (rc == 1 && seq[0] == '\t') { const char **p; struct abuf ab; - int i, x, y, xn, yn, xy; + int i, k, x, y, xn, yn, xy, itemlen; itemlen = linenoiseMaxCompletionWidth(&l->lc) + 4; xn = MAX(1, (l->ws.ws_col - 1) / itemlen); yn = (l->lc.len + (xn - 1)) / xn; diff --git a/third_party/lua/cosmo.h b/third_party/lua/cosmo.h index 6b8980b8b..06d136548 100644 --- a/third_party/lua/cosmo.h +++ b/third_party/lua/cosmo.h @@ -8,8 +8,8 @@ COSMOPOLITAN_C_START_ char *LuaFormatStack(lua_State *) dontdiscard; int LuaCallWithTrace(lua_State *, int, int, lua_State *); -int LuaEncodeJsonData(lua_State *, char **, int, char *, int); -int LuaEncodeLuaData(lua_State *, char **, int, char *, int); +int LuaEncodeJsonData(lua_State *, char **, char *, int); +int LuaEncodeLuaData(lua_State *, char **, char *, int); int LuaEncodeUrl(lua_State *); int LuaParseUrl(lua_State *); int LuaPushHeader(lua_State *, struct HttpMessage *, char *, int); diff --git a/third_party/lua/ldo.c b/third_party/lua/ldo.c index e873e6de8..b9a1670cb 100644 --- a/third_party/lua/ldo.c +++ b/third_party/lua/ldo.c @@ -27,6 +27,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #define ldo_c #define LUA_CORE +#include "libc/log/log.h" #include "libc/runtime/gc.h" #include "third_party/lua/lapi.h" #include "third_party/lua/ldebug.h" @@ -148,7 +149,7 @@ l_noret luaD_throw (lua_State *L, int errcode) { lua_unlock(L); g->panic(L); /* call panic function (last chance to jump out) */ } - abort(); + __die(); } } } diff --git a/third_party/lua/lrepl.c b/third_party/lua/lrepl.c index cf3d131e4..5bdd29561 100644 --- a/third_party/lua/lrepl.c +++ b/third_party/lua/lrepl.c @@ -29,15 +29,18 @@ #include "libc/alg/alg.h" #include "libc/calls/calls.h" #include "libc/calls/sigbits.h" +#include "libc/errno.h" #include "libc/intrin/kprintf.h" #include "libc/intrin/nomultics.internal.h" #include "libc/log/check.h" #include "libc/macros.internal.h" #include "libc/runtime/gc.h" #include "libc/runtime/runtime.h" +#include "libc/str/str.h" #include "libc/sysv/consts/sa.h" #include "libc/x/x.h" #include "third_party/linenoise/linenoise.h" +#include "third_party/lua/cosmo.h" #include "third_party/lua/lauxlib.h" #include "third_party/lua/lprefix.h" #include "third_party/lua/lrepl.h" @@ -87,37 +90,74 @@ static const char *g_historypath; #endif -static void lua_readline_addcompletion (linenoiseCompletions *c, const char *s) { - char **p, *t; +static void lua_readline_addcompletion (linenoiseCompletions *c, char *s) { + char **p; if ((p = realloc(c->cvec, (c->len + 1) * sizeof(*p)))) { c->cvec = p; - if ((t = strdup(s))) { - c->cvec[c->len++] = t; - } + c->cvec[c->len++] = s; } } void lua_readline_completions (const char *p, linenoiseCompletions *c) { int i; + bool found; lua_State *L; const char *name; - for (i = 0; i < ARRAYLEN(kKeywordHints); ++i) { - if (startswithi(kKeywordHints[i], p)) { - lua_readline_addcompletion(c, kKeywordHints[i]); - } - } + char *a, *b, *component; + + // start searching globals L = globalL; lua_pushglobaltable(L); + + // traverse parent objects + // split foo.bar and foo:bar + a = p; + b = strpbrk(a, ".:"); + while (b) { + component = strndup(a, b - a); + found = false; + lua_pushnil(L); // search key + while (lua_next(L, -2)) { + if (lua_type(L, -2) == LUA_TSTRING) { + name = lua_tostring(L, -2); + if (!strcmp(name, component)) { + lua_remove(L, -3); // remove table + lua_remove(L, -2); // remove key + found = true; + break; + } + } + lua_pop(L, 1); // pop value + } + free(component); + if (!found) { + lua_pop(L, 1); // pop table + return; + } + a = b + 1; + b = strpbrk(a, ".:"); + } + + // search final object lua_pushnil(L); - while (lua_next(L, -2) != 0) { - name = lua_tostring(L, -2); - if (startswithi(name, p)) { - lua_readline_addcompletion(c, name); + while (lua_next(L, -2)) { + if (lua_type(L, -2) == LUA_TSTRING) { + name = lua_tostring(L, -2); + if (startswithi(name, a)) { + lua_readline_addcompletion(c, xasprintf("%.*s%s", a - p, p, name)); + } } lua_pop(L, 1); } + lua_pop(L, 1); + + for (i = 0; i < ARRAYLEN(kKeywordHints); ++i) { + if (startswithi(kKeywordHints[i], p)) { + lua_readline_addcompletion(c, xstrdup(kKeywordHints[i])); + } + } if (lua_repl_completions_callback) { lua_repl_completions_callback(p, c); } @@ -191,7 +231,7 @@ static ssize_t pushline (lua_State *L, int firstline) { prmt = strdup(get_prompt(L, firstline)); lua_pop(L, 1); /* remove prompt */ LUA_REPL_UNLOCK; - rc = linenoiseEdit(lua_repl_linenoise, 0, &b, !firstline || lua_repl_blocking); + rc = linenoiseEdit(lua_repl_linenoise, prmt, &b, !firstline || lua_repl_blocking); free(prmt); if (rc != -1) { if (b && *b) { @@ -207,6 +247,9 @@ static ssize_t pushline (lua_State *L, int firstline) { LUA_REPL_LOCK; rc = b ? 1 : -1; } + if (!(rc == -1 && errno == EAGAIN)) { + write(1, "\n", 1); + } if (rc == -1 || (!rc && !b)) { return rc; } diff --git a/third_party/lua/ltests.c b/third_party/lua/ltests.c index 6b1606902..f0cf71020 100644 --- a/third_party/lua/ltests.c +++ b/third_party/lua/ltests.c @@ -1724,7 +1724,7 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) { lua_error(L1); } else if EQ("abort") { - abort(); + __die(); } else if EQ("throw") { #if defined(__cplusplus) diff --git a/third_party/lua/lua.main.c b/third_party/lua/lua.main.c index afbc7280b..3ff767466 100644 --- a/third_party/lua/lua.main.c +++ b/third_party/lua/lua.main.c @@ -324,7 +324,6 @@ static void doREPL (lua_State *L) { progname = oldprogname; return; } - lua_writeline(); if (status == LUA_OK) status = lua_runchunk(L, 0, LUA_MULTRET); if (status == LUA_OK) { @@ -335,7 +334,6 @@ static void doREPL (lua_State *L) { } lua_freerepl(); lua_settop(L, 0); /* clear stack */ - lua_writeline(); progname = oldprogname; } diff --git a/third_party/lua/lua.mk b/third_party/lua/lua.mk index 4b892d8d4..e63b3fe92 100644 --- a/third_party/lua/lua.mk +++ b/third_party/lua/lua.mk @@ -77,7 +77,7 @@ o/$(MODE)/third_party/lua/lua.com: \ @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \ -o o/$(MODE)/third_party/lua/.lua/.symtab $< - @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ + @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ o/$(MODE)/third_party/lua/.lua/.symtab o//third_party/lua/lgc.o: \ diff --git a/third_party/lua/luaencodejsondata.c b/third_party/lua/luaencodejsondata.c index d894ff0e5..16c9ba5e6 100644 --- a/third_party/lua/luaencodejsondata.c +++ b/third_party/lua/luaencodejsondata.c @@ -25,8 +25,8 @@ #include "third_party/lua/lauxlib.h" #include "third_party/lua/lua.h" -int LuaEncodeJsonData(lua_State *L, char **buf, int level, char *numformat, - int idx) { +static int LuaEncodeJsonDataImpl(lua_State *L, char **buf, int level, + char *numformat, int idx) { char *s; bool isarray; size_t tbllen, z; @@ -93,7 +93,7 @@ int LuaEncodeJsonData(lua_State *L, char **buf, int level, char *numformat, for (size_t i = 1; i <= tbllen; i++) { if (i > 1) appendw(buf, ','); lua_rawgeti(L, -1, i); // table/-2, value/-1 - LuaEncodeJsonData(L, buf, level - 1, numformat, -1); + LuaEncodeJsonDataImpl(L, buf, level - 1, numformat, -1); lua_pop(L, 1); } } else { @@ -121,7 +121,7 @@ int LuaEncodeJsonData(lua_State *L, char **buf, int level, char *numformat, lua_remove(L, -1); // remove copied key: tab/-3, key/-2, val/-1 } appendw(buf, '"' | ':' << 010); - LuaEncodeJsonData(L, buf, level - 1, numformat, -1); + LuaEncodeJsonDataImpl(L, buf, level - 1, numformat, -1); lua_pop(L, 1); // table/-2, key/-1 } // stack: table/-1, as the key was popped by lua_next @@ -137,3 +137,9 @@ int LuaEncodeJsonData(lua_State *L, char **buf, int level, char *numformat, unreachable; } } + +int LuaEncodeJsonData(lua_State *L, char **buf, char *numformat, int idx) { + int rc; + rc = LuaEncodeJsonDataImpl(L, buf, 64, numformat, idx); + return rc; +} diff --git a/third_party/lua/luaencodeluadata.c b/third_party/lua/luaencodeluadata.c index b07bf852c..15aa03518 100644 --- a/third_party/lua/luaencodeluadata.c +++ b/third_party/lua/luaencodeluadata.c @@ -16,42 +16,98 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" #include "libc/bits/bits.h" #include "libc/fmt/itoa.h" #include "libc/math.h" +#include "libc/mem/mem.h" #include "libc/stdio/append.internal.h" +#include "libc/x/x.h" #include "third_party/lua/cosmo.h" #include "third_party/lua/lauxlib.h" #include "third_party/lua/lua.h" -int LuaEncodeLuaData(lua_State *L, char **buf, int level, char *numformat, - int idx) { +struct Visited { + int n; + void **p; +}; + +static bool PushVisit(struct Visited *visited, void *p) { + int i; + for (i = 0; i < visited->n; ++i) { + if (visited->p[i] == p) { + return false; + } + } + visited->p = xrealloc(visited->p, ++visited->n * sizeof(*visited->p)); + visited->p[visited->n - 1] = p; + return true; +} + +static void PopVisit(struct Visited *visited) { + assert(visited->n > 0); + --visited->n; +} + +static int LuaEncodeLuaDataImpl(lua_State *L, char **buf, int level, + char *numformat, int idx, + struct Visited *visited) { char *s; - int ktype; + bool didcomma; lua_Integer i; + int ktype, vtype; size_t tbllen, buflen, slen; char ibuf[21], fmt[] = "%.14g"; if (level > 0) { switch (lua_type(L, idx)) { + case LUA_TNIL: appendw(buf, READ32LE("nil")); return 0; + case LUA_TSTRING: s = lua_tolstring(L, idx, &slen); EscapeLuaString(s, slen, buf); return 0; + case LUA_TFUNCTION: appendf(buf, "\"func@%p\"", lua_touserdata(L, idx)); return 0; - case LUA_TUSERDATA: - appendf(buf, "\"udata@%p\"", lua_touserdata(L, idx)); - return 0; + case LUA_TLIGHTUSERDATA: appendf(buf, "\"light@%p\"", lua_touserdata(L, idx)); return 0; + case LUA_TTHREAD: appendf(buf, "\"thread@%p\"", lua_touserdata(L, idx)); return 0; + + case LUA_TUSERDATA: + if (luaL_callmeta(L, idx, "__repr")) { + if (lua_type(L, -1) == LUA_TSTRING) { + s = lua_tolstring(L, -1, &slen); + appendd(buf, s, slen); + } else { + appendf(buf, "[[error %s returned a %s value]]", "__repr", + luaL_typename(L, -1)); + } + lua_pop(L, 1); + return 0; + } + if (luaL_callmeta(L, idx, "__tostring")) { + if (lua_type(L, -1) == LUA_TSTRING) { + s = lua_tolstring(L, -1, &slen); + EscapeLuaString(s, slen, buf); + } else { + appendf(buf, "[[error %s returned a %s value]]", "__tostring", + luaL_typename(L, -1)); + } + lua_pop(L, 1); + return 0; + } + appendf(buf, "\"udata@%p\"", lua_touserdata(L, idx)); + return 0; + case LUA_TNUMBER: if (lua_isinteger(L, idx)) { appendd(buf, ibuf, @@ -69,11 +125,13 @@ int LuaEncodeLuaData(lua_State *L, char **buf, int level, char *numformat, fmt[4] = *numformat; break; default: - return luaL_error(L, "numformat string not allowed"); + luaL_error(L, "numformat string not allowed"); + unreachable; } appendf(buf, fmt, lua_tonumber(L, idx)); } return 0; + case LUA_TBOOLEAN: if (lua_toboolean(L, idx)) { appendw(buf, READ32LE("true")); @@ -81,26 +139,47 @@ int LuaEncodeLuaData(lua_State *L, char **buf, int level, char *numformat, appendw(buf, READ64LE("false\0\0")); } return 0; + case LUA_TTABLE: i = 0; + didcomma = false; appendw(buf, '{'); + lua_pushvalue(L, idx); lua_pushnil(L); // push the first key - while (lua_next(L, -2) != 0) { + while (lua_next(L, -2)) { + ++i; ktype = lua_type(L, -2); - if (i++ > 0) appendw(buf, ','); + vtype = lua_type(L, -1); if (ktype != LUA_TNUMBER || lua_tointeger(L, -2) != i) { - appendw(buf, '['); - lua_pushvalue(L, -2); // table/-4, key/-3, value/-2, key/-1 - LuaEncodeLuaData(L, buf, level - 1, numformat, -1); - lua_remove(L, -1); // remove copied key: table/-3, key/-2, value/-1 - appendw(buf, ']' | '=' << 010); + if (PushVisit(visited, lua_touserdata(L, -2))) { + if (i > 1) appendw(buf, ',' | ' ' << 8); + didcomma = true; + appendw(buf, '['); + LuaEncodeLuaDataImpl(L, buf, level - 1, numformat, -2, visited); + appendw(buf, ']' | '=' << 010); + PopVisit(visited); + } else { + // TODO: Strange Lua data structure, do nothing. + lua_pop(L, 1); + continue; + } + } + if (PushVisit(visited, lua_touserdata(L, -1))) { + if (!didcomma && i > 1) appendw(buf, ',' | ' ' << 8); + LuaEncodeLuaDataImpl(L, buf, level - 1, numformat, -1, visited); + PopVisit(visited); + } else { + // TODO: Strange Lua data structure, do nothing. + lua_pop(L, 1); + continue; } - LuaEncodeLuaData(L, buf, level - 1, numformat, -1); lua_pop(L, 1); // table/-2, key/-1 } + lua_pop(L, 1); // table // stack: table/-1, as the key was popped by lua_next appendw(buf, '}'); return 0; + default: luaL_error(L, "can't serialize value of this type"); unreachable; @@ -110,3 +189,12 @@ int LuaEncodeLuaData(lua_State *L, char **buf, int level, char *numformat, unreachable; } } + +int LuaEncodeLuaData(lua_State *L, char **buf, char *numformat, int idx) { + int rc; + struct Visited visited = {0}; + rc = LuaEncodeLuaDataImpl(L, buf, 64, numformat, idx, &visited); + assert(!visited.n); + free(visited.p); + return rc; +} diff --git a/third_party/lua/luaformatstack.c b/third_party/lua/luaformatstack.c index bf155443c..f6b362dce 100644 --- a/third_party/lua/luaformatstack.c +++ b/third_party/lua/luaformatstack.c @@ -28,7 +28,7 @@ dontdiscard char *LuaFormatStack(lua_State *L) { for (i = 1; i <= top; i++) { if (i > 1) appendw(&b, '\n'); appendf(&b, "\t%d\t%s\t", i, luaL_typename(L, i)); - LuaEncodeLuaData(L, &b, 64, "g", -1); + LuaEncodeLuaData(L, &b, "g", i); } return b; } diff --git a/third_party/make/make.mk b/third_party/make/make.mk index b759ab637..fd472595e 100644 --- a/third_party/make/make.mk +++ b/third_party/make/make.mk @@ -131,7 +131,7 @@ o/$(MODE)/third_party/make/make.com: \ @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \ -o o/$(MODE)/third_party/make/.make/.symtab $< - @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ + @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ o/$(MODE)/third_party/make/.make/.symtab $(THIRD_PARTY_MAKE_OBJS): \ diff --git a/third_party/python/python.mk b/third_party/python/python.mk index 840617bb9..3448ae3b9 100644 --- a/third_party/python/python.mk +++ b/third_party/python/python.mk @@ -4197,7 +4197,7 @@ o/$(MODE)/third_party/python/python.com: \ @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \ -o o/$(MODE)/third_party/python/.python/.symtab $< - @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ + @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ o/$(MODE)/third_party/python/.python/.symtab ################################################################################ diff --git a/third_party/quickjs/quickjs.mk b/third_party/quickjs/quickjs.mk index 3fa14c00a..4749a6bc8 100644 --- a/third_party/quickjs/quickjs.mk +++ b/third_party/quickjs/quickjs.mk @@ -154,7 +154,7 @@ o/$(MODE)/third_party/quickjs/qjs.com: \ @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \ -o o/$(MODE)/third_party/quickjs/.qjs/.symtab $< - @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ + @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ o/$(MODE)/third_party/quickjs/.qjs/.symtab o/$(MODE)/third_party/quickjs/qjsc.com.dbg: \ diff --git a/third_party/sqlite3/sqlite3.mk b/third_party/sqlite3/sqlite3.mk index 4094baec5..81b204bf0 100644 --- a/third_party/sqlite3/sqlite3.mk +++ b/third_party/sqlite3/sqlite3.mk @@ -83,7 +83,7 @@ o/$(MODE)/third_party/sqlite3/sqlite3.com: \ @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \ -o o/$(MODE)/third_party/sqlite3/.sqlite3/.symtab $< - @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ + @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ o/$(MODE)/third_party/sqlite3/.sqlite3/.symtab $(THIRD_PARTY_SQLITE3_A): \ diff --git a/tool/build/build.mk b/tool/build/build.mk index b2d1b9eb9..9df4dace4 100644 --- a/tool/build/build.mk +++ b/tool/build/build.mk @@ -97,7 +97,7 @@ o/$(MODE)/tool/build/blinkenlights.com: \ @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \ -o o/$(MODE)/tool/build/.blinkenlights/.symtab $< - @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ + @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ o/$(MODE)/tool/build/.blinkenlights/.symtab o/$(MODE)/tool/build/ar.com.dbg: \ diff --git a/tool/net/help.txt b/tool/net/help.txt index 85470e687..d267d3390 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -585,7 +585,6 @@ FUNCTIONS ignored if used outside of request handling code. - numformat: sets numeric format to be used, which can be 'g', 'f', or 'a' [experimental api] - - maxdepth: (number=64) sets the max number of nested tables. EncodeLua(value[,options:table]) → json:str Turns passed Lua value into a Lua string. The following options @@ -593,7 +592,6 @@ FUNCTIONS - useoutput: (bool=false) encodes the result directly to the output buffer and returns `nil` value. This option is ignored if used outside of request handling code. - - maxdepth: (number=64) sets the max number of nested tables. EncodeLatin1(utf-8:str[,flags:int]) → iso-8859-1:str Turns UTF-8 into ISO-8859-1 string. @@ -1421,11 +1419,12 @@ UNIX MODULE The following values may also be OR'd into `flags`: - - `O_CREAT`: create file if it doesn't exist + - `O_CREAT` create file if it doesn't exist - `O_TRUNC` automatic ftruncate(fd,0) if exists - - `O_CLOEXEC`: automatic close() upon execve() - - `O_EXCL`: exclusive access (see below) - - `O_APPEND`: open file for append only + - `O_CLOEXEC` automatic close() upon execve() + - `O_EXCL` exclusive access (see below) + - `O_APPEND` open file for append only + - `O_NONBLOCK` asks read/write to fail with EAGAIN rather than block - `O_DIRECT` it's complicated (not supported on Apple and OpenBSD) - `O_DIRECTORY` useful for stat'ing (hint on UNIX but required on NT) - `O_TMPFILE` try to make temp more secure (Linux and Windows only) @@ -1615,7 +1614,7 @@ UNIX MODULE - `O_CLOEXEC`: Automatically close file descriptor upon execve() - - `O_NONBLOCK`: Request `EAGAIN` be raised rather than blocking. + - `O_NONBLOCK`: Request `EAGAIN` be raised rather than blocking - `O_DIRECT`: Enable packet mode w/ atomic reads and writes, so long as they're no larger than `PIPE_BUF` (guaranteed to be 512+ bytes) @@ -2634,6 +2633,52 @@ UNIX MODULE Returns information about resource limit. + unix.gmtime(unixts:int) + ├─→ year,mon,mday,hour,min,sec,gmtoffsec,wday,yday,dst:int,zone:str + └─→ nil,unix.Errno + + Breaks down UNIX timestamp into Zulu Time numbers. + + - `mon` 1 ≤ mon ≤ 12 + - `mday` 1 ≤ mday ≤ 31 + - `hour` 0 ≤ hour ≤ 23 + - `min` 0 ≤ min ≤ 59 + - `sec` 0 ≤ sec ≤ 60 + - `gmtoff` ±93600 seconds + - `wday` 0 ≤ wday ≤ 6 + - `yday` 0 ≤ yday ≤ 365 + - `dst` 1 if daylight savings, 0 if not, -1 if not unknown + + unix.localtime(unixts:int) + ├─→ year,mon,mday,hour,min,sec,gmtoffsec,wday,yday,dst:int,zone:str + └─→ nil,unix.Errno + + Breaks down UNIX timestamp into local time numbers. + + This follows the same API as gmtime() which has further details. + + Your redbean ships with a subset of the time zone database. + + - `/zip/usr/share/zoneinfo/Honolulu` + - `/zip/usr/share/zoneinfo/Anchorage` + - `/zip/usr/share/zoneinfo/GST` + - `/zip/usr/share/zoneinfo/Boulder` + - `/zip/usr/share/zoneinfo/Chicago` + - `/zip/usr/share/zoneinfo/New_York` + - `/zip/usr/share/zoneinfo/UTC` + - `/zip/usr/share/zoneinfo/London` + - `/zip/usr/share/zoneinfo/Berlin` + - `/zip/usr/share/zoneinfo/Israel` + - `/zip/usr/share/zoneinfo/Beijing` + - `/zip/usr/share/zoneinfo/Japan` + - `/zip/usr/share/zoneinfo/Sydney` + + You can control which timezone is used using the `TZ` environment + variable. If your time zone isn't included in the above list, you + can simply copy it inside your redbean. The same is also the case + for future updates to the database, which can be swapped out when + needed, without having to recompile. + unix.stat(path:str[, flags:int[, dirfd:int]]) ├─→ unix.Stat └─→ nil, unix.Errno @@ -2847,7 +2892,7 @@ UNIX MODULE actually consumes. For example, for small file systems, your system might report this number as being 8, which means 4096 bytes. - On Windows NT, if compression is enabled for a file, then this + On Windows NT, if `O_COMPRESSED` is used for a file, then this number will reflect the size *after* compression. you can use: st = assert(unix.stat("moby.txt")) diff --git a/tool/net/lunix.c b/tool/net/lunix.c index f1b1fc567..7c46553f6 100644 --- a/tool/net/lunix.c +++ b/tool/net/lunix.c @@ -98,15 +98,9 @@ struct UnixDir { DIR *dir; }; -struct UnixStat { - int refs; - struct stat st; -}; - struct UnixErrno { - int refs; - uint16_t errno; - uint16_t winerr; + int errno; + int winerr; const char *call; }; @@ -137,12 +131,17 @@ static void *LuaUnixAlloc(lua_State *L, size_t n) { } static void LuaPushSigset(lua_State *L, struct sigset set) { - struct sigset *sp; - sp = lua_newuserdatauv(L, sizeof(*sp), 1); + struct sigset *sp = lua_newuserdatauv(L, sizeof(*sp), 1); luaL_setmetatable(L, "unix.Sigset"); *sp = set; } +static void LuaPushStat(lua_State *L, struct stat *st) { + struct stat *stp = lua_newuserdatauv(L, sizeof(*stp), 1); + luaL_setmetatable(L, "unix.Stat"); + *stp = *st; +} + static void LuaSetIntField(lua_State *L, const char *k, lua_Integer v) { lua_pushinteger(L, v); lua_setfield(L, -2, k); @@ -163,19 +162,8 @@ static dontinline int ReturnString(lua_State *L, const char *x) { return 1; } -static void LuaUnixPushErrno(lua_State *L, const char *sc, int uerr, int werr) { - struct UnixErrno *ue, **uep; - ue = LuaUnixAlloc(L, sizeof(*ue)); - ue->refs = 1; - ue->call = sc; - ue->errno = uerr; - ue->winerr = werr; - uep = lua_newuserdatauv(L, sizeof(*uep), 1); - luaL_setmetatable(L, "unix.Errno"); - *uep = ue; -} - -static dontinline int SysretErrno(lua_State *L, const char *call, int olderr) { +static int SysretErrno(lua_State *L, const char *call, int olderr) { + struct UnixErrno *ep; int i, unixerr, winerr; unixerr = errno; winerr = GetLastError(); @@ -183,7 +171,11 @@ static dontinline int SysretErrno(lua_State *L, const char *call, int olderr) { WARNF("errno should not be %d", unixerr); } lua_pushnil(L); - LuaUnixPushErrno(L, call, unixerr, winerr); + ep = lua_newuserdatauv(L, sizeof(*ep), 1); + luaL_setmetatable(L, "unix.Errno"); + ep->errno = unixerr; + ep->winerr = winerr; + ep->call = call; errno = olderr; return 2; } @@ -936,49 +928,33 @@ static int LuaUnixWrite(lua_State *L) { return SysretInteger(L, "write", olderr, rc); } -static int ReturnStat(lua_State *L, struct UnixStat *ust) { - struct UnixStat **ustp; - ust->refs = 1; - ustp = lua_newuserdatauv(L, sizeof(*ustp), 1); - luaL_setmetatable(L, "unix.Stat"); - *ustp = ust; - return 1; -} - // unix.stat(path:str[, flags:int[, dirfd:int]]) // ├─→ unix.Stat // └─→ nil, unix.Errno static int LuaUnixStat(lua_State *L) { - const char *path; - struct UnixStat *ust; - int dirfd, flags, olderr = errno; - path = luaL_checkstring(L, 1); - flags = luaL_optinteger(L, 2, 0); - dirfd = luaL_optinteger(L, 3, AT_FDCWD); - if ((ust = LuaUnixAllocRaw(L, sizeof(*ust)))) { - if (!fstatat(dirfd, path, &ust->st, flags)) { - return ReturnStat(L, ust); - } - free(ust); + struct stat st; + int olderr = errno; + if (!fstatat(luaL_optinteger(L, 3, AT_FDCWD), luaL_checkstring(L, 1), &st, + luaL_optinteger(L, 2, 0))) { + LuaPushStat(L, &st); + return 1; + } else { + return SysretErrno(L, "stat", olderr); } - return SysretErrno(L, "stat", olderr); } // unix.fstat(fd:int) // ├─→ unix.Stat // └─→ nil, unix.Errno static int LuaUnixFstat(lua_State *L) { - int fd, olderr = errno; - struct UnixStat *ust; - olderr = errno; - fd = luaL_checkinteger(L, 1); - if ((ust = LuaUnixAllocRaw(L, sizeof(*ust)))) { - if (!fstat(fd, &ust->st)) { - return ReturnStat(L, ust); - } - free(ust); + struct stat st; + int olderr = errno; + if (!fstat(luaL_checkinteger(L, 1), &st)) { + LuaPushStat(L, &st); + return 1; + } else { + return SysretErrno(L, "fstat", olderr); } - return SysretErrno(L, "fstat", olderr); } static bool IsSockoptBool(int l, int x) { @@ -1746,9 +1722,7 @@ static int LuaUnixMinor(lua_State *L) { // unix.Stat object static dontinline struct stat *GetUnixStat(lua_State *L) { - struct UnixStat **ust; - ust = luaL_checkudata(L, 1, "unix.Stat"); - return &(*ust)->st; + return luaL_checkudata(L, 1, "unix.Stat"); } // unix.Stat:size() @@ -1853,28 +1827,12 @@ static int LuaUnixStatFlags(lua_State *L) { return ReturnInteger(L, GetUnixStat(L)->st_flags); } -static void FreeUnixStat(struct UnixStat *stat) { - if (!--stat->refs) { - free(stat); - } -} - static int LuaUnixStatToString(lua_State *L) { struct stat *st = GetUnixStat(L); lua_pushstring(L, "unix.Stat{}"); return 1; } -static int LuaUnixStatGc(lua_State *L) { - struct UnixStat **ust; - ust = luaL_checkudata(L, 1, "unix.Stat"); - if (*ust) { - FreeUnixStat(*ust); - *ust = 0; - } - return 0; -} - static const luaL_Reg kLuaUnixStatMeth[] = { {"atim", LuaUnixStatAtim}, // {"birthtim", LuaUnixStatBirthtim}, // @@ -1897,7 +1855,6 @@ static const luaL_Reg kLuaUnixStatMeth[] = { static const luaL_Reg kLuaUnixStatMeta[] = { {"__tostring", LuaUnixStatToString}, // - {"__gc", LuaUnixStatGc}, // {0}, // }; @@ -1913,10 +1870,8 @@ static void LuaUnixStatObj(lua_State *L) { //////////////////////////////////////////////////////////////////////////////// // unix.Errno object -static dontinline struct UnixErrno *GetUnixErrno(lua_State *L) { - struct UnixErrno **ep; - ep = luaL_checkudata(L, 1, "unix.Errno"); - return *ep; +static struct UnixErrno *GetUnixErrno(lua_State *L) { + return luaL_checkudata(L, 1, "unix.Errno"); } static int LuaUnixErrnoErrno(lua_State *L) { @@ -1948,22 +1903,6 @@ static int LuaUnixErrnoToString(lua_State *L) { return 1; } -static void FreeUnixErrno(struct UnixErrno *errno) { - if (!--errno->refs) { - free(errno); - } -} - -static int LuaUnixErrnoGc(lua_State *L) { - struct UnixErrno **ue; - ue = luaL_checkudata(L, 1, "unix.Errno"); - if (*ue) { - FreeUnixErrno(*ue); - *ue = 0; - } - return 0; -} - static const luaL_Reg kLuaUnixErrnoMeth[] = { {"strerror", LuaUnixErrnoToString}, // {"errno", LuaUnixErrnoErrno}, // @@ -1975,7 +1914,6 @@ static const luaL_Reg kLuaUnixErrnoMeth[] = { static const luaL_Reg kLuaUnixErrnoMeta[] = { {"__tostring", LuaUnixErrnoToString}, // - {"__gc", LuaUnixErrnoGc}, // {0}, // }; diff --git a/tool/net/net.mk b/tool/net/net.mk index 5a0dc7c88..3de3d5ec2 100644 --- a/tool/net/net.mk +++ b/tool/net/net.mk @@ -117,9 +117,10 @@ o/$(MODE)/tool/net/redbean.com: \ @$(COMPILE) -AMKDIR -T$@ mkdir -p o/$(MODE)/tool/net/.redbean @$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.redbean/.ape bs=64 count=11 conv=notrunc 2>/dev/null @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com -o o/$(MODE)/tool/net/.redbean/.symtab $< + @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ + o/$(MODE)/tool/net/.redbean/.symtab @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ o/$(MODE)/tool/net/.redbean/.ape \ - o/$(MODE)/tool/net/.redbean/.symtab \ tool/net/help.txt \ tool/net/.init.lua \ tool/net/favicon.ico \ @@ -262,9 +263,10 @@ o/$(MODE)/tool/net/redbean-demo.com: \ @$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.redbean-demo/.ape bs=64 count=11 conv=notrunc 2>/dev/null @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \ -o o/$(MODE)/tool/net/.redbean-demo/.symtab $< + @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ + o/$(MODE)/tool/net/.redbean-demo/.symtab @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ o/$(MODE)/tool/net/.redbean-demo/.ape \ - o/$(MODE)/tool/net/.redbean-demo/.symtab \ tool/net/help.txt # REDBEAN-STATIC.COM @@ -284,9 +286,10 @@ o/$(MODE)/tool/net/redbean-static.com: \ @$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.redbean-static/.ape bs=64 count=11 conv=notrunc 2>/dev/null @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \ -o o/$(MODE)/tool/net/.redbean-static/.symtab $< + @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ + o/$(MODE)/tool/net/.redbean-static/.symtab @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ o/$(MODE)/tool/net/.redbean-static/.ape \ - o/$(MODE)/tool/net/.redbean-static/.symtab \ tool/net/help.txt \ tool/net/favicon.ico \ tool/net/redbean.png @@ -320,9 +323,10 @@ o/$(MODE)/tool/net/redbean-unsecure.com: \ @$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.redbean-unsecure/.ape bs=64 count=11 conv=notrunc 2>/dev/null @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \ -o o/$(MODE)/tool/net/.redbean-unsecure/.symtab $< + @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ + o/$(MODE)/tool/net/.redbean-unsecure/.symtab @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ o/$(MODE)/tool/net/.redbean-unsecure/.ape \ - o/$(MODE)/tool/net/.redbean-unsecure/.symtab \ tool/net/help.txt \ tool/net/favicon.ico \ tool/net/redbean.png @@ -363,9 +367,10 @@ o/$(MODE)/tool/net/redbean-original.com: \ @$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.redbean-original/.ape bs=64 count=11 conv=notrunc 2>/dev/null @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \ -o o/$(MODE)/tool/net/.redbean-original/.symtab $< + @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ + o/$(MODE)/tool/net/.redbean-original/.symtab @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ o/$(MODE)/tool/net/.redbean-original/.ape \ - o/$(MODE)/tool/net/.redbean-original/.symtab \ tool/net/help.txt \ tool/net/favicon.ico \ tool/net/redbean.png @@ -436,6 +441,8 @@ o/$(MODE)/tool/net/redbean-assimilate.com: \ @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ @$(COMPILE) -AMKDIR -T$@ mkdir -p o/$(MODE)/tool/net/.redbean-assimilate @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com -o o/$(MODE)/tool/net/.redbean-assimilate/.symtab $< + @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ + o/$(MODE)/tool/net/.redbean-assimilate/.symtab @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ o/$(MODE)/tool/net/.redbean-assimilate/.symtab \ tool/net/help.txt \ diff --git a/tool/net/redbean.c b/tool/net/redbean.c index dd53ed025..6af6e3eef 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -4120,7 +4120,7 @@ static int LuaLog(lua_State *L) { } static int LuaEncodeSmth(lua_State *L, - int Encoder(lua_State *, char **, int, char *, int)) { + int Encoder(lua_State *, char **, char *, int)) { int useoutput = false; int maxdepth = 64; char *numformat = "%.14g"; @@ -4129,15 +4129,14 @@ static int LuaEncodeSmth(lua_State *L, lua_settop(L, 2); // discard any extra arguments lua_getfield(L, 2, "useoutput"); // ignore useoutput outside of request handling - if (ishandlingrequest && lua_isboolean(L, -1)) + if (ishandlingrequest && lua_isboolean(L, -1)) { useoutput = lua_toboolean(L, -1); - lua_getfield(L, 2, "maxdepth"); - maxdepth = luaL_optinteger(L, -1, maxdepth); + } lua_getfield(L, 2, "numformat"); numformat = luaL_optstring(L, -1, numformat); } lua_settop(L, 1); // keep the passed argument on top - Encoder(L, useoutput ? &outbuf : &p, maxdepth, numformat, -1); + Encoder(L, useoutput ? &outbuf : &p, numformat, -1); if (useoutput) { lua_pushnil(L); } else { @@ -5185,7 +5184,7 @@ static void LuaPrint(lua_State *L) { if (n > 0) { for (i = 1; i <= n; i++) { if (i > 1) appendw(&b, '\t'); - LuaEncodeLuaData(L, &b, 64, "g", i); + LuaEncodeLuaData(L, &b, "g", i); } appendw(&b, '\n'); WRITE(1, b, appendz(b).i); @@ -5222,7 +5221,6 @@ static void LuaInterpreter(lua_State *L) { } for (;;) { status = lua_loadline(L); - write(1, "\n", 1); if (status == -1) break; // eof if (status == -2) { if (errno == EINTR) { @@ -6502,7 +6500,6 @@ static int HandleReadline(void) { if (status == -1) { OnTerm(SIGHUP); // eof INFOF("got repl eof"); - write(1, "\n", 1); return -1; } else if (errno == EINTR) { errno = 0; @@ -6516,7 +6513,6 @@ static int HandleReadline(void) { return -1; } } - write(1, "\n", 1); linenoiseDisableRawMode(); LUA_REPL_LOCK; if (status == LUA_OK) { diff --git a/tool/plinko/plinko.mk b/tool/plinko/plinko.mk index dd96951fb..f841c1c40 100644 --- a/tool/plinko/plinko.mk +++ b/tool/plinko/plinko.mk @@ -55,7 +55,7 @@ o/$(MODE)/tool/plinko/plinko.com: \ @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ @$(COMPILE) -AMKDIR -T$@ mkdir -p o/$(MODE)/tool/plinko/.redbean @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com -o o/$(MODE)/tool/plinko/.plinko/.symtab $< - @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ + @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ o/$(MODE)/tool/plinko/.plinko/.symtab $(TOOL_PLINKO_OBJS): \ diff --git a/tool/viz/viz.mk b/tool/viz/viz.mk index 7dfba9880..744f644f8 100644 --- a/tool/viz/viz.mk +++ b/tool/viz/viz.mk @@ -88,7 +88,7 @@ o/$(MODE)/tool/viz/printimage.com: \ @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \ -o o/$(MODE)/tool/viz/.printimage/.symtab $< - @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ + @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ o/$(MODE)/tool/viz/.printimage/.symtab o/$(MODE)/tool/viz/printvideo.com: \ @@ -98,7 +98,7 @@ o/$(MODE)/tool/viz/printvideo.com: \ @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \ -o o/$(MODE)/tool/viz/.printvideo/.symtab $< - @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ + @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ o/$(MODE)/tool/viz/.printvideo/.symtab o/$(MODE)/tool/viz/derasterize.o: \ diff --git a/usr/share/zoneinfo/Anchorage b/usr/share/zoneinfo/Anchorage new file mode 100644 index 0000000000000000000000000000000000000000..cdf0572be31d3052a98494e3d01802b83737f23c GIT binary patch literal 977 zcmb8sUr1AN6bJC%=G>o~)yl0u(`-5CoUZHL{FCdS_s%U>Hqtf9i&gJ(z=X374+=r7m(tA9> zmJJ=BphJ;Z{*weB6Zol8c{EQizNCEaiV~%q8*a=}&bxjjk-I|@6y>Ey$7IUt zojnr$kKKyMlyq0CVEv#6F6a+usBh?Q$x<%tsDzD*8#ejPu<80y*kYZBE$LaAk2B%x z3wXtc9NrhbnUko$GMk21P37R#cV}ekTgTGyn&C-!?U_-yxOW7$b@s#dy-9doFb=Qx z9)vfP_hlH5jh1@Yq3?hlH8v~t9p6H5$tQP;T$-{(jK(41EofxYkE!qxLhxaP@I*!SvMijT3a zw*uFL;|t+iz28v6Jy=`mF6Y zxbPF+{#?NPj@j4n&WyI-U5}^W-8Z!THjj_Pa-X(e_f0T6k0)xKL_^-!gG5_Dg*t6e^sp`NW?k;9f)uE)$jJt^UyO@1%{2b<|ou4Qn8cwiID%yCVwDC%yJ4p7$>coI3`C@**=ocm7FFwuL-T(jq literal 0 HcmV?d00001 From cc0d1ec0760237fd8f67c10c5922b5e31064f753 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Wed, 27 Apr 2022 20:18:34 -0700 Subject: [PATCH 08/18] Fix some bugs - addr2line backtrace should continue on eintr - lua crashes if we try to iterate a non-table --- libc/log/backtrace2.greg.c | 12 +++++++++++- third_party/linenoise/linenoise.c | 2 +- third_party/lua/lrepl.c | 18 ++++++++++-------- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/libc/log/backtrace2.greg.c b/libc/log/backtrace2.greg.c index 359a7580a..61f0f8666 100644 --- a/libc/log/backtrace2.greg.c +++ b/libc/log/backtrace2.greg.c @@ -116,7 +116,17 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) { _exit(127); } close(pipefds[1]); - while ((got = read(pipefds[0], buf, kBacktraceBufSize)) > 0) { + for (;;) { + got = read(pipefds[0], buf, kBacktraceBufSize); + if (!got) break; + if (got == -1 && errno == EINTR) { + errno = 0; + continue; + } + if (got == -1) { + kprintf("error reading backtrace %m\n"); + break; + } p1 = buf; p3 = p1 + got; /* diff --git a/third_party/linenoise/linenoise.c b/third_party/linenoise/linenoise.c index 2eac70d57..8c713720d 100644 --- a/third_party/linenoise/linenoise.c +++ b/third_party/linenoise/linenoise.c @@ -1834,7 +1834,7 @@ void linenoiseEnd(struct linenoiseState *l) { } static int CompareStrings(const void *a, const void *b) { - return strcasecmp(*(const char **)a, *(const char **)b); + return strcmp(*(const char **)a, *(const char **)b); } /** diff --git a/third_party/lua/lrepl.c b/third_party/lua/lrepl.c index 5bdd29561..5f343f36f 100644 --- a/third_party/lua/lrepl.c +++ b/third_party/lua/lrepl.c @@ -140,18 +140,20 @@ void lua_readline_completions (const char *p, linenoiseCompletions *c) { } // search final object - lua_pushnil(L); - while (lua_next(L, -2)) { - if (lua_type(L, -2) == LUA_TSTRING) { - name = lua_tostring(L, -2); - if (startswithi(name, a)) { - lua_readline_addcompletion(c, xasprintf("%.*s%s", a - p, p, name)); + if (lua_type(L, -1) == LUA_TTABLE) { + lua_pushnil(L); + while (lua_next(L, -2)) { + if (lua_type(L, -2) == LUA_TSTRING) { + name = lua_tostring(L, -2); + if (startswithi(name, a)) { + lua_readline_addcompletion(c, xasprintf("%.*s%s", a - p, p, name)); + } } + lua_pop(L, 1); } - lua_pop(L, 1); } - lua_pop(L, 1); + lua_pop(L, 1); // pop table for (i = 0; i < ARRAYLEN(kKeywordHints); ++i) { if (startswithi(kKeywordHints[i], p)) { From 92ebef16eecef298609b6baee5de0c1e09c4c642 Mon Sep 17 00:00:00 2001 From: Steve Phillips Date: Wed, 27 Apr 2022 21:19:48 -0700 Subject: [PATCH 09/18] Add complex square root (#394) --- libc/tinymath/complex.h | 133 ++++++++++++++++++++++++++ libc/tinymath/complex_impl.internal.h | 22 +++++ libc/tinymath/csqrt.c | 133 ++++++++++++++++++++++++++ 3 files changed, 288 insertions(+) create mode 100644 libc/tinymath/complex.h create mode 100644 libc/tinymath/complex_impl.internal.h create mode 100644 libc/tinymath/csqrt.c diff --git a/libc/tinymath/complex.h b/libc/tinymath/complex.h new file mode 100644 index 000000000..008b3c7e3 --- /dev/null +++ b/libc/tinymath/complex.h @@ -0,0 +1,133 @@ +#ifndef _COMPLEX_H +#define _COMPLEX_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define complex _Complex +#ifdef __GNUC__ +#define _Complex_I (__extension__ (0.0f+1.0fi)) +#else +#define _Complex_I (0.0f+1.0fi) +#endif +#define I _Complex_I + +double complex cacos(double complex); +float complex cacosf(float complex); +long double complex cacosl(long double complex); + +double complex casin(double complex); +float complex casinf(float complex); +long double complex casinl(long double complex); + +double complex catan(double complex); +float complex catanf(float complex); +long double complex catanl(long double complex); + +double complex ccos(double complex); +float complex ccosf(float complex); +long double complex ccosl(long double complex); + +double complex csin(double complex); +float complex csinf(float complex); +long double complex csinl(long double complex); + +double complex ctan(double complex); +float complex ctanf(float complex); +long double complex ctanl(long double complex); + +double complex cacosh(double complex); +float complex cacoshf(float complex); +long double complex cacoshl(long double complex); + +double complex casinh(double complex); +float complex casinhf(float complex); +long double complex casinhl(long double complex); + +double complex catanh(double complex); +float complex catanhf(float complex); +long double complex catanhl(long double complex); + +double complex ccosh(double complex); +float complex ccoshf(float complex); +long double complex ccoshl(long double complex); + +double complex csinh(double complex); +float complex csinhf(float complex); +long double complex csinhl(long double complex); + +double complex ctanh(double complex); +float complex ctanhf(float complex); +long double complex ctanhl(long double complex); + +double complex cexp(double complex); +float complex cexpf(float complex); +long double complex cexpl(long double complex); + +double complex clog(double complex); +float complex clogf(float complex); +long double complex clogl(long double complex); + +double cabs(double complex); +float cabsf(float complex); +long double cabsl(long double complex); + +double complex cpow(double complex, double complex); +float complex cpowf(float complex, float complex); +long double complex cpowl(long double complex, long double complex); + +double complex csqrt(double complex); +float complex csqrtf(float complex); +long double complex csqrtl(long double complex); + +double carg(double complex); +float cargf(float complex); +long double cargl(long double complex); + +double cimag(double complex); +float cimagf(float complex); +long double cimagl(long double complex); + +double complex conj(double complex); +float complex conjf(float complex); +long double complex conjl(long double complex); + +double complex cproj(double complex); +float complex cprojf(float complex); +long double complex cprojl(long double complex); + +double creal(double complex); +float crealf(float complex); +long double creall(long double complex); + +#ifndef __cplusplus +#define __CIMAG(x, t) \ + (+(union { _Complex t __z; t __xy[2]; }){(_Complex t)(x)}.__xy[1]) + +#define creal(x) ((double)(x)) +#define crealf(x) ((float)(x)) +#define creall(x) ((long double)(x)) + +#define cimag(x) __CIMAG(x, double) +#define cimagf(x) __CIMAG(x, float) +#define cimagl(x) __CIMAG(x, long double) +#endif + +#if __STDC_VERSION__ >= 201112L +#if defined(_Imaginary_I) +#define __CMPLX(x, y, t) ((t)(x) + _Imaginary_I*(t)(y)) +#elif defined(__clang__) +#define __CMPLX(x, y, t) (+(_Complex t){ (t)(x), (t)(y) }) +#else +#define __CMPLX(x, y, t) (__builtin_complex((t)(x), (t)(y))) +#endif +#define CMPLX(x, y) __CMPLX(x, y, double) +#define CMPLXF(x, y) __CMPLX(x, y, float) +#define CMPLXL(x, y) __CMPLX(x, y, long double) +#endif + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libc/tinymath/complex_impl.internal.h b/libc/tinymath/complex_impl.internal.h new file mode 100644 index 000000000..b92cb6f3a --- /dev/null +++ b/libc/tinymath/complex_impl.internal.h @@ -0,0 +1,22 @@ +#ifndef _COMPLEX_IMPL_H +#define _COMPLEX_IMPL_H + +#include +#include "libc/math.h" + +#undef __CMPLX +#undef CMPLX +#undef CMPLXF +#undef CMPLXL + +#define __CMPLX(x, y, t) \ + ((union { _Complex t __z; t __xy[2]; }){.__xy = {(x),(y)}}.__z) + +#define CMPLX(x, y) __CMPLX(x, y, double) +#define CMPLXF(x, y) __CMPLX(x, y, float) +#define CMPLXL(x, y) __CMPLX(x, y, long double) + +hidden double complex __ldexp_cexp(double complex,int); +hidden float complex __ldexp_cexpf(float complex,int); + +#endif diff --git a/libc/tinymath/csqrt.c b/libc/tinymath/csqrt.c new file mode 100644 index 000000000..7a0ff816b --- /dev/null +++ b/libc/tinymath/csqrt.c @@ -0,0 +1,133 @@ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Musl Libc │ +│ Copyright © 2005-2014 Rich Felker, et al. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/tinymath/complex_impl.internal.h" + +asm(".ident\t\"\\n\\n\ +Musl libc (MIT License)\\n\ +Copyright 2005-2014 Rich Felker, et. al.\""); +asm(".include \"libc/disclaimer.inc\""); +/* clang-format off */ + +/* origin: FreeBSD /usr/src/lib/msun/src/s_csqrt.c */ +/*- + * Copyright (c) 2007 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * gcc doesn't implement complex multiplication or division correctly, + * so we need to handle infinities specially. We turn on this pragma to + * notify conforming c99 compilers that the fast-but-incorrect code that + * gcc generates is acceptable, since the special cases have already been + * handled. + */ +#pragma STDC CX_LIMITED_RANGE ON + +/* We risk spurious overflow for components >= DBL_MAX / (1 + sqrt(2)). */ +#define THRESH 0x1.a827999fcef32p+1022 + +double complex csqrt(double complex z) +{ + double complex result; + double a, b; + double t; + int scale; + + a = creal(z); + b = cimag(z); + + /* Handle special cases. */ + if (z == 0) + return CMPLX(0, b); + if (isinf(b)) + return CMPLX(INFINITY, b); + if (isnan(a)) { + t = (b - b) / (b - b); /* raise invalid if b is not a NaN */ + return CMPLX(a, t); /* return NaN + NaN i */ + } + if (isinf(a)) { + /* + * csqrt(inf + NaN i) = inf + NaN i + * csqrt(inf + y i) = inf + 0 i + * csqrt(-inf + NaN i) = NaN +- inf i + * csqrt(-inf + y i) = 0 + inf i + */ + if (signbit(a)) + return CMPLX(fabs(b - b), copysign(a, b)); + else + return CMPLX(a, copysign(b - b, b)); + } + /* + * The remaining special case (b is NaN) is handled just fine by + * the normal code path below. + */ + + /* Scale to avoid overflow. */ + if (fabs(a) >= THRESH || fabs(b) >= THRESH) { + a *= 0.25; + b *= 0.25; + scale = 1; + } else { + scale = 0; + } + + /* Algorithm 312, CACM vol 10, Oct 1967. */ + if (a >= 0) { + t = sqrt((a + hypot(a, b)) * 0.5); + result = CMPLX(t, b / (2 * t)); + } else { + t = sqrt((-a + hypot(a, b)) * 0.5); + result = CMPLX(fabs(b) / (2 * t), copysign(t, b)); + } + + /* Rescale. */ + if (scale) + result *= 2; + return result; +} From e3a7ab1804c91ccf142789be6b4604b70a1c2319 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Wed, 27 Apr 2022 20:18:34 -0700 Subject: [PATCH 10/18] Fix some bugs - addr2line backtrace should continue on eintr - lua crashes if we try to iterate a non-table --- libc/tinymath/csqrt.c | 3 +- tool/net/lunix.c | 72 +++++++++++++++---------------------------- 2 files changed, 27 insertions(+), 48 deletions(-) diff --git a/libc/tinymath/csqrt.c b/libc/tinymath/csqrt.c index 7a0ff816b..d229d2684 100644 --- a/libc/tinymath/csqrt.c +++ b/libc/tinymath/csqrt.c @@ -67,7 +67,8 @@ asm(".include \"libc/disclaimer.inc\""); * gcc generates is acceptable, since the special cases have already been * handled. */ -#pragma STDC CX_LIMITED_RANGE ON +// TODO(elimisteve): write test proving why we do / don't need it +// #pragma STDC CX_LIMITED_RANGE ON /* We risk spurious overflow for components >= DBL_MAX / (1 + sqrt(2)). */ #define THRESH 0x1.a827999fcef32p+1022 diff --git a/tool/net/lunix.c b/tool/net/lunix.c index 7c46553f6..661d655bc 100644 --- a/tool/net/lunix.c +++ b/tool/net/lunix.c @@ -93,11 +93,6 @@ * @support Linux, Mac, Windows, FreeBSD, NetBSD, OpenBSD */ -struct UnixDir { - int refs; - DIR *dir; -}; - struct UnixErrno { int errno; int winerr; @@ -1721,7 +1716,7 @@ static int LuaUnixMinor(lua_State *L) { //////////////////////////////////////////////////////////////////////////////// // unix.Stat object -static dontinline struct stat *GetUnixStat(lua_State *L) { +static struct stat *GetUnixStat(lua_State *L) { return luaL_checkudata(L, 1, "unix.Stat"); } @@ -2051,38 +2046,29 @@ static void LuaUnixSigsetObj(lua_State *L) { //////////////////////////////////////////////////////////////////////////////// // unix.Dir object -static struct UnixDir **GetUnixDirSelf(lua_State *L) { +static DIR **GetUnixDirSelf(lua_State *L) { return luaL_checkudata(L, 1, "unix.Dir"); } static DIR *GetDirOrDie(lua_State *L) { - struct UnixDir **udir; - udir = GetUnixDirSelf(L); - assert((*udir)->dir); - if (*udir) return (*udir)->dir; + DIR **dirp; + dirp = GetUnixDirSelf(L); + if (*dirp) return *dirp; luaL_argerror(L, 1, "unix.UnixDir is closed"); unreachable; } -static int FreeUnixDir(struct UnixDir *dir) { - int rc; - if (--dir->refs) return 0; - rc = closedir(dir->dir); - free(dir); - return rc; -} - // unix.Dir:close() // ├─→ true // └─→ nil, unix.Errno static int LuaUnixDirClose(lua_State *L) { + DIR **dirp; int rc, olderr; - struct UnixDir **udir; - udir = GetUnixDirSelf(L); - if (*udir) { + dirp = GetUnixDirSelf(L); + if (*dirp) { olderr = 0; - rc = FreeUnixDir(*udir); - *udir = 0; + rc = closedir(*dirp); + *dirp = 0; return SysretBool(L, "closedir", olderr, rc); } else { lua_pushboolean(L, true); @@ -2138,12 +2124,11 @@ static int LuaUnixDirRewind(lua_State *L) { return 0; } -static int ReturnDir(lua_State *L, struct UnixDir *udir) { - struct UnixDir **udirp; - udir->refs = 1; - udirp = lua_newuserdatauv(L, sizeof(*udirp), 1); +static int ReturnDir(lua_State *L, DIR *dir) { + DIR **dirp; + dirp = lua_newuserdatauv(L, sizeof(*dirp), 1); luaL_setmetatable(L, "unix.Dir"); - *udirp = udir; + *dirp = dir; return 1; } @@ -2151,33 +2136,26 @@ static int ReturnDir(lua_State *L, struct UnixDir *udir) { // ├─→ state:unix.Dir // └─→ nil, unix.Errno static int LuaUnixOpendir(lua_State *L) { + DIR *dir; int olderr = errno; - const char *path; - struct UnixDir *udir; - path = luaL_checkstring(L, 1); - if ((udir = LuaUnixAlloc(L, sizeof(*udir)))) { - if ((udir->dir = opendir(path))) { - return ReturnDir(L, udir); - } - free(udir); + if ((dir = opendir(luaL_checkstring(L, 1)))) { + return ReturnDir(L, dir); + } else { + return SysretErrno(L, "opendir", olderr); } - return SysretErrno(L, "opendir", olderr); } // unix.fdopendir(fd:int) // ├─→ next:function, state:unix.Dir // └─→ nil, unix.Errno static int LuaUnixFdopendir(lua_State *L) { - int fd, olderr = errno; - struct UnixDir *udir; - fd = luaL_checkinteger(L, 1); - if ((udir = LuaUnixAlloc(L, sizeof(*udir)))) { - if ((udir->dir = fdopendir(fd))) { - return ReturnDir(L, udir); - } - free(udir); + DIR *dir; + int olderr = errno; + if ((dir = fdopendir(luaL_checkinteger(L, 1)))) { + return ReturnDir(L, dir); + } else { + return SysretErrno(L, "fdopendir", olderr); } - return SysretErrno(L, "fdopendir", olderr); } static const luaL_Reg kLuaUnixDirMeth[] = { From 9a6bd304a58b89277b8a2d490bfda9fd12d19370 Mon Sep 17 00:00:00 2001 From: Paul Kulchenko Date: Wed, 27 Apr 2022 21:57:52 -0700 Subject: [PATCH 11/18] Fix reporting of Lua stack items from various hook calls (#395) Some hooks can be called after OnHttpRequest, which may leave an anchored item on stack, so this have to be taken into account to avoid spurious reports. --- tool/net/luacheck.h | 16 +++++++++------- tool/net/redbean.c | 11 ++++++----- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/tool/net/luacheck.h b/tool/net/luacheck.h index c5506387a..b800e48f8 100644 --- a/tool/net/luacheck.h +++ b/tool/net/luacheck.h @@ -7,13 +7,15 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -#define AssertLuaStackIsEmpty(L) \ - do { \ - if (lua_gettop(L)) { \ - char *s = LuaFormatStack(L); \ - WARNF("lua stack should be empty!\n%s", s); \ - free(s); \ - } \ +#define AssertLuaStackIsAt(L, level) \ + do { \ + if (lua_gettop(L) > level) { \ + char *s = LuaFormatStack(L); \ + WARNF("lua stack should be at %d;" \ + " extra values ignored:\n%s", \ + level, s); \ + free(s); \ + } \ } while (0) COSMOPOLITAN_C_END_ diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 6af6e3eef..43b851a20 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -1071,7 +1071,7 @@ static bool LuaEvalCode(const char *code) { lua_pop(L, 1); // pop error return false; } - AssertLuaStackIsEmpty(L); + AssertLuaStackIsAt(L, 0); return true; } @@ -1099,7 +1099,7 @@ static bool LuaOnClientConnection(void) { dropit = false; } lua_pop(L, 1); // pop result or error - AssertLuaStackIsEmpty(L); + AssertLuaStackIsAt(L, 0); return dropit; #else return false; @@ -1123,7 +1123,7 @@ static void LuaOnProcessCreate(int pid) { LogLuaError("OnProcessCreate", lua_tostring(L, -1)); lua_pop(L, 1); // pop error } - AssertLuaStackIsEmpty(L); + AssertLuaStackIsAt(L, 0); #endif } @@ -1136,7 +1136,7 @@ static void LuaOnProcessDestroy(int pid) { LogLuaError("OnProcessDestroy", lua_tostring(L, -1)); lua_pop(L, 1); // pop error } - AssertLuaStackIsEmpty(L); + AssertLuaStackIsAt(L, 0); #endif } @@ -1154,12 +1154,13 @@ static inline bool IsHookDefined(const char *s) { static void CallSimpleHook(const char *s) { #ifndef STATIC lua_State *L = GL; + int n = lua_gettop(L); lua_getglobal(L, s); if (LuaCallWithTrace(L, 0, 0, NULL) != LUA_OK) { LogLuaError(s, lua_tostring(L, -1)); lua_pop(L, 1); // pop error } - AssertLuaStackIsEmpty(L); + AssertLuaStackIsAt(L, n); #endif } From 47b3274665acb7ad94117c7fc8c1ca4ef40018a4 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Thu, 28 Apr 2022 09:42:36 -0700 Subject: [PATCH 12/18] Make improvements - Add rusage to redbean Lua API - Add more redbean documentation - Add pledge() to redbean Lua API - Polyfill OpenBSD pledge() for Linux - Increase PATH_MAX limit to 1024 characters - Untrack sibling processes after fork() on Windows --- examples/time.c | 2 +- libc/calls/calls.h | 17 +- libc/calls/calls.mk | 1 + libc/calls/chdir-nt.c | 2 +- libc/calls/copyfile.c | 3 +- libc/calls/execlp.c | 2 +- libc/calls/execve-sysv.c | 4 +- libc/calls/execvpe.c | 2 +- libc/calls/faccessat-nt.c | 5 +- libc/calls/fchdir-nt.c | 2 +- libc/calls/fchmodat-nt.c | 2 +- libc/calls/fileexists.c | 2 +- libc/calls/fstatat-nt.c | 2 +- libc/calls/getcwd-nt.c | 2 +- libc/calls/getcwd.c | 2 +- libc/calls/getexecutablename.c | 4 +- libc/calls/getresgid.c | 51 ++ libc/calls/getresuid.c | 51 ++ libc/calls/gettimeofday-nt.c | 2 +- libc/calls/internal.h | 15 +- libc/calls/isdirectory-nt.c | 2 +- libc/calls/islinux.c | 32 + libc/calls/isregularfile-nt.c | 2 +- libc/calls/issymlink-nt.c | 2 +- libc/calls/linkat-nt.c | 4 +- libc/calls/mkdirat-nt.c | 2 +- libc/calls/mkntcmdline.c | 14 +- libc/calls/mkntenvblock.c | 8 +- libc/calls/mkntpath.c | 7 +- libc/calls/mkntpathat.c | 4 +- libc/calls/ntspawn.c | 22 +- libc/calls/ntspawn.h | 4 +- libc/calls/open-nt.c | 2 +- libc/calls/openbsd.internal.h | 2 - libc/calls/readlinkat-nt.c | 2 +- libc/calls/realpath.c | 2 +- libc/calls/renameat-nt.c | 4 +- libc/calls/sched_setaffinity.c | 4 +- libc/calls/sigaction.c | 3 + libc/calls/strace.internal.h | 2 +- libc/calls/symlinkat-nt.c | 4 +- libc/calls/sync-nt.c | 2 +- libc/calls/truncate-nt.c | 2 +- libc/calls/ttyname.c | 2 +- libc/calls/ttyname_r.c | 2 +- libc/calls/unlinkat-nt.c | 5 +- libc/calls/utimensat-nt.c | 2 +- libc/dns/gethoststxt.c | 2 +- libc/dns/lookupprotobyname.c | 2 +- libc/dns/lookupprotobynumber.c | 2 +- libc/dns/lookupservicesbyname.c | 2 +- libc/dns/lookupservicesbyport.c | 2 +- libc/fmt/fmt.c | 2 +- libc/integral/normalize.inc | 8 +- libc/intrin/kprintf.greg.c | 2 +- libc/intrin/nomultics.c | 1 + libc/intrin/nomultics.internal.h | 3 +- libc/log/appendresourcereport.c | 7 +- libc/log/commandvenv.c | 2 +- libc/mem/mem.mk | 1 + libc/mem/pledge.c | 406 ++++++++++ libc/runtime/findcombinary.c | 2 +- libc/runtime/finddebugbinary.c | 7 +- libc/runtime/fork-nt.c | 20 +- libc/runtime/getargmax.c | 33 + libc/runtime/mprotect-nt.greg.c | 2 +- libc/runtime/paginate.c | 4 +- libc/runtime/printargs.greg.c | 27 +- libc/runtime/runtime.h | 1 + libc/runtime/sysconf.c | 3 +- libc/runtime/winmain.greg.c | 4 +- libc/stdio/spawn.c | 4 +- libc/stdio/spawnp.c | 2 +- libc/stdio/systemexec.c | 7 +- libc/stdio/tmpfile.c | 4 +- libc/sysv/calls/getresuid.s | 2 - libc/sysv/calls/pledge.s | 2 - libc/sysv/calls/sys_getresgid.s | 2 + libc/sysv/calls/sys_getresuid.s | 2 + libc/sysv/calls/{getresgid.s => sys_pledge.s} | 2 +- libc/sysv/consts.sh | 109 +-- libc/sysv/consts/EXIT_FAILURE.S | 2 - libc/sysv/consts/PIPE_BUF.S | 2 +- libc/sysv/consts/_ARG_MAX.S | 2 + libc/sysv/consts/_NAME_MAX.S | 2 + libc/sysv/consts/{EXIT_SUCCESS.S => _NSIG.S} | 2 +- libc/sysv/consts/_PATH_MAX.S | 2 + libc/sysv/consts/_POSIX2_BC_BASE_MAX.S | 2 - libc/sysv/consts/_POSIX2_BC_DIM_MAX.S | 2 - libc/sysv/consts/_POSIX2_BC_SCALE_MAX.S | 2 - libc/sysv/consts/_POSIX2_BC_STRING_MAX.S | 2 - libc/sysv/consts/_POSIX2_CHARCLASS_NAME_MAX.S | 2 - libc/sysv/consts/_POSIX2_COLL_WEIGHTS_MAX.S | 2 - libc/sysv/consts/_POSIX2_C_BIND.S | 2 - libc/sysv/consts/_POSIX2_EXPR_NEST_MAX.S | 2 - libc/sysv/consts/_POSIX2_LINE_MAX.S | 2 - libc/sysv/consts/_POSIX2_RE_DUP_MAX.S | 2 - libc/sysv/consts/_POSIX2_VERSION.S | 2 - libc/sysv/consts/_POSIX_ADVISORY_INFO.S | 2 - libc/sysv/consts/_POSIX_AIO_LISTIO_MAX.S | 2 - libc/sysv/consts/_POSIX_AIO_MAX.S | 2 - libc/sysv/consts/_POSIX_ARG_MAX.S | 2 - libc/sysv/consts/_POSIX_ASYNCHRONOUS_IO.S | 2 - libc/sysv/consts/_POSIX_BARRIERS.S | 2 - libc/sysv/consts/_POSIX_CHILD_MAX.S | 2 - libc/sysv/consts/_POSIX_CHOWN_RESTRICTED.S | 2 - libc/sysv/consts/_POSIX_CLOCKRES_MIN.S | 2 - libc/sysv/consts/_POSIX_CLOCK_SELECTION.S | 2 - libc/sysv/consts/_POSIX_CPUTIME.S | 2 - libc/sysv/consts/_POSIX_DELAYTIMER_MAX.S | 2 - libc/sysv/consts/_POSIX_FSYNC.S | 2 - libc/sysv/consts/_POSIX_HOST_NAME_MAX.S | 2 - libc/sysv/consts/_POSIX_IPV6.S | 2 - libc/sysv/consts/_POSIX_JOB_CONTROL.S | 2 - libc/sysv/consts/_POSIX_LINK_MAX.S | 2 - libc/sysv/consts/_POSIX_LOGIN_NAME_MAX.S | 2 - libc/sysv/consts/_POSIX_MAPPED_FILES.S | 2 - libc/sysv/consts/_POSIX_MAX_CANON.S | 2 - libc/sysv/consts/_POSIX_MAX_INPUT.S | 2 - libc/sysv/consts/_POSIX_MEMLOCK.S | 2 - libc/sysv/consts/_POSIX_MEMLOCK_RANGE.S | 2 - libc/sysv/consts/_POSIX_MEMORY_PROTECTION.S | 2 - libc/sysv/consts/_POSIX_MESSAGE_PASSING.S | 2 - libc/sysv/consts/_POSIX_MONOTONIC_CLOCK.S | 2 - libc/sysv/consts/_POSIX_MQ_OPEN_MAX.S | 2 - libc/sysv/consts/_POSIX_MQ_PRIO_MAX.S | 2 - libc/sysv/consts/_POSIX_NAME_MAX.S | 2 - libc/sysv/consts/_POSIX_NGROUPS_MAX.S | 2 - libc/sysv/consts/_POSIX_NO_TRUNC.S | 2 - libc/sysv/consts/_POSIX_OPEN_MAX.S | 2 - libc/sysv/consts/_POSIX_PATH_MAX.S | 2 - libc/sysv/consts/_POSIX_PIPE_BUF.S | 2 - libc/sysv/consts/_POSIX_RAW_SOCKETS.S | 2 - libc/sysv/consts/_POSIX_READER_WRITER_LOCKS.S | 2 - libc/sysv/consts/_POSIX_REALTIME_SIGNALS.S | 2 - libc/sysv/consts/_POSIX_REGEXP.S | 2 - libc/sysv/consts/_POSIX_RE_DUP_MAX.S | 2 - libc/sysv/consts/_POSIX_RTSIG_MAX.S | 2 - libc/sysv/consts/_POSIX_SAVED_IDS.S | 2 - libc/sysv/consts/_POSIX_SEMAPHORES.S | 2 - libc/sysv/consts/_POSIX_SEM_NSEMS_MAX.S | 2 - libc/sysv/consts/_POSIX_SEM_VALUE_MAX.S | 2 - .../consts/_POSIX_SHARED_MEMORY_OBJECTS.S | 2 - libc/sysv/consts/_POSIX_SHELL.S | 2 - libc/sysv/consts/_POSIX_SIGQUEUE_MAX.S | 2 - libc/sysv/consts/_POSIX_SPAWN.S | 2 - libc/sysv/consts/_POSIX_SPIN_LOCKS.S | 2 - libc/sysv/consts/_POSIX_SSIZE_MAX.S | 2 - libc/sysv/consts/_POSIX_SS_REPL_MAX.S | 2 - libc/sysv/consts/_POSIX_STREAM_MAX.S | 2 - libc/sysv/consts/_POSIX_SYMLINK_MAX.S | 2 - libc/sysv/consts/_POSIX_SYMLOOP_MAX.S | 2 - libc/sysv/consts/_POSIX_THREADS.S | 2 - .../consts/_POSIX_THREAD_ATTR_STACKADDR.S | 2 - .../consts/_POSIX_THREAD_ATTR_STACKSIZE.S | 2 - libc/sysv/consts/_POSIX_THREAD_CPUTIME.S | 2 - .../_POSIX_THREAD_DESTRUCTOR_ITERATIONS.S | 2 - libc/sysv/consts/_POSIX_THREAD_KEYS_MAX.S | 2 - .../_POSIX_THREAD_PRIORITY_SCHEDULING.S | 2 - .../consts/_POSIX_THREAD_PROCESS_SHARED.S | 2 - .../consts/_POSIX_THREAD_SAFE_FUNCTIONS.S | 2 - libc/sysv/consts/_POSIX_THREAD_THREADS_MAX.S | 2 - libc/sysv/consts/_POSIX_TIMEOUTS.S | 2 - libc/sysv/consts/_POSIX_TIMERS.S | 2 - libc/sysv/consts/_POSIX_TIMER_MAX.S | 2 - .../sysv/consts/_POSIX_TRACE_EVENT_NAME_MAX.S | 2 - libc/sysv/consts/_POSIX_TRACE_NAME_MAX.S | 2 - libc/sysv/consts/_POSIX_TRACE_SYS_MAX.S | 2 - .../sysv/consts/_POSIX_TRACE_USER_EVENT_MAX.S | 2 - libc/sysv/consts/_POSIX_TTY_NAME_MAX.S | 2 - libc/sysv/consts/_POSIX_TZNAME_MAX.S | 2 - libc/sysv/consts/_POSIX_V6_LP64_OFF64.S | 2 - libc/sysv/consts/_POSIX_V7_LP64_OFF64.S | 2 - libc/sysv/consts/_POSIX_VDISABLE.S | 2 - libc/sysv/consts/_POSIX_VERSION.S | 2 - libc/sysv/consts/_posix.h | 213 ++---- libc/sysv/consts/limits.h | 12 +- libc/sysv/consts/nrlinux.h | 330 +++++++++ libc/sysv/syscalls.sh | 6 +- libc/testlib/testlib.h | 4 +- libc/testlib/testmain.c | 1 + libc/testlib/testrunner.c | 4 +- libc/time/localtime.c | 6 - libc/time/time.h | 2 + libc/time/timezone.c | 23 + libc/time/tz.internal.h | 2 + net/https/getsslcachefile.c | 5 +- net/https/getsslroots.c | 4 +- test/libc/calls/commandv_test.c | 4 +- test/libc/calls/getcwd_test.c | 2 +- test/libc/calls/mkntcmdline_test.c | 2 +- test/libc/calls/mkntenvblock_test.c | 2 +- test/libc/calls/seccomp_test.c | 11 +- test/libc/mem/pledge_test.c | 62 ++ test/libc/{calls => runtime}/fork_test.c | 50 ++ test/libc/runtime/getdosargv_test.c | 16 +- third_party/linenoise/linenoise.c | 6 +- third_party/lua/lrepl.c | 18 +- .../_sysconfigdata_m_cosmo_x86_64_cosmo.py | 2 +- third_party/python/pyconfig.h | 10 +- third_party/zip/unix.c | 20 +- tool/build/compile.c | 2 +- tool/build/lib/demangle.c | 2 +- tool/build/rle.c | 4 +- tool/build/runit.c | 2 +- tool/net/demo/unix-subprocess.lua | 2 + tool/net/help.txt | 697 +++++++++++++++--- tool/net/lfuncs.c | 50 +- tool/net/lunix.c | 307 ++++++-- tool/net/redbean.c | 12 +- tool/net/sandbox.h | 3 + tool/viz/derasterize.c | 2 +- 212 files changed, 2251 insertions(+), 834 deletions(-) create mode 100644 libc/calls/getresgid.c create mode 100644 libc/calls/getresuid.c create mode 100644 libc/calls/islinux.c create mode 100644 libc/mem/pledge.c create mode 100644 libc/runtime/getargmax.c delete mode 100644 libc/sysv/calls/getresuid.s delete mode 100644 libc/sysv/calls/pledge.s create mode 100644 libc/sysv/calls/sys_getresgid.s create mode 100644 libc/sysv/calls/sys_getresuid.s rename libc/sysv/calls/{getresgid.s => sys_pledge.s} (50%) delete mode 100644 libc/sysv/consts/EXIT_FAILURE.S create mode 100644 libc/sysv/consts/_ARG_MAX.S create mode 100644 libc/sysv/consts/_NAME_MAX.S rename libc/sysv/consts/{EXIT_SUCCESS.S => _NSIG.S} (53%) create mode 100644 libc/sysv/consts/_PATH_MAX.S delete mode 100644 libc/sysv/consts/_POSIX2_BC_BASE_MAX.S delete mode 100644 libc/sysv/consts/_POSIX2_BC_DIM_MAX.S delete mode 100644 libc/sysv/consts/_POSIX2_BC_SCALE_MAX.S delete mode 100644 libc/sysv/consts/_POSIX2_BC_STRING_MAX.S delete mode 100644 libc/sysv/consts/_POSIX2_CHARCLASS_NAME_MAX.S delete mode 100644 libc/sysv/consts/_POSIX2_COLL_WEIGHTS_MAX.S delete mode 100644 libc/sysv/consts/_POSIX2_C_BIND.S delete mode 100644 libc/sysv/consts/_POSIX2_EXPR_NEST_MAX.S delete mode 100644 libc/sysv/consts/_POSIX2_LINE_MAX.S delete mode 100644 libc/sysv/consts/_POSIX2_RE_DUP_MAX.S delete mode 100644 libc/sysv/consts/_POSIX2_VERSION.S delete mode 100644 libc/sysv/consts/_POSIX_ADVISORY_INFO.S delete mode 100644 libc/sysv/consts/_POSIX_AIO_LISTIO_MAX.S delete mode 100644 libc/sysv/consts/_POSIX_AIO_MAX.S delete mode 100644 libc/sysv/consts/_POSIX_ARG_MAX.S delete mode 100644 libc/sysv/consts/_POSIX_ASYNCHRONOUS_IO.S delete mode 100644 libc/sysv/consts/_POSIX_BARRIERS.S delete mode 100644 libc/sysv/consts/_POSIX_CHILD_MAX.S delete mode 100644 libc/sysv/consts/_POSIX_CHOWN_RESTRICTED.S delete mode 100644 libc/sysv/consts/_POSIX_CLOCKRES_MIN.S delete mode 100644 libc/sysv/consts/_POSIX_CLOCK_SELECTION.S delete mode 100644 libc/sysv/consts/_POSIX_CPUTIME.S delete mode 100644 libc/sysv/consts/_POSIX_DELAYTIMER_MAX.S delete mode 100644 libc/sysv/consts/_POSIX_FSYNC.S delete mode 100644 libc/sysv/consts/_POSIX_HOST_NAME_MAX.S delete mode 100644 libc/sysv/consts/_POSIX_IPV6.S delete mode 100644 libc/sysv/consts/_POSIX_JOB_CONTROL.S delete mode 100644 libc/sysv/consts/_POSIX_LINK_MAX.S delete mode 100644 libc/sysv/consts/_POSIX_LOGIN_NAME_MAX.S delete mode 100644 libc/sysv/consts/_POSIX_MAPPED_FILES.S delete mode 100644 libc/sysv/consts/_POSIX_MAX_CANON.S delete mode 100644 libc/sysv/consts/_POSIX_MAX_INPUT.S delete mode 100644 libc/sysv/consts/_POSIX_MEMLOCK.S delete mode 100644 libc/sysv/consts/_POSIX_MEMLOCK_RANGE.S delete mode 100644 libc/sysv/consts/_POSIX_MEMORY_PROTECTION.S delete mode 100644 libc/sysv/consts/_POSIX_MESSAGE_PASSING.S delete mode 100644 libc/sysv/consts/_POSIX_MONOTONIC_CLOCK.S delete mode 100644 libc/sysv/consts/_POSIX_MQ_OPEN_MAX.S delete mode 100644 libc/sysv/consts/_POSIX_MQ_PRIO_MAX.S delete mode 100644 libc/sysv/consts/_POSIX_NAME_MAX.S delete mode 100644 libc/sysv/consts/_POSIX_NGROUPS_MAX.S delete mode 100644 libc/sysv/consts/_POSIX_NO_TRUNC.S delete mode 100644 libc/sysv/consts/_POSIX_OPEN_MAX.S delete mode 100644 libc/sysv/consts/_POSIX_PATH_MAX.S delete mode 100644 libc/sysv/consts/_POSIX_PIPE_BUF.S delete mode 100644 libc/sysv/consts/_POSIX_RAW_SOCKETS.S delete mode 100644 libc/sysv/consts/_POSIX_READER_WRITER_LOCKS.S delete mode 100644 libc/sysv/consts/_POSIX_REALTIME_SIGNALS.S delete mode 100644 libc/sysv/consts/_POSIX_REGEXP.S delete mode 100644 libc/sysv/consts/_POSIX_RE_DUP_MAX.S delete mode 100644 libc/sysv/consts/_POSIX_RTSIG_MAX.S delete mode 100644 libc/sysv/consts/_POSIX_SAVED_IDS.S delete mode 100644 libc/sysv/consts/_POSIX_SEMAPHORES.S delete mode 100644 libc/sysv/consts/_POSIX_SEM_NSEMS_MAX.S delete mode 100644 libc/sysv/consts/_POSIX_SEM_VALUE_MAX.S delete mode 100644 libc/sysv/consts/_POSIX_SHARED_MEMORY_OBJECTS.S delete mode 100644 libc/sysv/consts/_POSIX_SHELL.S delete mode 100644 libc/sysv/consts/_POSIX_SIGQUEUE_MAX.S delete mode 100644 libc/sysv/consts/_POSIX_SPAWN.S delete mode 100644 libc/sysv/consts/_POSIX_SPIN_LOCKS.S delete mode 100644 libc/sysv/consts/_POSIX_SSIZE_MAX.S delete mode 100644 libc/sysv/consts/_POSIX_SS_REPL_MAX.S delete mode 100644 libc/sysv/consts/_POSIX_STREAM_MAX.S delete mode 100644 libc/sysv/consts/_POSIX_SYMLINK_MAX.S delete mode 100644 libc/sysv/consts/_POSIX_SYMLOOP_MAX.S delete mode 100644 libc/sysv/consts/_POSIX_THREADS.S delete mode 100644 libc/sysv/consts/_POSIX_THREAD_ATTR_STACKADDR.S delete mode 100644 libc/sysv/consts/_POSIX_THREAD_ATTR_STACKSIZE.S delete mode 100644 libc/sysv/consts/_POSIX_THREAD_CPUTIME.S delete mode 100644 libc/sysv/consts/_POSIX_THREAD_DESTRUCTOR_ITERATIONS.S delete mode 100644 libc/sysv/consts/_POSIX_THREAD_KEYS_MAX.S delete mode 100644 libc/sysv/consts/_POSIX_THREAD_PRIORITY_SCHEDULING.S delete mode 100644 libc/sysv/consts/_POSIX_THREAD_PROCESS_SHARED.S delete mode 100644 libc/sysv/consts/_POSIX_THREAD_SAFE_FUNCTIONS.S delete mode 100644 libc/sysv/consts/_POSIX_THREAD_THREADS_MAX.S delete mode 100644 libc/sysv/consts/_POSIX_TIMEOUTS.S delete mode 100644 libc/sysv/consts/_POSIX_TIMERS.S delete mode 100644 libc/sysv/consts/_POSIX_TIMER_MAX.S delete mode 100644 libc/sysv/consts/_POSIX_TRACE_EVENT_NAME_MAX.S delete mode 100644 libc/sysv/consts/_POSIX_TRACE_NAME_MAX.S delete mode 100644 libc/sysv/consts/_POSIX_TRACE_SYS_MAX.S delete mode 100644 libc/sysv/consts/_POSIX_TRACE_USER_EVENT_MAX.S delete mode 100644 libc/sysv/consts/_POSIX_TTY_NAME_MAX.S delete mode 100644 libc/sysv/consts/_POSIX_TZNAME_MAX.S delete mode 100644 libc/sysv/consts/_POSIX_V6_LP64_OFF64.S delete mode 100644 libc/sysv/consts/_POSIX_V7_LP64_OFF64.S delete mode 100644 libc/sysv/consts/_POSIX_VDISABLE.S delete mode 100644 libc/sysv/consts/_POSIX_VERSION.S create mode 100644 libc/sysv/consts/nrlinux.h create mode 100644 libc/time/timezone.c create mode 100644 test/libc/mem/pledge_test.c rename test/libc/{calls => runtime}/fork_test.c (76%) diff --git a/examples/time.c b/examples/time.c index b0ee8b974..a09d0b0aa 100644 --- a/examples/time.c +++ b/examples/time.c @@ -67,7 +67,7 @@ int main(int argc, char *argv[]) { char *exepath; struct rusage r; long double real; - char exebuf[PATH_MAX + 1]; + char exebuf[PATH_MAX]; if (argc >= 2) { if ((exepath = commandv(argv[1], exebuf, sizeof(exebuf)))) { real = nowl(); diff --git a/libc/calls/calls.h b/libc/calls/calls.h index 2d99b4136..ee64d4a59 100644 --- a/libc/calls/calls.h +++ b/libc/calls/calls.h @@ -17,6 +17,10 @@ #include "libc/sysv/consts/s.h" #include "libc/sysv/consts/sig.h" +#define _POSIX_VERSION 200809L +#define _POSIX2_VERSION _POSIX_VERSION +#define _XOPEN_VERSION 700 + #define EOF -1 /* end of file */ #define WEOF -1u /* end of file (multibyte) */ #define _IOFBF 0 /* fully buffered */ @@ -48,14 +52,14 @@ #define S_ISLNK(mode) (((mode)&S_IFMT) == S_IFLNK) #define S_ISSOCK(mode) (((mode)&S_IFMT) == S_IFSOCK) -#define WCOREDUMP(s) ((s)&0x80) -#define WEXITSTATUS(s) (((s)&0xff00) >> 8) +#define WCOREDUMP(s) (0x80 & (s)) +#define WEXITSTATUS(s) ((0xff00 & (s)) >> 8) #define WIFCONTINUED(s) ((s) == 0xffff) #define WIFEXITED(s) (!WTERMSIG(s)) -#define WIFSIGNALED(s) (((s)&0xffff) - 1u < 0xffu) -#define WIFSTOPPED(s) ((short)((((s)&0xffff) * 0x10001) >> 8) > 0x7f00) +#define WIFSIGNALED(s) ((0xffff & (s)) - 1u < 0xffu) +#define WIFSTOPPED(s) ((short)(((0xffff & (s)) * 0x10001) >> 8) > 0x7f00) #define WSTOPSIG(s) WEXITSTATUS(s) -#define WTERMSIG(s) ((s)&0x7f) +#define WTERMSIG(s) (127 & (s)) #define W_STOPCODE(s) ((s) << 8 | 0177) #if !(__ASSEMBLER__ + __LINKER__ + 0) @@ -182,6 +186,8 @@ int setpriority(int, unsigned, int); int setregid(uint32_t, uint32_t); int setresgid(uint32_t, uint32_t, uint32_t); int setresuid(uint32_t, uint32_t, uint32_t); +int getresgid(uint32_t *, uint32_t *, uint32_t *); +int getresuid(uint32_t *, uint32_t *, uint32_t *); int setreuid(uint32_t, uint32_t); int setrlimit(int, const struct rlimit *); int setsid(void); @@ -233,6 +239,7 @@ ssize_t write(int, const void *, size_t); struct dirent *readdir(DIR *); void rewinddir(DIR *); void sync(void); +int pledge(const char *, const char *); int clone(int (*)(void *), void *, size_t, int, void *, int *, void *, size_t, int *); diff --git a/libc/calls/calls.mk b/libc/calls/calls.mk index 72322331c..5130f390a 100644 --- a/libc/calls/calls.mk +++ b/libc/calls/calls.mk @@ -109,6 +109,7 @@ o//libc/calls/fcntl.o: \ o/$(MODE)/libc/calls/execl.o \ o/$(MODE)/libc/calls/execle.o \ o/$(MODE)/libc/calls/execlp.o \ +o/$(MODE)/libc/calls/copyfile.o \ o/$(MODE)/libc/calls/execve-nt.o \ o/$(MODE)/libc/calls/execve-sysv.o \ o/$(MODE)/libc/calls/readlinkat-nt.o \ diff --git a/libc/calls/chdir-nt.c b/libc/calls/chdir-nt.c index cd5f664f9..65c027ce5 100644 --- a/libc/calls/chdir-nt.c +++ b/libc/calls/chdir-nt.c @@ -29,7 +29,7 @@ textwindows int sys_chdir_nt(const char *path) { uint32_t n; int e, ms, err, len; - char16_t path16[PATH_MAX + 1], var[4]; + char16_t path16[PATH_MAX], var[4]; if ((len = __mkntpath(path, path16)) == -1) return -1; if (!len) return enoent(); if (len && path16[len - 1] != u'\\') { diff --git a/libc/calls/copyfile.c b/libc/calls/copyfile.c index 1e36a0f80..270532537 100644 --- a/libc/calls/copyfile.c +++ b/libc/calls/copyfile.c @@ -28,6 +28,7 @@ #include "libc/nt/enum/filesharemode.h" #include "libc/nt/files.h" #include "libc/nt/runtime.h" +#include "libc/nt/struct/filetime.h" #include "libc/sysv/consts/at.h" #include "libc/sysv/consts/madv.h" #include "libc/sysv/consts/o.h" @@ -37,7 +38,7 @@ static textwindows int sys_copyfile_nt(const char *src, const char *dst, int flags) { int64_t fhsrc, fhdst; struct NtFileTime accessed, modified; - char16_t src16[PATH_MAX + 1], dst16[PATH_MAX + 1]; + char16_t src16[PATH_MAX], dst16[PATH_MAX]; if (__mkntpath(src, src16) == -1) return -1; if (__mkntpath(dst, dst16) == -1) return -1; if (CopyFile(src16, dst16, !!(flags & COPYFILE_NOCLOBBER))) { diff --git a/libc/calls/execlp.c b/libc/calls/execlp.c index bc59b49d7..a2d97538d 100644 --- a/libc/calls/execlp.c +++ b/libc/calls/execlp.c @@ -39,7 +39,7 @@ int execlp(const char *prog, const char *arg, ... /*, NULL*/) { char *exe; char **argv; va_list va, vb; - char pathbuf[PATH_MAX + 1]; + char pathbuf[PATH_MAX]; if (!(exe = commandv(prog, pathbuf, sizeof(pathbuf)))) return -1; va_copy(vb, va); va_start(va, arg); diff --git a/libc/calls/execve-sysv.c b/libc/calls/execve-sysv.c index 4c8eccfd3..60c4f5189 100644 --- a/libc/calls/execve-sysv.c +++ b/libc/calls/execve-sysv.c @@ -33,8 +33,8 @@ int sys_execve(const char *prog, char *const argv[], char *const envp[]) { shargs = alloca((i + 2) * sizeof(char *)); memcpy(shargs + 2, argv + 1, i * sizeof(char *)); if (IsFreebsd() || IsNetbsd()) { - shargs[0] = firstnonnull( - commandv("bash", alloca(PATH_MAX + 1), PATH_MAX + 1), _PATH_BSHELL); + shargs[0] = firstnonnull(commandv("bash", alloca(PATH_MAX), PATH_MAX), + _PATH_BSHELL); } else { shargs[0] = _PATH_BSHELL; } diff --git a/libc/calls/execvpe.c b/libc/calls/execvpe.c index ed1fd82a9..40824c924 100644 --- a/libc/calls/execvpe.c +++ b/libc/calls/execvpe.c @@ -36,7 +36,7 @@ */ int execvpe(const char *prog, char *const argv[], char *const *envp) { char *exe; - char pathbuf[PATH_MAX + 1]; + char pathbuf[PATH_MAX]; if (IsAsan() && !__asan_is_valid(prog, 1)) return efault(); if (!(exe = commandv(prog, pathbuf, sizeof(pathbuf)))) return -1; return execve(exe, argv, envp); diff --git a/libc/calls/faccessat-nt.c b/libc/calls/faccessat-nt.c index f8298dcb7..2630da0fb 100644 --- a/libc/calls/faccessat-nt.c +++ b/libc/calls/faccessat-nt.c @@ -21,8 +21,9 @@ #include "libc/sysv/consts/at.h" #include "libc/sysv/errfuns.h" -int sys_faccessat_nt(int dirfd, const char *path, int mode, uint32_t flags) { - char16_t path16[PATH_MAX + 1]; +textwindows int sys_faccessat_nt(int dirfd, const char *path, int mode, + uint32_t flags) { + char16_t path16[PATH_MAX]; if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1; return __fix_enotdir(ntaccesscheck(path16, mode), path16); } diff --git a/libc/calls/fchdir-nt.c b/libc/calls/fchdir-nt.c index 3c089a653..ab8db742c 100644 --- a/libc/calls/fchdir-nt.c +++ b/libc/calls/fchdir-nt.c @@ -24,7 +24,7 @@ textwindows int sys_fchdir_nt(int dirfd) { uint32_t len; - char16_t dir[PATH_MAX + 1]; + char16_t dir[PATH_MAX]; if (!__isfdkind(dirfd, kFdFile)) return ebadf(); len = GetFinalPathNameByHandle(g_fds.p[dirfd].handle, dir, ARRAYLEN(dir), kNtFileNameNormalized | kNtVolumeNameDos); diff --git a/libc/calls/fchmodat-nt.c b/libc/calls/fchmodat-nt.c index d8698aa2e..7b0ba5f7c 100644 --- a/libc/calls/fchmodat-nt.c +++ b/libc/calls/fchmodat-nt.c @@ -23,7 +23,7 @@ textwindows int sys_fchmodat_nt(int dirfd, const char *path, uint32_t mode, int flags) { uint32_t attr; - uint16_t path16[PATH_MAX + 1]; + uint16_t path16[PATH_MAX]; if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1; if ((attr = GetFileAttributes(path16)) != -1u) { if (mode & 0200) { diff --git a/libc/calls/fileexists.c b/libc/calls/fileexists.c index 64aa86cac..876a1cac9 100644 --- a/libc/calls/fileexists.c +++ b/libc/calls/fileexists.c @@ -48,7 +48,7 @@ bool fileexists(const char *path) { bool res; union metastat st; struct ZiposUri zipname; - uint16_t path16[PATH_MAX + 1]; + uint16_t path16[PATH_MAX]; e = errno; if (IsAsan() && !__asan_is_valid(path, 1)) { efault(); diff --git a/libc/calls/fstatat-nt.c b/libc/calls/fstatat-nt.c index b17d6d324..468752e70 100644 --- a/libc/calls/fstatat-nt.c +++ b/libc/calls/fstatat-nt.c @@ -29,7 +29,7 @@ textwindows int sys_fstatat_nt(int dirfd, const char *path, struct stat *st, int flags) { int rc; int64_t fh; - uint16_t path16[PATH_MAX + 1]; + uint16_t path16[PATH_MAX]; if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1; if ((fh = CreateFile( path16, kNtFileReadAttributes, 0, 0, kNtOpenExisting, diff --git a/libc/calls/getcwd-nt.c b/libc/calls/getcwd-nt.c index 0847b8653..475824679 100644 --- a/libc/calls/getcwd-nt.c +++ b/libc/calls/getcwd-nt.c @@ -28,7 +28,7 @@ textwindows char *sys_getcwd_nt(char *buf, size_t size) { uint64_t w; wint_t x, y; uint32_t n, i, j; - char16_t p[PATH_MAX + 1]; + char16_t p[PATH_MAX]; if ((n = GetCurrentDirectory(ARRAYLEN(p), p))) { if (4 + n + 1 <= size && 4 + n + 1 <= ARRAYLEN(p)) { tprecode16to8(buf, size, p); diff --git a/libc/calls/getcwd.c b/libc/calls/getcwd.c index caa17091f..158185a56 100644 --- a/libc/calls/getcwd.c +++ b/libc/calls/getcwd.c @@ -51,7 +51,7 @@ char *getcwd(char *buf, size_t size) { } } else if (weaken(malloc)) { assert(!__vforked); - if (!size) size = PATH_MAX + 1; + if (!size) size = PATH_MAX; if (!(p = weaken(malloc)(size))) { STRACE("getcwd(%p, %'zu) %m", buf, size); return 0; diff --git a/libc/calls/getexecutablename.c b/libc/calls/getexecutablename.c index a191df534..455942bb7 100644 --- a/libc/calls/getexecutablename.c +++ b/libc/calls/getexecutablename.c @@ -32,7 +32,7 @@ #define KERN_PROC_PATHNAME_FREEBSD 12 #define KERN_PROC_PATHNAME_NETBSD 5 -char program_executable_name[PATH_MAX + 1]; +char program_executable_name[PATH_MAX]; static inline void GetProgramExecutableNameImpl(char *p, char *e) { char *q; @@ -40,7 +40,7 @@ static inline void GetProgramExecutableNameImpl(char *p, char *e) { size_t i, n; union { int cmd[4]; - char16_t path16[PATH_MAX + 1]; + char16_t path16[PATH_MAX]; } u; if (IsWindows()) { diff --git a/libc/calls/getresgid.c b/libc/calls/getresgid.c new file mode 100644 index 000000000..1760de051 --- /dev/null +++ b/libc/calls/getresgid.c @@ -0,0 +1,51 @@ +/*-*- 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 │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/calls/strace.internal.h" +#include "libc/dce.h" + +/** + * Gets real, effective, and "saved" group ids. + * + * @param real receives real user id, or null to do nothing + * @param effective receives effective user id, or null to do nothing + * @param saved receives saved user id, or null to do nothing + * @return 0 on success or -1 w/ errno + * @see setresuid() + */ +int getresgid(uint32_t *real, uint32_t *effective, uint32_t *saved) { + int rc, gid; + if (IsWindows()) { + gid = getgid(); + if (real) *real = gid; + if (effective) *effective = gid; + if (saved) *saved = gid; + rc = 0; + } else if (saved) { + rc = sys_getresgid(real, effective, saved); + } else { + if (real) *real = sys_getgid(); + if (effective) *effective = sys_getegid(); + rc = 0; + } + STRACE("getresgid([%d], [%d], [%d]) → %d% m", real ? *real : 0, + effective ? *effective : 0, saved ? *saved : 0, rc); + return rc; +} diff --git a/libc/calls/getresuid.c b/libc/calls/getresuid.c new file mode 100644 index 000000000..80a0f10a7 --- /dev/null +++ b/libc/calls/getresuid.c @@ -0,0 +1,51 @@ +/*-*- 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 │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/calls/strace.internal.h" +#include "libc/dce.h" + +/** + * Gets real, effective, and "saved" user ids. + * + * @param real receives real user id, or null to do nothing + * @param effective receives effective user id, or null to do nothing + * @param saved receives saved user id, or null to do nothing + * @return 0 on success or -1 w/ errno + * @see setresuid() + */ +int getresuid(uint32_t *real, uint32_t *effective, uint32_t *saved) { + int rc, uid; + if (IsWindows()) { + uid = getuid(); + if (real) *real = uid; + if (effective) *effective = uid; + if (saved) *saved = uid; + rc = 0; + } else if (saved) { + rc = sys_getresuid(real, effective, saved); + } else { + if (real) *real = sys_getuid(); + if (effective) *effective = sys_geteuid(); + rc = 0; + } + STRACE("getresuid([%d], [%d], [%d]) → %d% m", real ? *real : 0, + effective ? *effective : 0, saved ? *saved : 0, rc); + return rc; +} diff --git a/libc/calls/gettimeofday-nt.c b/libc/calls/gettimeofday-nt.c index becf42dd2..cf113f721 100644 --- a/libc/calls/gettimeofday-nt.c +++ b/libc/calls/gettimeofday-nt.c @@ -25,7 +25,7 @@ #include "libc/str/str.h" #include "libc/time/struct/timezone.h" -int sys_gettimeofday_nt(struct timeval *tv, struct timezone *tz) { +textwindows int sys_gettimeofday_nt(struct timeval *tv, struct timezone *tz) { struct NtFileTime ft; GetSystemTimeAsFileTime(&ft); if (tv) *tv = FileTimeToTimeVal(ft); diff --git a/libc/calls/internal.h b/libc/calls/internal.h index 12aa6e8af..50c0e3a85 100644 --- a/libc/calls/internal.h +++ b/libc/calls/internal.h @@ -163,6 +163,8 @@ i32 sys_getpgid(i32) hidden; i32 sys_getpgrp(void) hidden; i32 sys_getppid(void) hidden; i32 sys_getpriority(i32, u32) hidden; +i32 sys_getresgid(u32 *, u32 *, u32 *); +i32 sys_getresuid(u32 *, u32 *, u32 *); i32 sys_getrlimit(i32, struct rlimit *) hidden; i32 sys_getrusage(i32, struct rusage *) hidden; i32 sys_getsid(int) hidden; @@ -184,6 +186,7 @@ i32 sys_openat(i32, const char *, i32, u32) hidden; i32 sys_pause(void) hidden; i32 sys_pipe(i32[hasatleast 2]) hidden; i32 sys_pipe2(i32[hasatleast 2], u32) hidden; +i32 sys_pledge(const char *, const char *) hidden; i32 sys_posix_openpt(i32) hidden; i32 sys_renameat(i32, const char *, i32, const char *) hidden; i32 sys_sched_setaffinity(i32, u64, const void *) hidden; @@ -193,8 +196,8 @@ i32 sys_setitimer(i32, const struct itimerval *, struct itimerval *) hidden; i32 sys_setpgid(i32, i32) hidden; i32 sys_setpriority(i32, u32, i32) hidden; i32 sys_setregid(u32, u32) hidden; -i32 sys_setresgid(uint32_t, uint32_t, uint32_t) hidden; -i32 sys_setresuid(uint32_t, uint32_t, uint32_t) hidden; +i32 sys_setresgid(u32, u32, u32) hidden; +i32 sys_setresuid(u32, u32, u32) hidden; i32 sys_setreuid(u32, u32) hidden; i32 sys_setrlimit(i32, const struct rlimit *) hidden; i32 sys_setsid(void) hidden; @@ -330,6 +333,7 @@ int ioctl_tiocgwinsz_nt(struct Fd *, struct winsize *) hidden; │ cosmopolitan § syscalls » windows nt » support ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ +bool __is_linux_2_6_23(void) hidden; int64_t __fix_enotdir(int64_t, char16_t *) hidden; int64_t __fix_enotdir3(int64_t, char16_t *, char16_t *) hidden; bool _check_interrupts(bool, struct Fd *) hidden; @@ -341,10 +345,9 @@ bool isregularfile_nt(const char *) hidden; bool issymlink_nt(const char *) hidden; bool32 ntsetprivilege(i64, const char16_t *, u32) hidden; char16_t *CreatePipeName(char16_t *) hidden; -int __mkntpath(const char *, char16_t[hasatleast PATH_MAX + 1]) hidden; -int __mkntpath2(const char *, char16_t[hasatleast PATH_MAX + 1], int) hidden; -int __mkntpathat(int, const char *, int, - char16_t[hasatleast PATH_MAX + 1]) hidden; +int __mkntpath(const char *, char16_t[hasatleast PATH_MAX]) hidden; +int __mkntpath2(const char *, char16_t[hasatleast PATH_MAX], int) hidden; +int __mkntpathat(int, const char *, int, char16_t[hasatleast PATH_MAX]) hidden; int sys_clock_gettime_nt(int, struct timespec *) hidden; int ntaccesscheck(const char16_t *, u32) paramsnonnull() hidden; int sys_getsetpriority_nt(int, int, int, int (*)(int)); diff --git a/libc/calls/isdirectory-nt.c b/libc/calls/isdirectory-nt.c index 36e1dd9db..d0579a967 100644 --- a/libc/calls/isdirectory-nt.c +++ b/libc/calls/isdirectory-nt.c @@ -28,7 +28,7 @@ bool isdirectory_nt(const char *path) { int e; uint32_t x; - char16_t path16[PATH_MAX + 1]; + char16_t path16[PATH_MAX]; e = errno; if (__mkntpath(path, path16) == -1) return -1; if ((x = GetFileAttributes(path16)) != -1u) { diff --git a/libc/calls/islinux.c b/libc/calls/islinux.c new file mode 100644 index 000000000..1209f959e --- /dev/null +++ b/libc/calls/islinux.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 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/internal.h" +#include "libc/dce.h" +#include "libc/errno.h" +#include "libc/sysv/consts/pr.h" + +bool __is_linux_2_6_23(void) { + int rc; + if (!IsLinux()) return false; + asm volatile("syscall" + : "=a"(rc) + : "0"(157), "D"(PR_GET_SECCOMP) + : "rcx", "r11", "memory"); + return rc != -EINVAL; +} diff --git a/libc/calls/isregularfile-nt.c b/libc/calls/isregularfile-nt.c index 91ffab2fd..45614ef64 100644 --- a/libc/calls/isregularfile-nt.c +++ b/libc/calls/isregularfile-nt.c @@ -28,7 +28,7 @@ bool isregularfile_nt(const char *path) { int e; uint32_t x; - char16_t path16[PATH_MAX + 1]; + char16_t path16[PATH_MAX]; e = errno; if (__mkntpath(path, path16) == -1) return -1; if ((x = GetFileAttributes(path16)) != -1u) { diff --git a/libc/calls/issymlink-nt.c b/libc/calls/issymlink-nt.c index ef9c3170a..4c074cadb 100644 --- a/libc/calls/issymlink-nt.c +++ b/libc/calls/issymlink-nt.c @@ -28,7 +28,7 @@ bool issymlink_nt(const char *path) { int e; uint32_t x; - char16_t path16[PATH_MAX + 1]; + char16_t path16[PATH_MAX]; e = errno; if (__mkntpath(path, path16) == -1) return -1; if ((x = GetFileAttributes(path16)) != -1u) { diff --git a/libc/calls/linkat-nt.c b/libc/calls/linkat-nt.c index dea46cee9..072c203f6 100644 --- a/libc/calls/linkat-nt.c +++ b/libc/calls/linkat-nt.c @@ -23,8 +23,8 @@ textwindows int sys_linkat_nt(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) { - char16_t newpath16[PATH_MAX + 1]; - char16_t oldpath16[PATH_MAX + 1]; + char16_t newpath16[PATH_MAX]; + char16_t oldpath16[PATH_MAX]; if (__mkntpathat(olddirfd, oldpath, 0, oldpath16) != -1 && __mkntpathat(newdirfd, newpath, 0, newpath16) != -1) { if (CreateHardLink(newpath16, oldpath16, NULL)) { diff --git a/libc/calls/mkdirat-nt.c b/libc/calls/mkdirat-nt.c index f15b90326..16133342a 100644 --- a/libc/calls/mkdirat-nt.c +++ b/libc/calls/mkdirat-nt.c @@ -23,7 +23,7 @@ textwindows int sys_mkdirat_nt(int dirfd, const char *path, uint32_t mode) { int e; - char16_t *p, path16[PATH_MAX + 1]; + char16_t *p, path16[PATH_MAX]; /* if (strlen(path) > 248) return enametoolong(); */ if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1; if (CreateDirectory(path16, 0)) return 0; diff --git a/libc/calls/mkntcmdline.c b/libc/calls/mkntcmdline.c index 78694df12..29eacc9b1 100644 --- a/libc/calls/mkntcmdline.c +++ b/libc/calls/mkntcmdline.c @@ -23,10 +23,12 @@ #include "libc/str/utf16.h" #include "libc/sysv/errfuns.h" -#define APPEND(c) \ - do { \ - cmdline[k++] = c; \ - if (k == ARG_MAX) return e2big(); \ +#define APPEND(c) \ + do { \ + cmdline[k++] = c; \ + if (k == ARG_MAX / 2) { \ + return e2big(); \ + } \ } while (0) static noasan bool NeedsQuotes(const char *s) { @@ -52,8 +54,8 @@ static noasan bool NeedsQuotes(const char *s) { * @return freshly allocated lpCommandLine or NULL w/ errno * @see libc/runtime/dosargv.c */ -textwindows noasan int mkntcmdline(char16_t cmdline[ARG_MAX], const char *prog, - char *const argv[]) { +textwindows noasan int mkntcmdline(char16_t cmdline[ARG_MAX / 2], + const char *prog, char *const argv[]) { char *arg; uint64_t w; wint_t x, y; diff --git a/libc/calls/mkntenvblock.c b/libc/calls/mkntenvblock.c index 5c94eb3d0..95287bb6d 100644 --- a/libc/calls/mkntenvblock.c +++ b/libc/calls/mkntenvblock.c @@ -55,9 +55,9 @@ static noasan void InsertString(char **a, size_t i, char *s) { * @param envp is an a NULL-terminated array of UTF-8 strings * @param extravar is a VAR=val string we consider part of envp or NULL * @return 0 on success, or -1 w/ errno - * @error E2BIG if total number of shorts exceeded ARG_MAX (0x8000) + * @error E2BIG if total number of shorts exceeded ARG_MAX/2 (32767) */ -textwindows noasan int mkntenvblock(char16_t envvars[ARG_MAX], +textwindows noasan int mkntenvblock(char16_t envvars[ARG_MAX / 2], char *const envp[], const char *extravar) { bool v; char *t; @@ -98,7 +98,9 @@ textwindows noasan int mkntenvblock(char16_t envvars[ARG_MAX], w = EncodeUtf16(x); do { envvars[k++] = w & 0xffff; - if (k == ARG_MAX) return e2big(); + if (k == ARG_MAX / 2) { + return e2big(); + } } while ((w >>= 16)); } while (x); } diff --git a/libc/calls/mkntpath.c b/libc/calls/mkntpath.c index ab5bdd6a8..a2a36a28a 100644 --- a/libc/calls/mkntpath.c +++ b/libc/calls/mkntpath.c @@ -50,7 +50,7 @@ textwindows static const char *FixNtMagicPath(const char *path, } textwindows int __mkntpath(const char *path, - char16_t path16[hasatleast PATH_MAX + 1]) { + char16_t path16[hasatleast PATH_MAX]) { return __mkntpath2(path, path16, -1); } @@ -68,8 +68,7 @@ textwindows int __mkntpath(const char *path, * @error ENAMETOOLONG */ textwindows int __mkntpath2(const char *path, - char16_t path16[hasatleast PATH_MAX + 1], - int flags) { + char16_t path16[hasatleast PATH_MAX], int flags) { /* * 1. Need +1 for NUL-terminator * 2. Need +1 for UTF-16 overflow @@ -101,7 +100,7 @@ textwindows int __mkntpath2(const char *path, m = 0; } n = tprecode8to16(p, z, q).ax; - if (n == z - 1) { + if (n >= z - 1) { STRACE("path too long for windows: %#s", path); return enametoolong(); } diff --git a/libc/calls/mkntpathat.c b/libc/calls/mkntpathat.c index f4e6fd592..f2258721b 100644 --- a/libc/calls/mkntpathat.c +++ b/libc/calls/mkntpathat.c @@ -25,9 +25,9 @@ #include "libc/sysv/errfuns.h" int __mkntpathat(int dirfd, const char *path, int flags, - char16_t file[hasatleast PATH_MAX + 1]) { + char16_t file[hasatleast PATH_MAX]) { + char16_t dir[PATH_MAX]; uint32_t dirlen, filelen; - char16_t dir[PATH_MAX + 1]; if ((filelen = __mkntpath2(path, file, flags)) == -1) return -1; if (!filelen) return enoent(); if (file[0] != u'\\' && dirfd != AT_FDCWD) { /* ProTip: \\?\C:\foo */ diff --git a/libc/calls/ntspawn.c b/libc/calls/ntspawn.c index 584c42a2c..fe63fa82a 100644 --- a/libc/calls/ntspawn.c +++ b/libc/calls/ntspawn.c @@ -32,8 +32,13 @@ #include "libc/nt/struct/startupinfo.h" struct SpawnBlock { - char16_t cmdline[ARG_MAX]; - char16_t envvars[ARG_MAX]; + union { + struct { + char16_t cmdline[ARG_MAX / 2]; + char16_t envvars[ARG_MAX / 2]; + }; + char __pad[ROUNDUP(ARG_MAX / 2 * 2 * sizeof(char16_t), FRAMESIZE)]; + }; }; /** @@ -68,20 +73,15 @@ textwindows int ntspawn( struct NtProcessInformation *opt_out_lpProcessInformation) { int rc; int64_t handle; - size_t blocksize; struct SpawnBlock *block; - char16_t prog16[PATH_MAX + 1]; + char16_t prog16[PATH_MAX]; rc = -1; block = NULL; if (__mkntpath(prog, prog16) == -1) return -1; - blocksize = ROUNDUP(sizeof(*block), FRAMESIZE); - if ((handle = CreateFileMapping( - -1, - &(struct NtSecurityAttributes){sizeof(struct NtSecurityAttributes), - NULL, false}, - pushpop(kNtPageReadwrite), 0, blocksize, NULL)) && + if ((handle = CreateFileMapping(-1, 0, pushpop(kNtPageReadwrite), 0, + sizeof(*block), 0)) && (block = MapViewOfFileEx(handle, kNtFileMapRead | kNtFileMapWrite, 0, 0, - blocksize, NULL)) && + sizeof(*block), 0)) && mkntcmdline(block->cmdline, prog, argv) != -1 && mkntenvblock(block->envvars, envp, extravar) != -1 && CreateProcess(prog16, block->cmdline, opt_lpProcessAttributes, diff --git a/libc/calls/ntspawn.h b/libc/calls/ntspawn.h index b794d5cce..df1955b43 100644 --- a/libc/calls/ntspawn.h +++ b/libc/calls/ntspawn.h @@ -6,8 +6,8 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -int mkntcmdline(char16_t[ARG_MAX], const char *, char *const[]) hidden; -int mkntenvblock(char16_t[ARG_MAX], char *const[], const char *) hidden; +int mkntcmdline(char16_t[ARG_MAX / 2], const char *, char *const[]) hidden; +int mkntenvblock(char16_t[ARG_MAX / 2], char *const[], const char *) hidden; int ntspawn(const char *, char *const[], char *const[], const char *, struct NtSecurityAttributes *, struct NtSecurityAttributes *, bool32, uint32_t, const char16_t *, const struct NtStartupInfo *, diff --git a/libc/calls/open-nt.c b/libc/calls/open-nt.c index 75c23d7dd..1955a5d18 100644 --- a/libc/calls/open-nt.c +++ b/libc/calls/open-nt.c @@ -38,7 +38,7 @@ static textwindows int64_t sys_open_nt_impl(int dirfd, const char *path, uint32_t flags, int32_t mode) { - char16_t path16[PATH_MAX + 1]; + char16_t path16[PATH_MAX]; uint32_t perm, share, disp, attr; if (__mkntpathat(dirfd, path, flags, path16) == -1) return -1; if (GetNtOpenFlags(flags, mode, &perm, &share, &disp, &attr) == -1) return -1; diff --git a/libc/calls/openbsd.internal.h b/libc/calls/openbsd.internal.h index 8a8b62bc7..b158ac9ff 100644 --- a/libc/calls/openbsd.internal.h +++ b/libc/calls/openbsd.internal.h @@ -8,8 +8,6 @@ COSMOPOLITAN_C_START_ typedef unsigned char u_char; -int pledge(const char *promises, const char *execpromises); - COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_COMPAT_OPENBSD_H_ */ diff --git a/libc/calls/readlinkat-nt.c b/libc/calls/readlinkat-nt.c index e18549177..e55136d35 100644 --- a/libc/calls/readlinkat-nt.c +++ b/libc/calls/readlinkat-nt.c @@ -40,7 +40,7 @@ textwindows ssize_t sys_readlinkat_nt(int dirfd, const char *path, char *buf, wint_t x, y; volatile char *memory; uint32_t i, j, n, mem; - char16_t path16[PATH_MAX + 1], *p; + char16_t path16[PATH_MAX], *p; struct NtReparseDataBuffer *rdb; if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1; mem = 16384; diff --git a/libc/calls/realpath.c b/libc/calls/realpath.c index 4f0a5aed6..cb79c7142 100644 --- a/libc/calls/realpath.c +++ b/libc/calls/realpath.c @@ -81,7 +81,7 @@ char *realpath(const char *filename, char *resolved) ssize_t rc; int e, up, check_dir=0; size_t k, p, q, l, l0, cnt=0, nup=0; - char output[PATH_MAX], stack[PATH_MAX+1], *z; + char output[PATH_MAX], stack[PATH_MAX], *z; if (!filename) { einval(); diff --git a/libc/calls/renameat-nt.c b/libc/calls/renameat-nt.c index 9b34e84f9..949d05d84 100644 --- a/libc/calls/renameat-nt.c +++ b/libc/calls/renameat-nt.c @@ -22,8 +22,8 @@ textwindows int sys_renameat_nt(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) { - char16_t oldpath16[PATH_MAX + 1]; - char16_t newpath16[PATH_MAX + 1]; + char16_t oldpath16[PATH_MAX]; + char16_t newpath16[PATH_MAX]; if (__mkntpathat(olddirfd, oldpath, 0, oldpath16) == -1 || __mkntpathat(newdirfd, newpath, 0, newpath16) == -1) { return -1; diff --git a/libc/calls/sched_setaffinity.c b/libc/calls/sched_setaffinity.c index 508d61748..f1c8df0cd 100644 --- a/libc/calls/sched_setaffinity.c +++ b/libc/calls/sched_setaffinity.c @@ -29,8 +29,8 @@ #include "libc/str/str.h" static textwindows dontinline int sys_sched_setaffinity_nt(int pid, - uint64_t bitsetsize, - const void *bitset) { + uint64_t bitsetsize, + const void *bitset) { int rc; uintptr_t mask; int64_t handle; diff --git a/libc/calls/sigaction.c b/libc/calls/sigaction.c index 093b56f00..17ac1962d 100644 --- a/libc/calls/sigaction.c +++ b/libc/calls/sigaction.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" #include "libc/bits/bits.h" +#include "libc/bits/weaken.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/sigbits.h" @@ -34,6 +35,8 @@ #include "libc/intrin/asan.internal.h" #include "libc/intrin/spinlock.h" #include "libc/limits.h" +#include "libc/log/backtrace.internal.h" +#include "libc/log/log.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" diff --git a/libc/calls/strace.internal.h b/libc/calls/strace.internal.h index 17a56ca44..509bfcd8f 100644 --- a/libc/calls/strace.internal.h +++ b/libc/calls/strace.internal.h @@ -8,7 +8,7 @@ #define _KERNTRACE 0 /* not configurable w/ flag yet */ #define _POLLTRACE 0 /* not configurable w/ flag yet */ #define _DATATRACE 1 /* not configurable w/ flag yet */ -#define _NTTRACE 1 /* not configurable w/ flag yet */ +#define _NTTRACE 0 /* not configurable w/ flag yet */ #define STRACE_PROLOGUE "%rSYS %5P %'18T " diff --git a/libc/calls/symlinkat-nt.c b/libc/calls/symlinkat-nt.c index 89e7bf7c1..1cbcc3907 100644 --- a/libc/calls/symlinkat-nt.c +++ b/libc/calls/symlinkat-nt.c @@ -53,8 +53,8 @@ textwindows int sys_symlinkat_nt(const char *target, int newdirfd, const char *linkpath) { int targetlen; uint32_t attrs, flags; - char16_t target16[PATH_MAX + 1]; - char16_t linkpath16[PATH_MAX + 1]; + char16_t target16[PATH_MAX]; + char16_t linkpath16[PATH_MAX]; // convert the paths if (__mkntpathat(newdirfd, linkpath, 0, linkpath16) == -1) return -1; diff --git a/libc/calls/sync-nt.c b/libc/calls/sync-nt.c index 5188199f0..31dbc395b 100644 --- a/libc/calls/sync-nt.c +++ b/libc/calls/sync-nt.c @@ -29,7 +29,7 @@ /** * Flushes all open file handles and, if possible, all disk drives. */ -int sys_sync_nt(void) { +textwindows int sys_sync_nt(void) { unsigned i; int64_t volume; uint32_t drives; diff --git a/libc/calls/truncate-nt.c b/libc/calls/truncate-nt.c index df64631a9..29c0819b5 100644 --- a/libc/calls/truncate-nt.c +++ b/libc/calls/truncate-nt.c @@ -28,7 +28,7 @@ textwindows int sys_truncate_nt(const char *path, uint64_t length) { int rc; bool32 ok; int64_t fh; - uint16_t path16[PATH_MAX + 1]; + uint16_t path16[PATH_MAX]; if (__mkntpath(path, path16) == -1) return -1; if ((fh = CreateFile(path16, kNtGenericWrite, kNtFileShareRead, NULL, kNtOpenExisting, kNtFileAttributeNormal, 0)) != -1) { diff --git a/libc/calls/ttyname.c b/libc/calls/ttyname.c index 084c0383d..94e064feb 100644 --- a/libc/calls/ttyname.c +++ b/libc/calls/ttyname.c @@ -19,7 +19,7 @@ #include "libc/calls/calls.h" #include "libc/log/log.h" -static char ttyname_buf[PATH_MAX + 1]; +static char ttyname_buf[PATH_MAX]; /** * Returns name of terminal. diff --git a/libc/calls/ttyname_r.c b/libc/calls/ttyname_r.c index 99d67d102..fcef19b89 100644 --- a/libc/calls/ttyname_r.c +++ b/libc/calls/ttyname_r.c @@ -63,7 +63,7 @@ static int ttyname_freebsd(int fd, char *buf, size_t size) { static int ttyname_linux(int fd, char *buf, size_t size) { struct stat st1, st2; if (!isatty(fd)) return errno; - char name[PATH_MAX + 1]; + char name[PATH_MAX]; FormatInt32(stpcpy(name, "/proc/self/fd/"), fd); ssize_t got; got = readlink(name, buf, size); diff --git a/libc/calls/unlinkat-nt.c b/libc/calls/unlinkat-nt.c index 9c08462f5..3b8555f03 100644 --- a/libc/calls/unlinkat-nt.c +++ b/libc/calls/unlinkat-nt.c @@ -39,8 +39,7 @@ * from failing for no reason at all. For example a unit test that * repeatedly opens and unlinks the same filename. */ -static textwindows int SyncDirectory(int df, char16_t path[PATH_MAX + 1], - int n) { +static textwindows int SyncDirectory(int df, char16_t path[PATH_MAX], int n) { int rc; int64_t fh; char16_t *p; @@ -129,7 +128,7 @@ static textwindows int sys_unlink_nt(const char16_t *path) { textwindows int sys_unlinkat_nt(int dirfd, const char *path, int flags) { int n, rc; - char16_t path16[PATH_MAX + 1]; + char16_t path16[PATH_MAX]; if ((n = __mkntpathat(dirfd, path, 0, path16)) == -1) { rc = -1; } else if (flags & AT_REMOVEDIR) { diff --git a/libc/calls/utimensat-nt.c b/libc/calls/utimensat-nt.c index 2694bf0cd..0a5972cde 100644 --- a/libc/calls/utimensat-nt.c +++ b/libc/calls/utimensat-nt.c @@ -35,7 +35,7 @@ textwindows int sys_utimensat_nt(int dirfd, const char *path, const struct timespec ts[2], int flags) { int i, rc; int64_t fh; - uint16_t path16[PATH_MAX + 1]; + uint16_t path16[PATH_MAX]; struct NtFileTime ft[2], *ftp[2]; if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1; if ((fh = CreateFile(path16, kNtFileWriteAttributes, kNtFileShareRead, NULL, diff --git a/libc/dns/gethoststxt.c b/libc/dns/gethoststxt.c index 360114cdf..16598bdde 100644 --- a/libc/dns/gethoststxt.c +++ b/libc/dns/gethoststxt.c @@ -57,7 +57,7 @@ static textwindows dontinline char *GetNtHostsTxtPath(char *pathbuf, const struct HostsTxt *GetHostsTxt(void) { FILE *f; const char *path; - char pathbuf[PATH_MAX + 1]; + char pathbuf[PATH_MAX]; struct HostsTxtInitialStaticMemory *init; init = &g_hoststxt_init; if (!g_hoststxt) { diff --git a/libc/dns/lookupprotobyname.c b/libc/dns/lookupprotobyname.c index 598f7cb3d..f319d2ed6 100644 --- a/libc/dns/lookupprotobyname.c +++ b/libc/dns/lookupprotobyname.c @@ -51,7 +51,7 @@ int LookupProtoByName(const char *protoname, char *buf, size_t bufsize, const char *path; size_t linesize; int found, result; - char pathbuf[PATH_MAX + 1]; + char pathbuf[PATH_MAX]; char *name, *number, *alias, *comment, *tok; if (!(path = filepath)) { path = "/etc/protocols"; diff --git a/libc/dns/lookupprotobynumber.c b/libc/dns/lookupprotobynumber.c index 935a79cfe..e86e1c4da 100644 --- a/libc/dns/lookupprotobynumber.c +++ b/libc/dns/lookupprotobynumber.c @@ -55,7 +55,7 @@ int LookupProtoByNumber(const int protonum, char *buf, size_t bufsize, int found; size_t linesize; const char *path; - char pathbuf[PATH_MAX + 1]; + char pathbuf[PATH_MAX]; char *name, *number, *comment, *tok; if (!(path = filepath)) { path = "/etc/protocols"; diff --git a/libc/dns/lookupservicesbyname.c b/libc/dns/lookupservicesbyname.c index bf1bb132c..14458531e 100644 --- a/libc/dns/lookupservicesbyname.c +++ b/libc/dns/lookupservicesbyname.c @@ -56,7 +56,7 @@ int LookupServicesByName(const char *servname, char *servproto, const char *path; size_t linesize; int found, result; - char pathbuf[PATH_MAX + 1]; + char pathbuf[PATH_MAX]; char *name, *port, *proto, *alias, *comment, *tok; if (!(path = filepath)) { path = "/etc/services"; diff --git a/libc/dns/lookupservicesbyport.c b/libc/dns/lookupservicesbyport.c index 301d5d5cf..4fa9a364c 100644 --- a/libc/dns/lookupservicesbyport.c +++ b/libc/dns/lookupservicesbyport.c @@ -59,7 +59,7 @@ int LookupServicesByPort(const int servport, char *servproto, const char *filepath) { FILE *f; char *line; - char pathbuf[PATH_MAX + 1]; + char pathbuf[PATH_MAX]; const char *path; size_t linesize; int found; diff --git a/libc/fmt/fmt.c b/libc/fmt/fmt.c index fec8e214f..0166660d7 100644 --- a/libc/fmt/fmt.c +++ b/libc/fmt/fmt.c @@ -351,7 +351,7 @@ hidden int __fmt(void *fn, void *arg, const char *format, va_list va) { // undocumented %r specifier // used for good carriage return // helps integrate loggers with repls - if (!__replmode) { + if (!__replstderr) { break; } else { p = "\r\e[K"; diff --git a/libc/integral/normalize.inc b/libc/integral/normalize.inc index a268369b6..4cf6d3d06 100644 --- a/libc/integral/normalize.inc +++ b/libc/integral/normalize.inc @@ -70,13 +70,13 @@ #define BUFSIZ 0x1000 /* best stdio default */ #define CACHELINE 0x40 /* nexgen32e */ #define CHAR_BIT 8 /* b/c von neumann */ -#define ARG_MAX 0x8000 /* b/c windows */ -#define PATH_MAX 511 /* b/c bloat */ +#define ARG_MAX 0xfffe /* for argv and envp; see CreateProcess (32767*2) */ +#define PATH_MAX 1024 /* b/c _XOPEN_PATH_MAX */ #define NAME_MAX 63 /* b/c dns */ -#define CHILD_MAX 25 /* only if malloc isn't linked */ +#define CHILD_MAX 16 /* only if malloc isn't linked */ #define OPEN_MAX 16 /* only if malloc isn't linked */ #define ATEXIT_MAX 32 /* only if malloc isn't linked */ -#define NSIG 128 /* it's complicated */ +#define NSIG 128 /* b/c freebsd */ #if defined(__LP64__) && !defined(__INT64_TYPE__) #include "libc/integral/lp64.inc" diff --git a/libc/intrin/kprintf.greg.c b/libc/intrin/kprintf.greg.c index 7ecf44b1b..9595606ae 100644 --- a/libc/intrin/kprintf.greg.c +++ b/libc/intrin/kprintf.greg.c @@ -565,7 +565,7 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt, va_list va, // undocumented %r specifier // used for good carriage return // helps integrate loggers with repls - if (!__replmode || __nocolor) { + if (!__replstderr || __nocolor) { break; } else { s = "\r\033[K"; diff --git a/libc/intrin/nomultics.c b/libc/intrin/nomultics.c index a02f288c1..e0003b831 100644 --- a/libc/intrin/nomultics.c +++ b/libc/intrin/nomultics.c @@ -25,3 +25,4 @@ * @see kprintf(), vflogf(), linenoise() */ char __replmode; +char __replstderr; diff --git a/libc/intrin/nomultics.internal.h b/libc/intrin/nomultics.internal.h index 50cf641ff..ffc15900c 100644 --- a/libc/intrin/nomultics.internal.h +++ b/libc/intrin/nomultics.internal.h @@ -3,7 +3,8 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -extern bool __replmode; +extern char __replmode; +extern char __replstderr; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/log/appendresourcereport.c b/libc/log/appendresourcereport.c index 2f160de5f..b48d1a315 100644 --- a/libc/log/appendresourcereport.c +++ b/libc/log/appendresourcereport.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/bits.h" #include "libc/calls/struct/rusage.h" #include "libc/fmt/itoa.h" #include "libc/log/log.h" @@ -102,7 +103,11 @@ void AppendResourceReport(char **b, struct rusage *ru, const char *nl) { } if (ru->ru_nvcsw + ru->ru_nivcsw > 1) { AppendInt(st, ru->ru_nvcsw + ru->ru_nivcsw); - appends(b, " context switch ("); + appends(b, " context switch"); + if ((ru->ru_nvcsw + ru->ru_nivcsw) > 1) { + appendw(b, READ16LE("es")); + } + appendw(b, READ16LE(" (")); AppendInt(st, (long double)ru->ru_nvcsw / (ru->ru_nvcsw + ru->ru_nivcsw) * 100); appends(b, "% consensual)"); diff --git a/libc/log/commandvenv.c b/libc/log/commandvenv.c index e8fd06130..5586e8cfc 100644 --- a/libc/log/commandvenv.c +++ b/libc/log/commandvenv.c @@ -47,7 +47,7 @@ */ const char *commandvenv(const char *var, const char *cmd) { const char *exepath; - static char pathbuf[PATH_MAX + 1]; + static char pathbuf[PATH_MAX]; if (*cmd == '/' || *cmd == '\\') return cmd; if ((exepath = getenv(var))) { if (isempty(exepath)) return NULL; diff --git a/libc/mem/mem.mk b/libc/mem/mem.mk index 052fba956..86a2ef8a9 100644 --- a/libc/mem/mem.mk +++ b/libc/mem/mem.mk @@ -29,6 +29,7 @@ LIBC_MEM_A_CHECKS = \ LIBC_MEM_A_DIRECTDEPS = \ LIBC_CALLS \ + LIBC_SYSV_CALLS \ LIBC_FMT \ LIBC_INTRIN \ LIBC_NEXGEN32E \ diff --git a/libc/mem/pledge.c b/libc/mem/pledge.c new file mode 100644 index 000000000..6247560bd --- /dev/null +++ b/libc/mem/pledge.c @@ -0,0 +1,406 @@ +/*-*- 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 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/calls/strace.internal.h" +#include "libc/calls/struct/filter.h" +#include "libc/dce.h" +#include "libc/intrin/kprintf.h" +#include "libc/macros.internal.h" +#include "libc/mem/mem.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/nrlinux.h" +#include "libc/sysv/consts/pr.h" +#include "libc/sysv/errfuns.h" +#include "tool/net/sandbox.h" + +static const uint16_t kPledgeLinuxDefault[] = { + __NR_linux_exit, // + __NR_linux_exit_group, // + 0, // +}; + +static const uint16_t kPledgeLinuxStdio[] = { + __NR_linux_clock_getres, // + __NR_linux_clock_gettime, // + __NR_linux_close, // + __NR_linux_dup, // + __NR_linux_dup2, // + __NR_linux_dup3, // + __NR_linux_fchdir, // + /* __NR_linux_fcntl, // */ + __NR_linux_fstat, // + __NR_linux_fsync, // + __NR_linux_ftruncate, // + __NR_linux_getdents, // + __NR_linux_getegid, // + __NR_linux_getrandom, // + __NR_linux_geteuid, // + __NR_linux_getgid, // + __NR_linux_getgroups, // + __NR_linux_getitimer, // + __NR_linux_getpgid, // + __NR_linux_getpgrp, // + __NR_linux_getpid, // + __NR_linux_getppid, // + __NR_linux_getresgid, // + __NR_linux_getresuid, // + __NR_linux_getrlimit, // + __NR_linux_getsid, // + __NR_linux_gettimeofday, // + __NR_linux_getuid, // + __NR_linux_lseek, // + __NR_linux_madvise, // + __NR_linux_brk, // + __NR_linux_mmap, // + __NR_linux_mprotect, // + __NR_linux_munmap, // + __NR_linux_nanosleep, // + __NR_linux_pipe, // + __NR_linux_pipe2, // + __NR_linux_poll, // + __NR_linux_pread, // + __NR_linux_preadv, // + __NR_linux_pwrite, // + __NR_linux_pwritev, // + __NR_linux_read, // + __NR_linux_readv, // + __NR_linux_recvfrom, // + __NR_linux_recvmsg, // + __NR_linux_select, // + __NR_linux_sendmsg, // + __NR_linux_sendto, // + __NR_linux_setitimer, // + __NR_linux_shutdown, // + __NR_linux_sigaction, // + __NR_linux_sigprocmask, // + __NR_linux_sigreturn, // + __NR_linux_socketpair, // + __NR_linux_umask, // + __NR_linux_wait4, // + __NR_linux_write, // + __NR_linux_writev, // + 0, // +}; + +static const uint16_t kPledgeLinuxRpath[] = { + __NR_linux_chdir, // + __NR_linux_getcwd, // + __NR_linux_openat, // + __NR_linux_fstatat, // + __NR_linux_faccessat, // + __NR_linux_readlinkat, // + __NR_linux_lstat, // + __NR_linux_chmod, // + __NR_linux_fchmod, // + __NR_linux_fchmodat, // + __NR_linux_chown, // + __NR_linux_fchown, // + __NR_linux_fchownat, // + __NR_linux_fstat, // + 0, // +}; + +static const uint16_t kPledgeLinuxWpath[] = { + __NR_linux_getcwd, // + __NR_linux_openat, // + __NR_linux_fstatat, // + __NR_linux_faccessat, // + __NR_linux_readlinkat, // + __NR_linux_lstat, // + __NR_linux_chmod, // + __NR_linux_fchmod, // + __NR_linux_fchmodat, // + __NR_linux_chown, // + __NR_linux_fchown, // + __NR_linux_fchownat, // + __NR_linux_fstat, // + 0, // +}; + +static const uint16_t kPledgeLinuxCpath[] = { + __NR_linux_rename, // + __NR_linux_renameat, // + __NR_linux_link, // + __NR_linux_linkat, // + __NR_linux_symlink, // + __NR_linux_symlinkat, // + __NR_linux_unlink, // + __NR_linux_unlinkat, // + __NR_linux_mkdir, // + __NR_linux_mkdirat, // + __NR_linux_rmdir, // + 0, // +}; + +static const uint16_t kPledgeLinuxDpath[] = { + __NR_linux_mknod, // + 0, // +}; + +static const uint16_t kPledgeLinuxTmppath[] = { + __NR_linux_lstat, // + __NR_linux_chmod, // + __NR_linux_chown, // + __NR_linux_unlink, // + __NR_linux_fstat, // + 0, // +}; + +static const uint16_t kPledgeLinuxInet[] = { + __NR_linux_socket, // + __NR_linux_listen, // + __NR_linux_bind, // + __NR_linux_connect, // + __NR_linux_accept4, // + __NR_linux_accept, // + __NR_linux_getpeername, // + __NR_linux_getsockname, // + __NR_linux_setsockopt, // + __NR_linux_getsockopt, // + 0, // +}; + +static const uint16_t kPledgeLinuxFattr[] = { + __NR_linux_utimes, // + __NR_linux_utimensat, // + __NR_linux_chmod, // + __NR_linux_fchmod, // + __NR_linux_fchmodat, // + __NR_linux_chown, // + __NR_linux_fchownat, // + __NR_linux_lchown, // + __NR_linux_fchown, // + __NR_linux_utimes, // + 0, // +}; + +static const uint16_t kPledgeLinuxUnix[] = { + __NR_linux_socket, // + __NR_linux_listen, // + __NR_linux_bind, // + __NR_linux_connect, // + __NR_linux_accept4, // + __NR_linux_accept, // + __NR_linux_getpeername, // + __NR_linux_getsockname, // + __NR_linux_setsockopt, // + __NR_linux_getsockopt, // + 0, // +}; + +static const uint16_t kPledgeLinuxDns[] = { + __NR_linux_sendto, // + __NR_linux_recvfrom, // + __NR_linux_socket, // + __NR_linux_connect, // + 0, // +}; + +static const uint16_t kPledgeLinuxProc[] = { + __NR_linux_fork, // + __NR_linux_vfork, // + __NR_linux_kill, // + __NR_linux_getpriority, // + __NR_linux_setpriority, // + __NR_linux_setrlimit, // + __NR_linux_setpgid, // + __NR_linux_setsid, // + 0, // +}; + +static const uint16_t kPledgeLinuxExec[] = { + __NR_linux_execve, // + 0, // +}; + +static const uint16_t kPledgeLinuxId[] = { + __NR_linux_setuid, // + __NR_linux_setreuid, // + __NR_linux_setresuid, // + __NR_linux_setgid, // + __NR_linux_setregid, // + __NR_linux_setresgid, // + __NR_linux_setgroups, // + __NR_linux_setrlimit, // + __NR_linux_getpriority, // + __NR_linux_setpriority, // + 0, // +}; + +static const struct Pledges { + const char *name; + const uint16_t *syscalls; +} kPledgeLinux[] = { + {"default", kPledgeLinuxDefault}, // + {"stdio", kPledgeLinuxStdio}, // + {"rpath", kPledgeLinuxRpath}, // + {"wpath", kPledgeLinuxWpath}, // + {"cpath", kPledgeLinuxCpath}, // + {"dpath", kPledgeLinuxDpath}, // + {"tmppath", kPledgeLinuxTmppath}, // + {"inet", kPledgeLinuxInet}, // + {"fattr", kPledgeLinuxFattr}, // + {"unix", kPledgeLinuxUnix}, // + {"dns", kPledgeLinuxDns}, // + {"proc", kPledgeLinuxProc}, // + {"exec", kPledgeLinuxExec}, // + {"id", kPledgeLinuxId}, // + {0}, // +}; + +static const struct sock_filter kFilterStart[] = { + _SECCOMP_MACHINE(AUDIT_ARCH_X86_64), // + _SECCOMP_LOAD_SYSCALL_NR(), // +}; + +static const struct sock_filter kFilterEnd[] = { + _SECCOMP_LOG_AND_RETURN_ERRNO(1), // EPERM +}; + +struct Filter { + size_t n; + struct sock_filter *p; +}; + +static bool AppendFilter(struct Filter *f, struct sock_filter *p, size_t n) { + size_t m; + struct sock_filter *q; + m = f->n + n; + if (!(q = realloc(f->p, m * sizeof(*f->p)))) return false; + memcpy(q + f->n, p, n * sizeof(*q)); + f->p = q; + f->n = m; + return true; +} + +static bool AppendPledge(struct Filter *f, const uint16_t *p) { + int i; + for (i = 0; p[i]; ++i) { + struct sock_filter fragment[] = {_SECCOMP_ALLOW_SYSCALL(p[i])}; + if (!AppendFilter(f, fragment, ARRAYLEN(fragment))) { + return false; + } + } + return true; +} + +static const uint16_t *FindPledge(const struct Pledges *p, const char *name) { + int i; + for (i = 0; p[i].name; ++i) { + if (!strcasecmp(name, p[i].name)) { + return p[i].syscalls; + } + } + return 0; +} + +static int sys_pledge_linux(const char *promises, const char *execpromises) { + bool ok; + int rc = -1; + struct Filter f = {0}; + const uint16_t *pledge; + char *s, *tok, *state, *start; + if (execpromises) return einval(); + if ((start = s = strdup(promises)) && + AppendFilter(&f, kFilterStart, ARRAYLEN(kFilterStart)) && + AppendPledge(&f, kPledgeLinuxDefault)) { + for (ok = true; (tok = strtok_r(start, " \t\r\n", &state)); start = 0) { + if (!(pledge = FindPledge(kPledgeLinux, tok)) || + !AppendPledge(&f, pledge)) { + ok = false; + } + } + if (ok && AppendFilter(&f, kFilterEnd, ARRAYLEN(kFilterEnd)) && + (rc = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) != -1) { + struct sock_fprog sandbox = {.len = f.n, .filter = f.p}; + rc = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &sandbox); + } + } + free(f.p); + free(s); + return rc; +} + +/** + * Restricts system operations. + * + * Only available on OpenBSD and Linux. + * + * By default exit and exit_group are always allowed. This is useful + * for processes that perform pure computation and interface with the + * parent via shared memory. + * + * `promises` is a string that may include any of the following groups + * delimited by spaces. + * + * - "stdio" allows clock_getres, clock_gettime, close, dup, dup2, dup3, + * fchdir, fstat, fsync, ftruncate, getdents, getegid, getrandom, + * geteuid, getgid, getgroups, getitimer, getpgid, getpgrp, getpid, + * getppid, getresgid, getresuid, getrlimit, getsid, gettimeofday, + * getuid, lseek, madvise, brk, mmap, mprotect, munmap, nanosleep, + * pipe, pipe2, poll, pread, preadv, pwrite, pwritev, read, readv, + * recvfrom, recvmsg, select, sendmsg, sendto, setitimer, shutdown, + * sigaction, sigprocmask, sigreturn, socketpair, umask, wait4, write, + * writev. + * + * - "rpath" allows chdir, getcwd, openat, fstatat, faccessat, + * readlinkat, lstat, chmod, fchmod, fchmodat, chown, fchown, + * fchownat, fstat. + * + * - "wpath" allows getcwd, openat, fstatat, faccessat, readlinkat, + * lstat, chmod, fchmod, fchmodat, chown, fchown, fchownat, fstat. + * + * - "cpath" allows rename, renameat, link, linkat, symlink, symlinkat, + * unlink, unlinkat, mkdir, mkdirat, rmdir. + * + * - "dpath" allows mknod + * + * - "tmppath" allows lstat, chmod, chown, unlink, fstat. + * + * - "inet" allows socket, listen, bind, connect, accept4, accept, + * getpeername, getsockname, setsockopt, getsockopt. + * + * - "fattr" allows utimes, utimensat, chmod, fchmod, fchmodat, chown, + * fchownat, lchown, fchown, utimes. + * + * - "unix" allows socket, listen, bind, connect, accept4, accept, + * getpeername, getsockname, setsockopt, getsockopt. + * + * - "dns" allows sendto, recvfrom, socket, connect. + * + * - "proc" allows fork, vfork, kill, getpriority, setpriority, + * setrlimit, setpgid, setsid. + * + * - "exec" allows execve. + * + * - "id" allows setuid, setreuid, setresuid, setgid, setregid, + * setresgid, setgroups, setrlimit, getpriority, setpriority. + * + */ +int pledge(const char *promises, const char *execpromises) { + int rc; + if (IsLinux()) { + rc = sys_pledge_linux(promises, execpromises); + } else { + rc = sys_pledge(promises, execpromises); + } + STRACE("pledge(%#s, %#s) → %d% m", promises, execpromises, rc); + return rc; +} diff --git a/libc/runtime/findcombinary.c b/libc/runtime/findcombinary.c index ecf7dfc5e..f780ade4d 100644 --- a/libc/runtime/findcombinary.c +++ b/libc/runtime/findcombinary.c @@ -24,7 +24,7 @@ struct FindComBinary { bool once; const char *res; - char buf[PATH_MAX + 1]; + char buf[PATH_MAX]; }; static struct FindComBinary g_findcombinary; diff --git a/libc/runtime/finddebugbinary.c b/libc/runtime/finddebugbinary.c index 4230c33bd..aee215169 100644 --- a/libc/runtime/finddebugbinary.c +++ b/libc/runtime/finddebugbinary.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/bits.h" #include "libc/calls/calls.h" +#include "libc/macros.internal.h" #include "libc/runtime/runtime.h" #include "libc/runtime/symbols.internal.h" #include "libc/str/str.h" @@ -30,7 +31,7 @@ const char *FindDebugBinary(void) { static bool once; static char *res; - static char buf[PATH_MAX + 1]; + static char buf[PATH_MAX]; char *p; size_t n; if (!once) { @@ -40,12 +41,12 @@ const char *FindDebugBinary(void) { if (n > 4 && READ32LE(p + n - 4) == READ32LE(".dbg")) { res = p; } else if (n > 4 && READ32LE(p + n - 4) == READ32LE(".com") && - n + 4 <= PATH_MAX) { + n + 4 < ARRAYLEN(buf)) { mempcpy(mempcpy(buf, p, n), ".dbg", 5); if (fileexists(buf)) { res = buf; } - } else if (n + 8 <= PATH_MAX) { + } else if (n + 8 < ARRAYLEN(buf)) { mempcpy(mempcpy(buf, p, n), ".com.dbg", 9); if (fileexists(buf)) { res = buf; diff --git a/libc/runtime/fork-nt.c b/libc/runtime/fork-nt.c index 093436195..4fd7fc4c5 100644 --- a/libc/runtime/fork-nt.c +++ b/libc/runtime/fork-nt.c @@ -212,9 +212,18 @@ textwindows void WinMainForked(void) { // since the handles closed on fork if (weaken(ForkNtStdinWorker)) weaken(ForkNtStdinWorker)(); struct Fds *fds = VEIL("r", &g_fds); - fds->__init_p[0].handle = GetStdHandle(kNtStdInputHandle); - fds->__init_p[1].handle = GetStdHandle(kNtStdOutputHandle); - fds->__init_p[2].handle = GetStdHandle(kNtStdErrorHandle); + fds->p[0].handle = fds->__init_p[0].handle = GetStdHandle(kNtStdInputHandle); + fds->p[1].handle = fds->__init_p[1].handle = GetStdHandle(kNtStdOutputHandle); + fds->p[2].handle = fds->__init_p[2].handle = GetStdHandle(kNtStdErrorHandle); + + // untrack the forked children of the parent since we marked the + // CreateProcess() process handle below as non-inheritable + for (i = 0; i < fds->n; ++i) { + if (fds->p[i].kind == kFdProcess) { + fds->p[i].kind = 0; + fds->f = MIN(i, fds->f); + } + } // restore the crash reporting stuff if (weaken(__wincrash_nt)) { @@ -269,9 +278,8 @@ textwindows int sys_fork_nt(void) { args = args2; } #endif - if (ntspawn(GetProgramExecutableName(), args, environ, forkvar, - &kNtIsInheritable, NULL, true, 0, NULL, &startinfo, - &procinfo) != -1) { + if (ntspawn(GetProgramExecutableName(), args, environ, forkvar, 0, 0, + true, 0, 0, &startinfo, &procinfo) != -1) { CloseHandle(procinfo.hThread); ok = WriteAll(writer, jb, sizeof(jb)) && WriteAll(writer, &_mmi.i, sizeof(_mmi.i)) && diff --git a/libc/runtime/getargmax.c b/libc/runtime/getargmax.c new file mode 100644 index 000000000..25aabc1ae --- /dev/null +++ b/libc/runtime/getargmax.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 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/dce.h" +#include "libc/runtime/runtime.h" + +/** + * Returns `ARG_MAX` for host platform. + */ +int __arg_max(void) { + if (IsWindows()) return 32767; + if (IsLinux()) return 128 * 1024; + if (IsNetbsd()) return 256 * 1024; + if (IsFreebsd()) return 512 * 1024; + if (IsOpenbsd()) return 512 * 1024; + if (IsXnu()) return 1024 * 1024; + return ARG_MAX; +} diff --git a/libc/runtime/mprotect-nt.greg.c b/libc/runtime/mprotect-nt.greg.c index 6c4c58673..5172e6e55 100644 --- a/libc/runtime/mprotect-nt.greg.c +++ b/libc/runtime/mprotect-nt.greg.c @@ -24,7 +24,7 @@ #define ADDR(x) ((char *)((int64_t)((uint64_t)(x) << 32) >> 16)) -privileged int sys_mprotect_nt(void *addr, size_t size, int prot) { +textwindows int sys_mprotect_nt(void *addr, size_t size, int prot) { int rc = 0; unsigned i; uint32_t op; diff --git a/libc/runtime/paginate.c b/libc/runtime/paginate.c index 51002f2f5..c37a00e0b 100644 --- a/libc/runtime/paginate.c +++ b/libc/runtime/paginate.c @@ -30,8 +30,8 @@ void __paginate(int fd, const char *s) { int tfd, pid; char *args[3] = {0}; - char tmppath[PATH_MAX + 1]; - char progpath[PATH_MAX + 1]; + char tmppath[PATH_MAX]; + char progpath[PATH_MAX]; if (strcmp(nulltoempty(getenv("TERM")), "dumb") && isatty(0) && isatty(1) && ((args[0] = commandv("less", progpath, sizeof(progpath))) || (args[0] = commandv("more", progpath, sizeof(progpath))))) { diff --git a/libc/runtime/printargs.greg.c b/libc/runtime/printargs.greg.c index d5a83c32e..8f18b5f02 100644 --- a/libc/runtime/printargs.greg.c +++ b/libc/runtime/printargs.greg.c @@ -140,10 +140,13 @@ textstartup void __printargs(const char *prologue) { uintptr_t *auxp; struct utsname uts; struct termios termios; - char path[PATH_MAX + 1]; int e, x, st, ft, flags; - struct pollfd pfds[128]; struct AuxiliaryValue *auxinfo; + union { + char path[PATH_MAX]; + struct pollfd pfds[128]; + } u; + st = __strace, __strace = 0; ft = g_ftrace, g_ftrace = 0; e = errno; @@ -240,15 +243,15 @@ textstartup void __printargs(const char *prologue) { PRINT(""); PRINT("FILE DESCRIPTORS"); - for (i = 0; i < ARRAYLEN(pfds); ++i) { - pfds[i].fd = i; - pfds[i].events = POLLIN; + for (i = 0; i < ARRAYLEN(u.pfds); ++i) { + u.pfds[i].fd = i; + u.pfds[i].events = POLLIN; } - if ((n = poll(pfds, ARRAYLEN(pfds), 0)) != -1) { - for (i = 0; i < ARRAYLEN(pfds); ++i) { - if (i && (pfds[i].revents & POLLNVAL)) continue; + if ((n = poll(u.pfds, ARRAYLEN(u.pfds), 0)) != -1) { + for (i = 0; i < ARRAYLEN(u.pfds); ++i) { + if (i && (u.pfds[i].revents & POLLNVAL)) continue; PRINT(" ☼ %d (revents=%#hx fcntl(F_GETFL)=%#x isatty()=%hhhd)", i, - pfds[i].revents, fcntl(i, F_GETFL), isatty(i)); + u.pfds[i].revents, fcntl(i, F_GETFL), isatty(i)); } } else { PRINT(" poll() returned %d %m", n); @@ -298,8 +301,8 @@ textstartup void __printargs(const char *prologue) { if (*__auxv) { for (auxp = __auxv; *auxp; auxp += 2) { if ((auxinfo = DescribeAuxv(auxp[0]))) { - ksnprintf(path, sizeof(path), auxinfo->fmt, auxp[1]); - PRINT(" ☼ %16s[%4ld] = %s", auxinfo->name, auxp[0], path); + ksnprintf(u.path, sizeof(u.path), auxinfo->fmt, auxp[1]); + PRINT(" ☼ %16s[%4ld] = %s", auxinfo->name, auxp[0], u.path); } else { PRINT(" ☼ %16s[%4ld] = %014p", "unknown", auxp[0], auxp[1]); } @@ -326,7 +329,7 @@ textstartup void __printargs(const char *prologue) { PRINT(" ☼ %s = %#s", "kNtWindowsDirectory", kNtWindowsDirectory); PRINT(" ☼ %s = %#s", "GetProgramExecutableName", GetProgramExecutableName()); PRINT(" ☼ %s = %#s", "GetInterpreterExecutableName", - GetInterpreterExecutableName(path, sizeof(path))); + GetInterpreterExecutableName(u.path, sizeof(u.path))); PRINT(" ☼ %s = %p", "RSP", __builtin_frame_address(0)); PRINT(" ☼ %s = %p", "GetStackAddr()", GetStackAddr(0)); PRINT(" ☼ %s = %p", "GetStaticStackAddr(0)", GetStaticStackAddr(0)); diff --git a/libc/runtime/runtime.h b/libc/runtime/runtime.h index 82142b687..989c6b2de 100644 --- a/libc/runtime/runtime.h +++ b/libc/runtime/runtime.h @@ -104,6 +104,7 @@ char *GetProgramExecutableName(void); char *GetInterpreterExecutableName(char *, size_t); void __printargs(const char *); void __paginate(int, const char *); +int __arg_max(void); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/runtime/sysconf.c b/libc/runtime/sysconf.c index cec42e6ad..6c685d528 100644 --- a/libc/runtime/sysconf.c +++ b/libc/runtime/sysconf.c @@ -22,6 +22,7 @@ #include "libc/macros.internal.h" #include "libc/runtime/clktck.h" #include "libc/runtime/sysconf.h" +#include "libc/sysv/consts/limits.h" #include "libc/sysv/consts/rlim.h" #include "libc/sysv/consts/rlimit.h" @@ -42,7 +43,7 @@ long sysconf(int name) { int n; switch (name) { case _SC_ARG_MAX: - return ARG_MAX; + return _ARG_MAX; case _SC_CHILD_MAX: return GetResourceLimit(RLIMIT_NPROC); case _SC_CLK_TCK: diff --git a/libc/runtime/winmain.greg.c b/libc/runtime/winmain.greg.c index ea72891ae..21a40d890 100644 --- a/libc/runtime/winmain.greg.c +++ b/libc/runtime/winmain.greg.c @@ -84,8 +84,8 @@ struct WinArgs { char *argv[4096]; char *envp[4092]; intptr_t auxv[2][2]; - char argblock[ARG_MAX]; - char envblock[ARG_MAX]; + char argblock[ARG_MAX / 2]; + char envblock[ARG_MAX / 2]; }; extern uint32_t __winmainpid; diff --git a/libc/stdio/spawn.c b/libc/stdio/spawn.c index 13ae5af10..de6cfa921 100644 --- a/libc/stdio/spawn.c +++ b/libc/stdio/spawn.c @@ -40,7 +40,7 @@ int posix_spawn(int *pid, const char *path, unsigned mode; sigset_t allsigs; struct sigaction dfl; - char *p, *q, opath[PATH_MAX + 1]; + char *p, *q, opath[PATH_MAX]; int s, fd, newfd, oflag, tempfd; if (!(*pid = vfork())) { if (attrp) { @@ -77,7 +77,7 @@ int posix_spawn(int *pid, const char *path, if (sscanf(p + 5, "%d,", &fd) != 1) _Exit(127); p = strchr(p, ',') + 1; q = strchr(p, '*'); - if (!q || q - p > PATH_MAX) _Exit(127); + if (!q || q - p >= PATH_MAX) _Exit(127); strncpy(opath, p, q - p); opath[q - p] = '\0'; if (sscanf(q + 1, "%o,%o)", &oflag, &mode) != 2) _Exit(127); diff --git a/libc/stdio/spawnp.c b/libc/stdio/spawnp.c index d6b62614e..67b9d0d67 100644 --- a/libc/stdio/spawnp.c +++ b/libc/stdio/spawnp.c @@ -31,7 +31,7 @@ int posix_spawnp(int *pid, const char *path, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[]) { - char pathbuf[PATH_MAX + 1]; + char pathbuf[PATH_MAX]; if (!(path = commandv(path, pathbuf, sizeof(pathbuf)))) return errno; return posix_spawn(pid, path, file_actions, attrp, argv, envp); } diff --git a/libc/stdio/systemexec.c b/libc/stdio/systemexec.c index 70134991a..5bda2ef06 100644 --- a/libc/stdio/systemexec.c +++ b/libc/stdio/systemexec.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/dce.h" +#include "libc/macros.internal.h" #include "libc/paths.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" @@ -29,14 +30,16 @@ */ int systemexec(const char *cmdline) { size_t n, m; - char *a, *b, *argv[4], comspec[PATH_MAX + 1]; + char *a, *b, *argv[4], comspec[PATH_MAX]; if (!IsWindows()) { argv[0] = _PATH_BSHELL; argv[1] = "-c"; } else { b = "cmd.exe"; a = kNtSystemDirectory; - if ((n = strlen(a)) + (m = strlen(b)) > PATH_MAX) return enametoolong(); + if ((n = strlen(a)) + (m = strlen(b)) >= ARRAYLEN(comspec)) { + return enametoolong(); + } memcpy(mempcpy(comspec, a, n), b, m + 1); argv[0] = comspec; argv[1] = "/C"; diff --git a/libc/stdio/tmpfile.c b/libc/stdio/tmpfile.c index d4c886338..8ec1a985a 100644 --- a/libc/stdio/tmpfile.c +++ b/libc/stdio/tmpfile.c @@ -32,10 +32,10 @@ */ FILE *tmpfile(void) { int fd; - char *tmp, *sep, tpl[PATH_MAX + 1]; + char *tmp, *sep, tpl[PATH_MAX]; tmp = firstnonnull(getenv("TMPDIR"), kTmpPath); sep = !isempty(tmp) && !endswith(tmp, "/") ? "/" : ""; - if ((snprintf)(tpl, PATH_MAX + 1, "%s%stmp.%s.XXXXXX", tmp, sep, + if ((snprintf)(tpl, PATH_MAX, "%s%stmp.%s.XXXXXX", tmp, sep, program_invocation_short_name) <= PATH_MAX) { if ((fd = mkostemps(tpl, 0, 0)) != -1) { return fdopen(fd, "w+"); diff --git a/libc/sysv/calls/getresuid.s b/libc/sysv/calls/getresuid.s deleted file mode 100644 index e50c6a170..000000000 --- a/libc/sysv/calls/getresuid.s +++ /dev/null @@ -1,2 +0,0 @@ -.include "o/libc/sysv/macros.internal.inc" -.scall getresuid,0xfff119168ffff076,globl diff --git a/libc/sysv/calls/pledge.s b/libc/sysv/calls/pledge.s deleted file mode 100644 index 8015bf37e..000000000 --- a/libc/sysv/calls/pledge.s +++ /dev/null @@ -1,2 +0,0 @@ -.include "o/libc/sysv/macros.internal.inc" -.scall pledge,0xfff06cffffffffff,globl diff --git a/libc/sysv/calls/sys_getresgid.s b/libc/sysv/calls/sys_getresgid.s new file mode 100644 index 000000000..f70e7c99d --- /dev/null +++ b/libc/sysv/calls/sys_getresgid.s @@ -0,0 +1,2 @@ +.include "o/libc/sysv/macros.internal.inc" +.scall sys_getresgid,0xfff11b169ffff078,globl diff --git a/libc/sysv/calls/sys_getresuid.s b/libc/sysv/calls/sys_getresuid.s new file mode 100644 index 000000000..4d61c6a0c --- /dev/null +++ b/libc/sysv/calls/sys_getresuid.s @@ -0,0 +1,2 @@ +.include "o/libc/sysv/macros.internal.inc" +.scall sys_getresuid,0xfff119168ffff076,globl diff --git a/libc/sysv/calls/getresgid.s b/libc/sysv/calls/sys_pledge.s similarity index 50% rename from libc/sysv/calls/getresgid.s rename to libc/sysv/calls/sys_pledge.s index 9ce4dee92..18fc40da1 100644 --- a/libc/sysv/calls/getresgid.s +++ b/libc/sysv/calls/sys_pledge.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall getresgid,0xfff11b169ffff078,globl +.scall sys_pledge,0xfff06cffffffffff,globl diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index 7cd16c0b2..b52068ef5 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -1229,7 +1229,11 @@ syscon mount MNT_SNAPSHOT 0 0x40000000 0x01000000 0 0 0 # confusing # limits # # group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary -syscon misc PIPE_BUF 4096 512 512 512 512 4096 # bsd consensus +syscon limits PIPE_BUF 4096 512 512 512 512 4096 # bsd consensus +syscon limits _ARG_MAX 128*1024 1024*1024 512*1024 512*1024 256*1024 32767*2 # bsd consensus +syscon limits _NAME_MAX 255 255 255 255 511 255 # probably higher on windows? +syscon limits _PATH_MAX 4096 1024 1024 1024 1024 512 # cosmopolitan libc imposes a lower 512 limit; nt theoretically goes up to 32767 +syscon limits _NSIG 64 32 128 32 64 32 # _SIG_MAXSIG on FreeBSD # unmount() flags # a.k.a. umount2() on linux @@ -1299,6 +1303,14 @@ syscon prio PRIO_MIN -20 -20 -20 -20 -20 -20 # unix consensus / p syscon prio PRIO_MAX 20 20 20 20 20 20 # unix consensus / poly nt syscon prio NZERO 20 20 20 20 20 20 # unix consensus / polyfilled nt +# getrusage() who +# +# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary +syscon rusage RUSAGE_SELF 0 0 0 0 0 0 # unix consensus & faked nt +syscon rusage RUSAGE_THREAD 1 99 1 1 1 1 # faked nt & unavailable on xnu +syscon rusage RUSAGE_CHILDREN -1 -1 -1 -1 -1 99 # unix consensus & unavailable on nt +syscon rusage RUSAGE_BOTH -2 99 99 99 99 99 # woop + # Teletypewriter Control, e.g. # # TCSETS → About 70,800 results (0.31 seconds) @@ -1733,11 +1745,6 @@ syscon misc NL_TEXTMAX 0x7fffffff 0x0800 0x0800 255 255 0 syscon misc NL_NMAX 0x7fffffff 1 1 0 0 0 syscon misc NL_SETD 1 1 0 1 1 0 -syscon rusage RUSAGE_SELF 0 0 0 0 0 0 # unix consensus & faked nt -syscon rusage RUSAGE_THREAD 1 99 1 1 1 1 # faked nt & unavailable on xnu -syscon rusage RUSAGE_CHILDREN -1 -1 -1 -1 -1 99 # unix consensus & unavailable on nt -syscon rusage RUSAGE_BOTH -2 99 99 99 99 99 # woop - syscon misc FSETLOCKING_QUERY 0 0 0 0 0 0 # consensus syscon misc FSETLOCKING_BYCALLER 2 0 0 0 0 0 syscon misc FSETLOCKING_INTERNAL 1 0 0 0 0 0 @@ -3184,84 +3191,6 @@ syscon in IN_OPEN 0x20 0 0 0 0 0 syscon in IN_Q_OVERFLOW 0x4000 0 0 0 0 0 syscon in IN_UNMOUNT 0x2000 0 0 0 0 0 -syscon posix _POSIX_ARG_MAX 0x1000 0x1000 0x1000 0x1000 0x1000 0 # unix consensus -syscon posix _POSIX_CHILD_MAX 25 25 25 25 25 0 # unix consensus -syscon posix _POSIX_HOST_NAME_MAX 255 255 255 255 255 0 # unix consensus -syscon posix _POSIX_LINK_MAX 8 8 8 8 8 0 # unix consensus -syscon posix _POSIX_LOGIN_NAME_MAX 9 9 9 9 9 0 # unix consensus -syscon posix _POSIX_MAX_CANON 255 255 255 255 255 0 # unix consensus -syscon posix _POSIX_MAX_INPUT 255 255 255 255 255 0 # unix consensus -syscon posix _POSIX_NAME_MAX 14 14 14 14 14 14 # forced consensus -syscon posix _POSIX_NGROUPS_MAX 8 8 8 8 8 0 # unix consensus -syscon posix _POSIX_OPEN_MAX 20 20 20 20 20 20 # forced consensus -syscon posix _POSIX_PATH_MAX 255 255 255 255 255 255 # forced consensus -syscon posix _POSIX_PIPE_BUF 0x0200 0x0200 0x0200 0x0200 0x0200 0 # unix consensus -syscon posix _POSIX_RE_DUP_MAX 255 255 255 255 255 0 # unix consensus -syscon posix _POSIX_SEM_NSEMS_MAX 0x0100 0x0100 0x0100 0x0100 0x0100 0 # unix consensus -syscon posix _POSIX_SEM_VALUE_MAX 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0 # unix consensus -syscon posix _POSIX_SSIZE_MAX 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0 # unix consensus -syscon posix _POSIX_STREAM_MAX 8 8 8 8 8 0 # unix consensus -syscon posix _POSIX_SYMLINK_MAX 255 255 255 255 255 0 # unix consensus -syscon posix _POSIX_SYMLOOP_MAX 8 8 8 8 8 0 # unix consensus -syscon posix _POSIX_THREAD_DESTRUCTOR_ITERATIONS 4 4 4 4 4 0 # unix consensus -syscon posix _POSIX_THREAD_KEYS_MAX 0x80 0x80 0x80 0x80 0x80 0 # unix consensus -syscon posix _POSIX_TTY_NAME_MAX 9 9 9 9 9 0 # unix consensus -syscon posix _POSIX_TZNAME_MAX 6 6 6 6 6 0 # unix consensus -syscon posix _POSIX_CLOCK_SELECTION 0x031069 -1 -1 -1 -1 0 # bsd consensus -syscon posix _POSIX_FSYNC 0x031069 0x030db0 0x030db0 0x030db0 0x030db0 0 # bsd consensus -syscon posix _POSIX_MAPPED_FILES 0x031069 0x030db0 0x030db0 0x030db0 0x030db0 0 # bsd consensus -syscon posix _POSIX_MEMORY_PROTECTION 0x031069 0x030db0 0x030db0 0x030db0 0x030db0 0 # bsd consensus -syscon posix _POSIX_READER_WRITER_LOCKS 0x031069 0x030db0 0x030db0 0x030db0 0x030db0 0 # bsd consensus -syscon posix _POSIX_THREADS 0x031069 0x030db0 0x030db0 0x030db0 0x030db0 0 # bsd consensus -syscon posix _POSIX_THREAD_ATTR_STACKADDR 0x031069 0x030db0 0x030db0 0x030db0 0x030db0 0 # bsd consensus -syscon posix _POSIX_THREAD_ATTR_STACKSIZE 0x031069 0x030db0 0x030db0 0x030db0 0x030db0 0 # bsd consensus -syscon posix _POSIX_ADVISORY_INFO 0x031069 -1 0x030db0 -1 -1 0 -syscon posix _POSIX_ASYNCHRONOUS_IO 0x031069 -1 0x030db0 -1 -1 0 -syscon posix _POSIX_BARRIERS 0x031069 -1 0x030db0 0x030db0 0x030db0 0 -syscon posix _POSIX_JOB_CONTROL 1 0x030db0 1 1 1 0 -syscon posix _POSIX_MEMLOCK 0x031069 -1 -1 0x030db0 0x030db0 0 -syscon posix _POSIX_MEMLOCK_RANGE 0x031069 -1 0x030db0 0x030db0 0x030db0 0 -syscon posix _POSIX_MESSAGE_PASSING 0x031069 -1 0x030db0 -1 -1 0 -syscon posix _POSIX_NO_TRUNC 1 0x030db0 1 1 1 0 -syscon posix _POSIX_RAW_SOCKETS 0x031069 -1 0x030db0 0x030db0 0x030db0 0 -syscon posix _POSIX_REALTIME_SIGNALS 0x031069 -1 0x030db0 -1 -1 0 -syscon posix _POSIX_REGEXP 1 0x030db0 1 1 1 0 -syscon posix _POSIX_SEMAPHORES 0x031069 -1 0x030db0 0x030db0 0x030db0 0 -syscon posix _POSIX_SHARED_MEMORY_OBJECTS 0x031069 -1 0x030db0 0x031069 0x031069 0 -syscon posix _POSIX_SHELL 1 0x030db0 1 1 1 0 -syscon posix _POSIX_SPAWN 0x031069 -1 0x030db0 0x030db0 0x030db0 0 -syscon posix _POSIX_SPIN_LOCKS 0x031069 -1 0x030db0 0x030db0 0x030db0 0 -syscon posix _POSIX_THREAD_PRIORITY_SCHEDULING 0x031069 -1 0x030db0 -1 -1 0 -syscon posix _POSIX_THREAD_PROCESS_SHARED 0x031069 0x030db0 0x030db0 -1 -1 0 -syscon posix _POSIX_THREAD_SAFE_FUNCTIONS 0x031069 0x030db0 -1 0x030db0 0x030db0 0 -syscon posix _POSIX_THREAD_THREADS_MAX 0x40 0x40 0x40 4 4 0 -syscon posix _POSIX_TIMEOUTS 0x031069 -1 0x030db0 0x030db0 0x030db0 0 -syscon posix _POSIX_TIMERS 0x031069 -1 0x030db0 -1 -1 0 -syscon posix _POSIX_VERSION 0x031069 0x030db0 0x030db0 0x031069 0x031069 0 -syscon posix _POSIX_VDISABLE 0 255 255 255 255 0 # bsd consensus -syscon posix _POSIX_AIO_LISTIO_MAX 2 2 2 0 0 0 -syscon posix _POSIX_AIO_MAX 1 1 1 0 0 0 -syscon posix _POSIX_CHOWN_RESTRICTED 0 0x030db0 1 1 1 0 -syscon posix _POSIX_CLOCKRES_MIN 0x01312d00 0 0x01312d00 0x01312d00 0x01312d00 0 -syscon posix _POSIX_CPUTIME 0 -1 0x030db0 0x031069 0x031069 0 -syscon posix _POSIX_DELAYTIMER_MAX 0x20 0x20 0x20 0 0 0 -syscon posix _POSIX_MONOTONIC_CLOCK 0 -1 0x030db0 0x030db0 0x030db0 0 -syscon posix _POSIX_MQ_OPEN_MAX 8 8 8 0 0 0 -syscon posix _POSIX_MQ_PRIO_MAX 0x20 0x20 0x20 0 0 0 -syscon posix _POSIX_RTSIG_MAX 8 8 8 0 0 0 -syscon posix _POSIX_SAVED_IDS 1 0x030db0 0 1 1 0 -syscon posix _POSIX_SIGQUEUE_MAX 0x20 0x20 0x20 0 0 0 -syscon posix _POSIX_THREAD_CPUTIME 0 -1 0x030db0 0x031069 0x031069 0 -syscon posix _POSIX_TIMER_MAX 0x20 0x20 0x20 0 0 0 -syscon posix _POSIX_IPV6 0x031069 0x030db0 0 0 0 0 -syscon posix _POSIX_SS_REPL_MAX 0 4 4 0 0 0 -syscon posix _POSIX_TRACE_EVENT_NAME_MAX 0 30 30 0 0 0 -syscon posix _POSIX_TRACE_NAME_MAX 0 8 8 0 0 0 -syscon posix _POSIX_TRACE_SYS_MAX 0 8 8 0 0 0 -syscon posix _POSIX_TRACE_USER_EVENT_MAX 0 0x20 0x20 0 0 0 -syscon posix _POSIX_V6_LP64_OFF64 1 1 0 0 0 0 -syscon posix _POSIX_V7_LP64_OFF64 1 1 0 0 0 0 - syscon misc TYPE_DISK 0 0 0 0 0 0 # consensus syscon misc TYPE_A 1 1 1 1 1 0 # unix consensus syscon misc TYPE_E 2 2 2 2 2 0 # unix consensus @@ -3277,18 +3206,6 @@ syscon misc TYPE_SCANNER 6 0 0 0 0 0 syscon misc TYPE_TAPE 1 0 0 0 0 0 syscon misc TYPE_WORM 4 0 0 0 0 0 -syscon misc _POSIX2_BC_BASE_MAX 99 99 99 99 99 0 # unix consensus -syscon misc _POSIX2_BC_DIM_MAX 0x0800 0x0800 0x0800 0x0800 0x0800 0 # unix consensus -syscon misc _POSIX2_BC_SCALE_MAX 99 99 99 99 99 0 # unix consensus -syscon misc _POSIX2_BC_STRING_MAX 0x03e8 0x03e8 0x03e8 0x03e8 0x03e8 0 # unix consensus -syscon misc _POSIX2_CHARCLASS_NAME_MAX 14 14 14 14 14 0 # unix consensus -syscon misc _POSIX2_COLL_WEIGHTS_MAX 2 2 2 2 2 0 # unix consensus -syscon misc _POSIX2_EXPR_NEST_MAX 0x20 0x20 0x20 0x20 0x20 0 # unix consensus -syscon misc _POSIX2_LINE_MAX 0x0800 0x0800 0x0800 0x0800 0x0800 0 # unix consensus -syscon misc _POSIX2_RE_DUP_MAX 255 255 255 255 255 0 # unix consensus -syscon misc _POSIX2_C_BIND 0x031069 0x030db0 0x030db0 0x030db0 0x030db0 0 # bsd consensus -syscon misc _POSIX2_VERSION 0x031069 0x030db0 0x030a2c 0x031069 0x031069 0 - syscon nd ND_RA_FLAG_MANAGED 0x80 0x80 0x80 0x80 0x80 0x80 # consensus syscon nd ND_RA_FLAG_OTHER 0x40 0x40 0x40 0x40 0x40 0x40 # consensus syscon nd ND_NA_FLAG_OVERRIDE 0x20 0x20 0x20 0x20 0x20 0x20000000 # unix consensus diff --git a/libc/sysv/consts/EXIT_FAILURE.S b/libc/sysv/consts/EXIT_FAILURE.S deleted file mode 100644 index 21df576a8..000000000 --- a/libc/sysv/consts/EXIT_FAILURE.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon exit,EXIT_FAILURE,1,1,1,1,1,1 diff --git a/libc/sysv/consts/PIPE_BUF.S b/libc/sysv/consts/PIPE_BUF.S index 2d0310f41..34d0d247e 100644 --- a/libc/sysv/consts/PIPE_BUF.S +++ b/libc/sysv/consts/PIPE_BUF.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon misc,PIPE_BUF,4096,512,512,512,512,4096 +.syscon limits,PIPE_BUF,4096,512,512,512,512,4096 diff --git a/libc/sysv/consts/_ARG_MAX.S b/libc/sysv/consts/_ARG_MAX.S new file mode 100644 index 000000000..1071d0042 --- /dev/null +++ b/libc/sysv/consts/_ARG_MAX.S @@ -0,0 +1,2 @@ +#include "libc/sysv/consts/syscon.internal.h" +.syscon limits,_ARG_MAX,128*1024,1024*1024,512*1024,512*1024,256*1024,32767*2 diff --git a/libc/sysv/consts/_NAME_MAX.S b/libc/sysv/consts/_NAME_MAX.S new file mode 100644 index 000000000..42d20d0bb --- /dev/null +++ b/libc/sysv/consts/_NAME_MAX.S @@ -0,0 +1,2 @@ +#include "libc/sysv/consts/syscon.internal.h" +.syscon limits,_NAME_MAX,255,255,255,255,511,255 diff --git a/libc/sysv/consts/EXIT_SUCCESS.S b/libc/sysv/consts/_NSIG.S similarity index 53% rename from libc/sysv/consts/EXIT_SUCCESS.S rename to libc/sysv/consts/_NSIG.S index 8f41a65ed..12571eff7 100644 --- a/libc/sysv/consts/EXIT_SUCCESS.S +++ b/libc/sysv/consts/_NSIG.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon exit,EXIT_SUCCESS,0,0,0,0,0,0 +.syscon limits,_NSIG,64,32,128,32,64,32 diff --git a/libc/sysv/consts/_PATH_MAX.S b/libc/sysv/consts/_PATH_MAX.S new file mode 100644 index 000000000..af56d92f4 --- /dev/null +++ b/libc/sysv/consts/_PATH_MAX.S @@ -0,0 +1,2 @@ +#include "libc/sysv/consts/syscon.internal.h" +.syscon limits,_PATH_MAX,4096,1024,1024,1024,1024,512 diff --git a/libc/sysv/consts/_POSIX2_BC_BASE_MAX.S b/libc/sysv/consts/_POSIX2_BC_BASE_MAX.S deleted file mode 100644 index d9def8b3a..000000000 --- a/libc/sysv/consts/_POSIX2_BC_BASE_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon misc,_POSIX2_BC_BASE_MAX,99,99,99,99,99,0 diff --git a/libc/sysv/consts/_POSIX2_BC_DIM_MAX.S b/libc/sysv/consts/_POSIX2_BC_DIM_MAX.S deleted file mode 100644 index c998bf8ee..000000000 --- a/libc/sysv/consts/_POSIX2_BC_DIM_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon misc,_POSIX2_BC_DIM_MAX,0x0800,0x0800,0x0800,0x0800,0x0800,0 diff --git a/libc/sysv/consts/_POSIX2_BC_SCALE_MAX.S b/libc/sysv/consts/_POSIX2_BC_SCALE_MAX.S deleted file mode 100644 index b72e2963d..000000000 --- a/libc/sysv/consts/_POSIX2_BC_SCALE_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon misc,_POSIX2_BC_SCALE_MAX,99,99,99,99,99,0 diff --git a/libc/sysv/consts/_POSIX2_BC_STRING_MAX.S b/libc/sysv/consts/_POSIX2_BC_STRING_MAX.S deleted file mode 100644 index f4ea24f4e..000000000 --- a/libc/sysv/consts/_POSIX2_BC_STRING_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon misc,_POSIX2_BC_STRING_MAX,0x03e8,0x03e8,0x03e8,0x03e8,0x03e8,0 diff --git a/libc/sysv/consts/_POSIX2_CHARCLASS_NAME_MAX.S b/libc/sysv/consts/_POSIX2_CHARCLASS_NAME_MAX.S deleted file mode 100644 index 596985ba5..000000000 --- a/libc/sysv/consts/_POSIX2_CHARCLASS_NAME_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon misc,_POSIX2_CHARCLASS_NAME_MAX,14,14,14,14,14,0 diff --git a/libc/sysv/consts/_POSIX2_COLL_WEIGHTS_MAX.S b/libc/sysv/consts/_POSIX2_COLL_WEIGHTS_MAX.S deleted file mode 100644 index b6f3057e5..000000000 --- a/libc/sysv/consts/_POSIX2_COLL_WEIGHTS_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon misc,_POSIX2_COLL_WEIGHTS_MAX,2,2,2,2,2,0 diff --git a/libc/sysv/consts/_POSIX2_C_BIND.S b/libc/sysv/consts/_POSIX2_C_BIND.S deleted file mode 100644 index 93adee85b..000000000 --- a/libc/sysv/consts/_POSIX2_C_BIND.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon misc,_POSIX2_C_BIND,0x031069,0x030db0,0x030db0,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX2_EXPR_NEST_MAX.S b/libc/sysv/consts/_POSIX2_EXPR_NEST_MAX.S deleted file mode 100644 index 238faf31c..000000000 --- a/libc/sysv/consts/_POSIX2_EXPR_NEST_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon misc,_POSIX2_EXPR_NEST_MAX,0x20,0x20,0x20,0x20,0x20,0 diff --git a/libc/sysv/consts/_POSIX2_LINE_MAX.S b/libc/sysv/consts/_POSIX2_LINE_MAX.S deleted file mode 100644 index 53324180d..000000000 --- a/libc/sysv/consts/_POSIX2_LINE_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon misc,_POSIX2_LINE_MAX,0x0800,0x0800,0x0800,0x0800,0x0800,0 diff --git a/libc/sysv/consts/_POSIX2_RE_DUP_MAX.S b/libc/sysv/consts/_POSIX2_RE_DUP_MAX.S deleted file mode 100644 index c03fc5b6d..000000000 --- a/libc/sysv/consts/_POSIX2_RE_DUP_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon misc,_POSIX2_RE_DUP_MAX,255,255,255,255,255,0 diff --git a/libc/sysv/consts/_POSIX2_VERSION.S b/libc/sysv/consts/_POSIX2_VERSION.S deleted file mode 100644 index ef440fb36..000000000 --- a/libc/sysv/consts/_POSIX2_VERSION.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon misc,_POSIX2_VERSION,0x031069,0x030db0,0x030a2c,0x031069,0x031069,0 diff --git a/libc/sysv/consts/_POSIX_ADVISORY_INFO.S b/libc/sysv/consts/_POSIX_ADVISORY_INFO.S deleted file mode 100644 index c2737a52b..000000000 --- a/libc/sysv/consts/_POSIX_ADVISORY_INFO.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_ADVISORY_INFO,0x031069,-1,0x030db0,-1,-1,0 diff --git a/libc/sysv/consts/_POSIX_AIO_LISTIO_MAX.S b/libc/sysv/consts/_POSIX_AIO_LISTIO_MAX.S deleted file mode 100644 index 38fc0e031..000000000 --- a/libc/sysv/consts/_POSIX_AIO_LISTIO_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_AIO_LISTIO_MAX,2,2,2,0,0,0 diff --git a/libc/sysv/consts/_POSIX_AIO_MAX.S b/libc/sysv/consts/_POSIX_AIO_MAX.S deleted file mode 100644 index e342f6a7c..000000000 --- a/libc/sysv/consts/_POSIX_AIO_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_AIO_MAX,1,1,1,0,0,0 diff --git a/libc/sysv/consts/_POSIX_ARG_MAX.S b/libc/sysv/consts/_POSIX_ARG_MAX.S deleted file mode 100644 index 315cf621c..000000000 --- a/libc/sysv/consts/_POSIX_ARG_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_ARG_MAX,0x1000,0x1000,0x1000,0x1000,0x1000,0 diff --git a/libc/sysv/consts/_POSIX_ASYNCHRONOUS_IO.S b/libc/sysv/consts/_POSIX_ASYNCHRONOUS_IO.S deleted file mode 100644 index 4de9f3bbd..000000000 --- a/libc/sysv/consts/_POSIX_ASYNCHRONOUS_IO.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_ASYNCHRONOUS_IO,0x031069,-1,0x030db0,-1,-1,0 diff --git a/libc/sysv/consts/_POSIX_BARRIERS.S b/libc/sysv/consts/_POSIX_BARRIERS.S deleted file mode 100644 index 8fbeb0fac..000000000 --- a/libc/sysv/consts/_POSIX_BARRIERS.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_BARRIERS,0x031069,-1,0x030db0,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX_CHILD_MAX.S b/libc/sysv/consts/_POSIX_CHILD_MAX.S deleted file mode 100644 index 3495a2ca0..000000000 --- a/libc/sysv/consts/_POSIX_CHILD_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_CHILD_MAX,25,25,25,25,25,0 diff --git a/libc/sysv/consts/_POSIX_CHOWN_RESTRICTED.S b/libc/sysv/consts/_POSIX_CHOWN_RESTRICTED.S deleted file mode 100644 index 4ff35272a..000000000 --- a/libc/sysv/consts/_POSIX_CHOWN_RESTRICTED.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_CHOWN_RESTRICTED,0,0x030db0,1,1,1,0 diff --git a/libc/sysv/consts/_POSIX_CLOCKRES_MIN.S b/libc/sysv/consts/_POSIX_CLOCKRES_MIN.S deleted file mode 100644 index 3bd0857d5..000000000 --- a/libc/sysv/consts/_POSIX_CLOCKRES_MIN.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_CLOCKRES_MIN,0x01312d00,0,0x01312d00,0x01312d00,0x01312d00,0 diff --git a/libc/sysv/consts/_POSIX_CLOCK_SELECTION.S b/libc/sysv/consts/_POSIX_CLOCK_SELECTION.S deleted file mode 100644 index c6894a953..000000000 --- a/libc/sysv/consts/_POSIX_CLOCK_SELECTION.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_CLOCK_SELECTION,0x031069,-1,-1,-1,-1,0 diff --git a/libc/sysv/consts/_POSIX_CPUTIME.S b/libc/sysv/consts/_POSIX_CPUTIME.S deleted file mode 100644 index 8be5a812d..000000000 --- a/libc/sysv/consts/_POSIX_CPUTIME.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_CPUTIME,0,-1,0x030db0,0x031069,0x031069,0 diff --git a/libc/sysv/consts/_POSIX_DELAYTIMER_MAX.S b/libc/sysv/consts/_POSIX_DELAYTIMER_MAX.S deleted file mode 100644 index 847687eea..000000000 --- a/libc/sysv/consts/_POSIX_DELAYTIMER_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_DELAYTIMER_MAX,0x20,0x20,0x20,0,0,0 diff --git a/libc/sysv/consts/_POSIX_FSYNC.S b/libc/sysv/consts/_POSIX_FSYNC.S deleted file mode 100644 index 98734a460..000000000 --- a/libc/sysv/consts/_POSIX_FSYNC.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_FSYNC,0x031069,0x030db0,0x030db0,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX_HOST_NAME_MAX.S b/libc/sysv/consts/_POSIX_HOST_NAME_MAX.S deleted file mode 100644 index cdd32150d..000000000 --- a/libc/sysv/consts/_POSIX_HOST_NAME_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_HOST_NAME_MAX,255,255,255,255,255,0 diff --git a/libc/sysv/consts/_POSIX_IPV6.S b/libc/sysv/consts/_POSIX_IPV6.S deleted file mode 100644 index b77770aab..000000000 --- a/libc/sysv/consts/_POSIX_IPV6.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_IPV6,0x031069,0x030db0,0,0,0,0 diff --git a/libc/sysv/consts/_POSIX_JOB_CONTROL.S b/libc/sysv/consts/_POSIX_JOB_CONTROL.S deleted file mode 100644 index 6075f5486..000000000 --- a/libc/sysv/consts/_POSIX_JOB_CONTROL.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_JOB_CONTROL,1,0x030db0,1,1,1,0 diff --git a/libc/sysv/consts/_POSIX_LINK_MAX.S b/libc/sysv/consts/_POSIX_LINK_MAX.S deleted file mode 100644 index b4e062ba0..000000000 --- a/libc/sysv/consts/_POSIX_LINK_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_LINK_MAX,8,8,8,8,8,0 diff --git a/libc/sysv/consts/_POSIX_LOGIN_NAME_MAX.S b/libc/sysv/consts/_POSIX_LOGIN_NAME_MAX.S deleted file mode 100644 index deb4123ba..000000000 --- a/libc/sysv/consts/_POSIX_LOGIN_NAME_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_LOGIN_NAME_MAX,9,9,9,9,9,0 diff --git a/libc/sysv/consts/_POSIX_MAPPED_FILES.S b/libc/sysv/consts/_POSIX_MAPPED_FILES.S deleted file mode 100644 index 20e7b821c..000000000 --- a/libc/sysv/consts/_POSIX_MAPPED_FILES.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_MAPPED_FILES,0x031069,0x030db0,0x030db0,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX_MAX_CANON.S b/libc/sysv/consts/_POSIX_MAX_CANON.S deleted file mode 100644 index 1abaa3203..000000000 --- a/libc/sysv/consts/_POSIX_MAX_CANON.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_MAX_CANON,255,255,255,255,255,0 diff --git a/libc/sysv/consts/_POSIX_MAX_INPUT.S b/libc/sysv/consts/_POSIX_MAX_INPUT.S deleted file mode 100644 index cecc9f5a7..000000000 --- a/libc/sysv/consts/_POSIX_MAX_INPUT.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_MAX_INPUT,255,255,255,255,255,0 diff --git a/libc/sysv/consts/_POSIX_MEMLOCK.S b/libc/sysv/consts/_POSIX_MEMLOCK.S deleted file mode 100644 index 351263a05..000000000 --- a/libc/sysv/consts/_POSIX_MEMLOCK.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_MEMLOCK,0x031069,-1,-1,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX_MEMLOCK_RANGE.S b/libc/sysv/consts/_POSIX_MEMLOCK_RANGE.S deleted file mode 100644 index 5dbf7e4e6..000000000 --- a/libc/sysv/consts/_POSIX_MEMLOCK_RANGE.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_MEMLOCK_RANGE,0x031069,-1,0x030db0,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX_MEMORY_PROTECTION.S b/libc/sysv/consts/_POSIX_MEMORY_PROTECTION.S deleted file mode 100644 index 12a14fdbd..000000000 --- a/libc/sysv/consts/_POSIX_MEMORY_PROTECTION.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_MEMORY_PROTECTION,0x031069,0x030db0,0x030db0,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX_MESSAGE_PASSING.S b/libc/sysv/consts/_POSIX_MESSAGE_PASSING.S deleted file mode 100644 index d5d784672..000000000 --- a/libc/sysv/consts/_POSIX_MESSAGE_PASSING.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_MESSAGE_PASSING,0x031069,-1,0x030db0,-1,-1,0 diff --git a/libc/sysv/consts/_POSIX_MONOTONIC_CLOCK.S b/libc/sysv/consts/_POSIX_MONOTONIC_CLOCK.S deleted file mode 100644 index 06db4eb35..000000000 --- a/libc/sysv/consts/_POSIX_MONOTONIC_CLOCK.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_MONOTONIC_CLOCK,0,-1,0x030db0,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX_MQ_OPEN_MAX.S b/libc/sysv/consts/_POSIX_MQ_OPEN_MAX.S deleted file mode 100644 index 0ea3ac38e..000000000 --- a/libc/sysv/consts/_POSIX_MQ_OPEN_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_MQ_OPEN_MAX,8,8,8,0,0,0 diff --git a/libc/sysv/consts/_POSIX_MQ_PRIO_MAX.S b/libc/sysv/consts/_POSIX_MQ_PRIO_MAX.S deleted file mode 100644 index 36272de45..000000000 --- a/libc/sysv/consts/_POSIX_MQ_PRIO_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_MQ_PRIO_MAX,0x20,0x20,0x20,0,0,0 diff --git a/libc/sysv/consts/_POSIX_NAME_MAX.S b/libc/sysv/consts/_POSIX_NAME_MAX.S deleted file mode 100644 index a7a828bb7..000000000 --- a/libc/sysv/consts/_POSIX_NAME_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_NAME_MAX,14,14,14,14,14,14 diff --git a/libc/sysv/consts/_POSIX_NGROUPS_MAX.S b/libc/sysv/consts/_POSIX_NGROUPS_MAX.S deleted file mode 100644 index 7ba077af6..000000000 --- a/libc/sysv/consts/_POSIX_NGROUPS_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_NGROUPS_MAX,8,8,8,8,8,0 diff --git a/libc/sysv/consts/_POSIX_NO_TRUNC.S b/libc/sysv/consts/_POSIX_NO_TRUNC.S deleted file mode 100644 index 9971eb2d7..000000000 --- a/libc/sysv/consts/_POSIX_NO_TRUNC.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_NO_TRUNC,1,0x030db0,1,1,1,0 diff --git a/libc/sysv/consts/_POSIX_OPEN_MAX.S b/libc/sysv/consts/_POSIX_OPEN_MAX.S deleted file mode 100644 index 6c9468640..000000000 --- a/libc/sysv/consts/_POSIX_OPEN_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_OPEN_MAX,20,20,20,20,20,20 diff --git a/libc/sysv/consts/_POSIX_PATH_MAX.S b/libc/sysv/consts/_POSIX_PATH_MAX.S deleted file mode 100644 index 909d3c2c7..000000000 --- a/libc/sysv/consts/_POSIX_PATH_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_PATH_MAX,255,255,255,255,255,255 diff --git a/libc/sysv/consts/_POSIX_PIPE_BUF.S b/libc/sysv/consts/_POSIX_PIPE_BUF.S deleted file mode 100644 index c831974fe..000000000 --- a/libc/sysv/consts/_POSIX_PIPE_BUF.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_PIPE_BUF,0x0200,0x0200,0x0200,0x0200,0x0200,0 diff --git a/libc/sysv/consts/_POSIX_RAW_SOCKETS.S b/libc/sysv/consts/_POSIX_RAW_SOCKETS.S deleted file mode 100644 index 9f4fc94b1..000000000 --- a/libc/sysv/consts/_POSIX_RAW_SOCKETS.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_RAW_SOCKETS,0x031069,-1,0x030db0,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX_READER_WRITER_LOCKS.S b/libc/sysv/consts/_POSIX_READER_WRITER_LOCKS.S deleted file mode 100644 index a1a4cc1c7..000000000 --- a/libc/sysv/consts/_POSIX_READER_WRITER_LOCKS.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_READER_WRITER_LOCKS,0x031069,0x030db0,0x030db0,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX_REALTIME_SIGNALS.S b/libc/sysv/consts/_POSIX_REALTIME_SIGNALS.S deleted file mode 100644 index be894edb5..000000000 --- a/libc/sysv/consts/_POSIX_REALTIME_SIGNALS.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_REALTIME_SIGNALS,0x031069,-1,0x030db0,-1,-1,0 diff --git a/libc/sysv/consts/_POSIX_REGEXP.S b/libc/sysv/consts/_POSIX_REGEXP.S deleted file mode 100644 index ce4e54768..000000000 --- a/libc/sysv/consts/_POSIX_REGEXP.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_REGEXP,1,0x030db0,1,1,1,0 diff --git a/libc/sysv/consts/_POSIX_RE_DUP_MAX.S b/libc/sysv/consts/_POSIX_RE_DUP_MAX.S deleted file mode 100644 index bfb673a04..000000000 --- a/libc/sysv/consts/_POSIX_RE_DUP_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_RE_DUP_MAX,255,255,255,255,255,0 diff --git a/libc/sysv/consts/_POSIX_RTSIG_MAX.S b/libc/sysv/consts/_POSIX_RTSIG_MAX.S deleted file mode 100644 index 2241f715a..000000000 --- a/libc/sysv/consts/_POSIX_RTSIG_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_RTSIG_MAX,8,8,8,0,0,0 diff --git a/libc/sysv/consts/_POSIX_SAVED_IDS.S b/libc/sysv/consts/_POSIX_SAVED_IDS.S deleted file mode 100644 index 82fffc4ea..000000000 --- a/libc/sysv/consts/_POSIX_SAVED_IDS.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_SAVED_IDS,1,0x030db0,0,1,1,0 diff --git a/libc/sysv/consts/_POSIX_SEMAPHORES.S b/libc/sysv/consts/_POSIX_SEMAPHORES.S deleted file mode 100644 index de3e8eaad..000000000 --- a/libc/sysv/consts/_POSIX_SEMAPHORES.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_SEMAPHORES,0x031069,-1,0x030db0,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX_SEM_NSEMS_MAX.S b/libc/sysv/consts/_POSIX_SEM_NSEMS_MAX.S deleted file mode 100644 index e9effe5ee..000000000 --- a/libc/sysv/consts/_POSIX_SEM_NSEMS_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_SEM_NSEMS_MAX,0x0100,0x0100,0x0100,0x0100,0x0100,0 diff --git a/libc/sysv/consts/_POSIX_SEM_VALUE_MAX.S b/libc/sysv/consts/_POSIX_SEM_VALUE_MAX.S deleted file mode 100644 index 763e10aff..000000000 --- a/libc/sysv/consts/_POSIX_SEM_VALUE_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_SEM_VALUE_MAX,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0 diff --git a/libc/sysv/consts/_POSIX_SHARED_MEMORY_OBJECTS.S b/libc/sysv/consts/_POSIX_SHARED_MEMORY_OBJECTS.S deleted file mode 100644 index a775360c0..000000000 --- a/libc/sysv/consts/_POSIX_SHARED_MEMORY_OBJECTS.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_SHARED_MEMORY_OBJECTS,0x031069,-1,0x030db0,0x031069,0x031069,0 diff --git a/libc/sysv/consts/_POSIX_SHELL.S b/libc/sysv/consts/_POSIX_SHELL.S deleted file mode 100644 index ea364b705..000000000 --- a/libc/sysv/consts/_POSIX_SHELL.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_SHELL,1,0x030db0,1,1,1,0 diff --git a/libc/sysv/consts/_POSIX_SIGQUEUE_MAX.S b/libc/sysv/consts/_POSIX_SIGQUEUE_MAX.S deleted file mode 100644 index b6e88a5ee..000000000 --- a/libc/sysv/consts/_POSIX_SIGQUEUE_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_SIGQUEUE_MAX,0x20,0x20,0x20,0,0,0 diff --git a/libc/sysv/consts/_POSIX_SPAWN.S b/libc/sysv/consts/_POSIX_SPAWN.S deleted file mode 100644 index 5cc922cef..000000000 --- a/libc/sysv/consts/_POSIX_SPAWN.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_SPAWN,0x031069,-1,0x030db0,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX_SPIN_LOCKS.S b/libc/sysv/consts/_POSIX_SPIN_LOCKS.S deleted file mode 100644 index 3da50e838..000000000 --- a/libc/sysv/consts/_POSIX_SPIN_LOCKS.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_SPIN_LOCKS,0x031069,-1,0x030db0,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX_SSIZE_MAX.S b/libc/sysv/consts/_POSIX_SSIZE_MAX.S deleted file mode 100644 index e06159aff..000000000 --- a/libc/sysv/consts/_POSIX_SSIZE_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_SSIZE_MAX,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0 diff --git a/libc/sysv/consts/_POSIX_SS_REPL_MAX.S b/libc/sysv/consts/_POSIX_SS_REPL_MAX.S deleted file mode 100644 index 3776e47a7..000000000 --- a/libc/sysv/consts/_POSIX_SS_REPL_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_SS_REPL_MAX,0,4,4,0,0,0 diff --git a/libc/sysv/consts/_POSIX_STREAM_MAX.S b/libc/sysv/consts/_POSIX_STREAM_MAX.S deleted file mode 100644 index 4067e637b..000000000 --- a/libc/sysv/consts/_POSIX_STREAM_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_STREAM_MAX,8,8,8,8,8,0 diff --git a/libc/sysv/consts/_POSIX_SYMLINK_MAX.S b/libc/sysv/consts/_POSIX_SYMLINK_MAX.S deleted file mode 100644 index ca36778c4..000000000 --- a/libc/sysv/consts/_POSIX_SYMLINK_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_SYMLINK_MAX,255,255,255,255,255,0 diff --git a/libc/sysv/consts/_POSIX_SYMLOOP_MAX.S b/libc/sysv/consts/_POSIX_SYMLOOP_MAX.S deleted file mode 100644 index 981d87de7..000000000 --- a/libc/sysv/consts/_POSIX_SYMLOOP_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_SYMLOOP_MAX,8,8,8,8,8,0 diff --git a/libc/sysv/consts/_POSIX_THREADS.S b/libc/sysv/consts/_POSIX_THREADS.S deleted file mode 100644 index 8198becab..000000000 --- a/libc/sysv/consts/_POSIX_THREADS.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_THREADS,0x031069,0x030db0,0x030db0,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX_THREAD_ATTR_STACKADDR.S b/libc/sysv/consts/_POSIX_THREAD_ATTR_STACKADDR.S deleted file mode 100644 index fc4fc4400..000000000 --- a/libc/sysv/consts/_POSIX_THREAD_ATTR_STACKADDR.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_THREAD_ATTR_STACKADDR,0x031069,0x030db0,0x030db0,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX_THREAD_ATTR_STACKSIZE.S b/libc/sysv/consts/_POSIX_THREAD_ATTR_STACKSIZE.S deleted file mode 100644 index 7fec49e45..000000000 --- a/libc/sysv/consts/_POSIX_THREAD_ATTR_STACKSIZE.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_THREAD_ATTR_STACKSIZE,0x031069,0x030db0,0x030db0,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX_THREAD_CPUTIME.S b/libc/sysv/consts/_POSIX_THREAD_CPUTIME.S deleted file mode 100644 index 1ceab7a7e..000000000 --- a/libc/sysv/consts/_POSIX_THREAD_CPUTIME.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_THREAD_CPUTIME,0,-1,0x030db0,0x031069,0x031069,0 diff --git a/libc/sysv/consts/_POSIX_THREAD_DESTRUCTOR_ITERATIONS.S b/libc/sysv/consts/_POSIX_THREAD_DESTRUCTOR_ITERATIONS.S deleted file mode 100644 index cfb21f89a..000000000 --- a/libc/sysv/consts/_POSIX_THREAD_DESTRUCTOR_ITERATIONS.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_THREAD_DESTRUCTOR_ITERATIONS,4,4,4,4,4,0 diff --git a/libc/sysv/consts/_POSIX_THREAD_KEYS_MAX.S b/libc/sysv/consts/_POSIX_THREAD_KEYS_MAX.S deleted file mode 100644 index f63502d07..000000000 --- a/libc/sysv/consts/_POSIX_THREAD_KEYS_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_THREAD_KEYS_MAX,0x80,0x80,0x80,0x80,0x80,0 diff --git a/libc/sysv/consts/_POSIX_THREAD_PRIORITY_SCHEDULING.S b/libc/sysv/consts/_POSIX_THREAD_PRIORITY_SCHEDULING.S deleted file mode 100644 index ce2a15df8..000000000 --- a/libc/sysv/consts/_POSIX_THREAD_PRIORITY_SCHEDULING.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_THREAD_PRIORITY_SCHEDULING,0x031069,-1,0x030db0,-1,-1,0 diff --git a/libc/sysv/consts/_POSIX_THREAD_PROCESS_SHARED.S b/libc/sysv/consts/_POSIX_THREAD_PROCESS_SHARED.S deleted file mode 100644 index f79252755..000000000 --- a/libc/sysv/consts/_POSIX_THREAD_PROCESS_SHARED.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_THREAD_PROCESS_SHARED,0x031069,0x030db0,0x030db0,-1,-1,0 diff --git a/libc/sysv/consts/_POSIX_THREAD_SAFE_FUNCTIONS.S b/libc/sysv/consts/_POSIX_THREAD_SAFE_FUNCTIONS.S deleted file mode 100644 index e1d5d06fe..000000000 --- a/libc/sysv/consts/_POSIX_THREAD_SAFE_FUNCTIONS.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_THREAD_SAFE_FUNCTIONS,0x031069,0x030db0,-1,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX_THREAD_THREADS_MAX.S b/libc/sysv/consts/_POSIX_THREAD_THREADS_MAX.S deleted file mode 100644 index 2fcdce6ed..000000000 --- a/libc/sysv/consts/_POSIX_THREAD_THREADS_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_THREAD_THREADS_MAX,0x40,0x40,0x40,4,4,0 diff --git a/libc/sysv/consts/_POSIX_TIMEOUTS.S b/libc/sysv/consts/_POSIX_TIMEOUTS.S deleted file mode 100644 index dddedfedd..000000000 --- a/libc/sysv/consts/_POSIX_TIMEOUTS.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_TIMEOUTS,0x031069,-1,0x030db0,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX_TIMERS.S b/libc/sysv/consts/_POSIX_TIMERS.S deleted file mode 100644 index 7f4c8ebed..000000000 --- a/libc/sysv/consts/_POSIX_TIMERS.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_TIMERS,0x031069,-1,0x030db0,-1,-1,0 diff --git a/libc/sysv/consts/_POSIX_TIMER_MAX.S b/libc/sysv/consts/_POSIX_TIMER_MAX.S deleted file mode 100644 index d7d3b7111..000000000 --- a/libc/sysv/consts/_POSIX_TIMER_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_TIMER_MAX,0x20,0x20,0x20,0,0,0 diff --git a/libc/sysv/consts/_POSIX_TRACE_EVENT_NAME_MAX.S b/libc/sysv/consts/_POSIX_TRACE_EVENT_NAME_MAX.S deleted file mode 100644 index 56c6f2efb..000000000 --- a/libc/sysv/consts/_POSIX_TRACE_EVENT_NAME_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_TRACE_EVENT_NAME_MAX,0,30,30,0,0,0 diff --git a/libc/sysv/consts/_POSIX_TRACE_NAME_MAX.S b/libc/sysv/consts/_POSIX_TRACE_NAME_MAX.S deleted file mode 100644 index c576681b8..000000000 --- a/libc/sysv/consts/_POSIX_TRACE_NAME_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_TRACE_NAME_MAX,0,8,8,0,0,0 diff --git a/libc/sysv/consts/_POSIX_TRACE_SYS_MAX.S b/libc/sysv/consts/_POSIX_TRACE_SYS_MAX.S deleted file mode 100644 index 6a3a96249..000000000 --- a/libc/sysv/consts/_POSIX_TRACE_SYS_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_TRACE_SYS_MAX,0,8,8,0,0,0 diff --git a/libc/sysv/consts/_POSIX_TRACE_USER_EVENT_MAX.S b/libc/sysv/consts/_POSIX_TRACE_USER_EVENT_MAX.S deleted file mode 100644 index 3beba770f..000000000 --- a/libc/sysv/consts/_POSIX_TRACE_USER_EVENT_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_TRACE_USER_EVENT_MAX,0,0x20,0x20,0,0,0 diff --git a/libc/sysv/consts/_POSIX_TTY_NAME_MAX.S b/libc/sysv/consts/_POSIX_TTY_NAME_MAX.S deleted file mode 100644 index 900f1b86e..000000000 --- a/libc/sysv/consts/_POSIX_TTY_NAME_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_TTY_NAME_MAX,9,9,9,9,9,0 diff --git a/libc/sysv/consts/_POSIX_TZNAME_MAX.S b/libc/sysv/consts/_POSIX_TZNAME_MAX.S deleted file mode 100644 index 0ce6a6993..000000000 --- a/libc/sysv/consts/_POSIX_TZNAME_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_TZNAME_MAX,6,6,6,6,6,0 diff --git a/libc/sysv/consts/_POSIX_V6_LP64_OFF64.S b/libc/sysv/consts/_POSIX_V6_LP64_OFF64.S deleted file mode 100644 index 3a3c260f4..000000000 --- a/libc/sysv/consts/_POSIX_V6_LP64_OFF64.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_V6_LP64_OFF64,1,1,0,0,0,0 diff --git a/libc/sysv/consts/_POSIX_V7_LP64_OFF64.S b/libc/sysv/consts/_POSIX_V7_LP64_OFF64.S deleted file mode 100644 index 351dd64b4..000000000 --- a/libc/sysv/consts/_POSIX_V7_LP64_OFF64.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_V7_LP64_OFF64,1,1,0,0,0,0 diff --git a/libc/sysv/consts/_POSIX_VDISABLE.S b/libc/sysv/consts/_POSIX_VDISABLE.S deleted file mode 100644 index b12b30623..000000000 --- a/libc/sysv/consts/_POSIX_VDISABLE.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_VDISABLE,0,255,255,255,255,0 diff --git a/libc/sysv/consts/_POSIX_VERSION.S b/libc/sysv/consts/_POSIX_VERSION.S deleted file mode 100644 index 34fb98fd9..000000000 --- a/libc/sysv/consts/_POSIX_VERSION.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_VERSION,0x031069,0x030db0,0x030db0,0x031069,0x031069,0 diff --git a/libc/sysv/consts/_posix.h b/libc/sysv/consts/_posix.h index b96c0f051..66f9062b7 100644 --- a/libc/sysv/consts/_posix.h +++ b/libc/sysv/consts/_posix.h @@ -1,168 +1,59 @@ #ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS__POSIX_H_ #define COSMOPOLITAN_LIBC_SYSV_CONSTS__POSIX_H_ -#include "libc/runtime/symbolic.h" -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ -extern const long _POSIX_ADVISORY_INFO; -extern const long _POSIX_AIO_LISTIO_MAX; -extern const long _POSIX_AIO_MAX; -extern const long _POSIX_ARG_MAX; -extern const long _POSIX_ASYNCHRONOUS_IO; -extern const long _POSIX_BARRIERS; -extern const long _POSIX_CHILD_MAX; -extern const long _POSIX_CHOWN_RESTRICTED; -extern const long _POSIX_CLOCKRES_MIN; -extern const long _POSIX_CLOCK_SELECTION; -extern const long _POSIX_CPUTIME; -extern const long _POSIX_DELAYTIMER_MAX; -extern const long _POSIX_FSYNC; -extern const long _POSIX_HOST_NAME_MAX; -extern const long _POSIX_IPV6; -extern const long _POSIX_JOB_CONTROL; -extern const long _POSIX_LINK_MAX; -extern const long _POSIX_LOGIN_NAME_MAX; -extern const long _POSIX_MAPPED_FILES; -extern const long _POSIX_MAX_CANON; -extern const long _POSIX_MAX_INPUT; -extern const long _POSIX_MEMLOCK; -extern const long _POSIX_MEMLOCK_RANGE; -extern const long _POSIX_MEMORY_PROTECTION; -extern const long _POSIX_MESSAGE_PASSING; -extern const long _POSIX_MONOTONIC_CLOCK; -extern const long _POSIX_MQ_OPEN_MAX; -extern const long _POSIX_MQ_PRIO_MAX; -extern const long _POSIX_NAME_MAX; -extern const long _POSIX_NGROUPS_MAX; -extern const long _POSIX_NO_TRUNC; -extern const long _POSIX_OPEN_MAX; -extern const long _POSIX_PATH_MAX; -extern const long _POSIX_PIPE_BUF; -extern const long _POSIX_RAW_SOCKETS; -extern const long _POSIX_READER_WRITER_LOCKS; -extern const long _POSIX_REALTIME_SIGNALS; -extern const long _POSIX_REGEXP; -extern const long _POSIX_RE_DUP_MAX; -extern const long _POSIX_RTSIG_MAX; -extern const long _POSIX_SAVED_IDS; -extern const long _POSIX_SEMAPHORES; -extern const long _POSIX_SEM_NSEMS_MAX; -extern const long _POSIX_SEM_VALUE_MAX; -extern const long _POSIX_SHARED_MEMORY_OBJECTS; -extern const long _POSIX_SHELL; -extern const long _POSIX_SIGQUEUE_MAX; -extern const long _POSIX_SPAWN; -extern const long _POSIX_SPIN_LOCKS; -extern const long _POSIX_SSIZE_MAX; -extern const long _POSIX_SS_REPL_MAX; -extern const long _POSIX_STREAM_MAX; -extern const long _POSIX_SYMLINK_MAX; -extern const long _POSIX_SYMLOOP_MAX; -extern const long _POSIX_THREADS; -extern const long _POSIX_THREAD_ATTR_STACKADDR; -extern const long _POSIX_THREAD_ATTR_STACKSIZE; -extern const long _POSIX_THREAD_CPUTIME; -extern const long _POSIX_THREAD_DESTRUCTOR_ITERATIONS; -extern const long _POSIX_THREAD_KEYS_MAX; -extern const long _POSIX_THREAD_PRIORITY_SCHEDULING; -extern const long _POSIX_THREAD_PROCESS_SHARED; -extern const long _POSIX_THREAD_SAFE_FUNCTIONS; -extern const long _POSIX_THREAD_THREADS_MAX; -extern const long _POSIX_TIMEOUTS; -extern const long _POSIX_TIMERS; -extern const long _POSIX_TIMER_MAX; -extern const long _POSIX_TRACE_EVENT_NAME_MAX; -extern const long _POSIX_TRACE_NAME_MAX; -extern const long _POSIX_TRACE_SYS_MAX; -extern const long _POSIX_TRACE_USER_EVENT_MAX; -extern const long _POSIX_TTY_NAME_MAX; -extern const long _POSIX_TZNAME_MAX; -extern const long _POSIX_V6_LP64_OFF64; -extern const long _POSIX_V7_LP64_OFF64; -extern const long _POSIX_VDISABLE; -extern const long _POSIX_VERSION; +/* The Open Group Base Specifications Issue 7, 2018 edition */ +/* IEEE Std 1003.1-2017 (Revision of IEEE Std 1003.1-2008) */ -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#define _POSIX_AIO_LISTIO_MAX 2 +#define _POSIX_AIO_MAX 1 +#define _POSIX_ARG_MAX 4096 +#define _POSIX_CHILD_MAX 25 +#define _POSIX_DELAYTIMER_MAX 32 +#define _POSIX_HOST_NAME_MAX 255 +#define _POSIX_LINK_MAX 8 +#define _POSIX_LOGIN_NAME_MAX 9 +#define _POSIX_MAX_CANON 255 +#define _POSIX_MAX_INPUT 255 +#define _POSIX_MQ_OPEN_MAX 8 +#define _POSIX_MQ_PRIO_MAX 32 +#define _POSIX_NAME_MAX 14 +#define _POSIX_NGROUPS_MAX 8 +#define _POSIX_OPEN_MAX 20 +#define _POSIX_PATH_MAX 256 +#define _POSIX_PIPE_BUF 512 +#define _POSIX_RE_DUP_MAX 255 +#define _POSIX_RTSIG_MAX 8 +#define _POSIX_SEM_NSEMS_MAX 256 +#define _POSIX_SEM_VALUE_MAX 32767 +#define _POSIX_SIGQUEUE_MAX 32 +#define _POSIX_SSIZE_MAX 32767 +#define _POSIX_SS_REPL_MAX 4 +#define _POSIX_STREAM_MAX 8 +#define _POSIX_SYMLINK_MAX 255 +#define _POSIX_SYMLOOP_MAX 8 +#define _POSIX_THREAD_DESTRUCTOR_ITERATIONS 4 +#define _POSIX_THREAD_KEYS_MAX 128 +#define _POSIX_THREAD_THREADS_MAX 64 +#define _POSIX_TIMER_MAX 32 +#define _POSIX_TRACE_EVENT_NAME_MAX 30 +#define _POSIX_TRACE_NAME_MAX 8 +#define _POSIX_TRACE_SYS_MAX 8 +#define _POSIX_TRACE_USER_EVENT_MAX 32 +#define _POSIX_TTY_NAME_MAX 9 +#define _POSIX_TZNAME_MAX 6 -#define _POSIX_ADVISORY_INFO SYMBOLIC(_POSIX_ADVISORY_INFO) -#define _POSIX_AIO_LISTIO_MAX SYMBOLIC(_POSIX_AIO_LISTIO_MAX) -#define _POSIX_AIO_MAX SYMBOLIC(_POSIX_AIO_MAX) -#define _POSIX_ARG_MAX SYMBOLIC(_POSIX_ARG_MAX) -#define _POSIX_ASYNCHRONOUS_IO SYMBOLIC(_POSIX_ASYNCHRONOUS_IO) -#define _POSIX_BARRIERS SYMBOLIC(_POSIX_BARRIERS) -#define _POSIX_CHILD_MAX SYMBOLIC(_POSIX_CHILD_MAX) -#define _POSIX_CHOWN_RESTRICTED SYMBOLIC(_POSIX_CHOWN_RESTRICTED) -#define _POSIX_CLOCKRES_MIN SYMBOLIC(_POSIX_CLOCKRES_MIN) -#define _POSIX_CLOCK_SELECTION SYMBOLIC(_POSIX_CLOCK_SELECTION) -#define _POSIX_CPUTIME SYMBOLIC(_POSIX_CPUTIME) -#define _POSIX_DELAYTIMER_MAX SYMBOLIC(_POSIX_DELAYTIMER_MAX) -#define _POSIX_FSYNC SYMBOLIC(_POSIX_FSYNC) -#define _POSIX_HOST_NAME_MAX SYMBOLIC(_POSIX_HOST_NAME_MAX) -#define _POSIX_IPV6 SYMBOLIC(_POSIX_IPV6) -#define _POSIX_JOB_CONTROL SYMBOLIC(_POSIX_JOB_CONTROL) -#define _POSIX_LINK_MAX SYMBOLIC(_POSIX_LINK_MAX) -#define _POSIX_LOGIN_NAME_MAX SYMBOLIC(_POSIX_LOGIN_NAME_MAX) -#define _POSIX_MAPPED_FILES SYMBOLIC(_POSIX_MAPPED_FILES) -#define _POSIX_MAX_CANON SYMBOLIC(_POSIX_MAX_CANON) -#define _POSIX_MAX_INPUT SYMBOLIC(_POSIX_MAX_INPUT) -#define _POSIX_MEMLOCK SYMBOLIC(_POSIX_MEMLOCK) -#define _POSIX_MEMLOCK_RANGE SYMBOLIC(_POSIX_MEMLOCK_RANGE) -#define _POSIX_MEMORY_PROTECTION SYMBOLIC(_POSIX_MEMORY_PROTECTION) -#define _POSIX_MESSAGE_PASSING SYMBOLIC(_POSIX_MESSAGE_PASSING) -#define _POSIX_MONOTONIC_CLOCK SYMBOLIC(_POSIX_MONOTONIC_CLOCK) -#define _POSIX_MQ_OPEN_MAX SYMBOLIC(_POSIX_MQ_OPEN_MAX) -#define _POSIX_MQ_PRIO_MAX SYMBOLIC(_POSIX_MQ_PRIO_MAX) -#define _POSIX_NAME_MAX SYMBOLIC(_POSIX_NAME_MAX) -#define _POSIX_NGROUPS_MAX SYMBOLIC(_POSIX_NGROUPS_MAX) -#define _POSIX_NO_TRUNC SYMBOLIC(_POSIX_NO_TRUNC) -#define _POSIX_OPEN_MAX SYMBOLIC(_POSIX_OPEN_MAX) -#define _POSIX_PATH_MAX SYMBOLIC(_POSIX_PATH_MAX) -#define _POSIX_PIPE_BUF SYMBOLIC(_POSIX_PIPE_BUF) -#define _POSIX_RAW_SOCKETS SYMBOLIC(_POSIX_RAW_SOCKETS) -#define _POSIX_READER_WRITER_LOCKS SYMBOLIC(_POSIX_READER_WRITER_LOCKS) -#define _POSIX_REALTIME_SIGNALS SYMBOLIC(_POSIX_REALTIME_SIGNALS) -#define _POSIX_REGEXP SYMBOLIC(_POSIX_REGEXP) -#define _POSIX_RE_DUP_MAX SYMBOLIC(_POSIX_RE_DUP_MAX) -#define _POSIX_RTSIG_MAX SYMBOLIC(_POSIX_RTSIG_MAX) -#define _POSIX_SAVED_IDS SYMBOLIC(_POSIX_SAVED_IDS) -#define _POSIX_SEMAPHORES SYMBOLIC(_POSIX_SEMAPHORES) -#define _POSIX_SEM_NSEMS_MAX SYMBOLIC(_POSIX_SEM_NSEMS_MAX) -#define _POSIX_SEM_VALUE_MAX SYMBOLIC(_POSIX_SEM_VALUE_MAX) -#define _POSIX_SHARED_MEMORY_OBJECTS SYMBOLIC(_POSIX_SHARED_MEMORY_OBJECTS) -#define _POSIX_SHELL SYMBOLIC(_POSIX_SHELL) -#define _POSIX_SIGQUEUE_MAX SYMBOLIC(_POSIX_SIGQUEUE_MAX) -#define _POSIX_SPAWN SYMBOLIC(_POSIX_SPAWN) -#define _POSIX_SPIN_LOCKS SYMBOLIC(_POSIX_SPIN_LOCKS) -#define _POSIX_SSIZE_MAX SYMBOLIC(_POSIX_SSIZE_MAX) -#define _POSIX_SS_REPL_MAX SYMBOLIC(_POSIX_SS_REPL_MAX) -#define _POSIX_STREAM_MAX SYMBOLIC(_POSIX_STREAM_MAX) -#define _POSIX_SYMLINK_MAX SYMBOLIC(_POSIX_SYMLINK_MAX) -#define _POSIX_SYMLOOP_MAX SYMBOLIC(_POSIX_SYMLOOP_MAX) -#define _POSIX_THREADS SYMBOLIC(_POSIX_THREADS) -#define _POSIX_THREAD_ATTR_STACKADDR SYMBOLIC(_POSIX_THREAD_ATTR_STACKADDR) -#define _POSIX_THREAD_ATTR_STACKSIZE SYMBOLIC(_POSIX_THREAD_ATTR_STACKSIZE) -#define _POSIX_THREAD_CPUTIME SYMBOLIC(_POSIX_THREAD_CPUTIME) -#define _POSIX_THREAD_DESTRUCTOR_ITERATIONS \ - SYMBOLIC(_POSIX_THREAD_DESTRUCTOR_ITERATIONS) -#define _POSIX_THREAD_KEYS_MAX SYMBOLIC(_POSIX_THREAD_KEYS_MAX) -#define _POSIX_THREAD_PRIORITY_SCHEDULING \ - SYMBOLIC(_POSIX_THREAD_PRIORITY_SCHEDULING) -#define _POSIX_THREAD_PROCESS_SHARED SYMBOLIC(_POSIX_THREAD_PROCESS_SHARED) -#define _POSIX_THREAD_SAFE_FUNCTIONS SYMBOLIC(_POSIX_THREAD_SAFE_FUNCTIONS) -#define _POSIX_THREAD_THREADS_MAX SYMBOLIC(_POSIX_THREAD_THREADS_MAX) -#define _POSIX_TIMEOUTS SYMBOLIC(_POSIX_TIMEOUTS) -#define _POSIX_TIMERS SYMBOLIC(_POSIX_TIMERS) -#define _POSIX_TIMER_MAX SYMBOLIC(_POSIX_TIMER_MAX) -#define _POSIX_TRACE_EVENT_NAME_MAX SYMBOLIC(_POSIX_TRACE_EVENT_NAME_MAX) -#define _POSIX_TRACE_NAME_MAX SYMBOLIC(_POSIX_TRACE_NAME_MAX) -#define _POSIX_TRACE_SYS_MAX SYMBOLIC(_POSIX_TRACE_SYS_MAX) -#define _POSIX_TRACE_USER_EVENT_MAX SYMBOLIC(_POSIX_TRACE_USER_EVENT_MAX) -#define _POSIX_TTY_NAME_MAX SYMBOLIC(_POSIX_TTY_NAME_MAX) -#define _POSIX_TZNAME_MAX SYMBOLIC(_POSIX_TZNAME_MAX) -#define _POSIX_V6_LP64_OFF64 SYMBOLIC(_POSIX_V6_LP64_OFF64) -#define _POSIX_V7_LP64_OFF64 SYMBOLIC(_POSIX_V7_LP64_OFF64) -#define _POSIX_VDISABLE SYMBOLIC(_POSIX_VDISABLE) -#define _POSIX_VERSION SYMBOLIC(_POSIX_VERSION) +#define _POSIX2_BC_BASE_MAX 99 +#define _POSIX2_BC_DIM_MAX 2048 +#define _POSIX2_BC_SCALE_MAX 99 +#define _POSIX2_BC_STRING_MAX 1000 +#define _POSIX2_CHARCLASS_NAME_MAX 14 +#define _POSIX2_COLL_WEIGHTS_MAX 2 +#define _POSIX2_EXPR_NEST_MAX 32 +#define _POSIX2_LINE_MAX 2048 +#define _POSIX2_RE_DUP_MAX 255 + +#define _XOPEN_IOV_MAX 16 +#define _XOPEN_NAME_MAX 255 +#define _XOPEN_PATH_MAX 1024 #endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS__POSIX_H_ */ diff --git a/libc/sysv/consts/limits.h b/libc/sysv/consts/limits.h index 4a5476963..b01418b22 100644 --- a/libc/sysv/consts/limits.h +++ b/libc/sysv/consts/limits.h @@ -4,11 +4,19 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -extern const long PIPE_BUF; +extern const int PIPE_BUF; +extern const int _ARG_MAX; +extern const int _NAME_MAX; +extern const int _PATH_MAX; +extern const int _NSIG; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#define PIPE_BUF SYMBOLIC(PIPE_BUF) +#define PIPE_BUF SYMBOLIC(PIPE_BUF) +#define _ARG_MAX SYMBOLIC(_ARG_MAX) +#define _NAME_MAX SYMBOLIC(_NAME_MAX) +#define _PATH_MAX SYMBOLIC(_PATH_MAX) +#define _NSIG SYMBOLIC(_NSIG) #endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_LIMITS_H_ */ diff --git a/libc/sysv/consts/nrlinux.h b/libc/sysv/consts/nrlinux.h new file mode 100644 index 000000000..59d6aa8e7 --- /dev/null +++ b/libc/sysv/consts/nrlinux.h @@ -0,0 +1,330 @@ +#ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS_NRLINUX_H_ +#define COSMOPOLITAN_LIBC_SYSV_CONSTS_NRLINUX_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +#define __NR_linux_exit 0x003c +#define __NR_linux_exit_group 0x00e7 +#define __NR_linux_read 0x0000 +#define __NR_linux_write 0x0001 +#define __NR_linux_open 0x0002 +#define __NR_linux_close 0x0003 +#define __NR_linux_stat 0x0004 +#define __NR_linux_fstat 0x0005 +#define __NR_linux_lstat 0x0006 +#define __NR_linux_poll 0x0007 +#define __NR_linux_ppoll 0x010f +#define __NR_linux_brk 0x000c +#define __NR_linux_sigreturn 0x000f +#define __NR_linux_lseek 0x0008 +#define __NR_linux_mmap 0x0009 +#define __NR_linux_msync 0x001a +#define __NR_linux_mprotect 0x000a +#define __NR_linux_munmap 0x000b +#define __NR_linux_sigaction 0x000d +#define __NR_linux_sigprocmask 0x000e +#define __NR_linux_ioctl 0x0010 +#define __NR_linux_pread 0x0011 +#define __NR_linux_pwrite 0x0012 +#define __NR_linux_readv 0x0013 +#define __NR_linux_writev 0x0014 +#define __NR_linux_access 0x0015 +#define __NR_linux_pipe 0x0016 +#define __NR_linux_select 0x0017 +#define __NR_linux_pselect6 0x010e +#define __NR_linux_sched_yield 0x0018 +#define __NR_linux_mremap 0x0019 +#define __NR_linux_mincore 0x001b +#define __NR_linux_madvise 0x001c +#define __NR_linux_shmget 0x001d +#define __NR_linux_shmat 0x001e +#define __NR_linux_shmctl 0x001f +#define __NR_linux_dup 0x0020 +#define __NR_linux_dup2 0x0021 +#define __NR_linux_pause 0x0022 +#define __NR_linux_nanosleep 0x0023 +#define __NR_linux_getitimer 0x0024 +#define __NR_linux_setitimer 0x0026 +#define __NR_linux_alarm 0x0025 +#define __NR_linux_getpid 0x0027 +#define __NR_linux_sendfile 0x0028 +#define __NR_linux_socket 0x0029 +#define __NR_linux_connect 0x002a +#define __NR_linux_accept 0x002b +#define __NR_linux_sendto 0x002c +#define __NR_linux_recvfrom 0x002d +#define __NR_linux_sendmsg 0x002e +#define __NR_linux_recvmsg 0x002f +#define __NR_linux_shutdown 0x0030 +#define __NR_linux_bind 0x0031 +#define __NR_linux_listen 0x0032 +#define __NR_linux_getsockname 0x0033 +#define __NR_linux_getpeername 0x0034 +#define __NR_linux_socketpair 0x0035 +#define __NR_linux_setsockopt 0x0036 +#define __NR_linux_getsockopt 0x0037 +#define __NR_linux_fork 0x0039 +#define __NR_linux_vfork 0x003a +#define __NR_linux_execve 0x003b +#define __NR_linux_wait4 0x003d +#define __NR_linux_kill 0x003e +#define __NR_linux_clone 0x0038 +#define __NR_linux_tkill 0x00c8 +#define __NR_linux_futex 0x00ca +#define __NR_linux_set_robust_list 0x0111 +#define __NR_linux_get_robust_list 0x0112 +#define __NR_linux_uname 0x003f +#define __NR_linux_semget 0x0040 +#define __NR_linux_semop 0x0041 +#define __NR_linux_semctl 0x0042 +#define __NR_linux_shmdt 0x0043 +#define __NR_linux_msgget 0x0044 +#define __NR_linux_msgsnd 0x0045 +#define __NR_linux_msgrcv 0x0046 +#define __NR_linux_msgctl 0x0047 +#define __NR_linux_fcntl 0x0048 +#define __NR_linux_flock 0x0049 +#define __NR_linux_fsync 0x004a +#define __NR_linux_fdatasync 0x004b +#define __NR_linux_truncate 0x004c +#define __NR_linux_ftruncate 0x004d +#define __NR_linux_getcwd 0x004f +#define __NR_linux_chdir 0x0050 +#define __NR_linux_fchdir 0x0051 +#define __NR_linux_rename 0x0052 +#define __NR_linux_mkdir 0x0053 +#define __NR_linux_rmdir 0x0054 +#define __NR_linux_creat 0x0055 +#define __NR_linux_link 0x0056 +#define __NR_linux_unlink 0x0057 +#define __NR_linux_symlink 0x0058 +#define __NR_linux_readlink 0x0059 +#define __NR_linux_chmod 0x005a +#define __NR_linux_fchmod 0x005b +#define __NR_linux_chown 0x005c +#define __NR_linux_fchown 0x005d +#define __NR_linux_lchown 0x005e +#define __NR_linux_umask 0x005f +#define __NR_linux_gettimeofday 0x0060 +#define __NR_linux_getrlimit 0x0061 +#define __NR_linux_getrusage 0x0062 +#define __NR_linux_sysinfo 0x0063 +#define __NR_linux_times 0x0064 +#define __NR_linux_ptrace 0x0065 +#define __NR_linux_syslog 0x0067 +#define __NR_linux_getuid 0x0066 +#define __NR_linux_getgid 0x0068 +#define __NR_linux_getppid 0x006e +#define __NR_linux_getpgrp 0x006f +#define __NR_linux_setsid 0x0070 +#define __NR_linux_getsid 0x007c +#define __NR_linux_getpgid 0x0079 +#define __NR_linux_setpgid 0x006d +#define __NR_linux_geteuid 0x006b +#define __NR_linux_getegid 0x006c +#define __NR_linux_getgroups 0x0073 +#define __NR_linux_setgroups 0x0074 +#define __NR_linux_setreuid 0x0071 +#define __NR_linux_setregid 0x0072 +#define __NR_linux_setuid 0x0069 +#define __NR_linux_setgid 0x006a +#define __NR_linux_setresuid 0x0075 +#define __NR_linux_setresgid 0x0077 +#define __NR_linux_getresuid 0x0076 +#define __NR_linux_getresgid 0x0078 +#define __NR_linux_sigpending 0x007f +#define __NR_linux_sigsuspend 0x0082 +#define __NR_linux_sigaltstack 0x0083 +#define __NR_linux_mknod 0x0085 +#define __NR_linux_mknodat 0x0103 +#define __NR_linux_statfs 0x0089 +#define __NR_linux_fstatfs 0x008a +#define __NR_linux_getpriority 0x008c +#define __NR_linux_setpriority 0x008d +#define __NR_linux_mlock 0x0095 +#define __NR_linux_munlock 0x0096 +#define __NR_linux_mlockall 0x0097 +#define __NR_linux_munlockall 0x0098 +#define __NR_linux_setrlimit 0x00a0 +#define __NR_linux_chroot 0x00a1 +#define __NR_linux_sync 0x00a2 +#define __NR_linux_acct 0x00a3 +#define __NR_linux_settimeofday 0x00a4 +#define __NR_linux_mount 0x00a5 +#define __NR_linux_reboot 0x00a9 +#define __NR_linux_quotactl 0x00b3 +#define __NR_linux_setfsuid 0x007a +#define __NR_linux_setfsgid 0x007b +#define __NR_linux_capget 0x007d +#define __NR_linux_capset 0x007e +#define __NR_linux_sigtimedwait 0x0080 +#define __NR_linux_rt_sigqueueinfo 0x0081 +#define __NR_linux_personality 0x0087 +#define __NR_linux_ustat 0x0088 +#define __NR_linux_sysfs 0x008b +#define __NR_linux_sched_setparam 0x008e +#define __NR_linux_sched_getparam 0x008f +#define __NR_linux_sched_setscheduler 0x0090 +#define __NR_linux_sched_getscheduler 0x0091 +#define __NR_linux_sched_get_priority_max 0x0092 +#define __NR_linux_sched_get_priority_min 0x0093 +#define __NR_linux_sched_rr_get_interval 0x0094 +#define __NR_linux_vhangup 0x0099 +#define __NR_linux_modify_ldt 0x009a +#define __NR_linux_pivot_root 0x009b +#define __NR_linux__sysctl 0x009c +#define __NR_linux_prctl 0x009d +#define __NR_linux_arch_prctl 0x009e +#define __NR_linux_adjtimex 0x009f +#define __NR_linux_umount2 0x00a6 +#define __NR_linux_swapon 0x00a7 +#define __NR_linux_swapoff 0x00a8 +#define __NR_linux_sethostname 0x00aa +#define __NR_linux_setdomainname 0x00ab +#define __NR_linux_iopl 0x00ac +#define __NR_linux_ioperm 0x00ad +#define __NR_linux_init_module 0x00af +#define __NR_linux_delete_module 0x00b0 +#define __NR_linux_gettid 0x00ba +#define __NR_linux_readahead 0x00bb +#define __NR_linux_setxattr 0x00bc +#define __NR_linux_fsetxattr 0x00be +#define __NR_linux_getxattr 0x00bf +#define __NR_linux_fgetxattr 0x00c1 +#define __NR_linux_listxattr 0x00c2 +#define __NR_linux_flistxattr 0x00c4 +#define __NR_linux_removexattr 0x00c5 +#define __NR_linux_fremovexattr 0x00c7 +#define __NR_linux_lsetxattr 0x00bd +#define __NR_linux_lgetxattr 0x00c0 +#define __NR_linux_llistxattr 0x00c3 +#define __NR_linux_lremovexattr 0x00c6 +#define __NR_linux_sched_setaffinity 0x00cb +#define __NR_linux_sched_getaffinity 0x00cc +#define __NR_linux_io_setup 0x00ce +#define __NR_linux_io_destroy 0x00cf +#define __NR_linux_io_getevents 0x00d0 +#define __NR_linux_io_submit 0x00d1 +#define __NR_linux_io_cancel 0x00d2 +#define __NR_linux_lookup_dcookie 0x00d4 +#define __NR_linux_epoll_create 0x00d5 +#define __NR_linux_epoll_wait 0x00e8 +#define __NR_linux_epoll_ctl 0x00e9 +#define __NR_linux_getdents 0x00d9 +#define __NR_linux_set_tid_address 0x00da +#define __NR_linux_restart_syscall 0x00db +#define __NR_linux_semtimedop 0x00dc +#define __NR_linux_fadvise 0x00dd +#define __NR_linux_timer_create 0x00de +#define __NR_linux_timer_settime 0x00df +#define __NR_linux_timer_gettime 0x00e0 +#define __NR_linux_timer_getoverrun 0x00e1 +#define __NR_linux_timer_delete 0x00e2 +#define __NR_linux_clock_settime 0x00e3 +#define __NR_linux_clock_gettime 0x00e4 +#define __NR_linux_clock_getres 0x00e5 +#define __NR_linux_clock_nanosleep 0x00e6 +#define __NR_linux_tgkill 0x00ea +#define __NR_linux_mbind 0x00ed +#define __NR_linux_set_mempolicy 0x00ee +#define __NR_linux_get_mempolicy 0x00ef +#define __NR_linux_mq_open 0x00f0 +#define __NR_linux_mq_unlink 0x00f1 +#define __NR_linux_mq_timedsend 0x00f2 +#define __NR_linux_mq_timedreceive 0x00f3 +#define __NR_linux_mq_notify 0x00f4 +#define __NR_linux_mq_getsetattr 0x00f5 +#define __NR_linux_kexec_load 0x00f6 +#define __NR_linux_waitid 0x00f7 +#define __NR_linux_add_key 0x00f8 +#define __NR_linux_request_key 0x00f9 +#define __NR_linux_keyctl 0x00fa +#define __NR_linux_ioprio_set 0x00fb +#define __NR_linux_ioprio_get 0x00fc +#define __NR_linux_inotify_init 0x00fd +#define __NR_linux_inotify_add_watch 0x00fe +#define __NR_linux_inotify_rm_watch 0x00ff +#define __NR_linux_openat 0x0101 +#define __NR_linux_mkdirat 0x0102 +#define __NR_linux_fchownat 0x0104 +#define __NR_linux_utime 0x0084 +#define __NR_linux_utimes 0x00eb +#define __NR_linux_futimesat 0x0105 +#define __NR_linux_fstatat 0x0106 +#define __NR_linux_unlinkat 0x0107 +#define __NR_linux_renameat 0x0108 +#define __NR_linux_linkat 0x0109 +#define __NR_linux_symlinkat 0x010a +#define __NR_linux_readlinkat 0x010b +#define __NR_linux_fchmodat 0x010c +#define __NR_linux_faccessat 0x010d +#define __NR_linux_unshare 0x0110 +#define __NR_linux_splice 0x0113 +#define __NR_linux_tee 0x0114 +#define __NR_linux_sync_file_range 0x0115 +#define __NR_linux_vmsplice 0x0116 +#define __NR_linux_migrate_pages 0x0100 +#define __NR_linux_move_pages 0x0117 +#define __NR_linux_preadv 0x0127 +#define __NR_linux_pwritev 0x0128 +#define __NR_linux_utimensat 0x0118 +#define __NR_linux_fallocate 0x011d +#define __NR_linux_accept4 0x0120 +#define __NR_linux_dup3 0x0124 +#define __NR_linux_pipe2 0x0125 +#define __NR_linux_epoll_pwait 0x0119 +#define __NR_linux_epoll_create1 0x0123 +#define __NR_linux_perf_event_open 0x012a +#define __NR_linux_inotify_init1 0x0126 +#define __NR_linux_rt_tgsigqueueinfo 0x0129 +#define __NR_linux_signalfd 0x011a +#define __NR_linux_signalfd4 0x0121 +#define __NR_linux_eventfd 0x011c +#define __NR_linux_eventfd2 0x0122 +#define __NR_linux_timerfd_create 0x011b +#define __NR_linux_timerfd_settime 0x011e +#define __NR_linux_timerfd_gettime 0x011f +#define __NR_linux_recvmmsg 0x012b +#define __NR_linux_fanotify_init 0x012c +#define __NR_linux_fanotify_mark 0x012d +#define __NR_linux_prlimit 0x012e +#define __NR_linux_name_to_handle_at 0x012f +#define __NR_linux_open_by_handle_at 0x0130 +#define __NR_linux_clock_adjtime 0x0131 +#define __NR_linux_syncfs 0x0132 +#define __NR_linux_sendmmsg 0x0133 +#define __NR_linux_setns 0x0134 +#define __NR_linux_getcpu 0x0135 +#define __NR_linux_process_vm_readv 0x0136 +#define __NR_linux_process_vm_writev 0x0137 +#define __NR_linux_kcmp 0x0138 +#define __NR_linux_finit_module 0x0139 +#define __NR_linux_sched_setattr 0x013a +#define __NR_linux_sched_getattr 0x013b +#define __NR_linux_renameat2 0x013c +#define __NR_linux_seccomp 0x013d +#define __NR_linux_getrandom 0x013e +#define __NR_linux_memfd_create 0x013f +#define __NR_linux_kexec_file_load 0x0140 +#define __NR_linux_bpf 0x0141 +#define __NR_linux_execveat 0x0142 +#define __NR_linux_userfaultfd 0x0143 +#define __NR_linux_membarrier 0x0144 +#define __NR_linux_mlock2 0x0145 +#define __NR_linux_copy_file_range 0x0146 +#define __NR_linux_preadv2 0x0147 +#define __NR_linux_pwritev2 0x0148 +#define __NR_linux_pkey_mprotect 0x0149 +#define __NR_linux_pkey_alloc 0x014a +#define __NR_linux_pkey_free 0x014b +#define __NR_linux_statx 0x014c +#define __NR_linux_io_pgetevents 0x014d +#define __NR_linux_rseq 0x014e +#define __NR_linux_pidfd_send_signal 0x01a8 +#define __NR_linux_io_uring_setup 0x01a9 +#define __NR_linux_io_uring_enter 0x01aa +#define __NR_linux_io_uring_register 0x01ab + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_NRLINUX_H_ */ diff --git a/libc/sysv/syscalls.sh b/libc/sysv/syscalls.sh index 92e5bc1a1..bbd647557 100755 --- a/libc/sysv/syscalls.sh +++ b/libc/sysv/syscalls.sh @@ -160,8 +160,8 @@ scall sys_setuid 0x0170170172017069 globl hidden scall sys_setgid 0x0b50b50b520b506a globl hidden scall sys_setresuid 0xfff11a137ffff075 globl hidden # polyfilled for xnu scall sys_setresgid 0xfff11c138ffff077 globl hidden # polyfilled for xnu -scall getresuid 0xfff119168ffff076 globl # semantics aren't well-defined -scall getresgid 0xfff11b169ffff078 globl # semantics aren't well-defined +scall sys_getresuid 0xfff119168ffff076 globl # semantics aren't well-defined +scall sys_getresgid 0xfff11b169ffff078 globl # semantics aren't well-defined scall sigpending 0x124034034203407f globl # a.k.a. rt_sigpending on linux scall sys_sigsuspend 0x12606f155206f082 globl hidden # a.k.a. rt_sigsuspend on Linux; openbsd:byvalue, sigsuspend_nocancel on XNU scall sys_sigaltstack 0x1191200352035083 globl hidden @@ -373,7 +373,7 @@ scall io_uring_setup 0xfffffffffffff1a9 globl # └─ gnu founder richard sta scall io_uring_enter 0xfffffffffffff1aa globl scall io_uring_register 0xfffffffffffff1ab globl #────────────────────────RHEL CLOUD────────────────────────── # ←┬─ red hat terminates community release of enterprise linux circa 2020 -scall pledge 0xfff06cffffffffff globl # └─ online linux services ban the president of united states of america +scall sys_pledge 0xfff06cffffffffff globl # └─ online linux services ban the president of united states of america scall msyscall 0xfff025ffffffffff globl # The Fifth Bell System Interface, Community Edition diff --git a/libc/testlib/testlib.h b/libc/testlib/testlib.h index 1ad271559..f0252c9dc 100644 --- a/libc/testlib/testlib.h +++ b/libc/testlib/testlib.h @@ -315,8 +315,8 @@ struct TestFixture { }; extern char g_fixturename[256]; -extern char g_testlib_olddir[PATH_MAX + 1]; -extern char g_testlib_tmpdir[PATH_MAX + 1]; +extern char g_testlib_olddir[PATH_MAX]; +extern char g_testlib_tmpdir[PATH_MAX]; extern bool g_testlib_shoulddebugbreak; /* set by testmain */ extern unsigned g_testlib_ran; /* set by wrappers */ extern unsigned g_testlib_failed; /* set by wrappers */ diff --git a/libc/testlib/testmain.c b/libc/testlib/testmain.c index fa079f0b2..05fbb3cd9 100644 --- a/libc/testlib/testmain.c +++ b/libc/testlib/testmain.c @@ -142,6 +142,7 @@ noasan int main(int argc, char *argv[]) { const char *comdbg; __log_level = kLogInfo; GetOpts(argc, argv); + setenv("GDB", "", true); // normalize this process FixIrregularFds(); diff --git a/libc/testlib/testrunner.c b/libc/testlib/testrunner.c index 0b076645c..b9fdfbfda 100644 --- a/libc/testlib/testrunner.c +++ b/libc/testlib/testrunner.c @@ -46,9 +46,9 @@ #include "libc/x/x.h" static int x; +char g_testlib_olddir[PATH_MAX]; +char g_testlib_tmpdir[PATH_MAX]; struct sigaction wanthandlers[31]; -char g_testlib_olddir[PATH_MAX + 1]; -char g_testlib_tmpdir[PATH_MAX + 1]; void testlib_finish(void) { if (g_testlib_failed) { diff --git a/libc/time/localtime.c b/libc/time/localtime.c index 68ac75a34..5e50d32de 100644 --- a/libc/time/localtime.c +++ b/libc/time/localtime.c @@ -292,10 +292,8 @@ update_tzname_etc(struct state const *sp, struct ttinfo const *ttisp) #if HAVE_TZNAME tzname[ttisp->tt_isdst] = (char *) &sp->chars[ttisp->tt_desigidx]; #endif -#if USG_COMPAT if (!ttisp->tt_isdst) timezone = - ttisp->tt_utoff; -#endif #if ALTZONE if (ttisp->tt_isdst) altzone = - ttisp->tt_utoff; @@ -311,10 +309,8 @@ settzname(void) #if HAVE_TZNAME tzname[0] = tzname[1] = (char *) (sp ? wildabbr : gmt); #endif -#if USG_COMPAT daylight = 0; timezone = 0; -#endif #if ALTZONE altzone = 0; #endif @@ -333,10 +329,8 @@ settzname(void) &sp->ttis[ sp->types[i]]; update_tzname_etc(sp, ttisp); -#if USG_COMPAT if (ttisp->tt_isdst) daylight = 1; -#endif } } diff --git a/libc/time/time.h b/libc/time/time.h index 47e498bee..510d19ba8 100644 --- a/libc/time/time.h +++ b/libc/time/time.h @@ -17,6 +17,8 @@ hidden extern const unsigned short kMonthYearDay[2][12]; extern char *tzname[2]; extern long CLOCKS_PER_SEC; +extern long timezone; +extern int daylight; int64_t clock(void); int64_t time(int64_t *); diff --git a/libc/time/timezone.c b/libc/time/timezone.c new file mode 100644 index 000000000..80d45411c --- /dev/null +++ b/libc/time/timezone.c @@ -0,0 +1,23 @@ +/*-*- 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 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/time/time.h" + +char *tzname[2]; +long timezone; +int daylight; diff --git a/libc/time/tz.internal.h b/libc/time/tz.internal.h index 5c4f2acaf..8dd16b6da 100644 --- a/libc/time/tz.internal.h +++ b/libc/time/tz.internal.h @@ -1,5 +1,6 @@ #ifndef COSMOPOLITAN_THIRD_PARTY_TZ_PRIVATE_H_ #define COSMOPOLITAN_THIRD_PARTY_TZ_PRIVATE_H_ +#include "libc/calls/calls.h" #include "libc/calls/weirdtypes.h" #include "libc/errno.h" #include "libc/inttypes.h" @@ -8,6 +9,7 @@ #include "libc/sysv/consts/ok.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ + /* clang-format off */ /* Private header for tzdb code. */ diff --git a/net/https/getsslcachefile.c b/net/https/getsslcachefile.c index 6023a471f..bbcffaf3c 100644 --- a/net/https/getsslcachefile.c +++ b/net/https/getsslcachefile.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/safemacros.internal.h" #include "libc/fmt/fmt.h" +#include "libc/macros.internal.h" #include "libc/runtime/runtime.h" #include "net/https/sslcache.h" @@ -26,10 +27,10 @@ * @return pointer to static memory */ char *GetSslCacheFile(void) { - static char sslcachefile[PATH_MAX + 1]; + static char sslcachefile[PATH_MAX]; if (snprintf(sslcachefile, sizeof(sslcachefile), "%s/%s.sslcache", firstnonnull(getenv("TMPDIR"), "/tmp"), - getenv("USER")) <= PATH_MAX) { + getenv("USER")) < ARRAYLEN(sslcachefile)) { return sslcachefile; } else { return 0; diff --git a/net/https/getsslroots.c b/net/https/getsslroots.c index d6bd77fcb..7b5bc2fc2 100644 --- a/net/https/getsslroots.c +++ b/net/https/getsslroots.c @@ -47,15 +47,15 @@ mbedtls_x509_crt *GetSslRoots(void) { size_t n, m; struct dirent *e; static bool once; + char path[PATH_MAX]; static mbedtls_x509_crt *c; - char path[PATH_MAX + 1]; if (!once) { if ((c = calloc(1, sizeof(*c)))) { m = stpcpy(path, "/zip/usr/share/ssl/root/") - path; if ((d = opendir(path))) { while ((e = readdir(d))) { if (e->d_type != DT_REG) continue; - if (m + (n = strlen(e->d_name)) > PATH_MAX) continue; + if (m + (n = strlen(e->d_name)) >= ARRAYLEN(path)) continue; memcpy(path + m, e->d_name, n + 1); CHECK((p = xslurp(path, &n))); CHECK_GE(mbedtls_x509_crt_parse(c, p, n + 1), 0, "%s", path); diff --git a/test/libc/calls/commandv_test.c b/test/libc/calls/commandv_test.c index e30ae0cda..955d41266 100644 --- a/test/libc/calls/commandv_test.c +++ b/test/libc/calls/commandv_test.c @@ -35,8 +35,8 @@ uint64_t i; char *oldpath; -char tmp[PATH_MAX + 1]; -char pathbuf[PATH_MAX + 1]; +char tmp[PATH_MAX]; +char pathbuf[PATH_MAX]; char testlib_enable_tmp_setup_teardown; void SetUp(void) { diff --git a/test/libc/calls/getcwd_test.c b/test/libc/calls/getcwd_test.c index 111d6d03f..022ca22b6 100644 --- a/test/libc/calls/getcwd_test.c +++ b/test/libc/calls/getcwd_test.c @@ -43,7 +43,7 @@ TEST(getcwd, testNullBuf_allocatesResult) { TEST(getcwd, testWindows_addsFunnyPrefix) { if (!IsWindows()) return; - char path[PATH_MAX + 1]; + char path[PATH_MAX]; ASSERT_NE(0, getcwd(path, sizeof(path))); EXPECT_STARTSWITH("//?/", path); } diff --git a/test/libc/calls/mkntcmdline_test.c b/test/libc/calls/mkntcmdline_test.c index 755e7f6db..db1b4afe8 100644 --- a/test/libc/calls/mkntcmdline_test.c +++ b/test/libc/calls/mkntcmdline_test.c @@ -23,7 +23,7 @@ #include "libc/str/str.h" #include "libc/testlib/testlib.h" -char16_t cmdline[ARG_MAX]; +char16_t cmdline[ARG_MAX / 2]; TEST(mkntcmdline, emptyArgvList_cantBeEmptyOnWindows) { char *argv[] = {NULL}; diff --git a/test/libc/calls/mkntenvblock_test.c b/test/libc/calls/mkntenvblock_test.c index b441a08fa..3c7c4bac2 100644 --- a/test/libc/calls/mkntenvblock_test.c +++ b/test/libc/calls/mkntenvblock_test.c @@ -20,7 +20,7 @@ #include "libc/runtime/gc.internal.h" #include "libc/testlib/testlib.h" -char16_t envvars[ARG_MAX]; +char16_t envvars[ARG_MAX / 2]; TEST(mkntenvblock, emptyList_onlyOutputsDoubleNulStringTerminator) { char *envp[] = {NULL}; diff --git a/test/libc/calls/seccomp_test.c b/test/libc/calls/seccomp_test.c index 6b576b9f0..3fe4a74b8 100644 --- a/test/libc/calls/seccomp_test.c +++ b/test/libc/calls/seccomp_test.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/calls/internal.h" #include "libc/calls/struct/bpf.h" #include "libc/calls/struct/filter.h" #include "libc/calls/struct/iovec.h" @@ -31,16 +32,6 @@ #include "libc/testlib/testlib.h" #include "tool/net/sandbox.h" -bool __is_linux_2_6_23(void) { - int rc; - if (!IsLinux()) return false; - asm volatile("syscall" - : "=a"(rc) - : "0"(157), "D"(PR_GET_SECCOMP) - : "rcx", "r11", "memory"); - return rc != -EINVAL; -} - void SetUp(void) { if (!__is_linux_2_6_23()) { exit(0); diff --git a/test/libc/mem/pledge_test.c b/test/libc/mem/pledge_test.c new file mode 100644 index 000000000..808141f41 --- /dev/null +++ b/test/libc/mem/pledge_test.c @@ -0,0 +1,62 @@ +/*-*- 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 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/dce.h" +#include "libc/errno.h" +#include "libc/mem/mem.h" +#include "libc/runtime/runtime.h" +#include "libc/sysv/consts/o.h" +#include "libc/testlib/testlib.h" + +void SetUp(void) { + if (!__is_linux_2_6_23() && !IsOpenbsd()) { + exit(0); + } +} + +TEST(pledge, default_allowsExit) { + int ws, pid; + ASSERT_NE(-1, (pid = fork())); + if (!pid) { + ASSERT_SYS(0, 0, pledge("", 0)); + _Exit(0); + } + EXPECT_NE(-1, wait(&ws)); + EXPECT_TRUE(WIFEXITED(ws)); + EXPECT_EQ(0, WEXITSTATUS(ws)); +} + +TEST(pledge, stdio_forbidsOpeningPasswd) { + int ws, pid; + ASSERT_NE(-1, (pid = fork())); + if (!pid) { + ASSERT_SYS(0, 0, pledge("stdio", 0)); + ASSERT_SYS(EPERM, -1, open("/etc/passwd", O_RDWR)); + _Exit(0); + } + EXPECT_NE(-1, wait(&ws)); + if (IsLinux()) { + EXPECT_TRUE(WIFEXITED(ws)); + EXPECT_EQ(0, WEXITSTATUS(ws)); + } else { + EXPECT_TRUE(WIFSIGNALED(ws)); + EXPECT_EQ(SIGABRT, WTERMSIG(ws)); + } +} diff --git a/test/libc/calls/fork_test.c b/test/libc/runtime/fork_test.c similarity index 76% rename from test/libc/calls/fork_test.c rename to test/libc/runtime/fork_test.c index 53e6c9a74..688d4cd04 100644 --- a/test/libc/calls/fork_test.c +++ b/test/libc/runtime/fork_test.c @@ -17,11 +17,16 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/calls/sigbits.h" +#include "libc/calls/struct/sigaction.h" +#include "libc/dce.h" +#include "libc/log/check.h" #include "libc/macros.internal.h" #include "libc/runtime/runtime.h" #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/msync.h" #include "libc/sysv/consts/prot.h" +#include "libc/sysv/consts/sig.h" #include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" @@ -79,6 +84,51 @@ TEST(fork, testSharedMemory) { EXPECT_NE(-1, munmap(privatevar, FRAMESIZE)); } +static volatile bool gotsigusr1; +static volatile bool gotsigusr2; + +static void OnSigusr1(int sig) { + gotsigusr1 = true; +} + +static void OnSigusr2(int sig) { + gotsigusr2 = true; +} + +TEST(fork, childToChild) { + if (IsWindows()) return; // :'( + sigset_t mask, oldmask; + int ws, parent, child1, child2; + gotsigusr1 = false; + gotsigusr2 = false; + parent = getpid(); + signal(SIGUSR1, OnSigusr1); + signal(SIGUSR2, OnSigusr2); + sigemptyset(&mask); + sigaddset(&mask, SIGUSR2); + sigprocmask(SIG_BLOCK, &mask, &oldmask); + ASSERT_NE(-1, (child1 = fork())); + if (!child1) { + kill(parent, SIGUSR2); + sigsuspend(0); + _Exit(!gotsigusr1); + } + sigdelset(&mask, SIGUSR2); + sigsuspend(&mask); + ASSERT_NE(-1, (child2 = fork())); + if (!child2) { + kill(child1, SIGUSR1); + _Exit(0); + } + ASSERT_NE(-1, wait(&ws)); + EXPECT_TRUE(WIFEXITED(ws)); + EXPECT_EQ(0, WEXITSTATUS(ws)); + ASSERT_NE(-1, wait(&ws)); + EXPECT_TRUE(WIFEXITED(ws)); + EXPECT_EQ(0, WEXITSTATUS(ws)); + sigprocmask(SIG_SETMASK, &oldmask, 0); +} + void ForkInSerial(void) { int pid, ws; ASSERT_NE(-1, (pid = fork())); diff --git a/test/libc/runtime/getdosargv_test.c b/test/libc/runtime/getdosargv_test.c index ccc7e20c0..83c1f247c 100644 --- a/test/libc/runtime/getdosargv_test.c +++ b/test/libc/runtime/getdosargv_test.c @@ -22,7 +22,7 @@ TEST(GetDosArgv, empty) { size_t max = 4; - size_t size = ARG_MAX; + size_t size = ARG_MAX / 2; char *buf = malloc(size * sizeof(char)); char **argv = malloc(max * sizeof(char *)); EXPECT_EQ(0, GetDosArgv(u"", buf, size, argv, max)); @@ -33,7 +33,7 @@ TEST(GetDosArgv, empty) { TEST(GetDosArgv, emptyish) { size_t max = 4; - size_t size = ARG_MAX; + size_t size = ARG_MAX / 2; char *buf = malloc(size * sizeof(char)); char **argv = malloc(max * sizeof(char *)); EXPECT_EQ(0, GetDosArgv(u" ", buf, size, argv, max)); @@ -44,7 +44,7 @@ TEST(GetDosArgv, emptyish) { TEST(GetDosArgv, basicUsage) { size_t max = 4; - size_t size = ARG_MAX; + size_t size = ARG_MAX / 2; char *buf = malloc(size * sizeof(char)); char **argv = malloc(max * sizeof(char *)); EXPECT_EQ(3, GetDosArgv(u"a\t \"b c\" d ", buf, size, argv, max)); @@ -58,7 +58,7 @@ TEST(GetDosArgv, basicUsage) { TEST(GetDosArgv, advancedUsage) { size_t max = 4; - size_t size = ARG_MAX; + size_t size = ARG_MAX / 2; char *buf = malloc(size * sizeof(char)); char **argv = malloc(max * sizeof(char *)); EXPECT_EQ(2, GetDosArgv(u"(╯°□°)╯︵ ┻━┻", buf, size, argv, max)); @@ -71,7 +71,7 @@ TEST(GetDosArgv, advancedUsage) { TEST(GetDosArgv, testAegeanGothicSupplementaryPlanes) { size_t max = 4; /* these symbols are almost as old as dos */ - size_t size = ARG_MAX; + size_t size = ARG_MAX / 2; char *buf = malloc(size * sizeof(char)); char **argv = malloc(max * sizeof(char *)); EXPECT_EQ(2, GetDosArgv(u"𐄷𐄸𐄹𐄺𐄻𐄼 𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷", buf, size, argv, max)); @@ -84,7 +84,7 @@ TEST(GetDosArgv, testAegeanGothicSupplementaryPlanes) { TEST(GetDosArgv, realWorldUsage) { size_t max = 512; - size_t size = ARG_MAX; + size_t size = ARG_MAX / 2; char *buf = malloc(size * sizeof(char)); char **argv = malloc(max * sizeof(char *)); EXPECT_EQ(5, GetDosArgv(u"C:\\Users\\jtunn\\printargs.com oh yes yes yes", @@ -145,7 +145,7 @@ TEST(GetDosArgv, quoteInMiddleOfArg_wontSplitArg) { TEST(GetDosArgv, waqQuoting1) { size_t max = 4; - size_t size = ARG_MAX; + size_t size = ARG_MAX / 2; char *buf = malloc(size * sizeof(char)); char **argv = malloc(max * sizeof(char *)); EXPECT_EQ(2, @@ -159,7 +159,7 @@ TEST(GetDosArgv, waqQuoting1) { TEST(GetDosArgv, waqQuoting2) { size_t max = 4; - size_t size = ARG_MAX; + size_t size = ARG_MAX / 2; char *buf = malloc(size * sizeof(char)); char **argv = malloc(max * sizeof(char *)); EXPECT_EQ(2, GetDosArgv(u"\"a\\\"b c\" d", buf, size, argv, max)); diff --git a/third_party/linenoise/linenoise.c b/third_party/linenoise/linenoise.c index 8c713720d..0f572ba11 100644 --- a/third_party/linenoise/linenoise.c +++ b/third_party/linenoise/linenoise.c @@ -2457,15 +2457,17 @@ static int linenoiseFallback(const char *prompt, char **res) { * @return chomped allocated string of read line or null on eof/error */ char *linenoise(const char *prompt) { - bool rm; char *res; + bool rm, rs; if (linenoiseFallback(prompt, &res)) return res; fflush(stdout); fflush(stdout); rm = __replmode; + rs = __replstderr; __replmode = true; + if (isatty(2)) __replstderr = true; res = linenoiseRaw(prompt, fileno(stdin), fileno(stdout)); - __replmode = false; + __replstderr = rs; __replmode = rm; return res; } diff --git a/third_party/lua/lrepl.c b/third_party/lua/lrepl.c index 5f343f36f..fb618b96c 100644 --- a/third_party/lua/lrepl.c +++ b/third_party/lua/lrepl.c @@ -34,11 +34,11 @@ #include "libc/intrin/nomultics.internal.h" #include "libc/log/check.h" #include "libc/macros.internal.h" +#include "libc/mem/mem.h" #include "libc/runtime/gc.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" #include "libc/sysv/consts/sa.h" -#include "libc/x/x.h" #include "third_party/linenoise/linenoise.h" #include "third_party/lua/cosmo.h" #include "third_party/lua/lauxlib.h" @@ -101,10 +101,11 @@ static void lua_readline_addcompletion (linenoiseCompletions *c, char *s) { void lua_readline_completions (const char *p, linenoiseCompletions *c) { int i; + size_t n; bool found; lua_State *L; const char *name; - char *a, *b, *component; + char *a, *b, *s, *component; // start searching globals L = globalL; @@ -144,9 +145,11 @@ void lua_readline_completions (const char *p, linenoiseCompletions *c) { lua_pushnil(L); while (lua_next(L, -2)) { if (lua_type(L, -2) == LUA_TSTRING) { - name = lua_tostring(L, -2); - if (startswithi(name, a)) { - lua_readline_addcompletion(c, xasprintf("%.*s%s", a - p, p, name)); + name = lua_tolstring(L, -2, &n); + if (startswithi(name, a) && (s = malloc(a - p + n + 1))) { + memcpy(s, p, a - p); + memcpy(s + (a - p), name, n + 1); + lua_readline_addcompletion(c, s); } } lua_pop(L, 1); @@ -157,7 +160,9 @@ void lua_readline_completions (const char *p, linenoiseCompletions *c) { for (i = 0; i < ARRAYLEN(kKeywordHints); ++i) { if (startswithi(kKeywordHints[i], p)) { - lua_readline_addcompletion(c, xstrdup(kKeywordHints[i])); + if ((s = strdup(kKeywordHints[i]))) { + lua_readline_addcompletion(c, s); + } } } if (lua_repl_completions_callback) { @@ -333,6 +338,7 @@ void lua_initrepl(lua_State *L, const char *progname) { lua_repl_linenoise = linenoiseBegin(prompt, 0, 1); lua_pop(L, 1); /* remove prompt */ __replmode = true; + if (isatty(2)) __replstderr = true; } LUA_REPL_UNLOCK; } diff --git a/third_party/python/Lib/_sysconfigdata_m_cosmo_x86_64_cosmo.py b/third_party/python/Lib/_sysconfigdata_m_cosmo_x86_64_cosmo.py index a105b4db2..d86335869 100644 --- a/third_party/python/Lib/_sysconfigdata_m_cosmo_x86_64_cosmo.py +++ b/third_party/python/Lib/_sysconfigdata_m_cosmo_x86_64_cosmo.py @@ -787,7 +787,7 @@ build_time_vars = {'ABIFLAGS': 'm', 'UNICODE_DEPS': '\\', 'UNIVERSALSDK': '', 'UPDATE_FILE': 'python3 ./Tools/scripts/update_file.py', - 'USE_COMPUTED_GOTOS': 0, + 'USE_COMPUTED_GOTOS': 1, 'USE_INLINE': 1, 'VERSION': '3.6', 'WANT_SIGFPE_HANDLER': 0, diff --git a/third_party/python/pyconfig.h b/third_party/python/pyconfig.h index 33d6a177a..c3ff787f0 100644 --- a/third_party/python/pyconfig.h +++ b/third_party/python/pyconfig.h @@ -38,7 +38,7 @@ #define HAVE_WORKING_TZSET 1 #define HAVE_STRUCT_TM_TM_ZONE 1 #define HAVE_TM_ZONE 1 /* deprecated */ -/* #undef HAVE_DECL_TZNAME */ +#define HAVE_DECL_TZNAME 1 /* #undef HAVE_ALTZONE */ /* #undef GETTIMEOFDAY_NO_TZ */ @@ -141,6 +141,7 @@ #define HAVE_FSTATAT 1 #define HAVE_FSYNC 1 #define HAVE_GETENTROPY 1 +#define HAVE_GETLOADAVG 1 /* #undef HAVE_FEXECVE */ /* #undef HAVE_FSTATVFS */ /* #undef HAVE_FTIME */ @@ -148,7 +149,6 @@ /* #define HAVE_SETGROUPS 1 */ /* #define HAVE_INITGROUPS 1 */ /* #define HAVE_GETGROUPLIST 1 */ -/* #undef HAVE_GETLOADAVG */ #define HAVE_FSEEKO 1 #define HAVE_FTELLO 1 @@ -162,8 +162,8 @@ /* #undef HAVE_GETHOSTBYNAME_R_5_ARG */ /* #undef HAVE_GETHOSTBYNAME_R_6_ARG */ -/* #undef HAVE_GETRESGID */ -/* #undef HAVE_GETRESUID */ +#define HAVE_GETRESGID 1 +#define HAVE_GETRESUID 1 /* #undef HAVE_GETSPENT */ /* #undef HAVE_GETSPNAM */ @@ -477,7 +477,7 @@ /* #undef TM_IN_SYS_TIME */ /* Define if you want to use computed gotos in ceval.c. */ -/* #define USE_COMPUTED_GOTOS 1 */ +#define USE_COMPUTED_GOTOS 1 /* Define to use the C99 inline keyword. */ #define USE_INLINE 1 diff --git a/third_party/zip/unix.c b/third_party/zip/unix.c index 7bf51a783..7bc237579 100644 --- a/third_party/zip/unix.c +++ b/third_party/zip/unix.c @@ -10,28 +10,16 @@ also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html */ #include "libc/calls/struct/dirent.h" +#include "libc/calls/calls.h" +#include "libc/sysv/consts/s.h" #include "third_party/zip/zip.h" - -#ifndef UTIL /* the companion #endif is a bit of ways down ... */ - #include "libc/time/time.h" -#include "libc/sysv/consts/_posix.h" #include "libc/calls/struct/stat.macros.h" - -#if defined(MINIX) || defined(__mpexl) -# ifdef S_IWRITE -# undef S_IWRITE -# endif /* S_IWRITE */ -# define S_IWRITE S_IWUSR -#endif /* MINIX */ - -#if (!defined(S_IWRITE) && defined(S_IWUSR)) -# define S_IWRITE S_IWUSR -#endif - #include "libc/calls/calls.h" #include "libc/sysv/consts/dt.h" +#ifndef UTIL /* the companion #endif is a bit of ways down ... */ + #define PAD 0 #define PATH_END '/' diff --git a/tool/build/compile.c b/tool/build/compile.c index a30fde12f..4088fc917 100644 --- a/tool/build/compile.c +++ b/tool/build/compile.c @@ -173,7 +173,7 @@ char *shortened; char *cachedcmd; char *colorflag; char *originalcmd; -char ccpath[PATH_MAX + 1]; +char ccpath[PATH_MAX]; struct stat st; struct Strings env; diff --git a/tool/build/lib/demangle.c b/tool/build/lib/demangle.c index 3f702da00..257178598 100644 --- a/tool/build/lib/demangle.c +++ b/tool/build/lib/demangle.c @@ -42,7 +42,7 @@ void CloseCxxFilt(void) { void SpawnCxxFilt(void) { int pipefds[2][2]; const char *cxxfilt; - char path[PATH_MAX + 1]; + char path[PATH_MAX]; cxxfilt = firstnonnull(emptytonull(getenv("CXXFILT")), "c++filt"); if (commandv(cxxfilt, path, sizeof(path))) { pipe2(pipefds[0], O_CLOEXEC); diff --git a/tool/build/rle.c b/tool/build/rle.c index 7958262fb..5ca186e8b 100644 --- a/tool/build/rle.c +++ b/tool/build/rle.c @@ -21,6 +21,7 @@ #include "libc/errno.h" #include "libc/fmt/fmt.h" #include "libc/log/check.h" +#include "libc/macros.internal.h" #include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" @@ -200,7 +201,8 @@ int Run(char **paths, size_t count) { rc = RunLengthCode(); } else { suffixlen = strlen(suffix_); - if (!IsTrustworthy() && strlen(paths[i]) + suffixlen + 1 > PATH_MAX) { + if (!IsTrustworthy() && + strlen(paths[i]) + suffixlen >= ARRAYLEN(pathbuf)) { return eoverflow(); } p = stpcpy(pathbuf, paths[i]); diff --git a/tool/build/runit.c b/tool/build/runit.c index eef2446e2..60e9e905e 100644 --- a/tool/build/runit.c +++ b/tool/build/runit.c @@ -116,7 +116,7 @@ uint16_t g_sshport; char g_hostname[128]; uint16_t g_runitdport; volatile bool alarmed; -char g_ssh[PATH_MAX + 1]; +char g_ssh[PATH_MAX]; int __sys_execve(const char *, char *const[], char *const[]) hidden; diff --git a/tool/net/demo/unix-subprocess.lua b/tool/net/demo/unix-subprocess.lua index 5d060a42b..04bf3fd28 100644 --- a/tool/net/demo/unix-subprocess.lua +++ b/tool/net/demo/unix-subprocess.lua @@ -46,7 +46,9 @@ function main() end end unix.close(reader) + Log(kLogWarn, 'wait() begin') unix.wait(-1) + Log(kLogWarn, 'wait() end') unix.sigaction(unix.SIGINT, oldint) unix.sigaction(unix.SIGQUIT, oldquit) unix.sigprocmask(unix.SIG_SETMASK, oldmask) diff --git a/tool/net/help.txt b/tool/net/help.txt index d267d3390..7339f14b8 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -74,8 +74,8 @@ FLAGS -P PATH pid file location -U INT daemon set user id -G INT daemon set group id - --strace enables system call tracing - --ftrace enables function call tracing + --strace enables system call tracing (see also -Z) + --ftrace enables function call tracing (see also -f) KEYBOARD @@ -303,7 +303,7 @@ REPL changes will propagate into forked clients. Your REPL is displayed only when redbean is run as a non-daemon in a - UNIX terminal or the Windows 10 command prompt or PowerShell. Since + Unix terminal or the Windows 10 command prompt or PowerShell. Since the REPL is a Lua REPL it's not included in a redbean-static builds. redbean uses the same keyboard shortcuts as GNU Readline and Emacs. @@ -341,6 +341,26 @@ REPL setuid privileges, provided it's configured to drop privileges in the most appropriate manner; see the UNIX section for further details. + +──────────────────────────────────────────────────────────────────────────────── + +LUA ENHANCEMENTS + + We've made some enhancements to the Lua language that should make it + more comfortable for C/C++ and Python developers. Some of these + + - redbean supports a printf modulus operator, like Python. For + example, you can say `"hello %s" % {"world"}` instead of + `string.format("hello %s", "world")`. + + - redbean supports octal (base 8) integer literals. For example + `0644 == 420` is the case in redbean, whereas in upstream Lua + `0644 == 644` would be the case. + + - redbean supports the GNU syntax for the ASCII ESC character in + string literals. For example, `"\e"` is the same as `"\x1b"`. + + ──────────────────────────────────────────────────────────────────────────────── GLOBALS @@ -1239,6 +1259,27 @@ FUNCTIONS the density of information. Cryptographic random should be in the ballpark of 7.9 whereas plaintext will be more like 4.5. + Benchmark(func[, count[, maxattempts]]) + └─→ nanos:real, ticks:int, overhead-ticks:int, tries:int + + Performs microbenchmark. + + The first value returned is the average number of nanoseconds that + `func` needed to execute. Nanoseconds are computed from RDTSC tick + counts, using an approximation that's measured beforehand with the + unix.clock_gettime() function. + + The `ticks` result is the canonical average number of clock ticks. + + This subroutine will subtract whatever the overhead happens to be + for benchmarking a function that does nothing. This overhead value + will be reported in the result. + + `tries` indicates if your microbenchmark needed to be repeated, + possibly because your system is under load and the benchmark was + preempted by the operating system, or moved to a different core. + + ──────────────────────────────────────────────────────────────────────────────── CONSTANTS @@ -1392,12 +1433,13 @@ MAXMIND MODULE For further details, please see maxmind.lua in redbean-demo.com. + ──────────────────────────────────────────────────────────────────────────────── UNIX MODULE - This module exposes the low-level UNIX system call interface. This - module works on all supported platforms, including Windows NT. + This module exposes the low-level System Five system call interface. + This module works on all supported platforms, including Windows NT. unix.open(path:str, flags:int[, mode:int[, dirfd:int]]) ├─→ fd:int @@ -1655,8 +1697,8 @@ UNIX MODULE assert(unix.wait()) end - unix.wait([pid:int, options:int]) - ├─→ pid:int, wstatus:int + unix.wait([pid:int[, options:int]]) + ├─→ pid:int, wstatus:int, unix.Rusage └─→ nil, unix.Errno Waits for subprocess to terminate. @@ -2095,7 +2137,12 @@ UNIX MODULE ├─→ seconds:int, nanos:int └─→ nil, unix.Errno - Returns nanosecond precision timestamp from the system. + Returns nanosecond precision timestamp from system, e.g. + + >: unix.clock_gettime() + 1651137352 774458779 + >: Benchmark(unix.clock_gettime) + 126 393 571 1 `clock` can be any one of of: @@ -2110,12 +2157,14 @@ UNIX MODULE - `CLOCK_BOOTTIME`: linux and openbsd - `CLOCK_REALTIME_ALARM`: linux-only - `CLOCK_BOOTTIME_ALARM`: linux-only - - `CLOCK_TAI`: ilnux-only + - `CLOCK_TAI`: linux-only Returns `EINVAL` if clock isn't supported on platform. This function only fails if `clock` is invalid. + This function goes fastest on Linux and Windows. + unix.nanosleep(seconds:int, nanos:int) ├─→ remseconds:int, remnanos:int └─→ nil, unix.Errno @@ -2631,7 +2680,25 @@ UNIX MODULE ├─→ soft:int, hard:int └─→ nil, unix.Errno - Returns information about resource limit. + Returns information about resource limits for current process. + + unix.getrusage([who:int]) + ├─→ unix.Rusage + └─→ nil, unix.Errno + + Returns information about resource usage for current process, e.g. + + >: unix.getrusage() + {utime={0, 53644000}, maxrss=44896, minflt=545, oublock=24, nvcsw=9} + + `who` defaults to `RUSAGE_SELF` and can be any of: + + - `RUSAGE_SELF`: current process + - `RUSAGE_THREAD`: current thread + - `RUSAGE_CHILDREN`: not supported on Windows NT + - `RUSAGE_BOTH`: not supported on non-Linux + + See the unix.Rusage section below for details on returned fields. unix.gmtime(unixts:int) ├─→ year,mon,mday,hour,min,sec,gmtoffsec,wday,yday,dst:int,zone:str @@ -2653,7 +2720,10 @@ UNIX MODULE ├─→ year,mon,mday,hour,min,sec,gmtoffsec,wday,yday,dst:int,zone:str └─→ nil,unix.Errno - Breaks down UNIX timestamp into local time numbers. + Breaks down UNIX timestamp into local time numbers, e.g. + + >: unix.localtime(unix.clock_gettime()) + 2022 4 28 2 14 22 -25200 4 117 1 "PDT" This follows the same API as gmtime() which has further details. @@ -2741,6 +2811,7 @@ UNIX MODULE returned unix.Dir ownership takes ownership of the file descriptor and will close it automatically when garbage collected. + ──────────────────────────────────────────────────────────────────────────────── UNIX DIR OBJECT @@ -2800,169 +2871,426 @@ UNIX MODULE Resets stream back to beginning. + +──────────────────────────────────────────────────────────────────────────────── + + UNIX RUSAGE OBJECT + + unix.Rusage objects are created by wait() or getrusage(). The + following accessor methods are available. + + unix.Rusage:utime() + └─→ seconds:int, nanos:int + + Returns amount of CPU consumed in userspace. + + It's always the case that `0 ≤ nanos < 1e9`. + + On Windows NT this is collected from GetProcessTimes(). + + unix.Rusage:stime() + └─→ seconds:int, nanos:int + + Returns amount of CPU consumed in kernelspace. + + It's always the case that `0 ≤ 𝑥 < 1e9`. + + On Windows NT this is collected from GetProcessTimes(). + + unix.Rusage:maxrss() + └─→ kilobytes:int + + Returns amount of physical memory used at peak consumption. + + On Windows NT this is collected from + NtProcessMemoryCountersEx::PeakWorkingSetSize / 1024. + + unid.Rusage:idrss() + └─→ integralkilobytes:int + + Returns integral private memory consumption w.r.t. scheduled ticks. + + If you chart memory usage over the lifetime of your process, then + this would be the space filled in beneath the chart. The frequency + of kernel scheduling is defined as unix.CLK_TCK. Each time a tick + happens, the kernel samples your process's memory usage, by adding + it to this value. You can derive the average consumption from this + value by computing how many ticks are in `utime + stime`. + + Currently only available on FreeBSD and NetBSD. + + unix.Rusage:ixrss() + └─→ integralkilobytes:int + + Returns integral shared memory consumption w.r.t. scheduled ticks. + + If you chart memory usage over the lifetime of your process, then + this would be the space filled in beneath the chart. The frequency + of kernel scheduling is defined as unix.CLK_TCK. Each time a tick + happens, the kernel samples your process's memory usage, by adding + it to this value. You can derive the average consumption from this + value by computing how many ticks are in `utime + stime`. + + Currently only available on FreeBSD and NetBSD. + + unis.Rusage:isrss() + └─→ integralkilobytes:int + + Returns integral stack memory consumption w.r.t. scheduled ticks. + + If you chart memory usage over the lifetime of your process, then + this would be the space filled in beneath the chart. The frequency + of kernel scheduling is defined as unix.CLK_TCK. Each time a tick + happens, the kernel samples your process's memory usage, by adding + it to this value. You can derive the average consumption from this + value by computing how many ticks are in `utime + stime`. + + This is only applicable to redbean if its built with MODE=tiny, + because redbean likes to allocate its own deterministic stack. + + Currently only available on FreeBSD and NetBSD. + + unix.Rusage:minflt() + └─→ count:int + + Returns number of minor page faults. + + This number indicates how many times redbean was preempted by the + kernel to memcpy() a 4096-byte page. This is one of the tradeoffs + fork() entails. This number is usually tinier, when your binaries + are tinier. + + Not available on Windows NT. + + unix.Rusage:majflt() + └─→ count:int + + Returns number of major page faults. + + This number indicates how many times redbean was preempted by the + kernel to perform i/o. For example, you might have used mmap() to + load a large file into memory lazily. + + On Windows NT this is NtProcessMemoryCountersEx::PageFaultCount. + + unix.Rusage:nswap() + └─→ count:int + + Returns number of swap operations. + + Operating systems like to reserve hard disk space to back their RAM + guarantees, like using a gold standard for fiat currency. When your + system is under heavy memory load, swap operations may happen while + redbean is working. This number keeps track of them. + + Not available on Linux, Windows NT. + + unix.Rusage:inblock() + └─→ count:int + + Returns number of times filesystem had to perform input. + + On Windows NT this is NtIoCounters::ReadOperationCount. + + unix.Rusage:oublock() + └─→ count:int + + Returns number of times filesystem had to perform output. + + On Windows NT this is NtIoCounters::WriteOperationCount. + + unix.Rusage:msgsnd() + └─→ count:int + + Returns count of ipc messages sent. + + Not available on Linux, Windows NT. + + unix.Rusage:msgrcv() + └─→ count:int + + Returns count of ipc messages received. + + Not available on Linux, Windows NT. + + unix.Rusage:nsignals() + └─→ count:int + + Returns number of signals received. + + Not available on Linux. + + unix.Rusage:nvcsw() + └─→ count:int + + Returns number of voluntary context switches. + + This number is a good thing. It means your redbean finished its work + quickly enough within a time slice that it was able to give back the + remaining time to the system. + + unix.Rusage:nivcsw() + └─→ count:int + + Returns number of non-consensual context switches. + + This number is a bad thing. It means your redbean was preempted by a + higher priority process after failing to finish its work, within the + allotted time slice. + + sandbox.pledge([promises:str]) + ├─→ true + └─→ nil, unix.Errno + + Restrict system operations. + + This can be used to sandbox your redbean workers. It allows finer + customization compared to the `-S` flag. + + By default exit and exit_group are always allowed. This is useful + for processes that perform pure computation and interface with the + parent via shared memory. + + Currently only available on OpenBSD and Linux. On Linux, the default + action when your policy is violated is to return `EPERM`. On OpenBSD + the kernel will kill the process. + + `promises` is a string that may include any of the following groups + delimited by spaces. + + stdio + + Allows clock_getres, clock_gettime, close, dup, dup2, dup3, + fchdir, fstat, fsync, ftruncate, getdents, getegid, getrandom, + geteuid, getgid, getgroups, getitimer, getpgid, getpgrp, getpid, + getppid, getresgid, getresuid, getrlimit, getsid, gettimeofday, + getuid, lseek, madvise, brk, mmap, mprotect, munmap, nanosleep, + pipe, pipe2, poll, pread, preadv, pwrite, pwritev, read, readv, + recvfrom, recvmsg, select, sendmsg, sendto, setitimer, shutdown, + sigaction, sigprocmask, sigreturn, socketpair, umask, wait4, + write, writev. + + rpath + + Allows chdir, getcwd, openat, fstatat, faccessat, readlinkat, + lstat, chmod, fchmod, fchmodat, chown, fchown, fchownat, fstat. + + wpath + + Allows getcwd, openat, fstatat, faccessat, readlinkat, lstat, + chmod, fchmod, fchmodat, chown, fchown, fchownat, fstat. + + cpath + + Allows rename, renameat, link, linkat, symlink, symlinkat, unlink, + unlinkat, mkdir, mkdirat, rmdir. + + dpath + + Allows mknod + + tmppath + + Allows lstat, chmod, chown, unlink, fstat. + + inet + + Allows socket, listen, bind, connect, accept4, accept, + getpeername, getsockname, setsockopt, getsockopt. + + fattr + + Allows utimes, utimensat, chmod, fchmod, fchmodat, chown, + fchownat, lchown, fchown, utimes. + + unix + + Allows socket, listen, bind, connect, accept4, accept, + getpeername, getsockname, setsockopt, getsockopt. + + dns + + Allows sendto, recvfrom, socket, connect. + + proc + + Allows fork, vfork, kill, getpriority, setpriority, setrlimit, + setpgid, setsid. + + exec + + Allows execve. + + id + + Allows setuid, setreuid, setresuid, setgid, setregid, setresgid, + setgroups, setrlimit, getpriority, setpriority. + + ──────────────────────────────────────────────────────────────────────────────── UNIX STAT OBJECT unix.Stat objects are created by stat() or fstat(). The following - methods are available: + accessor methods are available. - unix.Stat:size() - └─→ bytes:int + unix.Stat:size() + └─→ bytes:int - Size of file in bytes. + Size of file in bytes. - unix.Stat:mode() - └─→ mode:int + unix.Stat:mode() + └─→ mode:int - Contains file type and permissions. + Contains file type and permissions. - For example, `0010644` is what you might see for a file and - `0040755` is what you might see for a directory. + For example, `0010644` is what you might see for a file and + `0040755` is what you might see for a directory. - To determine the file type: + To determine the file type: - - `(st:mode() & 0170000) == 0010000` means fifo or pipe - - `(st:mode() & 0170000) == 0020000` means character device - - `(st:mode() & 0170000) == 0040000` means directory - - `(st:mode() & 0170000) == 0060000` means block device - - `(st:mode() & 0170000) == 0100000` means regular file - - `(st:mode() & 0170000) == 0120000` means symbolic link - - `(st:mode() & 0170000) == 0140000` means socket + - `(st:mode() & 0170000) == 0010000` means fifo or pipe + - `(st:mode() & 0170000) == 0020000` means character device + - `(st:mode() & 0170000) == 0040000` means directory + - `(st:mode() & 0170000) == 0060000` means block device + - `(st:mode() & 0170000) == 0100000` means regular file + - `(st:mode() & 0170000) == 0120000` means symbolic link + - `(st:mode() & 0170000) == 0140000` means socket - unix.Stat:uid() - └─→ uid:int + unix.Stat:uid() + └─→ uid:int - User ID of file owner. + User ID of file owner. - unix.Stat:gid() - └─→ gid:int + unix.Stat:gid() + └─→ gid:int - Group ID of file owner. + Group ID of file owner. - unix.Stat:birthtim() - └─→ unixts:int, nanos:int + unix.Stat:birthtim() + └─→ unixts:int, nanos:int - File birth time. + File birth time. - This field should be accurate on Apple, Windows, and BSDs. On Linux - this is the mimimum of atim/mtim/ctim. On Windows NT nanos is only - accurate to hectonanoseconds. + This field should be accurate on Apple, Windows, and BSDs. On Linux + this is the mimimum of atim/mtim/ctim. On Windows NT nanos is only + accurate to hectonanoseconds. - Here's an example of how you might print a file timestamp: + Here's an example of how you might print a file timestamp: - st = assert(unix.stat('/etc/passwd')) - unixts, nanos = st:birthtim() - year,mon,mday,hour,min,sec,gmtoffsec = unix.localtime(unixts) - Write('%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.9d%+.2d%.2d % { - year, mon, mday, hour, min, sec, nanos, - gmtoffsec / (60 * 60), math.abs(gmtoffsec) % 60}) + st = assert(unix.stat('/etc/passwd')) + unixts, nanos = st:birthtim() + year,mon,mday,hour,min,sec,gmtoffsec = unix.localtime(unixts) + Write('%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.9d%+.2d%.2d % { + year, mon, mday, hour, min, sec, nanos, + gmtoffsec / (60 * 60), math.abs(gmtoffsec) % 60}) - unix.Stat:mtim() - └─→ unixts:int, nanos:int + unix.Stat:mtim() + └─→ unixts:int, nanos:int - Last modified time. + Last modified time. - unix.Stat:atim() - └─→ unixts:int, nanos:int + unix.Stat:atim() + └─→ unixts:int, nanos:int - Last access time. + Last access time. - Please note that file systems are sometimes mounted with `noatime` - out of concern for i/o performance. Linux also provides `O_NOATIME` - as an option for open(). + Please note that file systems are sometimes mounted with `noatime` + out of concern for i/o performance. Linux also provides `O_NOATIME` + as an option for open(). - On Windows NT this is the same as birth time. + On Windows NT this is the same as birth time. - unix.Stat:ctim() - └─→ unixts:int, nanos:int + unix.Stat:ctim() + └─→ unixts:int, nanos:int - Complicated time. + Complicated time. - Means time file status was last changed on UNIX. + Means time file status was last changed on UNIX. - On Windows NT this is the same as birth time. + On Windows NT this is the same as birth time. - unix.Stat:blocks() - └─→ count512:int + unix.Stat:blocks() + └─→ count512:int - Number of 512-byte blocks used by storage medium. + Number of 512-byte blocks used by storage medium. - This provides some indication of how much physical storage a file - actually consumes. For example, for small file systems, your system - might report this number as being 8, which means 4096 bytes. + This provides some indication of how much physical storage a file + actually consumes. For example, for small file systems, your system + might report this number as being 8, which means 4096 bytes. - On Windows NT, if `O_COMPRESSED` is used for a file, then this - number will reflect the size *after* compression. you can use: + On Windows NT, if `O_COMPRESSED` is used for a file, then this + number will reflect the size *after* compression. you can use: - st = assert(unix.stat("moby.txt")) - print('file size is %d bytes' % {st:size()}) - print('file takes up %d bytes of space' % {st:blocks() * 512}) - if GetHostOs() == 'WINDOWS' and st:flags() & 0x800 then - print('thanks to file system compression') - end + st = assert(unix.stat("moby.txt")) + print('file size is %d bytes' % {st:size()}) + print('file takes up %d bytes of space' % {st:blocks() * 512}) + if GetHostOs() == 'WINDOWS' and st:flags() & 0x800 then + print('thanks to file system compression') + end - To tell whether or not compression is being used on a file, + To tell whether or not compression is being used on a file, - unix.Stat:blksize() - └─→ bytes:int + unix.Stat:blksize() + └─→ bytes:int - Block size that underlying device uses. + Block size that underlying device uses. - This field might be of assistance in computing optimal i/o sizes. + This field might be of assistance in computing optimal i/o sizes. - Please note this field has no relationship to blocks, as the latter - is fixed at a 512 byte size. + Please note this field has no relationship to blocks, as the latter + is fixed at a 512 byte size. - unix.Stat:ino() - └─→ inode:int + unix.Stat:ino() + └─→ inode:int - Inode number. + Inode number. - This can be used to detect some other process used rename() to swap - out a file underneath you, so you can do a refresh. redbean does it - during each main process heartbeat for its own use cases. + This can be used to detect some other process used rename() to swap + out a file underneath you, so you can do a refresh. redbean does it + during each main process heartbeat for its own use cases. - On Windows NT this is set to NtByHandleFileInformation::FileIndex. + On Windows NT this is set to NtByHandleFileInformation::FileIndex. - unix.Stat:dev() - └─→ dev:int + unix.Stat:dev() + └─→ dev:int - ID of device containing file. + ID of device containing file. - On Windows NT this is set to - NtByHandleFileInformation::VolumeSerialNumber. + On Windows NT this is set to + NtByHandleFileInformation::VolumeSerialNumber. - unix.Stat:rdev() - └─→ rdev:int + unix.Stat:rdev() + └─→ rdev:int - Information about device type. + Information about device type. + + This value may be set to 0 or -1 for files that aren't devices, + depending on the operating system. unix.major() and unix.minor() + may be used to extract the device numbers. - This value may be set to 0 or -1 for files that aren't devices, - depending on the operating system. unix.major() and unix.minor() - may be used to extract the device numbers. ──────────────────────────────────────────────────────────────────────────────── UNIX SIGSET OBJECT + The unix.Sigset class defines a mutable bitset that may currently + contain 128 entries. See `unix.NSIG` to find out how many signals + your operating system actually supports. + unix.Sigset(sig:int, ...) └─→ unix.Sigset - Creates new signal bitset. + Constructs new signal bitset object. unix.Sigset:add(sig:int) Adds signal to bitset. - Invalid signal numbers are ignored. - unix.Sigset:remove(sig:int) Removes signal from bitset. - Invalid signal numbers are ignored. - unix.Sigset:fill() Sets all bits in signal bitset to true. @@ -2981,9 +3309,10 @@ UNIX MODULE Returns Lua code string that recreates object. + ──────────────────────────────────────────────────────────────────────────────── - UNIX SIGNALS + UNIX SIGNAL MAGNUMS unix.SIGINT Terminal CTRL-C keystroke. @@ -3068,9 +3397,75 @@ UNIX MODULE unix.SIGPWR Not implemented in most community editions of system five. + ──────────────────────────────────────────────────────────────────────────────── - UNIX ERRORS + UNIX ERRNO OBJECT + + This object is returned by system calls that fail. We prefer returning + an object because for many system calls, an error is part their normal + operation. For example, it's often desirable to use the errno() method + when performing a read() to check for EINTR. + + unix.Errno:errno() + └─→ errno:int + + Returns error magic number. + + The error number is always different for different platforms. On + UNIX systems, error numbers occupy the range [1,127] in practice. + The System V ABI reserves numbers as high as 4095. On Windows NT, + error numbers can go up to 65535. + + unix.Errno:winerr() + └─→ errno:int + + Returns Windows error number. + + On UNIX systems this is always 0. On Windows NT this will normally + be the same as errno(). Because Windows defines so many error codes, + there's oftentimes a multimapping between its error codes and System + Five. In those cases, this value reflect the GetLastError() result + at the time the error occurred. + + unix.Errno:name() + └─→ symbol:str + + Returns string of symbolic name of System Five error code. + + For example, this might return `"EINTR"`. + + unix.Errno:call() + └─→ symbol:str + + Returns name of system call that failed. + + For example, this might return `"read"` if read() was what failed. + + unix.Errno:doc() + └─→ symbol:str + + Returns English string describing System Five error code. + + For example, this might return `"Interrupted system call"`. + + unix.Errno:__tostring() + └─→ str + + Returns verbose string describing error. + + Different information components are delimited by slash. + + For example, this might return `"EINTR/4/Interrupted system call"`. + + On Windows NT this will include additional information about the + Windows error (including FormatMessage() output) if the WIN32 error + differs from the System Five error code. + + +──────────────────────────────────────────────────────────────────────────────── + + UNIX ERROR MAGNUMS unix.EINVAL Invalid argument. @@ -3177,7 +3572,7 @@ UNIX MODULE unix.ENAMETOOLONG Filename too long. Cosmopolitan Libc currently defines `PATH_MAX` as - 512 characters. On UNIX, that limit should only apply to system call + 1024 characters. On UNIX that limit should only apply to system call wrappers like realpath(). On Windows NT it's observed by all system calls that accept a pathname. @@ -3509,23 +3904,85 @@ UNIX MODULE no data available; barely in posix; returned by ioctl; very close in spirit to EPIPE? + ──────────────────────────────────────────────────────────────────────────────── -LUA ENHANCEMENTS + UNIX MISCELLANEOUS MAGNUMS - We've made some enhancements to the Lua language that should make it - more comfortable for C/C++ and Python developers. Some of these + unix.ARG_MAX - - redbean supports a printf modulus operator, like Python. For - example, you can say `"hello %s" % {"world"}` instead of - `string.format("hello %s", "world")`. + Returns maximum length of arguments for new processes. - - redbean supports octal (base 8) integer literals. For example - `0644 == 420` is the case in redbean, whereas in upstream Lua - `0644 == 644` would be the case. + This is the character limit when calling execve(). It's the sum of + the lengths of `argv` and `envp` including any nul terminators and + pointer arrays. For example to see how much your shell `envp` uses + + $ echo $(($(env | wc -c) + 1 + ($(env | wc -l) + 1) * 8)) + 758 + + POSIX mandates this be 4096 or higher. On Linux this it's 128*1024. + On Windows NT it's 32767*2 because CreateProcess lpCommandLine and + environment block are separately constrained to 32,767 characters. + Most other systems define this limit much higher. + + unix.BUFSIZ + + Returns default buffer size. + + The UNIX module does not perform any buffering between calls. + + Each time a read or write is performed via the UNIX API your redbean + will allocate a buffer of this size by default. This current default + would be 4096 across platforms. + + unix.CLK_TCK + + Returns the scheduler frequency. + + This is granularity at which the kernel does work. For example, the + Linux kernel normally operates at 100hz so its CLK_TCK will be 100. + + This value is useful for making sense out of unix.Rusage data. + + unix.PIPE_BUF + + Returns maximum size at which pipe i/o is guaranteed atomic. + + POSIX requires this be at least 512. Linux is more generous and + allows 4096. On Windows NT this is currently 4096, and it's the + parameter redbean passes to CreateNamedPipe(). + + unix.PATH_MAX + + Returns maximum length of file path. + + This applies to a complete path being passed to system calls. + + POSIX.1 XSI requires this be at least 1024 so that's what most + platforms support. On Windows NT, the limit is technically 260 + characters. Your redbean works around that by prefixing `//?/` + to your paths as needed. On Linux this limit will be 4096, but + that won't be the case for functions such as realpath that are + implemented at the C library level; however such functions are + the exception rather than the norm, and report enametoolong(), + when exceeding the libc limit. + + unix.NAME_MAX + + Returns maximum length of file path component. + + POSIX requires this be at least 14. Most operating systems define it + as 255. It's a good idea to not exceed 253 since that's the limit on + DNS labels. + + unix.NSIG + + Returns maximum number of signals supported by underlying system. + + The limit for unix.Sigset is 128 to support FreeBSD, but most + operating systems define this much lower, like 32. This constant + reflects the value chosen by the underlying operating system. - - redbean supports the GNU syntax for the ASCII ESC character in - string literals. For example, `"\e"` is the same as `"\x1b"`. ──────────────────────────────────────────────────────────────────────────────── diff --git a/tool/net/lfuncs.c b/tool/net/lfuncs.c index d828985ba..e318d4903 100644 --- a/tool/net/lfuncs.c +++ b/tool/net/lfuncs.c @@ -24,6 +24,7 @@ #include "libc/log/check.h" #include "libc/log/log.h" #include "libc/macros.internal.h" +#include "libc/math.h" #include "libc/mem/mem.h" #include "libc/nexgen32e/bench.h" #include "libc/nexgen32e/bsf.h" @@ -33,6 +34,7 @@ #include "libc/nexgen32e/rdtscp.h" #include "libc/rand/rand.h" #include "libc/runtime/gc.internal.h" +#include "libc/runtime/runtime.h" #include "libc/runtime/sysconf.h" #include "libc/sock/sock.h" #include "libc/sysv/consts/af.h" @@ -375,7 +377,7 @@ int LuaGetRandomBytes(lua_State *L) { luaL_argerror(L, 1, "not in range 1..256"); unreachable; } - p = malloc(n); + p = xmalloc(n); CHECK_EQ(n, getrandom(p, n, 0)); lua_pushlstring(L, p, n); free(p); @@ -585,23 +587,30 @@ static int64_t GetInterrupts(void) { } } +static int DoNothing(lua_State *L) { + return 0; +} + int LuaBenchmark(lua_State *L) { - double avgticks; uint64_t t1, t2; int64_t interrupts; + double avgticks, overhead; int core, iter, count, tries, attempts, maxattempts; luaL_checktype(L, 1, LUA_TFUNCTION); count = luaL_optinteger(L, 2, 100); maxattempts = luaL_optinteger(L, 3, 10); + lua_gc(L, LUA_GCSTOP); + for (attempts = 0;;) { + lua_gc(L, LUA_GCCOLLECT); sched_yield(); core = TSC_AUX_CORE(Rdpid()); interrupts = GetInterrupts(); for (avgticks = iter = 1; iter < count; ++iter) { - t1 = __startbench(); - lua_pushvalue(L, 1); + lua_pushcfunction(L, DoNothing); + t1 = __startbench_m(); lua_call(L, 0, 0); - t2 = __endbench(); + t2 = __endbench_m(); avgticks += 1. / iter * ((int)(t2 - t1) - avgticks); } ++attempts; @@ -611,8 +620,33 @@ int LuaBenchmark(lua_State *L) { return luaL_error(L, "system is under too much load to run benchmark"); } } - lua_pushnumber(L, ConvertTicksToNanos(avgticks)); - lua_pushinteger(L, avgticks); + overhead = avgticks; + + for (attempts = 0;;) { + lua_gc(L, LUA_GCCOLLECT); + sched_yield(); + core = TSC_AUX_CORE(Rdpid()); + interrupts = GetInterrupts(); + for (avgticks = iter = 1; iter < count; ++iter) { + lua_pushvalue(L, 1); + t1 = __startbench_m(); + lua_call(L, 0, 0); + t2 = __endbench_m(); + avgticks += 1. / iter * ((int)(t2 - t1) - avgticks); + } + ++attempts; + if (TSC_AUX_CORE(Rdpid()) == core && GetInterrupts() == interrupts) { + break; + } else if (attempts >= maxattempts) { + return luaL_error(L, "system is under too much load to run benchmark"); + } + } + avgticks = MAX(avgticks - overhead, 0); + + lua_gc(L, LUA_GCRESTART); + lua_pushinteger(L, ConvertTicksToNanos(round(avgticks))); + lua_pushinteger(L, round(avgticks)); + lua_pushinteger(L, round(overhead)); lua_pushinteger(L, attempts); - return 3; + return 4; } diff --git a/tool/net/lunix.c b/tool/net/lunix.c index 661d655bc..1fa9f6c00 100644 --- a/tool/net/lunix.c +++ b/tool/net/lunix.c @@ -26,6 +26,7 @@ #include "libc/calls/struct/bpf.h" #include "libc/calls/struct/dirent.h" #include "libc/calls/struct/itimerval.h" +#include "libc/calls/struct/rusage.h" #include "libc/calls/struct/siginfo.h" #include "libc/calls/struct/sigset.h" #include "libc/calls/struct/stat.h" @@ -68,6 +69,7 @@ #include "libc/sysv/consts/poll.h" #include "libc/sysv/consts/rlim.h" #include "libc/sysv/consts/rlimit.h" +#include "libc/sysv/consts/rusage.h" #include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/shut.h" #include "libc/sysv/consts/sig.h" @@ -125,6 +127,14 @@ static void *LuaUnixAlloc(lua_State *L, size_t n) { return p; } +static lua_Integer FixLimit(long x) { + if (0 <= x && x < RLIM_INFINITY) { + return x; + } else { + return -1; + } +} + static void LuaPushSigset(lua_State *L, struct sigset set) { struct sigset *sp = lua_newuserdatauv(L, sizeof(*sp), 1); luaL_setmetatable(L, "unix.Sigset"); @@ -137,6 +147,12 @@ static void LuaPushStat(lua_State *L, struct stat *st) { *stp = *st; } +static void LuaPushRusage(lua_State *L, struct rusage *set) { + struct rusage *sp = lua_newuserdatauv(L, sizeof(*sp), 1); + luaL_setmetatable(L, "unix.Rusage"); + *sp = *set; +} + static void LuaSetIntField(lua_State *L, const char *k, lua_Integer v) { lua_pushinteger(L, v); lua_setfield(L, -2, k); @@ -175,7 +191,7 @@ static int SysretErrno(lua_State *L, const char *call, int olderr) { return 2; } -static int SysretBool(lua_State *L, const char *call, int olderr, int rc) { +int SysretBool(lua_State *L, const char *call, int olderr, int rc) { if (!IsTiny() && (rc != 0 && rc != -1)) { WARNF("syscall supposed to return 0 / -1 but got %d", rc); } @@ -525,8 +541,8 @@ static int LuaUnixCommandv(lua_State *L) { char *pathbuf, *resolved; olderr = errno; prog = luaL_checkstring(L, 1); - if ((pathbuf = LuaUnixAllocRaw(L, PATH_MAX + 1))) { - if ((resolved = commandv(prog, pathbuf, PATH_MAX + 1))) { + if ((pathbuf = LuaUnixAllocRaw(L, PATH_MAX))) { + if ((resolved = commandv(prog, pathbuf, PATH_MAX))) { lua_pushstring(L, resolved); free(pathbuf); return 1; @@ -587,19 +603,31 @@ static int LuaUnixSetrlimit(lua_State *L) { // ├─→ soft:int, hard:int // └─→ nil, unix.Errno static int LuaUnixGetrlimit(lua_State *L) { + int olderr = errno; struct rlimit rlim; - int rc, olderr, resource; - olderr = errno; - resource = luaL_checkinteger(L, 1); - if (!getrlimit(resource, &rlim)) { - lua_pushinteger(L, rlim.rlim_cur < RLIM_INFINITY ? rlim.rlim_cur : -1); - lua_pushinteger(L, rlim.rlim_max < RLIM_INFINITY ? rlim.rlim_max : -1); - return 3; + if (!getrlimit(luaL_checkinteger(L, 1), &rlim)) { + lua_pushinteger(L, FixLimit(rlim.rlim_cur)); + lua_pushinteger(L, FixLimit(rlim.rlim_max)); + return 2; } else { return SysretErrno(L, "getrlimit", olderr); } } +// unix.getrusage([who:int]) +// ├─→ unix.Rusage +// └─→ nil, unix.Errno +static int LuaUnixGetrusage(lua_State *L) { + struct rusage ru; + int olderr = errno; + if (!getrusage(luaL_optinteger(L, 1, RUSAGE_SELF), &ru)) { + LuaPushRusage(L, &ru); + return 1; + } else { + return SysretErrno(L, "getrusage", olderr); + } +} + // unix.kill(pid:int, sig:int) // ├─→ true // └─→ nil, unix.Errno @@ -621,11 +649,13 @@ static int LuaUnixRaise(lua_State *L) { // ├─→ pid:int, wstatus:int // └─→ nil, unix.Errno static int LuaUnixWait(lua_State *L) { + struct rusage ru; int pid, wstatus, olderr = errno; if ((pid = wait4(luaL_optinteger(L, 1, -1), &wstatus, - luaL_optinteger(L, 2, 0), 0)) != -1) { + luaL_optinteger(L, 2, 0), &ru)) != -1) { lua_pushinteger(L, pid); lua_pushinteger(L, wstatus); + LuaPushRusage(L, &ru); return 3; } else { return SysretErrno(L, "wait", olderr); @@ -1254,6 +1284,14 @@ static int LuaUnixSiocgifconf(lua_State *L) { return 1; } +// sandbox.pledge([promises:str]) +// ├─→ true +// └─→ nil, unix.Errno +static int LuaUnixPledge(lua_State *L) { + int olderr = errno; + return SysretBool(L, "pledge", olderr, pledge(luaL_checkstring(L, 1), 0)); +} + // unix.gethostname() // ├─→ host:str // └─→ nil, unix.Errno @@ -1533,7 +1571,7 @@ static int LuaUnixSigaction(lua_State *L) { luaL_argerror(L, 2, "sigaction handler not integer or function"); unreachable; } - sa.sa_flags = luaL_optinteger(L, 3, SA_RESTART); + sa.sa_flags = luaL_optinteger(L, 3, 0); if (!lua_isnoneornil(L, 4)) { mask = luaL_checkudata(L, 4, "unix.Sigset"); sa.sa_mask.__bits[0] |= mask->__bits[0]; @@ -1611,34 +1649,16 @@ static int LuaUnixSetitimer(lua_State *L) { } } -static dontinline int LuaUnixStr(lua_State *L, char *f(int)) { +static int LuaUnixStr(lua_State *L, char *f(int)) { return ReturnString(L, f(luaL_checkinteger(L, 1))); } -// unix.strerdoc(errno:int) -// └─→ mediummessage:str -static int LuaUnixStrerdoc(lua_State *L) { - return LuaUnixStr(L, strerdoc); -} - // unix.strsignal(sig:int) // └─→ symbol:str static int LuaUnixStrsignal(lua_State *L) { return LuaUnixStr(L, strsignal); } -// unix.strerrno(errno:int) -// └─→ symbol:int -static int LuaUnixStrerrno(lua_State *L) { - return LuaUnixStr(L, strerrno); -} - -// unix.strerror(errno:int) -// └─→ longmessage:str -static int LuaUnixStrerror(lua_State *L) { - return LuaUnixStr(L, strerror); -} - // unix.WIFEXITED(wstatus) // └─→ bool static int LuaUnixWifexited(lua_State *L) { @@ -1780,7 +1800,7 @@ static int LuaUnixStatBlksize(lua_State *L) { return ReturnInteger(L, GetUnixStat(L)->st_blksize); } -static dontinline int UnixStatTim(lua_State *L, struct timespec *ts) { +static dontinline int ReturnTimespec(lua_State *L, struct timespec *ts) { lua_pushinteger(L, ts->tv_sec); lua_pushinteger(L, ts->tv_nsec); return 2; @@ -1789,25 +1809,25 @@ static dontinline int UnixStatTim(lua_State *L, struct timespec *ts) { // unix.Stat:atim() // └─→ unixts:int, nanos:int static int LuaUnixStatAtim(lua_State *L) { - return UnixStatTim(L, &GetUnixStat(L)->st_atim); + return ReturnTimespec(L, &GetUnixStat(L)->st_atim); } // unix.Stat:mtim() // └─→ unixts:int, nanos:int static int LuaUnixStatMtim(lua_State *L) { - return UnixStatTim(L, &GetUnixStat(L)->st_mtim); + return ReturnTimespec(L, &GetUnixStat(L)->st_mtim); } // unix.Stat:ctim() // └─→ unixts:int, nanos:int static int LuaUnixStatCtim(lua_State *L) { - return UnixStatTim(L, &GetUnixStat(L)->st_ctim); + return ReturnTimespec(L, &GetUnixStat(L)->st_ctim); } // unix.Stat:birthtim() // └─→ unixts:int, nanos:int static int LuaUnixStatBirthtim(lua_State *L) { - return UnixStatTim(L, &GetUnixStat(L)->st_birthtim); + return ReturnTimespec(L, &GetUnixStat(L)->st_birthtim); } // unix.Stat:gen() @@ -1824,7 +1844,7 @@ static int LuaUnixStatFlags(lua_State *L) { static int LuaUnixStatToString(lua_State *L) { struct stat *st = GetUnixStat(L); - lua_pushstring(L, "unix.Stat{}"); + lua_pushstring(L, "unix.Stat()"); return 1; } @@ -1862,6 +1882,180 @@ static void LuaUnixStatObj(lua_State *L) { lua_pop(L, 1); } +//////////////////////////////////////////////////////////////////////////////// +// unix.Rusage object + +static struct rusage *GetUnixRusage(lua_State *L) { + return luaL_checkudata(L, 1, "unix.Rusage"); +} + +static dontinline int ReturnTimeval(lua_State *L, struct timeval *tv) { + lua_pushinteger(L, tv->tv_sec); + lua_pushinteger(L, tv->tv_usec * 1000); + return 2; +} + +// unix.Rusage:utime() +// └─→ unixts:int, nanos:int +static int LuaUnixRusageUtime(lua_State *L) { + return ReturnTimeval(L, &GetUnixRusage(L)->ru_utime); +} + +// unix.Rusage:stime() +// └─→ unixts:int, nanos:int +static int LuaUnixRusageStime(lua_State *L) { + return ReturnTimeval(L, &GetUnixRusage(L)->ru_stime); +} + +// unix.Rusage:maxrss() +// └─→ kilobytes:int +static int LuaUnixRusageMaxrss(lua_State *L) { + return ReturnInteger(L, GetUnixRusage(L)->ru_maxrss); +} + +// unix.Rusage:ixrss() +// └─→ integralkilobytes:int +static int LuaUnixRusageIxrss(lua_State *L) { + return ReturnInteger(L, GetUnixRusage(L)->ru_ixrss); +} + +// unid.Rusage:idrss() +// └─→ integralkilobytes:int +static int LuaUnixRusageIdrss(lua_State *L) { + return ReturnInteger(L, GetUnixRusage(L)->ru_idrss); +} + +// unis.Rusage:isrss() +// └─→ integralkilobytes:int +static int LuaUnixRusageIsrss(lua_State *L) { + return ReturnInteger(L, GetUnixRusage(L)->ru_isrss); +} + +// unix.Rusage:minflt() +// └─→ count:int +static int LuaUnixRusageMinflt(lua_State *L) { + return ReturnInteger(L, GetUnixRusage(L)->ru_minflt); +} + +// unix.Rusage:majflt() +// └─→ count:int +static int LuaUnixRusageMajflt(lua_State *L) { + return ReturnInteger(L, GetUnixRusage(L)->ru_majflt); +} + +// unix.Rusage:nswap() +// └─→ count:int +static int LuaUnixRusageNswap(lua_State *L) { + return ReturnInteger(L, GetUnixRusage(L)->ru_nswap); +} + +// unix.Rusage:inblock() +// └─→ count:int +static int LuaUnixRusageInblock(lua_State *L) { + return ReturnInteger(L, GetUnixRusage(L)->ru_inblock); +} + +// unix.Rusage:oublock() +// └─→ count:int +static int LuaUnixRusageOublock(lua_State *L) { + return ReturnInteger(L, GetUnixRusage(L)->ru_oublock); +} + +// unix.Rusage:msgsnd() +// └─→ count:int +static int LuaUnixRusageMsgsnd(lua_State *L) { + return ReturnInteger(L, GetUnixRusage(L)->ru_msgsnd); +} + +// unix.Rusage:msgrcv() +// └─→ count:int +static int LuaUnixRusageMsgrcv(lua_State *L) { + return ReturnInteger(L, GetUnixRusage(L)->ru_msgrcv); +} + +// unix.Rusage:nsignals() +// └─→ count:int +static int LuaUnixRusageNsignals(lua_State *L) { + return ReturnInteger(L, GetUnixRusage(L)->ru_nsignals); +} + +// unix.Rusage:nvcsw() +// └─→ count:int +static int LuaUnixRusageNvcsw(lua_State *L) { + return ReturnInteger(L, GetUnixRusage(L)->ru_nvcsw); +} + +// unix.Rusage:nivcsw() +// └─→ count:int +static int LuaUnixRusageNivcsw(lua_State *L) { + return ReturnInteger(L, GetUnixRusage(L)->ru_nivcsw); +} + +static int LuaUnixRusageToString(lua_State *L) { + char *b = 0; + struct rusage *ru = GetUnixRusage(L); + appends(&b, "{"); + appendf(&b, "%s={%ld, %ld}", "utime", ru->ru_utime.tv_sec, + ru->ru_utime.tv_usec * 1000); + if (ru->ru_stime.tv_sec || ru->ru_stime.tv_usec) { + appendw(&b, READ16LE(", ")); + appendf(&b, "%s={%ld, %ld}", "stime", ru->ru_stime.tv_sec, + ru->ru_stime.tv_usec * 1000); + } + if (ru->ru_maxrss) appendf(&b, ", %s=%ld", "maxrss", ru->ru_maxrss); + if (ru->ru_ixrss) appendf(&b, ", %s=%ld", "ixrss", ru->ru_ixrss); + if (ru->ru_idrss) appendf(&b, ", %s=%ld", "idrss", ru->ru_idrss); + if (ru->ru_isrss) appendf(&b, ", %s=%ld", "isrss", ru->ru_isrss); + if (ru->ru_minflt) appendf(&b, ", %s=%ld", "minflt", ru->ru_minflt); + if (ru->ru_majflt) appendf(&b, ", %s=%ld", "majflt", ru->ru_majflt); + if (ru->ru_nswap) appendf(&b, ", %s=%ld", "nswap", ru->ru_nswap); + if (ru->ru_inblock) appendf(&b, ", %s=%ld", "inblock", ru->ru_inblock); + if (ru->ru_oublock) appendf(&b, ", %s=%ld", "oublock", ru->ru_oublock); + if (ru->ru_msgsnd) appendf(&b, ", %s=%ld", "msgsnd", ru->ru_msgsnd); + if (ru->ru_msgrcv) appendf(&b, ", %s=%ld", "msgrcv", ru->ru_msgrcv); + if (ru->ru_nsignals) appendf(&b, ", %s=%ld", "nsignals", ru->ru_nsignals); + if (ru->ru_nvcsw) appendf(&b, ", %s=%ld", "nvcsw", ru->ru_nvcsw); + if (ru->ru_nivcsw) appendf(&b, ", %s=%ld", "nivcsw", ru->ru_nivcsw); + appendw(&b, '}'); + lua_pushlstring(L, b, appendz(b).i); + return 1; +} + +static const luaL_Reg kLuaUnixRusageMeth[] = { + {"utime", LuaUnixRusageUtime}, // + {"stime", LuaUnixRusageStime}, // + {"maxrss", LuaUnixRusageMaxrss}, // + {"ixrss", LuaUnixRusageIxrss}, // + {"idrss", LuaUnixRusageIdrss}, // + {"isrss", LuaUnixRusageIsrss}, // + {"minflt", LuaUnixRusageMinflt}, // + {"majflt", LuaUnixRusageMajflt}, // + {"nswap", LuaUnixRusageNswap}, // + {"inblock", LuaUnixRusageInblock}, // + {"oublock", LuaUnixRusageOublock}, // + {"msgsnd", LuaUnixRusageMsgsnd}, // + {"msgrcv", LuaUnixRusageMsgrcv}, // + {"nsignals", LuaUnixRusageNsignals}, // + {"nvcsw", LuaUnixRusageNvcsw}, // + {"nivcsw", LuaUnixRusageNivcsw}, // + {0}, // +}; + +static const luaL_Reg kLuaUnixRusageMeta[] = { + {"__repr", LuaUnixRusageToString}, // + {"__tostring", LuaUnixRusageToString}, // + {0}, // +}; + +static void LuaUnixRusageObj(lua_State *L) { + luaL_newmetatable(L, "unix.Rusage"); + luaL_setfuncs(L, kLuaUnixRusageMeta, 0); + luaL_newlibtable(L, kLuaUnixRusageMeth); + luaL_setfuncs(L, kLuaUnixRusageMeth, 0); + lua_setfield(L, -2, "__index"); + lua_pop(L, 1); +} + //////////////////////////////////////////////////////////////////////////////// // unix.Errno object @@ -1869,6 +2063,8 @@ static struct UnixErrno *GetUnixErrno(lua_State *L) { return luaL_checkudata(L, 1, "unix.Errno"); } +// unix.Errno:errno() +// └─→ errno:int static int LuaUnixErrnoErrno(lua_State *L) { return ReturnInteger(L, GetUnixErrno(L)->errno); } @@ -1885,6 +2081,10 @@ static int LuaUnixErrnoDoc(lua_State *L) { return ReturnString(L, strerdoc(GetUnixErrno(L)->errno)); } +static int LuaUnixErrnoCall(lua_State *L) { + return ReturnString(L, GetUnixErrno(L)->call); +} + static int LuaUnixErrnoToString(lua_State *L) { char msg[256]; struct UnixErrno *e; @@ -1899,12 +2099,12 @@ static int LuaUnixErrnoToString(lua_State *L) { } static const luaL_Reg kLuaUnixErrnoMeth[] = { - {"strerror", LuaUnixErrnoToString}, // - {"errno", LuaUnixErrnoErrno}, // - {"winerr", LuaUnixErrnoWinerr}, // - {"name", LuaUnixErrnoName}, // - {"doc", LuaUnixErrnoDoc}, // - {0}, // + {"errno", LuaUnixErrnoErrno}, // + {"winerr", LuaUnixErrnoWinerr}, // + {"name", LuaUnixErrnoName}, // + {"call", LuaUnixErrnoCall}, // + {"doc", LuaUnixErrnoDoc}, // + {0}, // }; static const luaL_Reg kLuaUnixErrnoMeta[] = { @@ -2231,6 +2431,7 @@ static const luaL_Reg kLuaUnix[] = { {"chroot", LuaUnixChroot}, // change root directory {"setrlimit", LuaUnixSetrlimit}, // prevent cpu memory bombs {"getrlimit", LuaUnixGetrlimit}, // query resource limits + {"getrusage", LuaUnixGetrusage}, // query resource usages {"getppid", LuaUnixGetppid}, // get parent process id {"getpgrp", LuaUnixGetpgrp}, // get process group id {"getpgid", LuaUnixGetpgid}, // get process group id of pid @@ -2272,12 +2473,10 @@ static const luaL_Reg kLuaUnix[] = { {"sigsuspend", LuaUnixSigsuspend}, // wait for signal {"setitimer", LuaUnixSetitimer}, // set alarm clock {"gmtime", LuaUnixGmtime}, // destructure unix timestamp + {"pledge", LuaUnixPledge}, // enables syscall sandbox {"localtime", LuaUnixLocaltime}, // localize unix timestamp {"major", LuaUnixMajor}, // extract device info {"minor", LuaUnixMinor}, // extract device info - {"strerror", LuaUnixStrerror}, // turn errno into string - {"strerrno", LuaUnixStrerrno}, // turn errno into string - {"strerdoc", LuaUnixStrerdoc}, // turn errno into string {"strsignal", LuaUnixStrsignal}, // turn signal into string {"WIFEXITED", LuaUnixWifexited}, // gets exit code from wait status {"WEXITSTATUS", LuaUnixWexitstatus}, // gets exit status from wait status @@ -2300,6 +2499,7 @@ int LuaUnix(lua_State *L) { GL = L; luaL_newlib(L, kLuaUnix); LuaUnixSigsetObj(L); + LuaUnixRusageObj(L); LuaUnixErrnoObj(L); LuaUnixStatObj(L); LuaUnixDirObj(L); @@ -2446,14 +2646,19 @@ int LuaUnix(lua_State *L) { LuaSetIntField(L, "SA_NOCLDWAIT", SA_NOCLDWAIT); LuaSetIntField(L, "SA_NOCLDSTOP", SA_NOCLDSTOP); - LuaSetIntField(L, "NSIG", NSIG); + // getrusage() who + LuaSetIntField(L, "RUSAGE_SELF", RUSAGE_SELF); + LuaSetIntField(L, "RUSAGE_THREAD", RUSAGE_THREAD); + LuaSetIntField(L, "RUSAGE_CHILDREN", RUSAGE_CHILDREN); + LuaSetIntField(L, "RUSAGE_BOTH", RUSAGE_BOTH); + + LuaSetIntField(L, "ARG_MAX", __arg_max()); LuaSetIntField(L, "BUFSIZ", BUFSIZ); - LuaSetIntField(L, "ARG_MAX", ARG_MAX); LuaSetIntField(L, "CLK_TCK", CLK_TCK); - LuaSetIntField(L, "PATH_MAX", PATH_MAX); - LuaSetIntField(L, "OPEN_MAX", OPEN_MAX); + LuaSetIntField(L, "NAME_MAX", _NAME_MAX); + LuaSetIntField(L, "NSIG", _NSIG); + LuaSetIntField(L, "PATH_MAX", _PATH_MAX); LuaSetIntField(L, "PIPE_BUF", PIPE_BUF); - LuaSetIntField(L, "CHILD_MAX", CHILD_MAX); return 1; } diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 43b851a20..0aee7cdbf 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -2707,8 +2707,8 @@ static void LaunchBrowser(const char *path) { // assign a loopback address if no server or unknown server address if (!servers.n || !addr.s_addr) addr.s_addr = htonl(INADDR_LOOPBACK); if (*path != '/') path = gc(xasprintf("/%s", path)); - if ((prog = commandv(GetSystemUrlLauncherCommand(), gc(malloc(PATH_MAX + 1)), - PATH_MAX + 1))) { + if ((prog = commandv(GetSystemUrlLauncherCommand(), gc(malloc(PATH_MAX)), + PATH_MAX))) { u = gc(xasprintf("http://%s:%d%s", inet_ntoa(addr), port, gc(EscapePath(path, -1, 0)))); DEBUGF("(srvr) opening browser with command %`'s %s", prog, u); @@ -6337,8 +6337,7 @@ static int EnableSandbox(void) { sandbox = &kSandboxOfflineProg; break; } - if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != -1 && - prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, sandbox) != -1) { + if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, sandbox) != -1) { return 0; } else { return -1; @@ -6899,6 +6898,11 @@ static void GetOpts(int argc, char *argv[]) { } void RedBean(int argc, char *argv[]) { + if (IsLinux()) { + // disable sneak privilege since we don't use them + // seccomp will fail later if this fails + prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); + } reader = read; writer = WritevAll; gmtoff = GetGmtOffset((lastrefresh = startserver = nowl())); diff --git a/tool/net/sandbox.h b/tool/net/sandbox.h index 37a796a80..82af014eb 100644 --- a/tool/net/sandbox.h +++ b/tool/net/sandbox.h @@ -29,4 +29,7 @@ #define _SECCOMP_LOG_AND_RETURN_ERRNO(MAGNUM) \ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | ((MAGNUM) & SECCOMP_RET_DATA)) +#define _SECCOMP_LOG_AND_KILL_PROCESS() \ + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | SECCOMP_RET_KILL_PROCESS) + #endif /* COSMOPOLITAN_TOOL_NET_SANDBOX_H_ */ diff --git a/tool/viz/derasterize.c b/tool/viz/derasterize.c index ed08c6fe4..0b35ca2d4 100644 --- a/tool/viz/derasterize.c +++ b/tool/viz/derasterize.c @@ -487,7 +487,7 @@ static void LoadFileViaImageMagick(const char *path, unsigned yn, unsigned xn, unsigned char rgb[yn][YS][xn][XS][CN]) { const char *convert; int pid, ws, pipefds[2]; - char pathbuf[PATH_MAX + 1], dim[32]; + char pathbuf[PATH_MAX], dim[32]; if (!(convert = commandv("convert", pathbuf, sizeof(pathbuf)))) { fputs("error: `convert` command not found\n" "try: apt-get install imagemagick\n", From 80c45333034887a1b8e77dd62d856c75feef8c8c Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Thu, 28 Apr 2022 10:42:22 -0700 Subject: [PATCH 13/18] Unbreak MODE=tiny build This change also fixes a minor issue with pledge() polyfill introduced the last change setting errno appropriately. Fixes #396 --- libc/calls/calls.mk | 1 + libc/mem/pledge.c | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/libc/calls/calls.mk b/libc/calls/calls.mk index 5130f390a..09231eb65 100644 --- a/libc/calls/calls.mk +++ b/libc/calls/calls.mk @@ -112,6 +112,7 @@ o/$(MODE)/libc/calls/execlp.o \ o/$(MODE)/libc/calls/copyfile.o \ o/$(MODE)/libc/calls/execve-nt.o \ o/$(MODE)/libc/calls/execve-sysv.o \ +o/$(MODE)/libc/calls/symlinkat-nt.o \ o/$(MODE)/libc/calls/readlinkat-nt.o \ o/$(MODE)/libc/calls/describeopenflags.greg.o \ o/$(MODE)/libc/calls/mkntenvblock.o: \ diff --git a/libc/mem/pledge.c b/libc/mem/pledge.c index 6247560bd..b115d719f 100644 --- a/libc/mem/pledge.c +++ b/libc/mem/pledge.c @@ -322,9 +322,14 @@ static int sys_pledge_linux(const char *promises, const char *execpromises) { AppendFilter(&f, kFilterStart, ARRAYLEN(kFilterStart)) && AppendPledge(&f, kPledgeLinuxDefault)) { for (ok = true; (tok = strtok_r(start, " \t\r\n", &state)); start = 0) { - if (!(pledge = FindPledge(kPledgeLinux, tok)) || - !AppendPledge(&f, pledge)) { + if (!(pledge = FindPledge(kPledgeLinux, tok))) { ok = false; + rc = einval(); + break; + } + if (!AppendPledge(&f, pledge)) { + ok = false; + break; } } if (ok && AppendFilter(&f, kFilterEnd, ARRAYLEN(kFilterEnd)) && From c9a981fdbe50e6c755997a795bf3a7fc1a5518bf Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Thu, 28 Apr 2022 20:36:33 -0700 Subject: [PATCH 14/18] Fix some more reported issues --- libc/complex.h | 33 +++++ libc/isystem/complex.h | 2 + libc/tinymath/complex.h | 133 ------------------- libc/tinymath/complex.internal.h | 11 ++ libc/tinymath/complex_impl.internal.h | 22 ---- libc/tinymath/csqrt.c | 11 +- test/libc/tinymath/complex_test.c | 34 +++++ test/libc/tinymath/csqrt_test.c | 28 ++++ third_party/lz4cli/lz4cli.mk | 1 + third_party/lz4cli/platform.h | 8 -- tool/net/help.txt | 176 +++++++++++++------------- tool/net/lunix.c | 2 +- 12 files changed, 206 insertions(+), 255 deletions(-) delete mode 100644 libc/tinymath/complex.h create mode 100644 libc/tinymath/complex.internal.h delete mode 100644 libc/tinymath/complex_impl.internal.h create mode 100644 test/libc/tinymath/complex_test.c create mode 100644 test/libc/tinymath/csqrt_test.c diff --git a/libc/complex.h b/libc/complex.h index 51f7dd322..ebde8e5b6 100644 --- a/libc/complex.h +++ b/libc/complex.h @@ -82,6 +82,39 @@ complex long double clogl(complex long double); complex long double conjl(complex long double); complex long double cpowl(complex long double, complex long double); +#ifndef __cplusplus +#define __CIMAG(x, t) \ + (+(union { \ + _Complex t __z; \ + t __xy[2]; \ + }){(_Complex t)(x)} \ + .__xy[1]) +#define creal(x) ((double)(x)) +#define crealf(x) ((float)(x)) +#define creall(x) ((long double)(x)) +#define cimag(x) __CIMAG(x, double) +#define cimagf(x) __CIMAG(x, float) +#define cimagl(x) __CIMAG(x, long double) +#endif + +#ifdef __GNUC__ +#define _Complex_I (__extension__(0.0f + 1.0fi)) +#else +#define _Complex_I (0.0f + 1.0fi) +#endif + +#ifdef _Imaginary_I +#define __CMPLX(x, y, t) ((t)(x) + _Imaginary_I * (t)(y)) +#elif defined(__clang__) +#define __CMPLX(x, y, t) (+(_Complex t){(t)(x), (t)(y)}) +#else +#define __CMPLX(x, y, t) (__builtin_complex((t)(x), (t)(y))) +#endif + +#define CMPLX(x, y) __CMPLX(x, y, double) +#define CMPLXF(x, y) __CMPLX(x, y, float) +#define CMPLXL(x, y) __CMPLX(x, y, long double) + #endif /* C11 */ COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/isystem/complex.h b/libc/isystem/complex.h index 8267a4409..1da2fa62f 100644 --- a/libc/isystem/complex.h +++ b/libc/isystem/complex.h @@ -1,4 +1,6 @@ #ifndef LIBC_ISYSTEM_COMPLEX_H_ #define LIBC_ISYSTEM_COMPLEX_H_ +#include "libc/complex.h" #include "libc/math.h" +#define I _Complex_I #endif diff --git a/libc/tinymath/complex.h b/libc/tinymath/complex.h deleted file mode 100644 index 008b3c7e3..000000000 --- a/libc/tinymath/complex.h +++ /dev/null @@ -1,133 +0,0 @@ -#ifndef _COMPLEX_H -#define _COMPLEX_H - -#ifdef __cplusplus -extern "C" { -#endif - -#define complex _Complex -#ifdef __GNUC__ -#define _Complex_I (__extension__ (0.0f+1.0fi)) -#else -#define _Complex_I (0.0f+1.0fi) -#endif -#define I _Complex_I - -double complex cacos(double complex); -float complex cacosf(float complex); -long double complex cacosl(long double complex); - -double complex casin(double complex); -float complex casinf(float complex); -long double complex casinl(long double complex); - -double complex catan(double complex); -float complex catanf(float complex); -long double complex catanl(long double complex); - -double complex ccos(double complex); -float complex ccosf(float complex); -long double complex ccosl(long double complex); - -double complex csin(double complex); -float complex csinf(float complex); -long double complex csinl(long double complex); - -double complex ctan(double complex); -float complex ctanf(float complex); -long double complex ctanl(long double complex); - -double complex cacosh(double complex); -float complex cacoshf(float complex); -long double complex cacoshl(long double complex); - -double complex casinh(double complex); -float complex casinhf(float complex); -long double complex casinhl(long double complex); - -double complex catanh(double complex); -float complex catanhf(float complex); -long double complex catanhl(long double complex); - -double complex ccosh(double complex); -float complex ccoshf(float complex); -long double complex ccoshl(long double complex); - -double complex csinh(double complex); -float complex csinhf(float complex); -long double complex csinhl(long double complex); - -double complex ctanh(double complex); -float complex ctanhf(float complex); -long double complex ctanhl(long double complex); - -double complex cexp(double complex); -float complex cexpf(float complex); -long double complex cexpl(long double complex); - -double complex clog(double complex); -float complex clogf(float complex); -long double complex clogl(long double complex); - -double cabs(double complex); -float cabsf(float complex); -long double cabsl(long double complex); - -double complex cpow(double complex, double complex); -float complex cpowf(float complex, float complex); -long double complex cpowl(long double complex, long double complex); - -double complex csqrt(double complex); -float complex csqrtf(float complex); -long double complex csqrtl(long double complex); - -double carg(double complex); -float cargf(float complex); -long double cargl(long double complex); - -double cimag(double complex); -float cimagf(float complex); -long double cimagl(long double complex); - -double complex conj(double complex); -float complex conjf(float complex); -long double complex conjl(long double complex); - -double complex cproj(double complex); -float complex cprojf(float complex); -long double complex cprojl(long double complex); - -double creal(double complex); -float crealf(float complex); -long double creall(long double complex); - -#ifndef __cplusplus -#define __CIMAG(x, t) \ - (+(union { _Complex t __z; t __xy[2]; }){(_Complex t)(x)}.__xy[1]) - -#define creal(x) ((double)(x)) -#define crealf(x) ((float)(x)) -#define creall(x) ((long double)(x)) - -#define cimag(x) __CIMAG(x, double) -#define cimagf(x) __CIMAG(x, float) -#define cimagl(x) __CIMAG(x, long double) -#endif - -#if __STDC_VERSION__ >= 201112L -#if defined(_Imaginary_I) -#define __CMPLX(x, y, t) ((t)(x) + _Imaginary_I*(t)(y)) -#elif defined(__clang__) -#define __CMPLX(x, y, t) (+(_Complex t){ (t)(x), (t)(y) }) -#else -#define __CMPLX(x, y, t) (__builtin_complex((t)(x), (t)(y))) -#endif -#define CMPLX(x, y) __CMPLX(x, y, double) -#define CMPLXF(x, y) __CMPLX(x, y, float) -#define CMPLXL(x, y) __CMPLX(x, y, long double) -#endif - -#ifdef __cplusplus -} -#endif -#endif diff --git a/libc/tinymath/complex.internal.h b/libc/tinymath/complex.internal.h new file mode 100644 index 000000000..40b2130f4 --- /dev/null +++ b/libc/tinymath/complex.internal.h @@ -0,0 +1,11 @@ +#ifndef COSMOPOLITAN_LIBC_TINYMATH_COMPLEX_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_TINYMATH_COMPLEX_INTERNAL_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +_Complex double __ldexp_cexp(_Complex double, int) hidden; +_Complex float __ldexp_cexpf(_Complex float, int) hidden; + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_TINYMATH_COMPLEX_INTERNAL_H_ */ diff --git a/libc/tinymath/complex_impl.internal.h b/libc/tinymath/complex_impl.internal.h deleted file mode 100644 index b92cb6f3a..000000000 --- a/libc/tinymath/complex_impl.internal.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef _COMPLEX_IMPL_H -#define _COMPLEX_IMPL_H - -#include -#include "libc/math.h" - -#undef __CMPLX -#undef CMPLX -#undef CMPLXF -#undef CMPLXL - -#define __CMPLX(x, y, t) \ - ((union { _Complex t __z; t __xy[2]; }){.__xy = {(x),(y)}}.__z) - -#define CMPLX(x, y) __CMPLX(x, y, double) -#define CMPLXF(x, y) __CMPLX(x, y, float) -#define CMPLXL(x, y) __CMPLX(x, y, long double) - -hidden double complex __ldexp_cexp(double complex,int); -hidden float complex __ldexp_cexpf(float complex,int); - -#endif diff --git a/libc/tinymath/csqrt.c b/libc/tinymath/csqrt.c index d229d2684..d182ec42b 100644 --- a/libc/tinymath/csqrt.c +++ b/libc/tinymath/csqrt.c @@ -25,7 +25,9 @@ │ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ │ │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/tinymath/complex_impl.internal.h" +#include "libc/complex.h" +#include "libc/math.h" +#include "libc/tinymath/complex.internal.h" asm(".ident\t\"\\n\\n\ Musl libc (MIT License)\\n\ @@ -73,9 +75,12 @@ asm(".include \"libc/disclaimer.inc\""); /* We risk spurious overflow for components >= DBL_MAX / (1 + sqrt(2)). */ #define THRESH 0x1.a827999fcef32p+1022 -double complex csqrt(double complex z) +/** + * Returns complex square root. + */ +complex double csqrt(complex double z) { - double complex result; + complex double result; double a, b; double t; int scale; diff --git a/test/libc/tinymath/complex_test.c b/test/libc/tinymath/complex_test.c new file mode 100644 index 000000000..70e6f493e --- /dev/null +++ b/test/libc/tinymath/complex_test.c @@ -0,0 +1,34 @@ +/*-*- 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 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/complex.h" +#include "libc/fmt/fmt.h" +#include "libc/testlib/testlib.h" + +TEST(complex, test) { + char buf[128]; + complex double z, z1; + sprintf(buf, "I = %.1f%+.1fi", creal(_Complex_I), cimag(_Complex_I)); + EXPECT_STREQ("I = 0.0+1.0i", buf); + z1 = _Complex_I * _Complex_I; // imaginary unit squared + sprintf(buf, "I * I = %.1f%+.1fi", creal(z1), cimag(z1)); + EXPECT_STREQ("I * I = -1.0+0.0i", buf); + z = 1.0 + 2.0 * _Complex_I; // usual way to form a complex number pre-C11 + sprintf(buf, "z = %.1f%+.1fi", creal(z), cimag(z)); + EXPECT_STREQ("z = 1.0+2.0i", buf); +} diff --git a/test/libc/tinymath/csqrt_test.c b/test/libc/tinymath/csqrt_test.c new file mode 100644 index 000000000..d585a1b08 --- /dev/null +++ b/test/libc/tinymath/csqrt_test.c @@ -0,0 +1,28 @@ +/*-*- 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 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/complex.h" +#include "libc/fmt/fmt.h" +#include "libc/runtime/gc.internal.h" +#include "libc/testlib/testlib.h" +#include "libc/x/x.h" + +TEST(csqrt, test) { + complex double x = csqrt(-1); + EXPECT_STREQ("0 1", gc(xasprintf("%g %g", creal(x), cimag(x)))); +} diff --git a/third_party/lz4cli/lz4cli.mk b/third_party/lz4cli/lz4cli.mk index 48116f25e..568db66c6 100644 --- a/third_party/lz4cli/lz4cli.mk +++ b/third_party/lz4cli/lz4cli.mk @@ -41,6 +41,7 @@ o/$(MODE)/third_party/lz4cli/datagen.o: \ THIRD_PARTY_LZ4CLI_DIRECTDEPS = \ LIBC_INTRIN \ LIBC_STDIO \ + LIBC_LOG \ LIBC_TIME \ LIBC_UNICODE diff --git a/third_party/lz4cli/platform.h b/third_party/lz4cli/platform.h index 00701a22e..6da8d6fe6 100644 --- a/third_party/lz4cli/platform.h +++ b/third_party/lz4cli/platform.h @@ -107,14 +107,10 @@ extern "C" { * Detect if isatty() and fileno() are available ************************************************/ #if (defined(__linux__) && (PLATFORM_POSIX_VERSION >= 1)) || (PLATFORM_POSIX_VERSION >= 200112L) || defined(__DJGPP__) -# include /* isatty */ # define IS_CONSOLE(stdStream) isatty(fileno(stdStream)) #elif defined(MSDOS) || defined(OS2) || defined(__CYGWIN__) -# include /* _isatty */ # define IS_CONSOLE(stdStream) _isatty(_fileno(stdStream)) #elif defined(WIN32) || defined(_WIN32) -# include /* _isatty */ -# include /* DeviceIoControl, HANDLE, FSCTL_SET_SPARSE */ #include "libc/stdio/stdio.h" /* FILE */ static __inline int IS_CONSOLE(FILE* stdStream) { @@ -130,11 +126,7 @@ static __inline int IS_CONSOLE(FILE* stdStream) * OS-specific Includes ******************************/ #if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(_WIN32) -# include /* _O_BINARY */ -# include /* _setmode, _fileno, _get_osfhandle */ # if !defined(__DJGPP__) -# include /* DeviceIoControl, HANDLE, FSCTL_SET_SPARSE */ -# include /* FSCTL_SET_SPARSE */ # define SET_BINARY_MODE(file) { int unused=_setmode(_fileno(file), _O_BINARY); (void)unused; } # define SET_SPARSE_FILE_MODE(file) { DWORD dw; DeviceIoControl((HANDLE) _get_osfhandle(_fileno(file)), FSCTL_SET_SPARSE, 0, 0, 0, 0, &dw, 0); } # else diff --git a/tool/net/help.txt b/tool/net/help.txt index 7339f14b8..0fe55737f 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -2700,6 +2700,94 @@ UNIX MODULE See the unix.Rusage section below for details on returned fields. + unix.pledge([promises:str]) + ├─→ true + └─→ nil, unix.Errno + + Restrict system operations. + + This can be used to sandbox your redbean workers. It allows finer + customization compared to the `-S` flag. + + By default exit and exit_group are always allowed. This is useful + for processes that perform pure computation and interface with the + parent via shared memory. + + Currently only available on OpenBSD and Linux. On Linux, the default + action when your policy is violated is to return `EPERM`. On OpenBSD + the kernel will kill the process. + + `promises` is a string that may include any of the following groups + delimited by spaces. + + stdio + + Allows clock_getres, clock_gettime, close, dup, dup2, dup3, + fchdir, fstat, fsync, ftruncate, getdents, getegid, getrandom, + geteuid, getgid, getgroups, getitimer, getpgid, getpgrp, getpid, + getppid, getresgid, getresuid, getrlimit, getsid, gettimeofday, + getuid, lseek, madvise, brk, mmap, mprotect, munmap, nanosleep, + pipe, pipe2, poll, pread, preadv, pwrite, pwritev, read, readv, + recvfrom, recvmsg, select, sendmsg, sendto, setitimer, shutdown, + sigaction, sigprocmask, sigreturn, socketpair, umask, wait4, + write, writev. + + rpath + + Allows chdir, getcwd, openat, fstatat, faccessat, readlinkat, + lstat, chmod, fchmod, fchmodat, chown, fchown, fchownat, fstat. + + wpath + + Allows getcwd, openat, fstatat, faccessat, readlinkat, lstat, + chmod, fchmod, fchmodat, chown, fchown, fchownat, fstat. + + cpath + + Allows rename, renameat, link, linkat, symlink, symlinkat, unlink, + unlinkat, mkdir, mkdirat, rmdir. + + dpath + + Allows mknod + + tmppath + + Allows lstat, chmod, chown, unlink, fstat. + + inet + + Allows socket, listen, bind, connect, accept4, accept, + getpeername, getsockname, setsockopt, getsockopt. + + fattr + + Allows utimes, utimensat, chmod, fchmod, fchmodat, chown, + fchownat, lchown, fchown, utimes. + + unix + + Allows socket, listen, bind, connect, accept4, accept, + getpeername, getsockname, setsockopt, getsockopt. + + dns + + Allows sendto, recvfrom, socket, connect. + + proc + + Allows fork, vfork, kill, getpriority, setpriority, setrlimit, + setpgid, setsid. + + exec + + Allows execve. + + id + + Allows setuid, setreuid, setresuid, setgid, setregid, setresgid, + setgroups, setrlimit, getpriority, setpriority. + unix.gmtime(unixts:int) ├─→ year,mon,mday,hour,min,sec,gmtoffsec,wday,yday,dst:int,zone:str └─→ nil,unix.Errno @@ -3038,94 +3126,6 @@ UNIX MODULE higher priority process after failing to finish its work, within the allotted time slice. - sandbox.pledge([promises:str]) - ├─→ true - └─→ nil, unix.Errno - - Restrict system operations. - - This can be used to sandbox your redbean workers. It allows finer - customization compared to the `-S` flag. - - By default exit and exit_group are always allowed. This is useful - for processes that perform pure computation and interface with the - parent via shared memory. - - Currently only available on OpenBSD and Linux. On Linux, the default - action when your policy is violated is to return `EPERM`. On OpenBSD - the kernel will kill the process. - - `promises` is a string that may include any of the following groups - delimited by spaces. - - stdio - - Allows clock_getres, clock_gettime, close, dup, dup2, dup3, - fchdir, fstat, fsync, ftruncate, getdents, getegid, getrandom, - geteuid, getgid, getgroups, getitimer, getpgid, getpgrp, getpid, - getppid, getresgid, getresuid, getrlimit, getsid, gettimeofday, - getuid, lseek, madvise, brk, mmap, mprotect, munmap, nanosleep, - pipe, pipe2, poll, pread, preadv, pwrite, pwritev, read, readv, - recvfrom, recvmsg, select, sendmsg, sendto, setitimer, shutdown, - sigaction, sigprocmask, sigreturn, socketpair, umask, wait4, - write, writev. - - rpath - - Allows chdir, getcwd, openat, fstatat, faccessat, readlinkat, - lstat, chmod, fchmod, fchmodat, chown, fchown, fchownat, fstat. - - wpath - - Allows getcwd, openat, fstatat, faccessat, readlinkat, lstat, - chmod, fchmod, fchmodat, chown, fchown, fchownat, fstat. - - cpath - - Allows rename, renameat, link, linkat, symlink, symlinkat, unlink, - unlinkat, mkdir, mkdirat, rmdir. - - dpath - - Allows mknod - - tmppath - - Allows lstat, chmod, chown, unlink, fstat. - - inet - - Allows socket, listen, bind, connect, accept4, accept, - getpeername, getsockname, setsockopt, getsockopt. - - fattr - - Allows utimes, utimensat, chmod, fchmod, fchmodat, chown, - fchownat, lchown, fchown, utimes. - - unix - - Allows socket, listen, bind, connect, accept4, accept, - getpeername, getsockname, setsockopt, getsockopt. - - dns - - Allows sendto, recvfrom, socket, connect. - - proc - - Allows fork, vfork, kill, getpriority, setpriority, setrlimit, - setpgid, setsid. - - exec - - Allows execve. - - id - - Allows setuid, setreuid, setresuid, setgid, setregid, setresgid, - setgroups, setrlimit, getpriority, setpriority. - ──────────────────────────────────────────────────────────────────────────────── diff --git a/tool/net/lunix.c b/tool/net/lunix.c index 1fa9f6c00..f5c69240a 100644 --- a/tool/net/lunix.c +++ b/tool/net/lunix.c @@ -646,7 +646,7 @@ static int LuaUnixRaise(lua_State *L) { } // unix.wait([pid:int, options:int]) -// ├─→ pid:int, wstatus:int +// ├─→ pid:int, wstatus:int, unix.Rusage // └─→ nil, unix.Errno static int LuaUnixWait(lua_State *L) { struct rusage ru; From 7aafa64ab30f972acb25e4b31c838a645a7c86fa Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Fri, 29 Apr 2022 00:36:14 -0700 Subject: [PATCH 15/18] Make improvements - Bump redbean up to 2.0 - Trim down the MODE=tiny build a bit - Add Indian Standard Time to zoneinfo --- examples/breakpoint.c | 2 +- examples/datauri.c | 1 - examples/img.c | 3 +- examples/ispell.c | 2 +- examples/panels.c | 3 +- examples/rlimit.c | 6 +- examples/seq.c | 9 +- libc/log/backtrace2.greg.c | 6 +- libc/log/checkfail_ndebug.c | 8 +- libc/time/localtime.c | 150 ++++++++++-------- libc/time/strftime.c | 172 +++++++++++---------- libc/time/time.mk | 3 +- third_party/dlmalloc/dlmalloc_abort.greg.c | 8 +- tool/net/help.txt | 37 +++-- tool/net/redbean.c | 2 +- usr/share/zoneinfo/GMT | Bin 0 -> 114 bytes usr/share/zoneinfo/India | Bin 0 -> 285 bytes 17 files changed, 233 insertions(+), 179 deletions(-) create mode 100644 usr/share/zoneinfo/GMT create mode 100644 usr/share/zoneinfo/India diff --git a/examples/breakpoint.c b/examples/breakpoint.c index c9a7a58e5..c4de23644 100644 --- a/examples/breakpoint.c +++ b/examples/breakpoint.c @@ -14,7 +14,7 @@ #include "libc/runtime/symbols.internal.h" int main(int argc, char *argv[]) { - // ShowCrashReports(); + ShowCrashReports(); if (IsDebuggerPresent(false)) { kprintf("debugger found!%n"); diff --git a/examples/datauri.c b/examples/datauri.c index 4c49d4c73..c07f569ff 100644 --- a/examples/datauri.c +++ b/examples/datauri.c @@ -43,7 +43,6 @@ void PrintUri(const char *path) { } int main(int argc, char *argv[]) { - ShowCrashReports(); int i; while ((i = getopt(argc, argv, "?h")) != -1) { switch (i) { diff --git a/examples/img.c b/examples/img.c index e456e3a85..37af07737 100644 --- a/examples/img.c +++ b/examples/img.c @@ -7,6 +7,7 @@ │ • http://creativecommons.org/publicdomain/zero/1.0/ │ ╚─────────────────────────────────────────────────────────────────*/ #endif +#include "libc/dce.h" #include "libc/log/log.h" #include "libc/runtime/gc.internal.h" #include "libc/stdio/stdio.h" @@ -67,7 +68,7 @@ void PrintImg(const char *path) { } int main(int argc, char *argv[]) { - ShowCrashReports(); + if (!NoDebug()) ShowCrashReports(); int i; while ((i = getopt(argc, argv, "?huas01234")) != -1) { switch (i) { diff --git a/examples/ispell.c b/examples/ispell.c index 0a90fef64..8ce96e96d 100644 --- a/examples/ispell.c +++ b/examples/ispell.c @@ -153,7 +153,7 @@ void LoadWords(void) { } int main(int argc, char *argv[]) { - ShowCrashReports(); + if (!NoDebug()) ShowCrashReports(); LoadWords(); SpellChecker(); return 0; diff --git a/examples/panels.c b/examples/panels.c index 53d6d1ba9..4e3361126 100644 --- a/examples/panels.c +++ b/examples/panels.c @@ -12,6 +12,7 @@ #include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/termios.h" #include "libc/calls/struct/winsize.h" +#include "libc/dce.h" #include "libc/log/check.h" #include "libc/log/gdb.h" #include "libc/log/log.h" @@ -152,7 +153,7 @@ void Draw(void) { int main(int argc, char *argv[]) { struct sigaction sa[2] = {{.sa_handler = OnShutdown}, {.sa_handler = OnInvalidate}}; - ShowCrashReports(); + if (!NoDebug()) ShowCrashReports(); Setup(); Enter(); GetTtySize(); diff --git a/examples/rlimit.c b/examples/rlimit.c index c98aa4113..47325062c 100644 --- a/examples/rlimit.c +++ b/examples/rlimit.c @@ -36,12 +36,12 @@ static void SetLimit(int resource, uint64_t soft, uint64_t hard) { lim.rlim_max = MIN(hard, old.rlim_max); lim.rlim_cur = MIN(soft, lim.rlim_max); if (!setrlimit(resource, &lim)) { - fprintf(stderr, "%snote: setrlimit(%s) downgraded to {%,ld, %,ld}\n", + fprintf(stderr, "%sNOTE: SETRLIMIT(%s) DOWNGRADED TO {%,ld, %,ld}\n", __strace_rlimit_name(resource), lim.rlim_cur, lim.rlim_max); return; } } - fprintf(stderr, "error: setrlimit(%s, %,ld, %,ld) failed %m%n", + fprintf(stderr, "ERROR: SETRLIMIT(%s, %,ld, %,ld) FAILED %m%n", __strace_rlimit_name(resource), soft, hard); exit(1); } @@ -63,7 +63,7 @@ int main(int argc, char *argv[]) { for (i = 0; i < RLIM_NLIMITS; ++i) { rc = getrlimit(i, &rlim); - printf("setrlimit(%-20s, %,16ld, %,16ld) → %d %s\n", + printf("SETRLIMIT(%-20s, %,16ld, %,16ld) → %d %s\n", __strace_rlimit_name(i), rlim.rlim_cur, rlim.rlim_max, rc, !rc ? "" : strerror(errno)); } diff --git a/examples/seq.c b/examples/seq.c index 7e2782cc1..dc7e2295a 100644 --- a/examples/seq.c +++ b/examples/seq.c @@ -7,8 +7,9 @@ │ • http://creativecommons.org/publicdomain/zero/1.0/ │ ╚─────────────────────────────────────────────────────────────────*/ #endif +#include "libc/calls/calls.h" #include "libc/fmt/conv.h" -#include "libc/stdio/stdio.h" +#include "libc/fmt/itoa.h" /** * @fileoverview Prints sequence of numbers. @@ -16,6 +17,7 @@ int main(int argc, char *argv[]) { long a, b, i; + char buf[21], *p; switch (argc) { case 2: a = 1; @@ -29,6 +31,9 @@ int main(int argc, char *argv[]) { return 1; } for (i = a; i <= b; ++i) { - printf("%ld\n", i); + p = buf; + p = FormatInt64(p, i); + *p++ = '\n'; + write(1, buf, p - buf); } } diff --git a/libc/log/backtrace2.greg.c b/libc/log/backtrace2.greg.c index 61f0f8666..376a2a580 100644 --- a/libc/log/backtrace2.greg.c +++ b/libc/log/backtrace2.greg.c @@ -184,8 +184,8 @@ void ShowBacktrace(int fd, const struct StackFrame *bp) { __strace = st; g_ftrace = ft; #else - kprintf("ShowBacktrace() needs these flags to show C backtrace:\n" - "\t-D__FNO_OMIT_FRAME_POINTER__\n" - "\t-fno-omit-frame-pointer\n"); + (fprintf)(stderr, "ShowBacktrace() needs these flags to show C backtrace:\n" + "\t-D__FNO_OMIT_FRAME_POINTER__\n" + "\t-fno-omit-frame-pointer\n"); #endif } diff --git a/libc/log/checkfail_ndebug.c b/libc/log/checkfail_ndebug.c index a5a09318d..410364304 100644 --- a/libc/log/checkfail_ndebug.c +++ b/libc/log/checkfail_ndebug.c @@ -17,10 +17,10 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/errno.h" -#include "libc/intrin/kprintf.h" #include "libc/log/internal.h" #include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" #include "libc/str/str.h" /** @@ -36,9 +36,9 @@ relegated void ___check_fail_ndebug(uint64_t want, uint64_t got, const char *opchar) { __restore_tty(); - kprintf("\n%serror: %s: check failed: 0x%x %s 0x%x (%s)\n", - !__nocolor ? "\e[J" : "", program_invocation_name, want, opchar, got, - strerror(errno)); + (fprintf)(stderr, "\n%serror: %s: check failed: 0x%x %s 0x%x (%s)\n", + !__nocolor ? "\e[J" : "", program_invocation_name, want, opchar, + got, strerror(errno)); __restorewintty(); _Exit(68); } diff --git a/libc/time/localtime.c b/libc/time/localtime.c index 5e50d32de..4fcca25b2 100644 --- a/libc/time/localtime.c +++ b/libc/time/localtime.c @@ -17,6 +17,7 @@ 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/GMT"); STATIC_YOINK("usr/share/zoneinfo/GST"); STATIC_YOINK("usr/share/zoneinfo/Honolulu"); STATIC_YOINK("usr/share/zoneinfo/Israel"); @@ -172,10 +173,10 @@ static bool increment_overflow(int *, int); static bool increment_overflow_time(time_t *, int_fast32_t); static int_fast32_t leapcorr(struct state const *, time_t); static bool normalize_overflow32(int_fast32_t *, int *, int); -static struct tm *timesub(time_t const *, int_fast32_t, struct state const *, - struct tm *); -static bool typesequiv(struct state const *, int, int); -static bool tzparse(char const *, struct state *, struct state *); +static struct tm *localtime_timesub(time_t const *, int_fast32_t, + struct state const *, struct tm *); +static bool localtime_typesequiv(struct state const *, int, int); +static bool localtime_tzparse(char const *, struct state *, struct state *); #ifdef ALL_STATE static struct state * lclptr; @@ -390,8 +391,8 @@ union local_storage { format if DOEXTEND. Use *LSP for temporary storage. Return 0 on success, an errno value on failure. */ static int -tzloadbody(char const *name, struct state *sp, bool doextend, - union local_storage *lsp) +localtime_tzloadbody(char const *name, struct state *sp, bool doextend, + union local_storage *lsp) { register int i; register int fid; @@ -628,7 +629,7 @@ tzloadbody(char const *name, struct state *sp, bool doextend, struct state *ts = &lsp->u.st; up->buf[nread - 1] = '\0'; - if (tzparse(&up->buf[1], ts, sp)) { + if (localtime_tzparse(&up->buf[1], ts, sp)) { /* Attempt to reuse existing abbreviations. Without this, America/Anchorage would be right on @@ -695,9 +696,9 @@ tzloadbody(char const *name, struct state *sp, bool doextend, int repeattype = sp->types[0]; for (i = 1; i < sp->timecnt; ++i) if (sp->ats[i] == repeatat - && typesequiv(sp, sp->types[i], repeattype)) { - sp->goback = true; - break; + && localtime_typesequiv(sp, sp->types[i], repeattype)) { + sp->goback = true; + break; } } if (TIME_T_MIN + SECSPERREPEAT <= sp->ats[sp->timecnt - 1]) { @@ -705,9 +706,9 @@ tzloadbody(char const *name, struct state *sp, bool doextend, int repeattype = sp->types[sp->timecnt - 1]; for (i = sp->timecnt - 2; i >= 0; --i) if (sp->ats[i] == repeatat - && typesequiv(sp, sp->types[i], repeattype)) { - sp->goahead = true; - break; + && localtime_typesequiv(sp, sp->types[i], repeattype)) { + sp->goahead = true; + break; } } } @@ -770,14 +771,14 @@ tzloadbody(char const *name, struct state *sp, bool doextend, /* Load tz data from the file named NAME into *SP. Read extended format if DOEXTEND. Return 0 on success, an errno value on failure. */ static int -tzload(char const *name, struct state *sp, bool doextend) +localtime_tzload(char const *name, struct state *sp, bool doextend) { #ifdef ALL_STATE union local_storage *lsp = malloc(sizeof *lsp); if (!lsp) { return HAVE_MALLOC_ERRNO ? errno : ENOMEM; } else { - int err = tzloadbody(name, sp, doextend, lsp); + int err = localtime_tzloadbody(name, sp, doextend, lsp); free(lsp); return err; } @@ -790,12 +791,12 @@ tzload(char const *name, struct state *sp, bool doextend) for (x = i = 0; i < sizeof(ls); i += 4096) { x += p[i]; /* make sure tzdata doesn't smash the stack */ } - return tzloadbody(name, sp, doextend, &ls); + return localtime_tzloadbody(name, sp, doextend, &ls); #endif } static bool -typesequiv(const struct state *sp, int a, int b) +localtime_typesequiv(const struct state *sp, int a, int b) { register bool result; @@ -1116,7 +1117,7 @@ transtime(const int year, register const struct rule *const rulep, */ static bool -tzparse(const char *name, struct state *sp, struct state *basep) +localtime_tzparse(const char *name, struct state *sp, struct state *basep) { const char * stdname; const char * dstname; @@ -1157,7 +1158,7 @@ tzparse(const char *name, struct state *sp, struct state *basep) sp->leapcnt = basep->leapcnt; memcpy(sp->lsis, basep->lsis, sp->leapcnt * sizeof *sp->lsis); } else { - load_ok = tzload(TZDEFRULES, sp, false) == 0; + load_ok = localtime_tzload(TZDEFRULES, sp, false) == 0; if (!load_ok) sp->leapcnt = 0; /* So, we're off a little. */ } @@ -1396,8 +1397,8 @@ tzparse(const char *name, struct state *sp, struct state *basep) static void gmtload(struct state *const sp) { - if (tzload(gmt, sp, true) != 0) - tzparse("GMT0", sp, NULL); + if (localtime_tzload(gmt, sp, true) != 0) + localtime_tzparse("GMT0", sp, NULL); } /* Initialize *SP to a value appropriate for the TZ setting NAME. @@ -1419,8 +1420,9 @@ zoneinit(struct state *sp, char const *name) sp->defaulttype = 0; return 0; } else { - int err = tzload(name, sp, true); - if (err != 0 && name && name[0] != ':' && tzparse(name, sp, NULL)) + int err = localtime_tzload(name, sp, true); + if (err != 0 && name && name[0] != ':' && + localtime_tzparse(name, sp, NULL)) err = 0; if (err == 0) scrub_abbrs(sp); @@ -1429,7 +1431,7 @@ zoneinit(struct state *sp, char const *name) } static void -tzset_unlocked(void) +localtime_tzset_unlocked(void) { char const *name = getenv("TZ"); struct state *sp = lclptr; @@ -1457,12 +1459,12 @@ tzset(void) { if (lock() != 0) return; - tzset_unlocked(); + localtime_tzset_unlocked(); unlock(); } static void -gmtcheck(void) +localtime_gmtcheck(void) { static bool gmt_is_set; if (lock() != 0) @@ -1566,7 +1568,7 @@ localsub(struct state const *sp, time_t const *timep, int_fast32_t setname, ** t += ttisp->tt_utoff; ** timesub(&t, 0L, sp, tmp); */ - result = timesub(&t, ttisp->tt_utoff, sp, tmp); + result = localtime_timesub(&t, ttisp->tt_utoff, sp, tmp); if (result) { result->tm_isdst = ttisp->tt_isdst; result->tm_zone = (char *) &sp->chars[ttisp->tt_desigidx]; @@ -1585,7 +1587,7 @@ localtime_tzset(time_t const *timep, struct tm *tmp, bool setname) return NULL; } if (setname || !lcl_is_set) - tzset_unlocked(); + localtime_tzset_unlocked(); tmp = localsub(lclptr, timep, setname, tmp); unlock(); return tmp; @@ -1613,7 +1615,7 @@ gmtsub(struct state const *sp, time_t const *timep, int_fast32_t offset, { register struct tm * result; - result = timesub(timep, offset, gmtptr, tmp); + result = localtime_timesub(timep, offset, gmtptr, tmp); /* ** Could get fancy here and deliver something such as ** "+xx" or "-xx" if offset is non-zero, @@ -1631,7 +1633,7 @@ gmtsub(struct state const *sp, time_t const *timep, int_fast32_t offset, struct tm * gmtime_r(const time_t *timep, struct tm *tmp) { - gmtcheck(); + localtime_gmtcheck(); return gmtsub(gmtptr, timep, 0, tmp); } @@ -1661,8 +1663,8 @@ leaps_thru_end_of(time_t y) } static struct tm * -timesub(const time_t *timep, int_fast32_t offset, - const struct state *sp, struct tm *tmp) +localtime_timesub(const time_t *timep, int_fast32_t offset, + const struct state *sp, struct tm *tmp) { register const struct lsinfo * lp; register time_t tdays; @@ -1783,11 +1785,16 @@ timesub(const time_t *timep, int_fast32_t offset, ** Normalize logic courtesy Paul Eggert. */ -static bool +static inline bool increment_overflow(int *ip, int j) { +#if defined(__GNUC__) && __GNUC__ >= 6 + int i = *ip; + if (__builtin_add_overflow(i, j, &i)) return true; + *ip = i; + return false; +#else register int const i = *ip; - /* ** If i >= 0 there can only be overflow if i + j > INT_MAX ** or if j > INT_MAX - i; given i >= 0, INT_MAX - i cannot overflow. @@ -1798,22 +1805,35 @@ increment_overflow(int *ip, int j) return true; *ip += j; return false; +#endif } -static bool +static inline bool increment_overflow32(int_fast32_t *const lp, int const m) { +#if defined(__GNUC__) && __GNUC__ >= 6 + int_fast32_t i = *lp; + if (__builtin_add_overflow(i, m, &i)) return true; + *lp = i; + return false; +#else register int_fast32_t const l = *lp; - if ((l >= 0) ? (m > INT_FAST32_MAX - l) : (m < INT_FAST32_MIN - l)) return true; *lp += m; return false; +#endif } -static bool +static inline bool increment_overflow_time(time_t *tp, int_fast32_t j) { +#if defined(__GNUC__) && __GNUC__ >= 6 + time_t i = *tp; + if (__builtin_add_overflow(i, j, &i)) return true; + *tp = i; + return false; +#else /* ** This is like ** 'if (! (TIME_T_MIN <= *tp + j && *tp + j <= TIME_T_MAX)) ...', @@ -1825,6 +1845,7 @@ increment_overflow_time(time_t *tp, int_fast32_t j) return true; *tp += j; return false; +#endif } static bool @@ -1868,13 +1889,14 @@ tmcomp(register const struct tm *const atmp, } static time_t -time2sub(struct tm *const tmp, - struct tm *(*funcp)(struct state const *, time_t const *, - int_fast32_t, struct tm *), - struct state const *sp, - const int_fast32_t offset, - bool *okayp, - bool do_norm_secs) +localtime_time2sub( + struct tm *const tmp, + struct tm *(*funcp)(struct state const *, time_t const *, + int_fast32_t, struct tm *), + struct state const *sp, + const int_fast32_t offset, + bool *okayp, + bool do_norm_secs) { register int dir; register int i, j; @@ -2067,12 +2089,13 @@ label: } static time_t -time2(struct tm * const tmp, - struct tm *(*funcp)(struct state const *, time_t const *, - int_fast32_t, struct tm *), - struct state const *sp, - const int_fast32_t offset, - bool *okayp) +localtime_time2( + struct tm * const tmp, + struct tm *(*funcp)(struct state const *, time_t const *, + int_fast32_t, struct tm *), + struct state const *sp, + const int_fast32_t offset, + bool *okayp) { time_t t; @@ -2081,16 +2104,17 @@ time2(struct tm * const tmp, ** (in case tm_sec contains a value associated with a leap second). ** If that fails, try with normalization of seconds. */ - t = time2sub(tmp, funcp, sp, offset, okayp, false); - return *okayp ? t : time2sub(tmp, funcp, sp, offset, okayp, true); + t = localtime_time2sub(tmp, funcp, sp, offset, okayp, false); + return *okayp ? t : localtime_time2sub(tmp,funcp,sp,offset,okayp,true); } static time_t -time1(struct tm *const tmp, - struct tm *(*funcp)(struct state const *, time_t const *, - int_fast32_t, struct tm *), - struct state const *sp, - const int_fast32_t offset) +localtime_time1( + struct tm *const tmp, + struct tm *(*funcp)(struct state const *, time_t const *, + int_fast32_t, struct tm *), + struct state const *sp, + const int_fast32_t offset) { register time_t t; register int samei, otheri; @@ -2107,7 +2131,7 @@ time1(struct tm *const tmp, } if (tmp->tm_isdst > 1) tmp->tm_isdst = 1; - t = time2(tmp, funcp, sp, offset, &okay); + t = localtime_time2(tmp, funcp, sp, offset, &okay); if (okay) return t; if (tmp->tm_isdst < 0) @@ -2146,7 +2170,7 @@ time1(struct tm *const tmp, tmp->tm_sec += (sp->ttis[otheri].tt_utoff - sp->ttis[samei].tt_utoff); tmp->tm_isdst = !tmp->tm_isdst; - t = time2(tmp, funcp, sp, offset, &okay); + t = localtime_time2(tmp, funcp, sp, offset, &okay); if (okay) return t; tmp->tm_sec -= (sp->ttis[otheri].tt_utoff @@ -2161,10 +2185,10 @@ static time_t mktime_tzname(struct state *sp, struct tm *tmp, bool setname) { if (sp) - return time1(tmp, localsub, sp, setname); + return localtime_time1(tmp, localsub, sp, setname); else { - gmtcheck(); - return time1(tmp, gmtsub, gmtptr, 0); + localtime_gmtcheck(); + return localtime_time1(tmp, gmtsub, gmtptr, 0); } } @@ -2177,7 +2201,7 @@ mktime(struct tm *tmp) errno = err; return -1; } - tzset_unlocked(); + localtime_tzset_unlocked(); t = mktime_tzname(lclptr, tmp, true); unlock(); return t; diff --git a/libc/time/strftime.c b/libc/time/strftime.c index 41d16607f..8ab0ae2d1 100644 --- a/libc/time/strftime.c +++ b/libc/time/strftime.c @@ -106,7 +106,7 @@ enum warn { IN_NONE, IN_SOME, IN_THIS, IN_ALL }; #endif /* !defined YEAR_2000_NAME */ static char * -_add(const char *str, char *pt, const char *ptlim) +strftime_add(const char *str, char *pt, const char *ptlim) { while (pt < ptlim && (*pt = *str++) != '\0') ++pt; @@ -114,11 +114,11 @@ _add(const char *str, char *pt, const char *ptlim) } static char * -_conv(int n, const char *format, char *pt, const char *ptlim) +strftime_conv(int n, const char *format, char *pt, const char *ptlim) { char buf[INT_STRLEN_MAXIMUM(int) + 1]; (sprintf)(buf, format, n); - return _add(buf, pt, ptlim); + return strftime_add(buf, pt, ptlim); } /* @@ -130,8 +130,13 @@ _conv(int n, const char *format, char *pt, const char *ptlim) */ static char * -_yconv(int a, int b, bool convert_top, bool convert_yy, - char *pt, const char *ptlim) +strftime_yconv( + int a, + int b, + bool convert_top, + bool convert_yy, + char *pt, + const char *ptlim) { register int lead; register int trail; @@ -148,17 +153,17 @@ _yconv(int a, int b, bool convert_top, bool convert_yy, } if (convert_top) { if (lead == 0 && trail < 0) - pt = _add("-0", pt, ptlim); - else pt = _conv(lead, "%02d", pt, ptlim); + pt = strftime_add("-0", pt, ptlim); + else pt = strftime_conv(lead, "%02d", pt, ptlim); } if (convert_yy) - pt = _conv(((trail < 0) ? -trail : trail), "%02d", pt, ptlim); + pt = strftime_conv(((trail < 0) ? -trail : trail), "%02d", pt, ptlim); return pt; } static char * -_fmt(const char *format, const struct tm *t, char *pt, - const char *ptlim, enum warn *warnp) +strftime_fmt(const char *format, const struct tm *t, char *pt, + const char *ptlim, enum warn *warnp) { for ( ; *format; ++format) { if (*format == '%') { @@ -168,27 +173,31 @@ label: --format; break; case 'A': - pt = _add((t->tm_wday < 0 || + pt = strftime_add( + (t->tm_wday < 0 || t->tm_wday >= DAYSPERWEEK) ? "?" : Locale->weekday[t->tm_wday], pt, ptlim); continue; case 'a': - pt = _add((t->tm_wday < 0 || - t->tm_wday >= DAYSPERWEEK) ? + pt = strftime_add( + (t->tm_wday < 0 || + t->tm_wday >= DAYSPERWEEK) ? "?" : Locale->wday[t->tm_wday], pt, ptlim); continue; case 'B': - pt = _add((t->tm_mon < 0 || - t->tm_mon >= MONSPERYEAR) ? + pt = strftime_add( + (t->tm_mon < 0 || + t->tm_mon >= MONSPERYEAR) ? "?" : Locale->month[t->tm_mon], pt, ptlim); continue; case 'b': case 'h': - pt = _add((t->tm_mon < 0 || - t->tm_mon >= MONSPERYEAR) ? + pt = strftime_add( + (t->tm_mon < 0 || + t->tm_mon >= MONSPERYEAR) ? "?" : Locale->mon[t->tm_mon], pt, ptlim); continue; @@ -200,14 +209,14 @@ label: ** something completely different. ** (ado, 1993-05-24) */ - pt = _yconv(t->tm_year, TM_YEAR_BASE, - true, false, pt, ptlim); + pt = strftime_yconv(t->tm_year, TM_YEAR_BASE, + true, false, pt, ptlim); continue; case 'c': { enum warn warn2 = IN_SOME; - pt = _fmt(Locale->c_fmt, t, pt, ptlim, &warn2); + pt = strftime_fmt(Locale->c_fmt, t, pt, ptlim, &warn2); if (warn2 == IN_ALL) warn2 = IN_THIS; if (warn2 > *warnp) @@ -215,10 +224,10 @@ label: } continue; case 'D': - pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp); + pt = strftime_fmt("%m/%d/%y", t, pt, ptlim, warnp); continue; case 'd': - pt = _conv(t->tm_mday, "%02d", pt, ptlim); + pt = strftime_conv(t->tm_mday, "%02d", pt, ptlim); continue; case 'E': case 'O': @@ -233,21 +242,21 @@ label: */ goto label; case 'e': - pt = _conv(t->tm_mday, "%2d", pt, ptlim); + pt = strftime_conv(t->tm_mday, "%2d", pt, ptlim); continue; case 'F': - pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp); + pt = strftime_fmt("%Y-%m-%d", t, pt, ptlim, warnp); continue; case 'H': - pt = _conv(t->tm_hour, "%02d", pt, ptlim); + pt = strftime_conv(t->tm_hour, "%02d", pt, ptlim); continue; case 'I': - pt = _conv((t->tm_hour % 12) ? - (t->tm_hour % 12) : 12, - "%02d", pt, ptlim); + pt = strftime_conv((t->tm_hour % 12) ? + (t->tm_hour % 12) : 12, + "%02d", pt, ptlim); continue; case 'j': - pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim); + pt = strftime_conv(t->tm_yday + 1, "%03d", pt, ptlim); continue; case 'k': /* @@ -260,14 +269,15 @@ label: ** "%l" have been swapped. ** (ado, 1993-05-24) */ - pt = _conv(t->tm_hour, "%2d", pt, ptlim); + pt = strftime_conv(t->tm_hour, "%2d", + pt, ptlim); continue; #ifdef KITCHEN_SINK case 'K': /* ** After all this time, still unclaimed! */ - pt = _add("kitchen sink", pt, ptlim); + pt = strftime_add("kitchen sink", pt, ptlim); continue; #endif /* defined KITCHEN_SINK */ case 'l': @@ -280,33 +290,38 @@ label: ** "%l" have been swapped. ** (ado, 1993-05-24) */ - pt = _conv((t->tm_hour % 12) ? - (t->tm_hour % 12) : 12, - "%2d", pt, ptlim); + pt = strftime_conv((t->tm_hour % 12) ? + (t->tm_hour % 12) : 12, + "%2d", pt, ptlim); continue; case 'M': - pt = _conv(t->tm_min, "%02d", pt, ptlim); + pt = strftime_conv(t->tm_min, "%02d", + pt, ptlim); continue; case 'm': - pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim); + pt = strftime_conv(t->tm_mon + 1, "%02d", + pt, ptlim); continue; case 'n': - pt = _add("\n", pt, ptlim); + pt = strftime_add("\n", pt, ptlim); continue; case 'p': - pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ? + pt = strftime_add( + (t->tm_hour >= (HOURSPERDAY / 2)) ? Locale->pm : Locale->am, pt, ptlim); continue; case 'R': - pt = _fmt("%H:%M", t, pt, ptlim, warnp); + pt = strftime_fmt("%H:%M", t, pt, ptlim, warnp); continue; case 'r': - pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp); + pt = strftime_fmt("%I:%M:%S %p", t, pt, + ptlim, warnp); continue; case 'S': - pt = _conv(t->tm_sec, "%02d", pt, ptlim); + pt = strftime_conv(t->tm_sec, "%02d", pt, + ptlim); continue; case 's': { @@ -338,19 +353,19 @@ label: uintmax_t n = mkt; (sprintf)(buf, "%"PRIuMAX, n); } - pt = _add(buf, pt, ptlim); + pt = strftime_add(buf, pt, ptlim); } continue; case 'T': - pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp); + pt = strftime_fmt("%H:%M:%S", t, pt, ptlim, warnp); continue; case 't': - pt = _add("\t", pt, ptlim); + pt = strftime_add("\t", pt, ptlim); continue; case 'U': - pt = _conv((t->tm_yday + DAYSPERWEEK - - t->tm_wday) / DAYSPERWEEK, - "%02d", pt, ptlim); + pt = strftime_conv((t->tm_yday + DAYSPERWEEK - + t->tm_wday) / DAYSPERWEEK, + "%02d", pt, ptlim); continue; case 'u': /* @@ -359,9 +374,9 @@ label: ** [1 (Monday) - 7]" ** (ado, 1993-05-24) */ - pt = _conv((t->tm_wday == 0) ? - DAYSPERWEEK : t->tm_wday, - "%d", pt, ptlim); + pt = strftime_conv((t->tm_wday == 0) ? + DAYSPERWEEK : t->tm_wday, + "%d", pt, ptlim); continue; case 'V': /* ISO 8601 week number */ case 'G': /* ISO 8601 year (four digits) */ @@ -434,16 +449,16 @@ label: DAYSPERNYEAR; } if (*format == 'V') - pt = _conv(w, "%02d", - pt, ptlim); + pt = strftime_conv(w, "%02d", + pt, ptlim); else if (*format == 'g') { *warnp = IN_ALL; - pt = _yconv(year, base, - false, true, - pt, ptlim); - } else pt = _yconv(year, base, - true, true, - pt, ptlim); + pt = strftime_yconv(year, base, + false, true, + pt, ptlim); + } else pt = strftime_yconv(year, base, + true, true, + pt, ptlim); } continue; case 'v': @@ -452,26 +467,27 @@ label: ** "date as dd-bbb-YYYY" ** (ado, 1993-05-24) */ - pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp); + pt = strftime_fmt("%e-%b-%Y", t, pt, ptlim, warnp); continue; case 'W': - pt = _conv((t->tm_yday + DAYSPERWEEK - - (t->tm_wday ? - (t->tm_wday - 1) : - (DAYSPERWEEK - 1))) / DAYSPERWEEK, + pt = strftime_conv( + (t->tm_yday + DAYSPERWEEK - + (t->tm_wday ? + (t->tm_wday - 1) : + (DAYSPERWEEK - 1))) / DAYSPERWEEK, "%02d", pt, ptlim); continue; case 'w': - pt = _conv(t->tm_wday, "%d", pt, ptlim); + pt = strftime_conv(t->tm_wday, "%d", pt, ptlim); continue; case 'X': - pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp); + pt = strftime_fmt(Locale->X_fmt, t, pt, ptlim, warnp); continue; case 'x': { enum warn warn2 = IN_SOME; - pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2); + pt = strftime_fmt(Locale->x_fmt, t, pt, ptlim, &warn2); if (warn2 == IN_ALL) warn2 = IN_THIS; if (warn2 > *warnp) @@ -480,17 +496,17 @@ label: continue; case 'y': *warnp = IN_ALL; - pt = _yconv(t->tm_year, TM_YEAR_BASE, - false, true, - pt, ptlim); + pt = strftime_yconv(t->tm_year, TM_YEAR_BASE, + false, true, + pt, ptlim); continue; case 'Y': - pt = _yconv(t->tm_year, TM_YEAR_BASE, - true, true, - pt, ptlim); + pt = strftime_yconv(t->tm_year, TM_YEAR_BASE, + true, true, + pt, ptlim); continue; case 'Z': - pt = _add(t->tm_zone, pt, ptlim); + pt = strftime_add(t->tm_zone, pt, ptlim); /* ** C99 and later say that %Z must be ** replaced by the empty string if the @@ -513,16 +529,16 @@ label: sign = "-"; diff = -diff; } else sign = "+"; - pt = _add(sign, pt, ptlim); + pt = strftime_add(sign, pt, ptlim); diff /= SECSPERMIN; diff = (diff / MINSPERHOUR) * 100 + (diff % MINSPERHOUR); - pt = _conv(diff, "%04d", pt, ptlim); + pt = strftime_conv(diff, "%04d", pt, ptlim); } continue; case '+': - pt = _fmt(Locale->date_fmt, t, pt, ptlim, - warnp); + pt = strftime_fmt(Locale->date_fmt, t, pt, + ptlim, warnp); continue; case '%': /* @@ -562,7 +578,7 @@ strftime(char *s, size_t maxsize, const char *format, const struct tm *t) enum warn warn = IN_NONE; tzset(); - p = _fmt(format, t, s, s + maxsize, &warn); + p = strftime_fmt(format, t, s, s + maxsize, &warn); if (!p) { errno = EOVERFLOW; return 0; diff --git a/libc/time/time.mk b/libc/time/time.mk index 0690eaa7c..da673d944 100644 --- a/libc/time/time.mk +++ b/libc/time/time.mk @@ -61,8 +61,7 @@ o/$(MODE)/libc/time/strftime.o: \ OVERRIDE_CFLAGS += \ -fno-jump-tables -o/$(MODE)/libc/time/localtime.o \ -o/$(MODE)/libc/time/strftime.o: \ +o/$(MODE)/libc/time/localtime.o: \ OVERRIDE_CFLAGS += \ -fdata-sections \ -ffunction-sections diff --git a/third_party/dlmalloc/dlmalloc_abort.greg.c b/third_party/dlmalloc/dlmalloc_abort.greg.c index 12ea05742..d2b2fd17a 100644 --- a/third_party/dlmalloc/dlmalloc_abort.greg.c +++ b/third_party/dlmalloc/dlmalloc_abort.greg.c @@ -17,13 +17,15 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/weaken.h" -#include "libc/intrin/kprintf.h" +#include "libc/calls/calls.h" #include "libc/log/log.h" #include "libc/runtime/runtime.h" -#include "third_party/dlmalloc/dlmalloc.h" +#include "libc/str/str.h" + +#define MESSAGE "dlmalloc_abort()\n" void dlmalloc_abort(void) { - kprintf("dlmalloc_abort()%n"); + write(2, MESSAGE, strlen(MESSAGE)); if (weaken(__die)) weaken(__die)(); _Exit(44); } diff --git a/tool/net/help.txt b/tool/net/help.txt index 0fe55737f..e516a0e48 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -434,6 +434,11 @@ SPECIAL PATHS If it exists, it'll be used as the / listing page icon, embedded as a base64 URI. + /usr/share/zoneinfo + This directory contains a subset of the timezone database. + Your `TZ` environment variable controls which one of these + files is used by functions such as unix.localtime(). + /usr/share/ssl/root This directory contains your root certificate authorities. It is needed so the Fetch() HTTPS client API can verify that a remote @@ -1914,7 +1919,7 @@ UNIX MODULE ├─→ true └─→ nil, unix.Errno - Changes user and gorup on file. + Changes user and group on file. Returns `ENOSYS` on Windows NT. @@ -2817,19 +2822,21 @@ UNIX MODULE Your redbean ships with a subset of the time zone database. - - `/zip/usr/share/zoneinfo/Honolulu` - - `/zip/usr/share/zoneinfo/Anchorage` - - `/zip/usr/share/zoneinfo/GST` - - `/zip/usr/share/zoneinfo/Boulder` - - `/zip/usr/share/zoneinfo/Chicago` - - `/zip/usr/share/zoneinfo/New_York` - - `/zip/usr/share/zoneinfo/UTC` - - `/zip/usr/share/zoneinfo/London` - - `/zip/usr/share/zoneinfo/Berlin` - - `/zip/usr/share/zoneinfo/Israel` - - `/zip/usr/share/zoneinfo/Beijing` - - `/zip/usr/share/zoneinfo/Japan` - - `/zip/usr/share/zoneinfo/Sydney` + - `/zip/usr/share/zoneinfo/Honolulu` Z-10 + - `/zip/usr/share/zoneinfo/Anchorage` Z -9 + - `/zip/usr/share/zoneinfo/GST` Z -8 + - `/zip/usr/share/zoneinfo/Boulder` Z -6 + - `/zip/usr/share/zoneinfo/Chicago` Z -5 + - `/zip/usr/share/zoneinfo/New_York` Z -4 + - `/zip/usr/share/zoneinfo/UTC` Z +0 + - `/zip/usr/share/zoneinfo/GMT` Z +0 + - `/zip/usr/share/zoneinfo/London` Z +1 + - `/zip/usr/share/zoneinfo/Berlin` Z +2 + - `/zip/usr/share/zoneinfo/Israel` Z +3 + - `/zip/usr/share/zoneinfo/India` Z +5 + - `/zip/usr/share/zoneinfo/Beijing` Z +8 + - `/zip/usr/share/zoneinfo/Japan` Z +9 + - `/zip/usr/share/zoneinfo/Sydney` Z+10 You can control which timezone is used using the `TZ` environment variable. If your time zone isn't included in the above list, you @@ -3988,5 +3995,5 @@ UNIX MODULE SEE ALSO - https://justine.lol/redbean/index.html + https://redbean.dev/ https://news.ycombinator.com/item?id=26271117 diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 0aee7cdbf..c5a3b2444 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -179,7 +179,7 @@ STATIC_YOINK("zip_uri_support"); #define REDBEAN "redbean" #endif -#define VERSION 0x010500 +#define VERSION 0x020000 #define HEARTBEAT 5000 /*ms*/ #define HASH_LOAD_FACTOR /* 1. / */ 4 #define READ(F, P, N) readv(F, &(struct iovec){P, N}, 1) diff --git a/usr/share/zoneinfo/GMT b/usr/share/zoneinfo/GMT new file mode 100644 index 0000000000000000000000000000000000000000..c63474664a289aa3c3c0d8b2ce06d484679754c0 GIT binary patch literal 114 hcmWHE%1kq2AP5+NDp(+@+)Bq&f=kD2c>UNLD8P-CHGgFOLTq+To!N|l6gbWNpH-HKl zyxl;meIpn+7#N~67^~5w?UK*{(az6b z8-Qq#8$dM39UvOy7BCHT4~T}kiG`Vk8Rn><3m``Uod$Fi&}lqirwM?Z=7HfnE}% Date: Fri, 29 Apr 2022 06:06:23 -0700 Subject: [PATCH 16/18] Polish redbean serialization --- libc/fmt/formatbinary64.c | 63 +++++++++++++ libc/fmt/formatflex64.c | 63 +++++++++++++ libc/fmt/formathex64.c | 64 +++++++++++++ libc/fmt/formatoctal64.c | 2 +- libc/fmt/itoa.h | 3 + test/libc/fmt/formatbinary64_test.c | 98 ++++++++++++++++++++ test/libc/fmt/formatflex64_test.c | 34 +++++++ test/libc/fmt/formathex64_test.c | 87 +++++++++++++++++ test/libc/fmt/formatoctal64_test.c | 2 +- third_party/lua/README.cosmo | 2 + third_party/lua/cosmo.h | 2 +- third_party/lua/lapi.c | 101 ++++++++++++++++++-- third_party/lua/lctype.h | 6 ++ third_party/lua/lobject.c | 9 +- third_party/lua/lrepl.c | 4 +- third_party/lua/luaencodejsondata.c | 139 +++++++++++++++------------- third_party/lua/luaencodeluadata.c | 115 ++++++++++++----------- third_party/lua/visitor.c | 38 ++++++++ third_party/lua/visitor.h | 16 ++++ tool/build/runitd.c | 1 + tool/net/help.txt | 50 ++++++++++ tool/net/lfuncs.c | 32 ++++++- tool/net/lfuncs.h | 3 + tool/net/redbean.c | 52 +++++++---- 24 files changed, 828 insertions(+), 158 deletions(-) create mode 100644 libc/fmt/formatbinary64.c create mode 100644 libc/fmt/formatflex64.c create mode 100644 libc/fmt/formathex64.c create mode 100644 test/libc/fmt/formatbinary64_test.c create mode 100644 test/libc/fmt/formatflex64_test.c create mode 100644 test/libc/fmt/formathex64_test.c create mode 100644 third_party/lua/visitor.c create mode 100644 third_party/lua/visitor.h diff --git a/libc/fmt/formatbinary64.c b/libc/fmt/formatbinary64.c new file mode 100644 index 000000000..5e4e1b011 --- /dev/null +++ b/libc/fmt/formatbinary64.c @@ -0,0 +1,63 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/itoa.h" +#include "libc/nexgen32e/bsr.h" + +static inline int PickGoodWidth(unsigned x) { + if (x < 16) { + if (x < 2) return 0; + if (x < 8) return 7; + return 15; + } else { + if (x < 32) return 31; + return 63; + } +} + +/** + * Converts unsigned 64-bit integer to binary string. + * + * @param p needs at least 67 bytes + * @param z is 0 for DIGITS, 1 for 0bDIGITS, 2 for 0bDIGITS if ≠0 + * @return pointer to nul byte + */ +char *FormatBinary64(char p[hasatleast 67], uint64_t x, char z) { + int i; + uint64_t b; + if (x) { + if (z) { + *p++ = '0'; + *p++ = 'b'; + } + i = PickGoodWidth(bsrl(x)); + do { + b = 1; + b <<= i; + *p++ = '0' + !!(x & b); + } while (i--); + } else { + if (z == 1) { + *p++ = '0'; + *p++ = 'b'; + } + *p++ = '0'; + } + *p = 0; + return p; +} diff --git a/libc/fmt/formatflex64.c b/libc/fmt/formatflex64.c new file mode 100644 index 000000000..2c8b7f106 --- /dev/null +++ b/libc/fmt/formatflex64.c @@ -0,0 +1,63 @@ +/*-*- 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 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/fmt.h" +#include "libc/fmt/itoa.h" +#include "libc/math.h" + +static inline int CountZeroesHex(uint64_t x) { + int n; + for (n = 0; x;) { + if (!(x & 15)) { + ++n; + } + x >>= 4; + } + return n; +} + +static inline int CountZeroesDec(int64_t s) { + int n, r; + uint64_t x; + x = s >= 0 ? s : -(uint64_t)s; + for (n = 0; x;) { + r = x % 10; + x = x / 10; + if (!r) ++n; + } + return n; +} + +/** + * Formats integer using decimal or hexadecimal. + * + * We choose hex vs. decimal based on whichever one has the most zeroes. + * We only bother counting zeroes for numbers outside -256 ≤ 𝑥 ≤ 256. + */ +char *FormatFlex64(char p[hasatleast 24], int64_t x, char z) { + int zhex, zdec; + if (-256 <= x && x <= 256) goto UseDecimal; + zhex = CountZeroesHex(x) * 16; + zdec = CountZeroesDec(x) * 10; + if (zdec >= zhex) { + UseDecimal: + return FormatInt64(p, x); + } else { + return FormatHex64(p, x, z); + } +} diff --git a/libc/fmt/formathex64.c b/libc/fmt/formathex64.c new file mode 100644 index 000000000..528150e4b --- /dev/null +++ b/libc/fmt/formathex64.c @@ -0,0 +1,64 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/itoa.h" +#include "libc/macros.internal.h" +#include "libc/nexgen32e/bsr.h" + +static inline int PickGoodWidth(unsigned x, char z) { + if (z) { + if (x < 16) { + if (x < 8) return 8; + return 16; + } else { + if (x < 32) return 32; + return 64; + } + } else { + return ROUNDUP(x + 1, 4); + } +} + +/** + * Converts unsigned 64-bit integer to hex string. + * + * @param p needs at least 19 bytes + * @param z is 0 for DIGITS, 1 for 0bDIGITS, 2 for 0bDIGITS if ≠0 + * @return pointer to nul byte + */ +char *FormatHex64(char p[hasatleast 19], uint64_t x, char z) { + int i; + if (x) { + if (z) { + *p++ = '0'; + *p++ = 'x'; + } + i = PickGoodWidth(bsrl(x), z); + do { + *p++ = "0123456789abcdef"[(x >> (i -= 4)) & 15]; + } while (i); + } else { + if (z == 1) { + *p++ = '0'; + *p++ = 'x'; + } + *p++ = '0'; + } + *p = 0; + return p; +} diff --git a/libc/fmt/formatoctal64.c b/libc/fmt/formatoctal64.c index c500467ab..c82171967 100644 --- a/libc/fmt/formatoctal64.c +++ b/libc/fmt/formatoctal64.c @@ -21,7 +21,7 @@ /** * Converts unsigned 64-bit integer to octal string. * - * @param p needs at least 12 bytes + * @param p needs at least 24 bytes * @param z ensures it starts with zero * @return pointer to nul byte */ diff --git a/libc/fmt/itoa.h b/libc/fmt/itoa.h index f123dc271..a492328d1 100644 --- a/libc/fmt/itoa.h +++ b/libc/fmt/itoa.h @@ -15,6 +15,9 @@ char *FormatInt64Thousands(char[hasatleast 27], int64_t); char *FormatUint64Thousands(char[hasatleast 27], uint64_t); char *FormatOctal32(char[hasatleast 13], uint32_t, bool); char *FormatOctal64(char[hasatleast 24], uint64_t, bool); +char *FormatBinary64(char[hasatleast 67], uint64_t, char); +char *FormatHex64(char[hasatleast 19], uint64_t, char); +char *FormatFlex64(char[hasatleast 24], int64_t, char); size_t uint64toarray_radix16(uint64_t, char[hasatleast 17]); size_t uint64toarray_fixed16(uint64_t, char[hasatleast 17], uint8_t); size_t uint64toarray_radix8(uint64_t, char[hasatleast 24]); diff --git a/test/libc/fmt/formatbinary64_test.c b/test/libc/fmt/formatbinary64_test.c new file mode 100644 index 000000000..930a946d8 --- /dev/null +++ b/test/libc/fmt/formatbinary64_test.c @@ -0,0 +1,98 @@ +/*-*- 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 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/fmt.h" +#include "libc/fmt/itoa.h" +#include "libc/testlib/ezbench.h" +#include "libc/testlib/testlib.h" + +char buf[67]; + +void SetUp(void) { + memset(buf, 0x55, sizeof(buf)); +} + +TEST(FormatBinary64, test1) { + EXPECT_EQ(1, FormatBinary64(buf, 0, 2) - buf); + EXPECT_STREQ("0", buf); +} + +TEST(FormatBinary64, test2) { + EXPECT_EQ(1, FormatBinary64(buf, 0, 0) - buf); + EXPECT_STREQ("0", buf); + EXPECT_EQ(3, FormatBinary64(buf, 0, 1) - buf); + EXPECT_STREQ("0b0", buf); +} + +TEST(FormatBinary64, test3) { + EXPECT_EQ(3, FormatBinary64(buf, 1, 2) - buf); + EXPECT_STREQ("0b1", buf); +} + +TEST(FormatBinary64, test4) { + EXPECT_EQ(1, FormatBinary64(buf, 1, 0) - buf); + EXPECT_STREQ("1", buf); +} + +TEST(FormatBinary64, test5) { + EXPECT_EQ(66, FormatBinary64(buf, 01777777777777777777777UL, 2) - buf); + EXPECT_STREQ( + "0b1111111111111111111111111111111111111111111111111111111111111111", + buf); +} + +TEST(FormatBinary64, test6) { + EXPECT_EQ(64, FormatBinary64(buf, 01777777777777777777777UL, 0) - buf); + EXPECT_STREQ( + "1111111111111111111111111111111111111111111111111111111111111111", buf); +} + +TEST(FormatBinary64, test7) { + EXPECT_EQ(66, FormatBinary64(buf, 0xEBF2AA499B9028EAul, 2) - buf); + EXPECT_STREQ( + "0b1110101111110010101010100100100110011011100100000010100011101010", + buf); +} + +TEST(FormatBinary64, test8) { + EXPECT_EQ(66, FormatBinary64(buf, 0x00F2AA499B9028EAul, 2) - buf); + EXPECT_STREQ( + "0b0000000011110010101010100100100110011011100100000010100011101010", + buf); +} + +TEST(FormatBinary64, testScalesToWordSizes) { + EXPECT_EQ(8 + 2, FormatBinary64(buf, 13, 2) - buf); + EXPECT_STREQ("0b00001101", buf); + EXPECT_EQ(16 + 2, FormatBinary64(buf, 31337, 2) - buf); + EXPECT_STREQ("0b0111101001101001", buf); + EXPECT_EQ(32 + 2, FormatBinary64(buf, 65536, 2) - buf); + EXPECT_STREQ("0b00000000000000010000000000000000", buf); +} + +BENCH(FormatBinary64, bench) { + EZBENCH2("FormatUint64 tiny", donothing, FormatUint64(buf, 1)); + EZBENCH2("FormatOctal64 tiny", donothing, FormatOctal64(buf, 1, true)); + EZBENCH2("FormatBinary64 tiny", donothing, FormatBinary64(buf, 1, 2)); + EZBENCH2("FormatUint64 big", donothing, + FormatUint64(buf, 01777777777777777777777UL)); + EZBENCH2("FormatOctal64 big", donothing, + FormatOctal64(buf, 01777777777777777777777UL, true)); + EZBENCH2("FormatBinary64 big", donothing, + FormatBinary64(buf, 01777777777777777777777UL, 2)); +} diff --git a/test/libc/fmt/formatflex64_test.c b/test/libc/fmt/formatflex64_test.c new file mode 100644 index 000000000..974966272 --- /dev/null +++ b/test/libc/fmt/formatflex64_test.c @@ -0,0 +1,34 @@ +/*-*- 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 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/fmt.h" +#include "libc/fmt/itoa.h" +#include "libc/testlib/testlib.h" + +char buf[25]; + +void SetUp(void) { + memset(buf, 0x55, sizeof(buf)); +} + +TEST(formatflex64, test) { + EXPECT_EQ(5, FormatFlex64(buf, 31337, 2) - buf); + EXPECT_STREQ("31337", buf); + EXPECT_EQ(10, FormatFlex64(buf, 0x80000000, 2) - buf); + EXPECT_STREQ("0x80000000", buf); +} diff --git a/test/libc/fmt/formathex64_test.c b/test/libc/fmt/formathex64_test.c new file mode 100644 index 000000000..9b41dda2f --- /dev/null +++ b/test/libc/fmt/formathex64_test.c @@ -0,0 +1,87 @@ +/*-*- 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 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/fmt.h" +#include "libc/fmt/itoa.h" +#include "libc/testlib/ezbench.h" +#include "libc/testlib/testlib.h" + +char buf[19]; + +void SetUp(void) { + memset(buf, 0x55, sizeof(buf)); +} + +TEST(FormatHex64, test1) { + EXPECT_EQ(1, FormatHex64(buf, 0, 2) - buf); + EXPECT_STREQ("0", buf); +} + +TEST(FormatHex64, test2) { + EXPECT_EQ(1, FormatHex64(buf, 0, 0) - buf); + EXPECT_STREQ("0", buf); + EXPECT_EQ(3, FormatHex64(buf, 0, 1) - buf); + EXPECT_STREQ("0x0", buf); +} + +TEST(FormatHex64, test3) { + EXPECT_EQ(4, FormatHex64(buf, 1, 2) - buf); + EXPECT_STREQ("0x01", buf); +} + +TEST(FormatHex64, test4) { + EXPECT_EQ(1, FormatHex64(buf, 1, 0) - buf); + EXPECT_STREQ("1", buf); +} + +TEST(FormatHex64, test5) { + EXPECT_EQ(18, FormatHex64(buf, 01777777777777777777777UL, 2) - buf); + EXPECT_STREQ("0xffffffffffffffff", buf); +} + +TEST(FormatHex64, test6) { + EXPECT_EQ(16, FormatHex64(buf, 01777777777777777777777UL, 0) - buf); + EXPECT_STREQ("ffffffffffffffff", buf); +} + +TEST(FormatHex64, test7) { + EXPECT_EQ(18, FormatHex64(buf, 0xEBF2AA499B9028EAul, 2) - buf); + EXPECT_STREQ("0xebf2aa499b9028ea", buf); +} + +TEST(FormatHex64, test8) { + EXPECT_EQ(18, FormatHex64(buf, 0x00F2AA499B9028EAul, 2) - buf); + EXPECT_STREQ("0x00f2aa499b9028ea", buf); +} + +TEST(FormatHex64, testScalesToWordSizes) { + EXPECT_EQ(2 + 2, FormatHex64(buf, 13, 2) - buf); + EXPECT_STREQ("0x0d", buf); + EXPECT_EQ(4 + 2, FormatHex64(buf, 31337, 2) - buf); + EXPECT_STREQ("0x7a69", buf); + EXPECT_EQ(8 + 2, FormatHex64(buf, 65536, 2) - buf); + EXPECT_STREQ("0x00010000", buf); +} + +BENCH(FormatHex64, bench) { + EZBENCH2("FormatUint64 tiny", donothing, FormatUint64(buf, 1)); + EZBENCH2("FormatOctal64 tiny", donothing, FormatOctal64(buf, 1, true)); + EZBENCH2("FormatHex64 tiny", donothing, FormatHex64(buf, 1, 2)); + EZBENCH2("FormatHex64 big", donothing, + FormatHex64(buf, 01777777777777777777777UL, 2)); +} diff --git a/test/libc/fmt/formatoctal64_test.c b/test/libc/fmt/formatoctal64_test.c index 421b48f17..82e3b675d 100644 --- a/test/libc/fmt/formatoctal64_test.c +++ b/test/libc/fmt/formatoctal64_test.c @@ -21,7 +21,7 @@ #include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" -char buf[25]; +char buf[24]; void SetUp(void) { memset(buf, 0x55, sizeof(buf)); diff --git a/third_party/lua/README.cosmo b/third_party/lua/README.cosmo index a418cc10c..4ec2c7a03 100644 --- a/third_party/lua/README.cosmo +++ b/third_party/lua/README.cosmo @@ -24,6 +24,8 @@ LOCAL MODIFICATIONS Integer literals such as `033` will now be interpreted as octal. + Integer literals such as `0b10` will now be interpreted as binary. + The `\e` string literal escape sequence has been added, which is equivalent to `\27` (the Lua version of `\033`) or the ASCII ESC character. It may be used for teletypewriter control like having diff --git a/third_party/lua/cosmo.h b/third_party/lua/cosmo.h index 06d136548..b717418c6 100644 --- a/third_party/lua/cosmo.h +++ b/third_party/lua/cosmo.h @@ -15,9 +15,9 @@ int LuaParseUrl(lua_State *); int LuaPushHeader(lua_State *, struct HttpMessage *, char *, int); int LuaPushHeaders(lua_State *, struct HttpMessage *, const char *); void EscapeLuaString(char *, size_t, char **); +void LuaPrintStack(lua_State *); void LuaPushLatin1(lua_State *, const char *, size_t); void LuaPushUrlParams(lua_State *, struct UrlParams *); -void LuaPrintStack(lua_State *); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/third_party/lua/lapi.c b/third_party/lua/lapi.c index 3723ef79b..fdda5f93d 100644 --- a/third_party/lua/lapi.c +++ b/third_party/lua/lapi.c @@ -504,26 +504,51 @@ static void *touserdata (const TValue *o) { } +/** + * lua_touserdata [-0, +0, –] + * + * If the value at the given index is a full userdata, returns its + * memory-block address. If the value is a light userdata, returns its value + * (a pointer). Otherwise, returns NULL. + */ LUA_API void *lua_touserdata (lua_State *L, int idx) { const TValue *o = index2value(L, idx); return touserdata(o); } +/** + * lua_tothread [-0, +0, –] + * + * Converts the value at the given index to a Lua thread (represented as + * lua_State*). This value must be a thread; otherwise, the function returns + * NULL. + */ LUA_API lua_State *lua_tothread (lua_State *L, int idx) { const TValue *o = index2value(L, idx); return (!ttisthread(o)) ? NULL : thvalue(o); } -/* -** Returns a pointer to the internal representation of an object. -** Note that ANSI C does not allow the conversion of a pointer to -** function to a 'void*', so the conversion here goes through -** a 'size_t'. (As the returned pointer is only informative, this -** conversion should not be a problem.) -*/ +/** + * lua_topointer [-0, +0, –] + * + * Converts the value at the given index to a generic C pointer (void*). The + * value can be a userdata, a table, a thread, a string, or a function; + * otherwise, lua_topointer returns NULL. Different objects will give + * different pointers. There is no way to convert the pointer back to its + * original value. + * + * Typically this function is used only for hashing and debug information. + */ LUA_API const void *lua_topointer (lua_State *L, int idx) { + /* + ** Returns a pointer to the internal representation of an object. + ** Note that ANSI C does not allow the conversion of a pointer to + ** function to a 'void*', so the conversion here goes through + ** a 'size_t'. (As the returned pointer is only informative, this + ** conversion should not be a problem.) + */ const TValue *o = index2value(L, idx); switch (ttypetag(o)) { case LUA_VLCF: return cast_voidp(cast_sizet(fvalue(o))); @@ -881,6 +906,12 @@ static Table *gettable (lua_State *L, int idx) { } +/** + * lua_rawget [-1, +1, –] + * + * Similar to lua_gettable, but does a raw access (i.e., without + * metamethods). + */ LUA_API int lua_rawget (lua_State *L, int idx) { Table *t; const TValue *val; @@ -901,6 +932,15 @@ LUA_API int lua_rawgeti (lua_State *L, int idx, lua_Integer n) { } +/** + * lua_rawgetp [-0, +1, –] + * + * Pushes onto the stack the value t[k], where t is the table at the given + * index and k is the pointer p represented as a light userdata. The access + * is raw; that is, it does not use the __index metavalue. + * + * Returns the type of the pushed value. + */ LUA_API int lua_rawgetp (lua_State *L, int idx, const void *p) { Table *t; TValue k; @@ -911,6 +951,17 @@ LUA_API int lua_rawgetp (lua_State *L, int idx, const void *p) { } +/** + * lua_createtable [-0, +1, m] + * + * Creates a new empty table and pushes it onto the stack. Parameter narr is + * a hint for how many elements the table will have as a sequence; parameter + * nrec is a hint for how many other elements the table will have. Lua may + * use these hints to preallocate memory for the new table. This + * preallocation may help performance when you know in advance how many + * elements the table will have. Otherwise you can use the function + * lua_newtable. + */ LUA_API void lua_createtable (lua_State *L, int narray, int nrec) { Table *t; lua_lock(L); @@ -924,6 +975,15 @@ LUA_API void lua_createtable (lua_State *L, int narray, int nrec) { } +/** + * lua_getmetatable [-0, +(0|1), –] + * + * int lua_getmetatable (lua_State *L, int index); + * + * If the value at the given index has a metatable, the function pushes that + * metatable onto the stack and returns 1. Otherwise, the function returns 0 + * and pushes nothing on the stack. + */ LUA_API int lua_getmetatable (lua_State *L, int objindex) { const TValue *obj; Table *mt; @@ -951,6 +1011,17 @@ LUA_API int lua_getmetatable (lua_State *L, int objindex) { } +/** + * lua_getiuservalue [-0, +1, –] + * + * int lua_getiuservalue (lua_State *L, int index, int n); + * + * Pushes onto the stack the n-th user value associated with the full + * userdata at the given index and returns the type of the pushed value. + * + * If the userdata does not have that value, pushes nil and returns + * LUA_TNONE. + */ LUA_API int lua_getiuservalue (lua_State *L, int idx, int n) { TValue *o; int t; @@ -1116,6 +1187,15 @@ LUA_API void lua_rawseti (lua_State *L, int idx, lua_Integer n) { } +/** + * lua_setmetatable [-1, +0, –] + * + * Pops a table or nil from the stack and sets that value as the new + * metatable for the value at the given index. (nil means no metatable.) + * + * (For historical reasons, this function returns an int, which now is always + * 1.) + */ LUA_API int lua_setmetatable (lua_State *L, int objindex) { TValue *obj; Table *mt; @@ -1156,6 +1236,13 @@ LUA_API int lua_setmetatable (lua_State *L, int objindex) { } +/** + * lua_setiuservalue [-1, +0, –] + * + * Pops a value from the stack and sets it as the new n-th user value + * associated to the full userdata at the given index. Returns 0 if the + * userdata does not have that value. + */ LUA_API int lua_setiuservalue (lua_State *L, int idx, int n) { TValue *o; int res; diff --git a/third_party/lua/lctype.h b/third_party/lua/lctype.h index b4988a444..1204fe0d3 100644 --- a/third_party/lua/lctype.h +++ b/third_party/lua/lctype.h @@ -45,6 +45,12 @@ ('a' <= c_ && c_ <= 'f')); \ }) +#define lisbdigit(C) \ + ({ \ + unsigned char c_ = (C); \ + '0' <= c_&& c_ <= '1'; \ + }) + #define lisprint(C) \ ({ \ unsigned char c_ = (C); \ diff --git a/third_party/lua/lobject.c b/third_party/lua/lobject.c index efadd9002..c86af6686 100644 --- a/third_party/lua/lobject.c +++ b/third_party/lua/lobject.c @@ -27,7 +27,6 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #define lobject_c #define LUA_CORE -#include "libc/intrin/kprintf.h" #include "third_party/lua/lctype.h" #include "third_party/lua/ldebug.h" #include "third_party/lua/ldo.h" @@ -285,6 +284,14 @@ static const char *l_str2int (const char *s, lua_Integer *result) { empty = 0; } } + if (s[0] == '0' && + (s[1] == 'b' || s[1] == 'B')) { /* [jart] binary */ + s += 2; /* skip '0b' */ + for (; lisbdigit(cast_uchar(*s)); s++) { + a = a * 2 + (*s - '0'); + empty = 0; + } + } else if (s[0] == '0') { /* [jart] octal is the best radix */ for (s += 1; lisdigit(cast_uchar(*s)); s++) { int d = *s - '0'; diff --git a/third_party/lua/lrepl.c b/third_party/lua/lrepl.c index fb618b96c..482b0577e 100644 --- a/third_party/lua/lrepl.c +++ b/third_party/lua/lrepl.c @@ -30,13 +30,13 @@ #include "libc/calls/calls.h" #include "libc/calls/sigbits.h" #include "libc/errno.h" -#include "libc/intrin/kprintf.h" #include "libc/intrin/nomultics.internal.h" #include "libc/log/check.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" #include "libc/runtime/gc.h" #include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/sysv/consts/sa.h" #include "third_party/linenoise/linenoise.h" @@ -330,7 +330,7 @@ void lua_initrepl(lua_State *L, const char *progname) { prompt = get_prompt(L, 1); if ((g_historypath = linenoiseGetHistoryPath(progname))) { if (linenoiseHistoryLoad(g_historypath) == -1) { - kprintf("%r%s: failed to load history: %m%n", g_historypath); + fprintf(stderr, "%r%s: failed to load history: %m%n", g_historypath); free(g_historypath); g_historypath = 0; } diff --git a/third_party/lua/luaencodejsondata.c b/third_party/lua/luaencodejsondata.c index 16c9ba5e6..706688ca0 100644 --- a/third_party/lua/luaencodejsondata.c +++ b/third_party/lua/luaencodejsondata.c @@ -16,23 +16,37 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" #include "libc/bits/bits.h" #include "libc/fmt/itoa.h" +#include "libc/mem/mem.h" #include "libc/runtime/gc.internal.h" #include "libc/stdio/append.internal.h" #include "net/http/escape.h" #include "third_party/lua/cosmo.h" #include "third_party/lua/lauxlib.h" #include "third_party/lua/lua.h" +#include "third_party/lua/visitor.h" static int LuaEncodeJsonDataImpl(lua_State *L, char **buf, int level, - char *numformat, int idx) { + char *numformat, int idx, + struct LuaVisited *visited) { char *s; bool isarray; - size_t tbllen, z; + size_t tbllen, i, z; char ibuf[21], fmt[] = "%.14g"; if (level > 0) { switch (lua_type(L, idx)) { + + case LUA_TNIL: + appendw(buf, READ32LE("null")); + return 0; + + case LUA_TBOOLEAN: + appendw(buf, lua_toboolean(L, idx) ? READ32LE("true") + : READ64LE("false\0\0")); + return 0; + case LUA_TSTRING: s = lua_tolstring(L, idx, &z); s = EscapeJsStringLiteral(s, z, &z); @@ -41,21 +55,7 @@ static int LuaEncodeJsonDataImpl(lua_State *L, char **buf, int level, appendw(buf, '"'); free(s); return 0; - case LUA_TNIL: - appendw(buf, READ32LE("null")); - return 0; - case LUA_TFUNCTION: - appendf(buf, "\"func@%p\"", lua_touserdata(L, idx)); - return 0; - case LUA_TUSERDATA: - appendf(buf, "\"udata@%p\"", lua_touserdata(L, idx)); - return 0; - case LUA_TLIGHTUSERDATA: - appendf(buf, "\"light@%p\"", lua_touserdata(L, idx)); - return 0; - case LUA_TTHREAD: - appendf(buf, "\"thread@%p\"", lua_touserdata(L, idx)); - return 0; + case LUA_TNUMBER: if (lua_isinteger(L, idx)) { appendd(buf, ibuf, @@ -78,58 +78,64 @@ static int LuaEncodeJsonDataImpl(lua_State *L, char **buf, int level, appendf(buf, fmt, lua_tonumber(L, idx)); } return 0; - case LUA_TBOOLEAN: - appends(buf, lua_toboolean(L, idx) ? "true" : "false"); - return 0; + case LUA_TTABLE: - tbllen = lua_rawlen(L, idx); - // encode tables with numeric indices and empty tables as arrays - isarray = - tbllen > 0 || // integer keys present - (lua_pushnil(L), lua_next(L, -2) == 0) || // no non-integer keys - (lua_pop(L, 2), false); // pop key/value pushed by lua_next - appendw(buf, isarray ? '[' : '{'); - if (isarray) { - for (size_t i = 1; i <= tbllen; i++) { - if (i > 1) appendw(buf, ','); - lua_rawgeti(L, -1, i); // table/-2, value/-1 - LuaEncodeJsonDataImpl(L, buf, level - 1, numformat, -1); - lua_pop(L, 1); + if (LuaPushVisit(visited, lua_topointer(L, idx))) { + tbllen = lua_rawlen(L, idx); + // encode tables with numeric indices and empty tables as arrays + isarray = + tbllen > 0 || // integer keys present + (lua_pushnil(L), !lua_next(L, -2)) || // no non-integer keys + (lua_pop(L, 2), false); // pop key/value pushed by lua_next + appendw(buf, isarray ? '[' : '{'); + if (isarray) { + for (i = 1; i <= tbllen; i++) { + if (i > 1) appendw(buf, ','); + lua_rawgeti(L, -1, i); // table/-2, value/-1 + LuaEncodeJsonDataImpl(L, buf, level - 1, numformat, -1, visited); + lua_pop(L, 1); + } + } else { + i = 1; + lua_pushnil(L); // push the first key + while (lua_next(L, -2)) { + if (!lua_isstring(L, -2)) { + luaL_error(L, "expected number or string as key value"); + unreachable; + } + if (i++ > 1) appendw(buf, ','); + appendw(buf, '"'); + if (lua_type(L, -2) == LUA_TSTRING) { + s = lua_tolstring(L, -2, &z); + s = EscapeJsStringLiteral(s, z, &z); + appendd(buf, s, z); + free(s); + } else { + // we'd still prefer to use lua_tostring on a numeric index, but + // can't use it in-place, as it breaks lua_next (changes numeric + // key to a string) + lua_pushvalue(L, -2); // table/-4, key/-3, value/-2, key/-1 + s = lua_tolstring(L, idx, &z); + appendd(buf, s, z); // use the copy + lua_remove(L, -1); // remove copied key: tab/-3, key/-2, val/-1 + } + appendw(buf, '"' | ':' << 010); + LuaEncodeJsonDataImpl(L, buf, level - 1, numformat, -1, visited); + lua_pop(L, 1); // table/-2, key/-1 + } + // stack: table/-1, as the key was popped by lua_next } + appendw(buf, isarray ? ']' : '}'); + LuaPopVisit(visited); + return 0; } else { - int i = 1; - lua_pushnil(L); // push the first key - while (lua_next(L, -2)) { - if (!lua_isstring(L, -2)) { - luaL_error(L, "expected number or string as key value"); - unreachable; - } - if (i++ > 1) appendw(buf, ','); - appendw(buf, '"'); - if (lua_type(L, -2) == LUA_TSTRING) { - s = lua_tolstring(L, -2, &z); - s = EscapeJsStringLiteral(s, z, &z); - appendd(buf, s, z); - free(s); - } else { - // we'd still prefer to use lua_tostring on a numeric index, but - // can't use it in-place, as it breaks lua_next (changes numeric - // key to a string) - lua_pushvalue(L, -2); // table/-4, key/-3, value/-2, key/-1 - s = lua_tolstring(L, idx, &z); - appendd(buf, s, z); // use the copy - lua_remove(L, -1); // remove copied key: tab/-3, key/-2, val/-1 - } - appendw(buf, '"' | ':' << 010); - LuaEncodeJsonDataImpl(L, buf, level - 1, numformat, -1); - lua_pop(L, 1); // table/-2, key/-1 - } - // stack: table/-1, as the key was popped by lua_next + // TODO(jart): don't leak memory with longjmp + luaL_error(L, "can't serialize cyclic data structure to json"); + unreachable; } - appendw(buf, isarray ? ']' : '}'); - return 0; + default: - luaL_error(L, "can't serialize value of this type"); + luaL_error(L, "won't serialize %s to json", luaL_typename(L, idx)); unreachable; } } else { @@ -140,6 +146,9 @@ static int LuaEncodeJsonDataImpl(lua_State *L, char **buf, int level, int LuaEncodeJsonData(lua_State *L, char **buf, char *numformat, int idx) { int rc; - rc = LuaEncodeJsonDataImpl(L, buf, 64, numformat, idx); + struct LuaVisited visited = {0}; + rc = LuaEncodeJsonDataImpl(L, buf, 64, numformat, idx, &visited); + assert(!visited.n); + free(visited.p); return rc; } diff --git a/third_party/lua/luaencodeluadata.c b/third_party/lua/luaencodeluadata.c index 15aa03518..cc78f5550 100644 --- a/third_party/lua/luaencodeluadata.c +++ b/third_party/lua/luaencodeluadata.c @@ -25,39 +25,44 @@ #include "libc/x/x.h" #include "third_party/lua/cosmo.h" #include "third_party/lua/lauxlib.h" +#include "third_party/lua/lctype.h" #include "third_party/lua/lua.h" +#include "third_party/lua/visitor.h" -struct Visited { - int n; - void **p; -}; - -static bool PushVisit(struct Visited *visited, void *p) { - int i; - for (i = 0; i < visited->n; ++i) { - if (visited->p[i] == p) { - return false; - } +static bool IsLuaIdentifier(lua_State *L, int idx) { + size_t i, n; + const char *p; + p = luaL_checklstring(L, idx, &n); + if (!lislalpha(p[0])) return false; + for (i = 1; i < n; ++i) { + if (!lislalnum(p[i])) return false; } - visited->p = xrealloc(visited->p, ++visited->n * sizeof(*visited->p)); - visited->p[visited->n - 1] = p; return true; } -static void PopVisit(struct Visited *visited) { - assert(visited->n > 0); - --visited->n; +// TODO: Can we be smarter with lua_rawlen? +static bool IsLuaArray(lua_State *L) { + int i; + lua_pushnil(L); + for (i = 1; lua_next(L, -2); ++i) { + if (!lua_isinteger(L, -2) || lua_tointeger(L, -2) != i) { + lua_pop(L, 2); + return false; + } + lua_pop(L, 1); + } + return true; } static int LuaEncodeLuaDataImpl(lua_State *L, char **buf, int level, char *numformat, int idx, - struct Visited *visited) { + struct LuaVisited *visited) { char *s; - bool didcomma; + bool isarray; lua_Integer i; int ktype, vtype; size_t tbllen, buflen, slen; - char ibuf[21], fmt[] = "%.14g"; + char ibuf[24], fmt[] = "%.14g"; if (level > 0) { switch (lua_type(L, idx)) { @@ -71,15 +76,15 @@ static int LuaEncodeLuaDataImpl(lua_State *L, char **buf, int level, return 0; case LUA_TFUNCTION: - appendf(buf, "\"func@%p\"", lua_touserdata(L, idx)); + appendf(buf, "\"func@%p\"", lua_topointer(L, idx)); return 0; case LUA_TLIGHTUSERDATA: - appendf(buf, "\"light@%p\"", lua_touserdata(L, idx)); + appendf(buf, "\"light@%p\"", lua_topointer(L, idx)); return 0; case LUA_TTHREAD: - appendf(buf, "\"thread@%p\"", lua_touserdata(L, idx)); + appendf(buf, "\"thread@%p\"", lua_topointer(L, idx)); return 0; case LUA_TUSERDATA: @@ -111,7 +116,7 @@ static int LuaEncodeLuaDataImpl(lua_State *L, char **buf, int level, case LUA_TNUMBER: if (lua_isinteger(L, idx)) { appendd(buf, ibuf, - FormatInt64(ibuf, luaL_checkinteger(L, idx)) - ibuf); + FormatFlex64(ibuf, luaL_checkinteger(L, idx), 2) - ibuf); } else { // TODO(jart): replace this api while (*numformat == '%' || *numformat == '.' || @@ -133,58 +138,52 @@ static int LuaEncodeLuaDataImpl(lua_State *L, char **buf, int level, return 0; case LUA_TBOOLEAN: - if (lua_toboolean(L, idx)) { - appendw(buf, READ32LE("true")); - } else { - appendw(buf, READ64LE("false\0\0")); - } + appendw(buf, lua_toboolean(L, idx) ? READ32LE("true") + : READ64LE("false\0\0")); return 0; case LUA_TTABLE: i = 0; - didcomma = false; - appendw(buf, '{'); - lua_pushvalue(L, idx); - lua_pushnil(L); // push the first key - while (lua_next(L, -2)) { - ++i; - ktype = lua_type(L, -2); - vtype = lua_type(L, -1); - if (ktype != LUA_TNUMBER || lua_tointeger(L, -2) != i) { - if (PushVisit(visited, lua_touserdata(L, -2))) { - if (i > 1) appendw(buf, ',' | ' ' << 8); - didcomma = true; + if (LuaPushVisit(visited, lua_topointer(L, idx))) { + appendw(buf, '{'); + lua_pushvalue(L, idx); // idx becomes invalid once we change stack + isarray = IsLuaArray(L); + lua_pushnil(L); // push the first key + while (lua_next(L, -2)) { + ktype = lua_type(L, -2); + vtype = lua_type(L, -1); + if (i++ > 0) appendw(buf, ',' | ' ' << 8); + if (isarray) { + // use {v₁′,v₂′,...} for lua-normal integer keys + } else if (ktype == LUA_TSTRING && IsLuaIdentifier(L, -2)) { + // use {𝑘=𝑣′} syntax when 𝑘 is legal as a lua identifier + s = lua_tolstring(L, -2, &slen); + appendd(buf, s, slen); + appendw(buf, '='); + } else { + // use {[𝑘′]=𝑣′} otherwise appendw(buf, '['); LuaEncodeLuaDataImpl(L, buf, level - 1, numformat, -2, visited); appendw(buf, ']' | '=' << 010); - PopVisit(visited); - } else { - // TODO: Strange Lua data structure, do nothing. - lua_pop(L, 1); - continue; } - } - if (PushVisit(visited, lua_touserdata(L, -1))) { - if (!didcomma && i > 1) appendw(buf, ',' | ' ' << 8); LuaEncodeLuaDataImpl(L, buf, level - 1, numformat, -1, visited); - PopVisit(visited); - } else { - // TODO: Strange Lua data structure, do nothing. - lua_pop(L, 1); - continue; + lua_pop(L, 1); // table/-2, key/-1 } - lua_pop(L, 1); // table/-2, key/-1 + lua_pop(L, 1); // table ref + LuaPopVisit(visited); + appendw(buf, '}'); + } else { + appendf(buf, "\"cyclic@%p\"", lua_topointer(L, idx)); } - lua_pop(L, 1); // table - // stack: table/-1, as the key was popped by lua_next - appendw(buf, '}'); return 0; default: + // TODO(jart): don't leak memory with longjmp luaL_error(L, "can't serialize value of this type"); unreachable; } } else { + // TODO(jart): don't leak memory with longjmp luaL_error(L, "too many nested tables"); unreachable; } @@ -192,7 +191,7 @@ static int LuaEncodeLuaDataImpl(lua_State *L, char **buf, int level, int LuaEncodeLuaData(lua_State *L, char **buf, char *numformat, int idx) { int rc; - struct Visited visited = {0}; + struct LuaVisited visited = {0}; rc = LuaEncodeLuaDataImpl(L, buf, 64, numformat, idx, &visited); assert(!visited.n); free(visited.p); diff --git a/third_party/lua/visitor.c b/third_party/lua/visitor.c new file mode 100644 index 000000000..8d0b28386 --- /dev/null +++ b/third_party/lua/visitor.c @@ -0,0 +1,38 @@ +/*-*- 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 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" +#include "libc/x/x.h" +#include "third_party/lua/visitor.h" + +bool LuaPushVisit(struct LuaVisited *visited, const void *p) { + int i; + for (i = 0; i < visited->n; ++i) { + if (visited->p[i] == p) { + return false; + } + } + visited->p = xrealloc(visited->p, ++visited->n * sizeof(*visited->p)); + visited->p[visited->n - 1] = p; + return true; +} + +void LuaPopVisit(struct LuaVisited *visited) { + assert(visited->n > 0); + --visited->n; +} diff --git a/third_party/lua/visitor.h b/third_party/lua/visitor.h new file mode 100644 index 000000000..dcf150e22 --- /dev/null +++ b/third_party/lua/visitor.h @@ -0,0 +1,16 @@ +#ifndef COSMOPOLITAN_THIRD_PARTY_LUA_VISITOR_H_ +#define COSMOPOLITAN_THIRD_PARTY_LUA_VISITOR_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +struct LuaVisited { + int n; + const void **p; +}; + +bool LuaPushVisit(struct LuaVisited *, const void *); +void LuaPopVisit(struct LuaVisited *); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_THIRD_PARTY_LUA_VISITOR_H_ */ diff --git a/tool/build/runitd.c b/tool/build/runitd.c index ae7d93228..71d4bb3c6 100644 --- a/tool/build/runitd.c +++ b/tool/build/runitd.c @@ -464,6 +464,7 @@ int main(int argc, char *argv[]) { /* __log_level = kLogDebug; */ GetOpts(argc, argv); for (i = 3; i < 16; ++i) close(i); + errno = 0; // poll()'ing /dev/null stdin file descriptor on xnu returns POLLNVAL?! if (IsWindows()) { CHECK_EQ(3, (g_bogusfd = open("/dev/null", O_RDONLY | O_CLOEXEC))); diff --git a/tool/net/help.txt b/tool/net/help.txt index e516a0e48..7596107c0 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -357,6 +357,10 @@ LUA ENHANCEMENTS `0644 == 420` is the case in redbean, whereas in upstream Lua `0644 == 644` would be the case. + - redbean supports binary (base 2) integer literals. For example + `0b1010 == 10` is the case in redbean, whereas in upstream Lua + `0b1010` would result in an error. + - redbean supports the GNU syntax for the ASCII ESC character in string literals. For example, `"\e"` is the same as `"\x1b"`. @@ -1284,6 +1288,31 @@ FUNCTIONS possibly because your system is under load and the benchmark was preempted by the operating system, or moved to a different core. + oct(int) + └─→ str + + Formats string as octal integer literal string. If the provided + value is zero, the result will be `"0"`. Otherwise the resulting + value will be the zero-prefixed octal string. The result is + currently modulo 2^64. Negative numbers are converted to unsigned. + + hex(int) + └─→ str + + Formats string as hexadecimal integer literal string. If the + provided value is zero, the result will be `"0"`. Otherwise the + resulting value will be the `"0x"`-prefixed hex string. The result + is currently modulo 2^64. Negative numbers are converted to + unsigned. + + bin(int) + └─→ str + + Formats string as binary integer literal string. If the provided + value is zero, the result will be `"0"`. Otherwise the resulting + value will be the `"0b"`-prefixed binary str. The result is + currently modulo 2^64. Negative numbers are converted to unsigned. + ──────────────────────────────────────────────────────────────────────────────── @@ -1745,6 +1774,27 @@ UNIX MODULE end end + unix.WIFEXITED(wstatus:int) + └─→ bool + + Returns true if process exited cleanly. + + unix.WEXITSTATUS(wstatus:int) + └─→ exitcode:uint8 + + Returns code passed to exit() assuming `WIFEXITED(wstatus)` is true. + + unix.WIFSIGNALED(wstatus:int) + └─→ bool + + Returns true if process terminated due to a signal. + + unix.WTERMSIG(wstatus:int) + └─→ sig:uint8 + + Returns signal that caused process to terminate assuming + `WIFSIGNALED(wstatus)` is true. + unix.getpid() └─→ pid:int diff --git a/tool/net/lfuncs.c b/tool/net/lfuncs.c index e318d4903..503fb899f 100644 --- a/tool/net/lfuncs.c +++ b/tool/net/lfuncs.c @@ -20,6 +20,7 @@ #include "libc/bits/popcnt.h" #include "libc/calls/calls.h" #include "libc/calls/struct/rusage.h" +#include "libc/fmt/itoa.h" #include "libc/intrin/kprintf.h" #include "libc/log/check.h" #include "libc/log/log.h" @@ -60,6 +61,33 @@ static int Rdpid(void) { return rdpid(); } +int LuaHex(lua_State *L) { + char b[19]; + uint64_t x; + x = luaL_checkinteger(L, 1); + FormatHex64(b, x, true); + lua_pushstring(L, b); + return 1; +} + +int LuaOct(lua_State *L) { + char b[24]; + uint64_t x; + x = luaL_checkinteger(L, 1); + FormatOctal64(b, x, true); + lua_pushstring(L, b); + return 1; +} + +int LuaBin(lua_State *L) { + char b[67]; + uint64_t x; + x = luaL_checkinteger(L, 1); + FormatBinary64(b, x, 2); + lua_pushstring(L, b); + return 1; +} + int LuaGetTime(lua_State *L) { lua_pushnumber(L, nowl()); return 1; @@ -258,7 +286,7 @@ int LuaPopcnt(lua_State *L) { int LuaBsr(lua_State *L) { long x; if ((x = luaL_checkinteger(L, 1))) { - lua_pushinteger(L, bsr(x)); + lua_pushinteger(L, bsrl(x)); return 1; } else { luaL_argerror(L, 1, "zero"); @@ -269,7 +297,7 @@ int LuaBsr(lua_State *L) { int LuaBsf(lua_State *L) { long x; if ((x = luaL_checkinteger(L, 1))) { - lua_pushinteger(L, bsf(x)); + lua_pushinteger(L, bsfl(x)); return 1; } else { luaL_argerror(L, 1, "zero"); diff --git a/tool/net/lfuncs.h b/tool/net/lfuncs.h index 991ec9eea..301c8a49e 100644 --- a/tool/net/lfuncs.h +++ b/tool/net/lfuncs.h @@ -12,6 +12,7 @@ int luaopen_argon2(lua_State *); int luaopen_lsqlite3(lua_State *); int LuaBenchmark(lua_State *); +int LuaBin(lua_State *); int LuaBsf(lua_State *); int LuaBsr(lua_State *); int LuaCategorizeIp(lua_State *); @@ -45,6 +46,7 @@ int LuaGetMonospaceWidth(lua_State *); int LuaGetRandomBytes(lua_State *); int LuaGetTime(lua_State *); int LuaHasControlCodes(lua_State *); +int LuaHex(lua_State *); int LuaIndentLines(lua_State *); int LuaIsAcceptableHost(lua_State *); int LuaIsAcceptablePath(lua_State *); @@ -58,6 +60,7 @@ int LuaIsValidHttpToken(lua_State *); int LuaLemur64(lua_State *); int LuaMd5(lua_State *); int LuaMeasureEntropy(lua_State *); +int LuaOct(lua_State *); int LuaParseHost(lua_State *); int LuaParseHttpDateTime(lua_State *); int LuaParseIp(lua_State *); diff --git a/tool/net/redbean.c b/tool/net/redbean.c index c5a3b2444..777b36463 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -192,6 +192,22 @@ STATIC_YOINK("zip_uri_support"); #define HeaderEqualCase(H, S) \ SlicesEqualCase(S, strlen(S), HeaderData(H), HeaderLength(H)) +#define TRACE_BEGIN \ + do { \ + if (!IsTiny()) { \ + if (funtrace) ++g_ftrace; \ + if (systrace) ++__strace; \ + } \ + } while (0) + +#define TRACE_END \ + do { \ + if (!IsTiny()) { \ + if (funtrace) --g_ftrace; \ + if (systrace) --__strace; \ + } \ + } while (0) + // letters not used: EIJNOQWXYnoqwxy // digits not used: 0123456789 // puncts not used: !"#$%&'()*+,-./;<=>@[\]^_`{|}~ @@ -5066,6 +5082,9 @@ static const luaL_Reg kLuaFuncs[] = { {"Underlong", LuaUnderlong}, // {"VisualizeControlCodes", LuaVisualizeControlCodes}, // {"Write", LuaWrite}, // + {"bin", LuaBin}, // + {"hex", LuaHex}, // + {"oct", LuaOct}, // #ifndef UNSECURE {"Fetch", LuaFetch}, // {"EvadeDragnetSurveillance", LuaEvadeDragnetSurveillance}, // @@ -5196,7 +5215,6 @@ static void LuaPrint(lua_State *L) { static void LuaInterpreter(lua_State *L) { int i, n, sig, status; const char *script; - if (funtrace) ftrace_install(); if (optind < __argc) { script = __argv[optind]; if (!strcmp(script, "-")) script = 0; @@ -5206,11 +5224,9 @@ static void LuaInterpreter(lua_State *L) { luaL_checkstack(L, n + 3, "too many script args"); for (i = 1; i <= n; i++) lua_rawgeti(L, -i, i); lua_remove(L, -i); // remove arg table from stack - if (funtrace) ++g_ftrace; - if (systrace) ++__strace; + TRACE_BEGIN; status = lua_runchunk(L, n, LUA_MULTRET); - if (systrace) --__strace; - if (funtrace) --g_ftrace; + TRACE_END; } lua_report(L, status); } else { @@ -5233,9 +5249,9 @@ static void LuaInterpreter(lua_State *L) { exit(1); } if (status == LUA_OK) { - if (funtrace) ++g_ftrace; + TRACE_BEGIN; status = lua_runchunk(GL, 0, LUA_MULTRET); - if (funtrace) --g_ftrace; + TRACE_END; } if (status == LUA_OK) { LuaPrint(GL); @@ -6365,19 +6381,10 @@ static int HandleConnection(size_t i) { meltdown = false; __isworker = true; connectionclose = false; - if (!IsTiny()) { - if (systrace) { - __strace = 1; - __kbirth = rdtsc(); - } - if (funtrace) { - if (ftrace_install() != -1) { - g_ftrace = 1; - } else { - WARNF("ftrace failed to install %m"); - } - } + if (!IsTiny() && systrace) { + __kbirth = rdtsc(); } + TRACE_BEGIN; if (sandboxed) { CHECK_NE(-1, EnableSandbox()); } @@ -6850,7 +6857,6 @@ static void GetOpts(int argc, char *argv[]) { CASE('S', ++sandboxed); CASE('v', ++__log_level); CASE('s', --__log_level); - CASE('f', funtrace = true); CASE('Z', systrace = true); CASE('b', logbodies = true); CASE('z', printport = true); @@ -6889,6 +6895,12 @@ static void GetOpts(int argc, char *argv[]) { CASE('C', ProgramFile(optarg, ProgramCertificate)); CASE('K', ProgramFile(optarg, ProgramPrivateKey)); #endif + case 'f': + funtrace = true; + if (ftrace_install() == -1) { + WARNF("ftrace failed to install %m"); + } + break; default: PrintUsage(2, EX_USAGE); } From e4b559c76ad431895115eff3cf24f491c306cb78 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Fri, 29 Apr 2022 06:35:27 -0700 Subject: [PATCH 17/18] Simplify the redbean JSON encoder --- third_party/lua/luaencodejsondata.c | 35 ++++++++++++----------------- third_party/lua/luaencodeluadata.c | 5 +++-- 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/third_party/lua/luaencodejsondata.c b/third_party/lua/luaencodejsondata.c index 706688ca0..3b18f0f7d 100644 --- a/third_party/lua/luaencodejsondata.c +++ b/third_party/lua/luaencodejsondata.c @@ -73,7 +73,9 @@ static int LuaEncodeJsonDataImpl(lua_State *L, char **buf, int level, fmt[4] = *numformat; break; default: - return luaL_error(L, "numformat string not allowed"); + free(visited->p); + luaL_error(L, "numformat string not allowed"); + unreachable; } appendf(buf, fmt, lua_tonumber(L, idx)); } @@ -81,7 +83,8 @@ static int LuaEncodeJsonDataImpl(lua_State *L, char **buf, int level, case LUA_TTABLE: if (LuaPushVisit(visited, lua_topointer(L, idx))) { - tbllen = lua_rawlen(L, idx); + lua_pushvalue(L, idx); // table ref + tbllen = lua_rawlen(L, -1); // encode tables with numeric indices and empty tables as arrays isarray = tbllen > 0 || // integer keys present @@ -99,27 +102,14 @@ static int LuaEncodeJsonDataImpl(lua_State *L, char **buf, int level, i = 1; lua_pushnil(L); // push the first key while (lua_next(L, -2)) { - if (!lua_isstring(L, -2)) { - luaL_error(L, "expected number or string as key value"); + if (lua_type(L, -2) != LUA_TSTRING) { + free(visited->p); + luaL_error(L, "json tables must be arrays or use string keys"); unreachable; } if (i++ > 1) appendw(buf, ','); - appendw(buf, '"'); - if (lua_type(L, -2) == LUA_TSTRING) { - s = lua_tolstring(L, -2, &z); - s = EscapeJsStringLiteral(s, z, &z); - appendd(buf, s, z); - free(s); - } else { - // we'd still prefer to use lua_tostring on a numeric index, but - // can't use it in-place, as it breaks lua_next (changes numeric - // key to a string) - lua_pushvalue(L, -2); // table/-4, key/-3, value/-2, key/-1 - s = lua_tolstring(L, idx, &z); - appendd(buf, s, z); // use the copy - lua_remove(L, -1); // remove copied key: tab/-3, key/-2, val/-1 - } - appendw(buf, '"' | ':' << 010); + LuaEncodeJsonDataImpl(L, buf, level - 1, numformat, -2, visited); + appendw(buf, ':'); LuaEncodeJsonDataImpl(L, buf, level - 1, numformat, -1, visited); lua_pop(L, 1); // table/-2, key/-1 } @@ -127,18 +117,21 @@ static int LuaEncodeJsonDataImpl(lua_State *L, char **buf, int level, } appendw(buf, isarray ? ']' : '}'); LuaPopVisit(visited); + lua_pop(L, 1); // table ref return 0; } else { - // TODO(jart): don't leak memory with longjmp + free(visited->p); luaL_error(L, "can't serialize cyclic data structure to json"); unreachable; } default: + free(visited->p); luaL_error(L, "won't serialize %s to json", luaL_typename(L, idx)); unreachable; } } else { + free(visited->p); luaL_error(L, "too many nested tables"); unreachable; } diff --git a/third_party/lua/luaencodeluadata.c b/third_party/lua/luaencodeluadata.c index cc78f5550..7e855286b 100644 --- a/third_party/lua/luaencodeluadata.c +++ b/third_party/lua/luaencodeluadata.c @@ -130,6 +130,7 @@ static int LuaEncodeLuaDataImpl(lua_State *L, char **buf, int level, fmt[4] = *numformat; break; default: + free(visited->p); luaL_error(L, "numformat string not allowed"); unreachable; } @@ -178,12 +179,12 @@ static int LuaEncodeLuaDataImpl(lua_State *L, char **buf, int level, return 0; default: - // TODO(jart): don't leak memory with longjmp + free(visited->p); luaL_error(L, "can't serialize value of this type"); unreachable; } } else { - // TODO(jart): don't leak memory with longjmp + free(visited->p); luaL_error(L, "too many nested tables"); unreachable; } From a85406da4c8a25151fdd7aa988f88394f4d9d004 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Fri, 29 Apr 2022 08:19:09 -0700 Subject: [PATCH 18/18] Fix some example code --- tool/net/demo/unix-info.lua | 224 ++++++++++++++++++------------------ tool/net/help.txt | 27 ++--- 2 files changed, 127 insertions(+), 124 deletions(-) diff --git a/tool/net/demo/unix-info.lua b/tool/net/demo/unix-info.lua index 42cc5d26c..0be90483d 100644 --- a/tool/net/demo/unix-info.lua +++ b/tool/net/demo/unix-info.lua @@ -22,20 +22,20 @@ unix.umask(mask) Write('
%.4o\r\n' % {mask}) Write('
unix.getsid(0)\r\n') -sid, errno = unix.getsid(0) +sid, err = unix.getsid(0) if sid then Write('
%d\r\n' % {sid}) else - Write('
%s\r\n' % {tostring(errno)}) + Write('
%s\r\n' % {err}) end Write('
unix.gethostname()\r\n') -Write('
%s\r\n' % {EscapeHtml(unix.gethostname())}) +Write('
%s\r\n' % {EscapeHtml(assert(unix.gethostname()))}) Write('
unix.getcwd()\r\n') -Write('
%s\r\n' % {EscapeHtml(unix.getcwd())}) +Write('
%s\r\n' % {EscapeHtml(assert(unix.getcwd()))}) function PrintResourceLimit(name, id) - soft, errno, hard = unix.getrlimit(id) + soft, hard = unix.getrlimit(id) Write('
getrlimit(%s)\r\n' % {name}) if soft then Write('
') @@ -54,7 +54,7 @@ function PrintResourceLimit(name, id) end Write('\r\n') else - Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) + Write('
%s\r\n' % {EscapeHtml(tostring(hard))}) end end PrintResourceLimit('RLIMIT_AS', unix.RLIMIT_AS) @@ -66,7 +66,7 @@ PrintResourceLimit('RLIMIT_NOFILE', unix.RLIMIT_NOFILE) Write('
unix.siocgifconf()\r\n') Write('
\r\n') -ifs, errno = unix.siocgifconf() +ifs, err = unix.siocgifconf() if ifs then for i = 1,#ifs do if ifs[i].netmask ~= 0 then @@ -77,215 +77,217 @@ if ifs then Write('%s %s/%d
\r\n' % {EscapeHtml(ifs[i].name), FormatIp(ifs[i].ip), cidr}) end else - Write('%s\r\n' % {EscapeHtml(tostring(errno))}) + Write('%s\r\n' % {err}) end -enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_DEBUG) +enabled, err = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_DEBUG) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_DEBUG)\r\n') -if errno then - Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) +if enabled then -- is nil on error + Write('
%d\r\n' % {enabled}) -- should be 0 or 1 else - Write('
%s\r\n' % {enabled}) + Write('
%s\r\n' % {err}) end -enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_ACCEPTCONN) +enabled, err = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_ACCEPTCONN) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_ACCEPTCONN)\r\n') -if errno then - Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) +if enabled then -- is nil on error + Write('
%d\r\n' % {enabled}) -- should be 0 or 1 else - Write('
%s\r\n' % {enabled}) + Write('
%s\r\n' % {err}) end -enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEADDR) +enabled, err = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEADDR) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEADDR)\r\n') -if errno then - Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) +if enabled then -- is nil on error + Write('
%d\r\n' % {enabled}) -- should be 0 or 1 else - Write('
%s\r\n' % {enabled}) + Write('
%s\r\n' % {err}) end -enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEPORT) +enabled, err = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEPORT) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEPORT)\r\n') -if errno then - Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) +if enabled then -- is nil on error + Write('
%d\r\n' % {enabled}) -- should be 0 or 1 else - Write('
%s\r\n' % {enabled}) + Write('
%s\r\n' % {err}) end -enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_KEEPALIVE) +enabled, err = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_KEEPALIVE) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_KEEPALIVE)\r\n') -if errno then - Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) +if enabled then -- is nil on error + Write('
%s\r\n' % {enabled}) -- should be 0 or 1 else - Write('
%s\r\n' % {enabled}) + Write('
%s\r\n' % {err}) end -enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_NODELAY) +enabled, err = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_NODELAY) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_NODELAY)\r\n') -if errno then - Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) +if enabled then -- is nil on error + Write('
%s\r\n' % {enabled}) -- should be 0 or 1 else - Write('
%s\r\n' % {enabled}) + Write('
%s\r\n' % {err}) end -secs, errno, micros = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVTIMEO) +secs, nanos = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVTIMEO) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVTIMEO)\r\n') -if errno then - Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) +if secs then -- is nil on error + Write('
%d seconds + %d nanoseconds\r\n' % {secs, nanos}) else - Write('
%d sec %d µs\r\n' % {secs, micros}) + err = nanos + Write('
%s\r\n' % {err}) end -secs, errno, micros = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDTIMEO) +secs, nanos = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDTIMEO) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDTIMEO)\r\n') -if errno then - Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) +if secs then -- is nil on error + Write('
%d seconds + %d nanoseconds\r\n' % {secs, nanos}) else - Write('
%d sec %d µs\r\n' % {secs, micros}) + err = nanos -- unix.Errno is always second result + Write('
%s\r\n' % {err}) end -enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_DONTROUTE) +enabled, err = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_DONTROUTE) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_DONTROUTE)\r\n') -if errno then - Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) +if enabled then -- is nil if error + Write('
%d\r\n' % {enabled}) -- should be 0 or 1 else - Write('
%s\r\n' % {enabled}) + Write('
%s\r\n' % {err}) end -bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDBUF) +bytes, err = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDBUF) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDBUF)\r\n') -if errno then - Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) -else +if bytes then -- is nil if error Write('
%d\r\n' % {bytes}) +else + Write('
%s\r\n' % {err}) end -bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVBUF) +bytes, err = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVBUF) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVBUF)\r\n') -if errno then - Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) -else +if bytes then -- is nil if error Write('
%d\r\n' % {bytes}) +else + Write('
%s\r\n' % {err}) end -bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_FASTOPEN) +bytes, err = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_FASTOPEN) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_FASTOPEN)\r\n') -if errno then - Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) -else +if bytes then -- is nil if error Write('
%d\r\n' % {bytes}) +else + Write('
%s\r\n' % {err}) end -enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_BROADCAST) +enabled, err = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_BROADCAST) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_BROADCAST)\r\n') -if errno then - Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) +if enabled then -- is nil if error + Write('
%d\r\n' % {enabled}) -- should be 1 or 0 else - Write('
%s\r\n' % {enabled}) + Write('
%s\r\n' % {err}) end -enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_CORK) +enabled, err = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_CORK) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_CORK)\r\n') -if errno then - Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) +if enabled then -- is nil if error + Write('
%d\r\n' % {enabled}) -- should be 1 or 0 else - Write('
%s\r\n' % {enabled}) + Write('
%s\r\n' % {err}) end -enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_QUICKACK) +enabled, err = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_QUICKACK) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_QUICKACK)\r\n') -if errno then - Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) +if enabled then + Write('
%d\r\n' % {enabled}) else - Write('
%s\r\n' % {enabled}) + Write('
%s\r\n' % {err}) end -enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_DEFER_ACCEPT) +enabled, err = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_DEFER_ACCEPT) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_DEFER_ACCEPT)\r\n') -if errno then - Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) -else +if enabled then Write('
%s\r\n' % {enabled}) +else + Write('
%s\r\n' % {err}) end -enabled, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_FASTOPEN_CONNECT) +enabled, err = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_FASTOPEN_CONNECT) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_FASTOPEN_CONNECT)\r\n') -if errno then - Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) +if err then + Write('
%s\r\n' % {err}) else Write('
%s\r\n' % {enabled}) end -bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDLOWAT) +bytes, err = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDLOWAT) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDLOWAT)\r\n') -if errno then - Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) -else +if bytes then Write('
%d\r\n' % {bytes}) +else + Write('
%s\r\n' % {err}) end -bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVLOWAT) +bytes, err = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVLOWAT) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVLOWAT)\r\n') -if errno then - Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) -else +if bytes then Write('
%d\r\n' % {bytes}) +else + Write('
%s\r\n' % {err}) end -bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPCNT) +bytes, err = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPCNT) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPCNT)\r\n') -if errno then - Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) -else +if bytes then Write('
%d\r\n' % {bytes}) +else + Write('
%s\r\n' % {err}) end -bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_MAXSEG) +bytes, err = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_MAXSEG) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_MAXSEG)\r\n') -if errno then - Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) -else +if bytes then Write('
%d\r\n' % {bytes}) +else + Write('
%s\r\n' % {err}) end -bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_SYNCNT) +bytes, err = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_SYNCNT) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_SYNCNT)\r\n') -if errno then - Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) -else +if bytes then Write('
%d\r\n' % {bytes}) +else + Write('
%s\r\n' % {err}) end -bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_NOTSENT_LOWAT) +bytes, err = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_NOTSENT_LOWAT) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_NOTSENT_LOWAT)\r\n') -if errno then - Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) -else +if bytes then Write('
%d\r\n' % {bytes}) +else + Write('
%s\r\n' % {err}) end -bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_WINDOW_CLAMP) +bytes, err = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_WINDOW_CLAMP) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_WINDOW_CLAMP)\r\n') -if errno then - Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) -else +if bytes then Write('
%d\r\n' % {bytes}) +else + Write('
%s\r\n' % {err}) end -bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPIDLE) +bytes, err = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPIDLE) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPIDLE)\r\n') -if errno then - Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) -else +if bytes then Write('
%d\r\n' % {bytes}) +else + Write('
%s\r\n' % {err}) end -bytes, errno = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPINTVL) +bytes, err = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPINTVL) Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPINTVL)\r\n') -if errno then - Write('
%s\r\n' % {EscapeHtml(tostring(errno))}) -else +if bytes then Write('
%d\r\n' % {bytes}) +else + Write('
%s\r\n' % {err}) end Write('
unix.environ()\r\n') diff --git a/tool/net/help.txt b/tool/net/help.txt index 7596107c0..45bc29b64 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -318,12 +318,13 @@ REPL redbean.com -i binarytrees.lua 15 - In this mode redbean won't start a web server and instead functions - like the `lua` command. The first command line argument becomes the - script you want to run. If you don't supply a script, then the repl - without a web server is displayed. This is useful for testing since - redbean extensions and modules for the Lua language, are still made - available. You can also write redbean scripts with shebang lines: + When the `-i` flag is passed (for interpreter mode), redbean won't + start a web server and instead functions like the `lua` command. The + first command line argument becomes the script you want to run. If you + don't supply a script, then the repl without a web server is + displayed. This is useful for testing since redbean extensions and + modules for the Lua language, are still made available. You can also + write redbean scripts with shebang lines: #!/usr/bin/redbean -i print('hello world') @@ -1452,14 +1453,14 @@ MAXMIND MODULE This module may be used to get city/country/asn/etc from IPs, e.g. -- .init.lua - maxmind = require "maxmind" + maxmind = require 'maxmind' asndb = maxmind.open('/usr/local/share/maxmind/GeoLite2-ASN.mmdb') -- request handler as = asndb:lookup(GetRemoteAddr()) if as then - asnum = as:get("autonomous_system_number") - asorg = as:get("autonomous_system_organization") + asnum = as:get('autonomous_system_number') + asorg = as:get('autonomous_system_organization') Write(EscapeHtml(asnum)) Write(' ') Write(EscapeHtml(asorg)) @@ -1483,7 +1484,7 @@ UNIX MODULE Returns a file descriptor integer that needs to be closed, e.g. - fd = assert(open("/etc/passwd", unix.O_RDONLY)) + fd = assert(unix.open("/etc/passwd", unix.O_RDONLY)) print(unix.read(fd)) unix.close(fd) @@ -1622,9 +1623,9 @@ UNIX MODULE Performs `$PATH` lookup of executable. - unix = require "unix" - prog = assert(unix.commandv("ls")) - unix.execve(prog, {prog, "-hal", "."}, {PATH="/bin"}) + unix = require 'unix' + prog = assert(unix.commandv('ls')) + unix.execve(prog, {prog, '-hal', '.'}, {'PATH=/bin'}) unix.exit(127) We automatically suffix `.com` and `.exe` for all platforms when