mirror of
https://github.com/jart/cosmopolitan.git
synced 2026-11-07 17:22:16 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aefad27c09 | ||
|
|
b0cebefd8e | ||
|
|
34fa4edae8 | ||
|
|
c7c9916eb8 | ||
|
|
88848afdff | ||
|
|
e0c68ba56b | ||
|
|
bc6f307aa5 | ||
|
|
7bc687dbc4 | ||
|
|
1bc2a45f4a | ||
|
|
6ad44a10c2 | ||
|
|
9607bee4b2 | ||
|
|
2143d61dfa | ||
|
|
fdea2482dd | ||
|
|
c7d64e1b8b | ||
|
|
977d10172c | ||
|
|
3293fad0a9 | ||
|
|
036845632f | ||
|
|
4a47191f51 | ||
|
|
a831042eca | ||
|
|
b4b5af498f | ||
|
|
18e3a2e7ce | ||
|
|
bc37215177 | ||
|
|
7e35bc854f | ||
|
|
6eb8da23d4 | ||
|
|
0b80275299 | ||
|
|
ee883eabb3 | ||
|
|
7666e63ef9 | ||
|
|
60734c784a | ||
|
|
deb866efd5 | ||
|
|
ad16ba5460 | ||
|
|
db041429ed | ||
|
|
eedf7d2db6 | ||
|
|
9ba2b50805 | ||
|
|
7ecf16e306 | ||
|
|
db617bfd58 | ||
|
|
9b1285b915 | ||
|
|
b444b3a6e0 | ||
|
|
b0fdf97054 | ||
|
|
2f786453ee | ||
|
|
694ef1aa8b | ||
|
|
bc0fc702fe | ||
|
|
bea62f3631 | ||
|
|
dee25272f1 | ||
|
|
5ddb5c2ada | ||
|
|
f1dc5cff44 | ||
|
|
f1e83d5240 | ||
|
|
2fe8338f92 | ||
|
|
4ca513cba2 | ||
|
|
455910e8f2 | ||
|
|
9c68bc19b5 | ||
|
|
66d1050af6 | ||
|
|
fbc4fcbb71 | ||
|
|
afc986f741 | ||
|
|
5eb7cd6643 | ||
|
|
a8ed4fdd09 | ||
|
|
7b69652854 | ||
|
|
b235492e71 | ||
|
|
fc81fd8d16 | ||
|
|
38930de8e0 | ||
|
|
0e557d041d | ||
|
|
1d676b36e6 | ||
|
|
10a92cee94 | ||
|
|
42a9ed0131 | ||
|
|
12cb0669fb | ||
|
|
7f6a7d6fff | ||
|
|
9f6bf6ea71 | ||
|
|
102edf4ea2 | ||
|
|
21968acf99 | ||
|
|
98861b23fc | ||
|
|
dab6d7a345 | ||
|
|
90119c422c | ||
|
|
5907304049 | ||
|
|
035b0e2a62 | ||
|
|
7b67b20dae | ||
|
|
f0b0f926bf | ||
|
|
29eb7e67bb | ||
|
|
f71f61cd40 | ||
|
|
53c6edfd18 | ||
|
|
42a3bb729a | ||
|
|
c97a858470 | ||
|
|
4acd12a514 | ||
|
|
b734eec836 | ||
|
|
fe01642a20 | ||
|
|
e939659b70 | ||
|
|
ed6d133a27 | ||
|
|
97fc2aab41 | ||
|
|
662e7b217f | ||
|
|
27f2777cc6 | ||
|
|
538ce338f4 | ||
|
|
a15958edc6 | ||
|
|
8db646f6b2 | ||
|
|
fde03f8487 | ||
|
|
f24c854b28 | ||
|
|
0b3c81dd4e | ||
|
|
98c5847727 | ||
|
|
fd7da586b5 | ||
|
|
a51ccc8fb1 | ||
|
|
c7e3d9f7ff | ||
|
|
9ba5b227d9 | ||
|
|
aca4214ff6 | ||
|
|
379cd77078 | ||
|
|
36e5861b0c | ||
|
|
cc8a9eb93c | ||
|
|
0158579493 | ||
|
|
2de3845b25 | ||
|
|
93e22c581f | ||
|
|
ec2db4e40e | ||
|
|
55b7aa1632 | ||
|
|
4705705548 | ||
|
|
c8e10eef30 | ||
|
|
624573207e | ||
|
|
906bd06a5a | ||
|
|
c8c81af0c7 | ||
|
|
af7bd80430 | ||
|
|
26c051c297 | ||
|
|
9cc1bd04b2 | ||
|
|
69402f4d78 | ||
|
|
838b54f906 | ||
|
|
2d43d400c6 | ||
|
|
c22b413ac4 | ||
|
|
22094ae9ca | ||
|
|
bda2a4d55e | ||
|
|
b490e23d63 | ||
|
|
b40140e6c5 | ||
|
|
3142758675 | ||
|
|
cf9252f429 | ||
|
|
5fae582e82 | ||
|
|
ef00a7d0c2 | ||
|
|
746660066f | ||
|
|
fd15b2d7a3 | ||
|
|
e228aa3e14 | ||
|
|
9ddbfd921e | ||
|
|
729f7045e3 | ||
|
|
e47d67ba9b | ||
|
|
2477677c85 | ||
|
|
abdf6c9c26 | ||
|
|
5c3f854acb | ||
|
|
ad0a7c67c4 | ||
|
|
1312f60245 | ||
|
|
cafdb456ed | ||
|
|
4e9566cd33 | ||
|
|
5ce5fb6f2a | ||
|
|
d3279d3c0d | ||
|
|
e62ff3e19c | ||
|
|
913b573661 | ||
|
|
9add248c9b | ||
|
|
beb090b83f | ||
|
|
107d335c0d | ||
|
|
bd6630d62d | ||
|
|
a120bc7149 | ||
|
|
baad1df71d | ||
|
|
4e44517c9c | ||
|
|
26663dea9c | ||
|
|
23da0d75a5 | ||
|
|
4b2a00fd4a | ||
|
|
2f4e6e8d77 | ||
|
|
dd249ff5d4 | ||
|
|
4abcba8d8f | ||
|
|
dc1afc968b | ||
|
|
5edc0819c0 | ||
|
|
706cb66310 | ||
|
|
a8bc7ac119 | ||
|
|
d8fac40f55 | ||
|
|
000d6dbb0f | ||
|
|
17a85e4790 | ||
|
|
ad11fc32ad | ||
|
|
dcf9596620 | ||
|
|
85c58be942 | ||
|
|
e4d6eb382a | ||
|
|
fef24d622a | ||
|
|
12cc2de22e | ||
|
|
70603fa6ea | ||
|
|
9255113011 | ||
|
|
333c3d1f0a | ||
|
|
518eabadf5 | ||
|
|
556a294363 | ||
|
|
80804ccfff | ||
|
|
4a7dd31567 | ||
|
|
d730fc668c | ||
|
|
e975245102 | ||
|
|
126a44dc49 | ||
|
|
476926790a | ||
|
|
dd8c4dbd7d | ||
|
|
0e59afb403 | ||
|
|
f68fc1f815 | ||
|
|
6107eb38f9 | ||
|
|
d50f4c02f6 | ||
|
|
0d74673213 | ||
|
|
8527462b95 | ||
|
|
8313dca982 | ||
|
|
87a6669900 | ||
|
|
ce2fbf9325 | ||
|
|
1bfb348403 | ||
|
|
aaed879ec7 | ||
|
|
8201ef2b3d | ||
|
|
b1c9801897 | ||
|
|
f7754ab608 | ||
|
|
96abe91c29 | ||
|
|
bb7942e557 | ||
|
|
b14dddcc18 | ||
|
|
65e425fbca | ||
|
|
774c67fcd3 | ||
|
|
3c58ecd00c | ||
|
|
5aa970bc4e |
No files matched your search
@@ -1,5 +1,8 @@
|
||||
name: build
|
||||
|
||||
env:
|
||||
COSMOCC_VERSION: 3.9.2
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
@@ -17,15 +20,50 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
mode: ["", tiny, rel, tinylinux, optlinux]
|
||||
mode: ["", tiny, rel, tinylinux]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
# Full checkout needed for git-restore-mtime-bare.
|
||||
fetch-depth: 0
|
||||
|
||||
# TODO(jart): fork this action.
|
||||
- uses: chetan/git-restore-mtime-action@v2
|
||||
|
||||
- uses: actions/cache/restore@v4
|
||||
id: cache
|
||||
with:
|
||||
path: |
|
||||
.cosmocc
|
||||
o
|
||||
key: ${{ env.COSMOCC_VERSION }}-${{ matrix.mode }}-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ env.COSMOCC_VERSION }}-${{ matrix.mode }}-
|
||||
${{ env.COSMOCC_VERSION }}-
|
||||
|
||||
- name: Restore mtimes
|
||||
if: steps.cache.outputs.cache-hit == 'true'
|
||||
run: |
|
||||
while read mtime file; do
|
||||
[ -f "$file" ] && touch -d "@$mtime" "$file"
|
||||
done < o/.mtimes
|
||||
|
||||
- name: support ape bins 1
|
||||
run: sudo cp build/bootstrap/ape.elf /usr/bin/ape
|
||||
run: sudo cp -a build/bootstrap/ape.elf /usr/bin/ape
|
||||
|
||||
- name: support ape bins 2
|
||||
run: sudo sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' >/proc/sys/fs/binfmt_misc/register"
|
||||
|
||||
- name: make matrix
|
||||
run: V=0 make -j2 MODE=${{ matrix.mode }}
|
||||
|
||||
- name: Save mtimes
|
||||
run: |
|
||||
find o -type f -exec stat -c "%Y %n" {} \; > o/.mtimes
|
||||
|
||||
- uses: actions/cache/save@v4
|
||||
with:
|
||||
path: |
|
||||
.cosmocc
|
||||
o
|
||||
key: ${{ env.COSMOCC_VERSION }}-${{ matrix.mode }}-${{ github.sha }}
|
||||
@@ -1,6 +1,7 @@
|
||||
# -*- conf -*-
|
||||
|
||||
/o
|
||||
/core
|
||||
/cosmocc
|
||||
/.cosmocc
|
||||
|
||||
@@ -15,3 +16,4 @@ __pycache__
|
||||
/tool/emacs/*.elc
|
||||
/perf.data
|
||||
/perf.data.old
|
||||
/qemu*core
|
||||
Vendored
+29
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Cosmopolitan",
|
||||
"includePath": [
|
||||
"${workspaceFolder}",
|
||||
"${workspaceFolder}/libc/isystem"
|
||||
],
|
||||
"defines": [
|
||||
"_COSMO_SOURCE",
|
||||
"MODE=\"aarch64\""
|
||||
],
|
||||
"compilerPath": "${workspaceFolder}/.cosmocc/3.9.2/bin/aarch64-linux-cosmo-gcc",
|
||||
"cStandard": "c17",
|
||||
"cppStandard": "c++17",
|
||||
"intelliSenseMode": "gcc-arm64",
|
||||
"compilerArgs": [
|
||||
"-nostdinc",
|
||||
"-iquote",
|
||||
"${workspaceFolder}",
|
||||
"-isystem",
|
||||
"${workspaceFolder}/libc/isystem",
|
||||
"-include",
|
||||
"libc/integral/normalize.inc"
|
||||
]
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
Vendored
+2
-12
@@ -1,15 +1,4 @@
|
||||
{
|
||||
"C_Cpp.default.compilerPath": ".cosmocc/3.8.0/bin/aarch64-linux-cosmo-c++",
|
||||
"C_Cpp.default.compilerArgs": [
|
||||
"-nostdinc",
|
||||
"-nostdlib",
|
||||
"-iquote.",
|
||||
"-isystemlibc/isystem",
|
||||
"-isystemthird_party/libcxx",
|
||||
"-includelibc/integral/normalize.inc",
|
||||
"-D_COSMO_SOURCE",
|
||||
"-D__aarch64__"
|
||||
],
|
||||
"[c]": {
|
||||
"editor.tabSize": 2,
|
||||
"editor.insertSpaces": true
|
||||
@@ -31,6 +20,7 @@
|
||||
"editor.insertSpaces": true
|
||||
},
|
||||
"files.associations": {
|
||||
"log.h": "c"
|
||||
"log.h": "c",
|
||||
"*.inc": "c"
|
||||
}
|
||||
}
|
||||
@@ -77,7 +77,8 @@ COMMA := ,
|
||||
PWD := $(shell pwd)
|
||||
|
||||
# detect wsl2 running cosmopolitan binaries on the host by checking whether:
|
||||
# - user ran build/bootstrap/make, in which case make's working directory is in wsl
|
||||
# - user ran .cosmocc/current/bin/make, in which case make's working directory
|
||||
# is in wsl
|
||||
# - user ran make, in which case cocmd's working directory is in wsl
|
||||
ifneq ($(findstring //wsl.localhost/,$(CURDIR) $(PWD)),)
|
||||
$(warning wsl2 interop is enabled)
|
||||
@@ -89,7 +90,7 @@ UNAME_S := $(shell uname -s)
|
||||
|
||||
# apple still distributes a 17 year old version of gnu make
|
||||
ifeq ($(MAKE_VERSION), 3.81)
|
||||
$(error please use build/bootstrap/make)
|
||||
$(error please use https://cosmo.zip/pub/cosmos/bin/make)
|
||||
endif
|
||||
|
||||
LC_ALL = C
|
||||
@@ -109,11 +110,12 @@ ECHO = $(BOOTSTRAP)/echo.ape
|
||||
CHMOD = $(BOOTSTRAP)/chmod.ape
|
||||
TOUCH = $(BOOTSTRAP)/touch.ape
|
||||
PKG = $(BOOTSTRAP)/package.ape
|
||||
MKDEPS = $(BOOTSTRAP)/mkdeps
|
||||
MKDEPS = build/bootstrap/mkdeps
|
||||
ZIPOBJ = $(BOOTSTRAP)/zipobj
|
||||
ZIPCOPY = $(BOOTSTRAP)/zipcopy
|
||||
ELF2PE = build/bootstrap/elf2pe
|
||||
PECHECK = $(BOOTSTRAP)/pecheck
|
||||
FIXUPOBJ = $(BOOTSTRAP)/fixupobj
|
||||
FIXUPOBJ = build/bootstrap/fixupobj
|
||||
OBJBINCOPY = $(BOOTSTRAP)/objbincopy
|
||||
MKDIR = $(BOOTSTRAP)/mkdir.ape -p
|
||||
COMPILE = $(BOOTSTRAP)/compile.ape -V9 -M2048m -P8192 $(QUOTA)
|
||||
@@ -135,7 +137,7 @@ ARCH = aarch64
|
||||
HOSTS ?= pi pi5 studio freebsdarm
|
||||
else
|
||||
ARCH = x86_64
|
||||
HOSTS ?= freebsd rhel7 xnu openbsd netbsd win10
|
||||
HOSTS ?= freebsd xnu win10 luna netbsd
|
||||
endif
|
||||
|
||||
ZIPOBJ_FLAGS += -a$(ARCH)
|
||||
@@ -147,16 +149,20 @@ export MODE
|
||||
export SOURCE_DATE_EPOCH
|
||||
export TMPDIR
|
||||
|
||||
COSMOCC = .cosmocc/3.8.0
|
||||
COSMOCC = .cosmocc/3.9.2
|
||||
BOOTSTRAP = $(COSMOCC)/bin
|
||||
TOOLCHAIN = $(COSMOCC)/bin/$(ARCH)-linux-cosmo-
|
||||
DOWNLOAD := $(shell build/download-cosmocc.sh $(COSMOCC) 3.8.0 813c6b2f95062d2e0a845307a79505424cb98cb038e8013334f8a22e3b92a474)
|
||||
DOWNLOAD := $(shell build/download-cosmocc.sh $(COSMOCC) 3.9.2 f4ff13af65fcd309f3f1cfd04275996fb7f72a4897726628a8c9cf732e850193)
|
||||
|
||||
IGNORE := $(shell $(MKDIR) $(TMPDIR))
|
||||
|
||||
ifeq ($(ARCH), x86_64)
|
||||
TLSCC = build/bootstrap/tlscc
|
||||
endif
|
||||
|
||||
AS = $(TOOLCHAIN)as
|
||||
CC = $(TOOLCHAIN)gcc
|
||||
CXX = $(TOOLCHAIN)g++
|
||||
CC = $(TLSCC) $(TOOLCHAIN)gcc
|
||||
CXX = $(TLSCC) $(TOOLCHAIN)g++
|
||||
CXXFILT = $(TOOLCHAIN)c++filt
|
||||
LD = $(TOOLCHAIN)ld.bfd
|
||||
NM = $(TOOLCHAIN)nm
|
||||
@@ -189,39 +195,42 @@ o/$(MODE): \
|
||||
o/$(MODE)/: o/$(MODE)
|
||||
o/$(MODE)/.: o/$(MODE)
|
||||
|
||||
# check if we're using o//third_party/make/make
|
||||
# we added sandboxing to guarantee cosmo's makefile is hermetic
|
||||
# it also shaves away 200ms of startup latency with native $(uniq)
|
||||
ifneq ($(LANDLOCKMAKE_VERSION),)
|
||||
ifeq ($(UNAME_S),Linux)
|
||||
ifeq ($(wildcard /usr/bin/ape),)
|
||||
$(warning please run ape/apeinstall.sh if you intend to use landlock make)
|
||||
$(shell sleep .5)
|
||||
endif
|
||||
endif
|
||||
ifneq ($(TOOLCHAIN),)
|
||||
.STRICT = 1
|
||||
endif
|
||||
endif
|
||||
|
||||
.PLEDGE += stdio rpath wpath cpath fattr proc
|
||||
.UNVEIL += \
|
||||
libc/integral \
|
||||
libc/stdbool.h \
|
||||
rwc:/dev/shm \
|
||||
# If you're using Cosmopolitan Make as your `make` command (you can
|
||||
# build it by saying `make o//third_party/make/make`) then these
|
||||
# variables will activate its pledge() and unveil() sandboxing
|
||||
# functionality when running on Linux. When cross-compiling, it's
|
||||
# important that your qemu binaries be statically compiled. See
|
||||
# third_party/qemu/ for our prebuilt qemu-aarch64 binary. Yes
|
||||
# binfmt_misc has to be configured to use it on foreign bins.
|
||||
.SANDBOXED = 1
|
||||
.PLEDGE := stdio rpath wpath cpath fattr proc
|
||||
.UNVEIL := \
|
||||
rwcx:o \
|
||||
rwcx:/tmp \
|
||||
rwcx:$(TMPDIR) \
|
||||
rx:/usr/bin/ape \
|
||||
rx:.cosmocc \
|
||||
rx:build/bootstrap \
|
||||
r:build/portcosmo.h \
|
||||
libc/integral \
|
||||
rwc:/dev/shm \
|
||||
/proc/stat \
|
||||
rw:/dev/null \
|
||||
rw:/dev/full \
|
||||
w:o/stack.log \
|
||||
/etc/hosts \
|
||||
/etc/ape.trust \
|
||||
/etc/ape.revoke \
|
||||
/etc/ssh/ssh_host_ed25519_key.pub \
|
||||
~/.runit.psk \
|
||||
~/.ssh/authorized_keys \
|
||||
~/.ssh/id_ed25519.pub \
|
||||
~/.ape.trust \
|
||||
~/.ape.revoke \
|
||||
/proc/self/status \
|
||||
rx:/usr/bin/qemu-aarch64 \
|
||||
rx:o/third_party/qemu/qemu-aarch64 \
|
||||
/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
|
||||
rx:/usr/bin/qemu-x86_64 \
|
||||
rx:/usr/libexec/qemu-binfmt/aarch64-binfmt-P
|
||||
endif
|
||||
|
||||
PKGS =
|
||||
|
||||
@@ -256,8 +265,10 @@ include third_party/gdtoa/BUILD.mk # ├──DYNAMIC RUNTIME
|
||||
include third_party/nsync/mem/BUILD.mk # │ You can now use stdio
|
||||
include libc/proc/BUILD.mk # │ You can now use threads
|
||||
include libc/dlopen/BUILD.mk # │ You can now use processes
|
||||
include libc/thread/BUILD.mk # │ You can finally call malloc()
|
||||
include third_party/libunwind/BUILD.mk # | You can finally call malloc()
|
||||
include libc/thread/BUILD.mk # │
|
||||
include third_party/zlib/BUILD.mk # │
|
||||
include third_party/haclstar/BUILD.mk # │
|
||||
include libc/stdio/BUILD.mk # │
|
||||
include tool/hello/BUILD.mk # │
|
||||
include third_party/tz/BUILD.mk # │
|
||||
@@ -274,6 +285,10 @@ include libc/BUILD.mk #─┘
|
||||
include libc/sock/BUILD.mk #─┐
|
||||
include net/http/BUILD.mk # ├──ONLINE RUNTIME
|
||||
include third_party/musl/BUILD.mk # │ You can communicate with the network
|
||||
include third_party/regex/BUILD.mk # │
|
||||
include third_party/tr/BUILD.mk # │
|
||||
include third_party/sed/BUILD.mk # │
|
||||
include libc/system/BUILD.mk # │
|
||||
include libc/x/BUILD.mk # │
|
||||
include dsp/scale/BUILD.mk # │
|
||||
include dsp/mpeg/BUILD.mk # │
|
||||
@@ -284,23 +299,25 @@ include third_party/stb/BUILD.mk # │
|
||||
include third_party/mbedtls/BUILD.mk # │
|
||||
include third_party/ncurses/BUILD.mk # │
|
||||
include third_party/readline/BUILD.mk # │
|
||||
include third_party/libunwind/BUILD.mk # |
|
||||
include third_party/libcxxabi/BUILD.mk # |
|
||||
include third_party/miniaudio/BUILD.mk # |
|
||||
include third_party/double-conversion/BUILD.mk # │
|
||||
include dsp/prog/BUILD.mk # │
|
||||
include ctl/BUILD.mk # │
|
||||
include third_party/libcxx/BUILD.mk # │
|
||||
include third_party/openmp/BUILD.mk # │
|
||||
include third_party/pcre/BUILD.mk # │
|
||||
include third_party/less/BUILD.mk # │
|
||||
include net/https/BUILD.mk # │
|
||||
include third_party/regex/BUILD.mk #─┘
|
||||
include net/https/BUILD.mk #─┘
|
||||
include third_party/tidy/BUILD.mk
|
||||
include third_party/BUILD.mk
|
||||
include third_party/nsync/testing/BUILD.mk
|
||||
include libc/testlib/BUILD.mk
|
||||
include tool/viz/lib/BUILD.mk
|
||||
include tool/args/BUILD.mk
|
||||
include tool/chat/BUILD.mk
|
||||
include test/math/BUILD.mk
|
||||
include test/c11/BUILD.mk
|
||||
include test/posix/BUILD.mk
|
||||
include test/ctl/BUILD.mk
|
||||
include test/libcxx/BUILD.mk
|
||||
@@ -309,11 +326,11 @@ include third_party/linenoise/BUILD.mk
|
||||
include third_party/maxmind/BUILD.mk
|
||||
include net/finger/BUILD.mk
|
||||
include third_party/double-conversion/test/BUILD.mk
|
||||
include third_party/dash/BUILD.mk
|
||||
include third_party/netcat/BUILD.mk
|
||||
include third_party/lua/BUILD.mk
|
||||
include third_party/tree/BUILD.mk
|
||||
include third_party/zstd/BUILD.mk
|
||||
include third_party/tr/BUILD.mk
|
||||
include third_party/sed/BUILD.mk
|
||||
include third_party/awk/BUILD.mk
|
||||
include third_party/hiredis/BUILD.mk
|
||||
include third_party/make/BUILD.mk
|
||||
@@ -342,7 +359,6 @@ include tool/decode/lib/BUILD.mk
|
||||
include tool/decode/BUILD.mk
|
||||
include tool/lambda/lib/BUILD.mk
|
||||
include tool/lambda/BUILD.mk
|
||||
include tool/plinko/lib/BUILD.mk
|
||||
include tool/plinko/BUILD.mk
|
||||
include test/tool/plinko/BUILD.mk
|
||||
include tool/net/BUILD.mk
|
||||
@@ -366,6 +382,7 @@ include test/libc/fmt/BUILD.mk
|
||||
include test/libc/time/BUILD.mk
|
||||
include test/libc/proc/BUILD.mk
|
||||
include test/libc/stdio/BUILD.mk
|
||||
include test/libc/system/BUILD.mk
|
||||
include test/libc/BUILD.mk
|
||||
include test/net/http/BUILD.mk
|
||||
include test/net/https/BUILD.mk
|
||||
@@ -410,86 +427,87 @@ o/$(MODE)/hdrs.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(HDRS) $(
|
||||
o/$(MODE)/incs.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(INCS) $(INCS),$(dir $(x)))) $(INCS) $(INCS)
|
||||
$(file >$@,$(INCS))
|
||||
o/$(MODE)/depend: o/$(MODE)/.x o/$(MODE)/srcs.txt o/$(MODE)/hdrs.txt o/$(MODE)/incs.txt $(SRCS) $(HDRS) $(INCS)
|
||||
$(COMPILE) -AMKDEPS -L320 $(MKDEPS) -o $@ -s -r o/$(MODE)/ @o/$(MODE)/srcs.txt @o/$(MODE)/hdrs.txt @o/$(MODE)/incs.txt
|
||||
$(COMPILE) -AMKDEPS -L320 $(MKDEPS) -o $@ -s -r o/$(MODE)/ -S c++:third_party/libcxx/ -S libc/isystem/ @o/$(MODE)/srcs.txt @o/$(MODE)/hdrs.txt @o/$(MODE)/incs.txt
|
||||
|
||||
o/$(MODE)/srcs-old.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(SRCS),$(dir $(x))))
|
||||
$(file >$@) $(foreach x,$(SRCS),$(file >>$@,$(x)))
|
||||
o/$(MODE)/hdrs-old.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(HDRS) $(INCS),$(dir $(x))))
|
||||
$(file >$@) $(foreach x,$(HDRS) $(INCS),$(file >>$@,$(x)))
|
||||
|
||||
TAGS: private .UNSANDBOXED = 1
|
||||
TAGS: private .SANDBOXED = 0
|
||||
TAGS: o/$(MODE)/srcs-old.txt $(SRCS) #o/$(MODE)/third_party/ctags/ctags
|
||||
@$(RM) $@
|
||||
@o/$(MODE)/third_party/ctags/ctags $(TAGSFLAGS) -L $< -o $@
|
||||
|
||||
HTAGS: private .UNSANDBOXED = 1
|
||||
HTAGS: private .SANDBOXED = 0
|
||||
HTAGS: o/$(MODE)/hdrs-old.txt $(filter-out third_party/libcxx/%,$(HDRS)) #o/$(MODE)/third_party/ctags/ctags
|
||||
@$(RM) $@
|
||||
@build/htags o/$(MODE)/third_party/ctags/ctags -L $< -o $@
|
||||
|
||||
loc: private .UNSANDBOXED = 1
|
||||
loc: private .SANDBOXED = 0
|
||||
loc: o/$(MODE)/tool/build/summy
|
||||
find -name \*.h -or -name \*.hpp -or -name \*.c -or -name \*.cc -or -name \*.cpp -or -name \*.S -or -name \*.mk | \
|
||||
$(XARGS) wc -l | grep total | awk '{print $$1}' | $<
|
||||
|
||||
# PLEASE: MAINTAIN TOPOLOGICAL ORDER
|
||||
# FROM HIGHEST LEVEL TO LOWEST LEVEL
|
||||
COSMOPOLITAN_OBJECTS = \
|
||||
COSMOPOLITAN = \
|
||||
CTL \
|
||||
THIRD_PARTY_DOUBLECONVERSION \
|
||||
THIRD_PARTY_OPENMP \
|
||||
TOOL_ARGS \
|
||||
NET_HTTP \
|
||||
DSP_AUDIO \
|
||||
LIBC_SOCK \
|
||||
LIBC_NT_WS2_32 \
|
||||
LIBC_NT_IPHLPAPI \
|
||||
LIBC_X \
|
||||
THIRD_PARTY_GETOPT \
|
||||
LIBC_CALLS \
|
||||
LIBC_DLOPEN \
|
||||
LIBC_ELF \
|
||||
LIBC_FMT \
|
||||
LIBC_INTRIN \
|
||||
LIBC_IRQ \
|
||||
LIBC_LOG \
|
||||
THIRD_PARTY_TZ \
|
||||
THIRD_PARTY_MUSL \
|
||||
THIRD_PARTY_ZLIB_GZ \
|
||||
LIBC_MEM \
|
||||
LIBC_NEXGEN32E \
|
||||
LIBC_NT_ADVAPI32 \
|
||||
LIBC_NT_BCRYPTPRIMITIVES \
|
||||
LIBC_NT_COMDLG32 \
|
||||
LIBC_NT_GDI32 \
|
||||
LIBC_NT_IPHLPAPI \
|
||||
LIBC_NT_KERNEL32 \
|
||||
LIBC_NT_NTDLL \
|
||||
LIBC_NT_PDH \
|
||||
LIBC_NT_POWRPROF \
|
||||
LIBC_NT_PSAPI \
|
||||
LIBC_NT_REALTIME \
|
||||
LIBC_NT_SHELL32 \
|
||||
LIBC_NT_SYNCHRONIZATION \
|
||||
LIBC_NT_USER32 \
|
||||
LIBC_NT_WINMM \
|
||||
LIBC_NT_WS2_32 \
|
||||
LIBC_PROC \
|
||||
LIBC_RUNTIME \
|
||||
LIBC_SOCK \
|
||||
LIBC_STDIO \
|
||||
LIBC_STR \
|
||||
LIBC_SYSTEM \
|
||||
LIBC_SYSV \
|
||||
LIBC_SYSV_CALLS \
|
||||
LIBC_THREAD \
|
||||
LIBC_TINYMATH \
|
||||
LIBC_VGA \
|
||||
LIBC_X \
|
||||
NET_HTTP \
|
||||
THIRD_PARTY_COMPILER_RT \
|
||||
THIRD_PARTY_DLMALLOC \
|
||||
THIRD_PARTY_DOUBLECONVERSION \
|
||||
THIRD_PARTY_GDTOA \
|
||||
THIRD_PARTY_GETOPT \
|
||||
THIRD_PARTY_LIBCXXABI \
|
||||
THIRD_PARTY_LIBUNWIND \
|
||||
LIBC_STDIO \
|
||||
THIRD_PARTY_GDTOA \
|
||||
THIRD_PARTY_REGEX \
|
||||
LIBC_THREAD \
|
||||
LIBC_PROC \
|
||||
THIRD_PARTY_NSYNC_MEM \
|
||||
LIBC_MEM \
|
||||
THIRD_PARTY_DLMALLOC \
|
||||
LIBC_DLOPEN \
|
||||
LIBC_RUNTIME \
|
||||
THIRD_PARTY_MUSL \
|
||||
THIRD_PARTY_NSYNC \
|
||||
LIBC_ELF \
|
||||
LIBC_IRQ \
|
||||
LIBC_CALLS \
|
||||
LIBC_SYSV_CALLS \
|
||||
LIBC_VGA \
|
||||
LIBC_NT_REALTIME \
|
||||
LIBC_NT_PSAPI \
|
||||
LIBC_NT_POWRPROF \
|
||||
LIBC_NT_PDH \
|
||||
LIBC_NT_GDI32 \
|
||||
LIBC_NT_COMDLG32 \
|
||||
LIBC_NT_USER32 \
|
||||
LIBC_NT_NTDLL \
|
||||
LIBC_NT_ADVAPI32 \
|
||||
LIBC_NT_SYNCHRONIZATION \
|
||||
LIBC_FMT \
|
||||
THIRD_PARTY_ZLIB \
|
||||
THIRD_PARTY_NSYNC_MEM \
|
||||
THIRD_PARTY_OPENMP \
|
||||
THIRD_PARTY_PUFF \
|
||||
THIRD_PARTY_COMPILER_RT \
|
||||
LIBC_TINYMATH \
|
||||
THIRD_PARTY_REGEX \
|
||||
THIRD_PARTY_TZ \
|
||||
THIRD_PARTY_XED \
|
||||
LIBC_STR \
|
||||
LIBC_SYSV \
|
||||
LIBC_INTRIN \
|
||||
LIBC_NT_BCRYPTPRIMITIVES \
|
||||
LIBC_NT_KERNEL32 \
|
||||
LIBC_NEXGEN32E
|
||||
THIRD_PARTY_ZLIB \
|
||||
THIRD_PARTY_ZLIB_GZ \
|
||||
TOOL_ARGS \
|
||||
|
||||
COSMOPOLITAN_H_PKGS = \
|
||||
APE \
|
||||
@@ -537,7 +555,7 @@ COSMOCC_PKGS = \
|
||||
THIRD_PARTY_INTEL
|
||||
|
||||
o/$(MODE)/cosmopolitan.a: \
|
||||
$(foreach x,$(COSMOPOLITAN_OBJECTS),$($(x)_A_OBJS))
|
||||
$(call reverse,$(call uniq,$(foreach x,$(COSMOPOLITAN),$($(x)))))
|
||||
|
||||
COSMOCC_HDRS = \
|
||||
$(wildcard libc/integral/*) \
|
||||
@@ -551,7 +569,7 @@ COSMOPOLITAN_H_ROOT_HDRS = \
|
||||
libc/integral/normalize.inc \
|
||||
$(foreach x,$(COSMOPOLITAN_H_PKGS),$($(x)_HDRS))
|
||||
|
||||
o/cosmopolitan.html: private .UNSANDBOXED = 1
|
||||
o/cosmopolitan.html: private .SANDBOXED = 0
|
||||
o/cosmopolitan.html: \
|
||||
o/$(MODE)/third_party/chibicc/chibicc.dbg \
|
||||
$(filter-out %.s,$(foreach x,$(COSMOPOLITAN_OBJECTS),$($(x)_SRCS))) \
|
||||
@@ -574,6 +592,7 @@ ifeq ($(ARCH), x86_64)
|
||||
TOOLCHAIN_ARTIFACTS = \
|
||||
o/$(MODE)/ape/ape.lds \
|
||||
o/$(MODE)/libc/crt/crt.o \
|
||||
o/$(MODE)/libc/crt/crtfastmath.o \
|
||||
o/$(MODE)/ape/ape.elf \
|
||||
o/$(MODE)/ape/ape.o \
|
||||
o/$(MODE)/ape/ape-copy-self.o \
|
||||
@@ -592,6 +611,7 @@ TOOLCHAIN_ARTIFACTS = \
|
||||
o/$(MODE)/ape/ape.elf \
|
||||
o/$(MODE)/ape/aarch64.lds \
|
||||
o/$(MODE)/libc/crt/crt.o \
|
||||
o/$(MODE)/libc/crt/crtfastmath.o \
|
||||
o/$(MODE)/cosmopolitan.a \
|
||||
o/$(MODE)/third_party/libcxx/libcxx.a \
|
||||
o/$(MODE)/tool/build/march-native \
|
||||
@@ -607,7 +627,7 @@ clean_toolchain:
|
||||
$(RM) $(TOOLCHAIN_ARTIFACTS)
|
||||
|
||||
aarch64: private .INTERNET = true
|
||||
aarch64: private .UNSANDBOXED = true
|
||||
aarch64: private .SANDBOXED = 0
|
||||
aarch64:
|
||||
$(MAKE) m=aarch64
|
||||
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
[](https://github.com/jart/cosmopolitan/actions/workflows/build.yml)
|
||||
# Cosmopolitan
|
||||
|
||||
[Cosmopolitan Libc](https://justine.lol/cosmopolitan/index.html) makes C
|
||||
[Cosmopolitan Libc](https://justine.lol/cosmopolitan/index.html) makes C/C++
|
||||
a build-once run-anywhere language, like Java, except it doesn't need an
|
||||
interpreter or virtual machine. Instead, it reconfigures stock GCC and
|
||||
Clang to output a POSIX-approved polyglot format that runs natively on
|
||||
Linux + Mac + Windows + FreeBSD + OpenBSD + NetBSD + BIOS with the best
|
||||
possible performance and the tiniest footprint imaginable.
|
||||
Linux + Mac + Windows + FreeBSD + OpenBSD 7.3 + NetBSD + BIOS with the
|
||||
best possible performance and the tiniest footprint imaginable.
|
||||
|
||||
## Background
|
||||
|
||||
@@ -87,15 +87,22 @@ ape/apeinstall.sh
|
||||
```
|
||||
|
||||
You can now build the mono repo with any modern version of GNU Make. To
|
||||
make life easier, we've included one in the cosmocc toolchain, which is
|
||||
guaranteed to be compatible and furthermore includes our extensions for
|
||||
doing build system sandboxing.
|
||||
bootstrap your build, you can install Cosmopolitan Make from this site:
|
||||
|
||||
https://cosmo.zip/pub/cosmos/bin/make
|
||||
|
||||
E.g.:
|
||||
|
||||
```sh
|
||||
build/bootstrap/make -j8
|
||||
curl -LO https://cosmo.zip/pub/cosmos/bin/make
|
||||
./make -j8
|
||||
o//examples/hello
|
||||
```
|
||||
|
||||
After you've built the repo once, you can also use the make from your
|
||||
cosmocc at `.cosmocc/current/bin/make`. You might even prefer to alias
|
||||
make to `$COSMO/.cosmocc/current/bin/make`.
|
||||
|
||||
Since the Cosmopolitan repository is very large, you might only want to
|
||||
build one particular thing. Here's an example of a target that can be
|
||||
compiled relatively quickly, which is a simple POSIX test that only
|
||||
@@ -103,7 +110,7 @@ depends on core LIBC packages.
|
||||
|
||||
```sh
|
||||
rm -rf o//libc o//test
|
||||
build/bootstrap/make o//test/posix/signal_test
|
||||
.cosmocc/current/bin/make o//test/posix/signal_test
|
||||
o//test/posix/signal_test
|
||||
```
|
||||
|
||||
@@ -112,21 +119,21 @@ list out each individual one. For example if you wanted to build and run
|
||||
all the unit tests in the `TEST_POSIX` package, you could say:
|
||||
|
||||
```sh
|
||||
build/bootstrap/make o//test/posix
|
||||
.cosmocc/current/bin/make o//test/posix
|
||||
```
|
||||
|
||||
Cosmopolitan provides a variety of build modes. For example, if you want
|
||||
really tiny binaries (as small as 12kb in size) then you'd say:
|
||||
|
||||
```sh
|
||||
build/bootstrap/make m=tiny
|
||||
.cosmocc/current/bin/make m=tiny
|
||||
```
|
||||
|
||||
You can furthermore cut out the bloat of other operating systems, and
|
||||
have Cosmopolitan become much more similar to Musl Libc.
|
||||
|
||||
```sh
|
||||
build/bootstrap/make m=tinylinux
|
||||
.cosmocc/current/bin/make m=tinylinux
|
||||
```
|
||||
|
||||
For further details, see [//build/config.mk](build/config.mk).
|
||||
@@ -242,16 +249,16 @@ server. You're welcome to join us! <https://discord.gg/FwAVVu7eJ4>
|
||||
|
||||
## Support Vector
|
||||
|
||||
| Platform | Min Version | Circa |
|
||||
| :--- | ---: | ---: |
|
||||
| AMD | K8 | 2003 |
|
||||
| Intel | Core | 2006 |
|
||||
| Linux | 2.6.18 | 2007 |
|
||||
| Windows | 8 [1] | 2012 |
|
||||
| Darwin (macOS) | 23.1.0+ | 2023 |
|
||||
| OpenBSD | 7 | 2021 |
|
||||
| FreeBSD | 13 | 2020 |
|
||||
| NetBSD | 9.2 | 2021 |
|
||||
| Platform | Min Version | Circa |
|
||||
| :--- | ---: | ---: |
|
||||
| AMD | K8 | 2003 |
|
||||
| Intel | Core | 2006 |
|
||||
| Linux | 2.6.18 | 2007 |
|
||||
| Windows | 8 [1] | 2012 |
|
||||
| Darwin (macOS) | 23.1.0+ | 2023 |
|
||||
| OpenBSD | 7.3 or earlier | 2023 |
|
||||
| FreeBSD | 13 | 2020 |
|
||||
| NetBSD | 9.2 | 2021 |
|
||||
|
||||
[1] See our [vista branch](https://github.com/jart/cosmopolitan/tree/vista)
|
||||
for a community supported version of Cosmopolitan that works on Windows
|
||||
|
||||
@@ -49,6 +49,8 @@ o/$(MODE)/ape/aarch64.lds: \
|
||||
libc/thread/tls.h \
|
||||
libc/calls/struct/timespec.h \
|
||||
libc/macros.h \
|
||||
libc/sysv/consts/prot.h \
|
||||
libc/dce.h \
|
||||
libc/str/str.h
|
||||
|
||||
APE_LOADER_LDFLAGS = \
|
||||
|
||||
+16
-2
@@ -4,6 +4,8 @@
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
#define CACHELINE 64
|
||||
|
||||
ENTRY(_start)
|
||||
OUTPUT_ARCH(aarch64)
|
||||
OUTPUT_FORMAT("elf64-littleaarch64",
|
||||
@@ -73,13 +75,16 @@ SECTIONS {
|
||||
KEEP(*(SORT_NONE(.fini)))
|
||||
} =0x1f2003d5
|
||||
|
||||
. += CONSTANT(MAXPAGESIZE);
|
||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
__privileged_start = .;
|
||||
|
||||
.privileged : {
|
||||
__privileged_start = ABSOLUTE(.) & -CONSTANT(MAXPAGESIZE);
|
||||
*(.privileged*)
|
||||
} =0x1f2003d6
|
||||
|
||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
_rodata = .;
|
||||
|
||||
.rodata : {
|
||||
KEEP(*(.rodata.pytab.0));
|
||||
KEEP(*(.rodata.pytab.1));
|
||||
@@ -200,6 +205,9 @@ SECTIONS {
|
||||
.data : {
|
||||
__data_start = .;
|
||||
KEEP(*(SORT_BY_NAME(.piro.data.sort.*)))
|
||||
. = ALIGN(CACHELINE);
|
||||
*(.data.rarechange)
|
||||
. = ALIGN(CACHELINE);
|
||||
*(.data .data.* .gnu.linkonce.d.*)
|
||||
KEEP(*(SORT_BY_NAME(.sort.data.*)))
|
||||
SORT(CONSTRUCTORS)
|
||||
@@ -214,6 +222,9 @@ SECTIONS {
|
||||
|
||||
.bss : {
|
||||
*(.dynbss)
|
||||
. = ALIGN(CACHELINE);
|
||||
*(.bss.rarechange)
|
||||
. = ALIGN(CACHELINE);
|
||||
*(.bss .bss.* .gnu.linkonce.b.*)
|
||||
KEEP(*(SORT_BY_NAME(.piro.bss.sort.*)))
|
||||
*(COMMON)
|
||||
@@ -259,6 +270,9 @@ SECTIONS {
|
||||
.debug_ranges 0 : { *(.debug_ranges) }
|
||||
.debug_macro 0 : { *(.debug_macro) }
|
||||
.debug_addr 0 : { *(.debug_addr) }
|
||||
.debug_names 0 : { *(.debug_names) }
|
||||
.debug_loclists 0 : { *(.debug_loclists) }
|
||||
.debug_str_offsets 0 : { *(.debug_str_offsets) }
|
||||
.ARM.attributes 0 : { KEEP(*(.ARM.attributes)) KEEP(*(.gnu.attributes)) }
|
||||
.note.gnu.arm.ident 0 : { KEEP(*(.note.gnu.arm.ident)) }
|
||||
|
||||
|
||||
@@ -789,6 +789,33 @@ ape.ident:
|
||||
.type ape.ident,@object
|
||||
.previous
|
||||
|
||||
#if SupportsFreebsd()
|
||||
//
|
||||
// This PT_NOTE enables the following kernel behaviors:
|
||||
//
|
||||
// - P_OSREL_SIGSEGV ensures SIGEGV signals aren't misdelivered as
|
||||
// as SIGBUS with invalid siginfo fields.
|
||||
//
|
||||
// - P_OSREL_SHUTDOWN_ENOTCONN is probably a good thing
|
||||
//
|
||||
// - P_OSREL_SIGWAIT prevents sigwait() from raising EINTR
|
||||
//
|
||||
// - P_OSREL_MAP_FSTRICT (basically MAP_SHARED_VALIDATE?)
|
||||
//
|
||||
.section .note.freebsd.ident,"a",@progbits
|
||||
.balign 4
|
||||
freebsd.ident:
|
||||
.long 2f-1f
|
||||
.long 4f-3f
|
||||
.long 1 // NT_FREEBSD_ABI_TAG
|
||||
1: .asciz "FreeBSD"
|
||||
2: .balign 4
|
||||
3: .long 1300000 // v13.0+ c. 2020
|
||||
4: .size freebsd.ident,.-freebsd.ident
|
||||
.type freebsd.ident,@object
|
||||
.previous
|
||||
#endif /* SupportsFreebsd() */
|
||||
|
||||
#if SupportsOpenbsd()
|
||||
.section .note.openbsd.ident,"a",@progbits
|
||||
.balign 4
|
||||
@@ -1086,6 +1113,19 @@ ape_pe: .ascin "PE",4
|
||||
.long PERDAT // Flags
|
||||
.previous
|
||||
|
||||
.section .pe.sections,"a",@progbits
|
||||
.ascin ".idata",8 // Section Name
|
||||
.long ape_idt_memsz // Virtual Size or Physical Address
|
||||
.long ape_idt_rva // Relative Virtual Address
|
||||
.long ape_idt_filesz // Physical Size
|
||||
.long ape_idt_offset // Physical Offset
|
||||
.long 0 // Relocation Table Offset
|
||||
.long 0 // Line Number Table Offset
|
||||
.short 0 // Relocation Count
|
||||
.short 0 // Line Number Count
|
||||
.long PEIMPS // Flags
|
||||
.previous
|
||||
|
||||
.section .pe.sections,"a",@progbits
|
||||
.ascin ".data",8 // Section Name
|
||||
.long ape_ram_memsz // Virtual Size or Physical Address
|
||||
@@ -1711,6 +1751,7 @@ kernel: movabs $ape_stack_vaddr,%rsp
|
||||
mov $0x79000,%esi
|
||||
mov $0x7f000,%edx
|
||||
call __reclaim_boot_pages
|
||||
movl $1,__pib(%rip)
|
||||
push $_HOSTMETAL // sets __hostos in crt.S
|
||||
pop %rcx
|
||||
pushq .Lenv0(%rip) // envp[0][0]
|
||||
@@ -1828,7 +1869,7 @@ __bss_end:
|
||||
.section .notice,"aR",@progbits
|
||||
.asciz "\n\n\
|
||||
Cosmopolitan\n\
|
||||
Copyright 2024 Justine Alexandra Roberts Tunney\n\
|
||||
Copyright 2025 Justine Alexandra Roberts Tunney\n\
|
||||
\n\
|
||||
Permission to use, copy, modify, and/or distribute this software for\n\
|
||||
any purpose with or without fee is hereby granted, provided that the\n\
|
||||
@@ -1844,4 +1885,4 @@ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\n\
|
||||
PERFORMANCE OF THIS SOFTWARE."
|
||||
|
||||
.end
|
||||
|
||||
|
||||
+55
-26
@@ -186,6 +186,8 @@
|
||||
/* uncomment if .com.dbg won't execute on your kernel (will break .com file) */
|
||||
/* #define APE_FIX_COM_DBG */
|
||||
|
||||
#define CACHELINE 64
|
||||
|
||||
#ifdef __x86__
|
||||
#define CODE_GRANULE 1
|
||||
#else
|
||||
@@ -199,7 +201,7 @@
|
||||
#endif
|
||||
|
||||
#ifndef IMAGE_BASE_VIRTUAL
|
||||
#define IMAGE_BASE_VIRTUAL 0x400000
|
||||
#define IMAGE_BASE_VIRTUAL 0x4200000
|
||||
#endif
|
||||
|
||||
#if IMAGE_BASE_VIRTUAL > 0xffffffff
|
||||
@@ -237,11 +239,10 @@ SECTIONS {
|
||||
KEEP(*(.elf.phdrs))
|
||||
ape_phdrs_end = .;
|
||||
|
||||
/* OpenBSD */
|
||||
/* ELF PT_NOTE data structures */
|
||||
. = ALIGN(. != 0 ? __SIZEOF_POINTER__ : 0);
|
||||
ape_note = .;
|
||||
KEEP(*(.note.openbsd.ident))
|
||||
KEEP(*(.note.netbsd.ident))
|
||||
KEEP(*(.note.*))
|
||||
ape_note_end = .;
|
||||
|
||||
/* Portable Executable */
|
||||
@@ -321,6 +322,7 @@ SECTIONS {
|
||||
/*BEGIN: Read Only Data */
|
||||
|
||||
.rodata ALIGN(CONSTANT(COMMONPAGESIZE)) : {
|
||||
_rodata = .;
|
||||
KEEP(*(.rodata.pytab.0));
|
||||
KEEP(*(.rodata.pytab.1));
|
||||
KEEP(*(.rodata.pytab.2));
|
||||
@@ -342,12 +344,6 @@ SECTIONS {
|
||||
|
||||
/*BEGIN: read-only data that's only needed for initialization */
|
||||
|
||||
#if SupportsWindows()
|
||||
/* Windows DLL Import Directory */
|
||||
KEEP(*(.idata.ro));
|
||||
KEEP(*(SORT_BY_NAME(.idata.ro.*)))
|
||||
#endif
|
||||
|
||||
/* Encoded Data Structures w/ Linear Initialization Order */
|
||||
KEEP(*(.initroprologue))
|
||||
KEEP(*(SORT_BY_NAME(.initro.*)))
|
||||
@@ -386,13 +382,34 @@ SECTIONS {
|
||||
_tbss_end = .;
|
||||
} :Tls
|
||||
|
||||
.idata : {
|
||||
#if SupportsWindows()
|
||||
KEEP(*(.dllstr));
|
||||
KEEP(*(.idata.ro));
|
||||
KEEP(*(SORT_BY_NAME(.idata.ro.*)));
|
||||
KEEP(*(SORT_BY_NAME(.piro.data.sort.iat.*)));
|
||||
KEEP(*(.data.nt.*));
|
||||
#endif
|
||||
. = ALIGN(. != 0 ? CONSTANT(COMMONPAGESIZE) : 0);
|
||||
} :Ram
|
||||
|
||||
.eh_frame : {
|
||||
__eh_frame_start = .;
|
||||
KEEP(*(.eh_frame))
|
||||
*(.eh_frame.*)
|
||||
__eh_frame_end = .;
|
||||
} :Ram
|
||||
|
||||
.data . : {
|
||||
/*BEGIN: Read/Write Data */
|
||||
#if SupportsWindows()
|
||||
KEEP(*(SORT_BY_NAME(.piro.data.sort.iat.*)))
|
||||
#endif
|
||||
/*BEGIN: NT FORK COPYING */
|
||||
KEEP(*(.dataprologue))
|
||||
|
||||
/* Precious Runtime Constants */
|
||||
. = ALIGN(CACHELINE);
|
||||
*(.data.rarechange)
|
||||
. = ALIGN(CACHELINE);
|
||||
|
||||
*(.data .data.*)
|
||||
*(.gnu_extab)
|
||||
*(.gcc_except_table .gcc_except_table.*)
|
||||
@@ -426,11 +443,6 @@ SECTIONS {
|
||||
KEEP(*(.dtors))
|
||||
__fini_array_end = .;
|
||||
|
||||
__eh_frame_start = .;
|
||||
KEEP(*(.eh_frame))
|
||||
*(.eh_frame.*)
|
||||
__eh_frame_end = .;
|
||||
|
||||
/*BEGIN: Post-Initialization Read-Only */
|
||||
. = ALIGN(. != 0 ? __SIZEOF_POINTER__ : 0);
|
||||
KEEP(*(SORT_BY_NAME(.piro.relo.sort.*)))
|
||||
@@ -439,7 +451,6 @@ SECTIONS {
|
||||
KEEP(*(.piro.pad.data))
|
||||
*(.igot.plt)
|
||||
KEEP(*(.dataepilogue))
|
||||
|
||||
. = ALIGN(. != 0 ? CONSTANT(COMMONPAGESIZE) : 0);
|
||||
/*END: NT FORK COPYING */
|
||||
_edata = .;
|
||||
@@ -463,6 +474,11 @@ SECTIONS {
|
||||
. += . > 0 ? CODE_GRANULE : 0;
|
||||
/*END: Post-Initialization Read-Only */
|
||||
|
||||
/* Precious Runtime Constants */
|
||||
. = ALIGN(CACHELINE);
|
||||
*(.bss.rarechange)
|
||||
. = ALIGN(CACHELINE);
|
||||
|
||||
/* Statically Allocated Empty Space */
|
||||
*(SORT_BY_ALIGNMENT(.bss))
|
||||
*(SORT_BY_ALIGNMENT(.bss.*))
|
||||
@@ -519,6 +535,9 @@ SECTIONS {
|
||||
.debug_rnglists 0 : { *(.debug_rnglists) }
|
||||
.debug_macro 0 : { *(.debug_macro) }
|
||||
.debug_addr 0 : { *(.debug_addr) }
|
||||
.debug_names 0 : { *(.debug_names) }
|
||||
.debug_loclists 0 : { *(.debug_loclists) }
|
||||
.debug_str_offsets 0 : { *(.debug_str_offsets) }
|
||||
.gnu.attributes 0 : { KEEP(*(.gnu.attributes)) }
|
||||
.GCC.command.line 0 : { *(.GCC.command.line) }
|
||||
|
||||
@@ -545,7 +564,7 @@ SECTIONS {
|
||||
}
|
||||
}
|
||||
|
||||
ape_pe_filealignment = 512;
|
||||
ape_pe_filealignment = 4096;
|
||||
ape_pe_sectionalignment = 4096;
|
||||
ape_pe_sizeofheaders = SIZEOF(.head);
|
||||
ape_pe_sizeofimage = ROUNDUP(_end - __executable_start, ape_pe_sectionalignment);
|
||||
@@ -582,11 +601,19 @@ ape_rom_memsz = ape_rom_filesz;
|
||||
ape_rom_align = CONSTANT(COMMONPAGESIZE);
|
||||
ape_rom_rva = RVA(ape_rom_vaddr);
|
||||
|
||||
ape_ram_vaddr = ADDR(.data);
|
||||
ape_idt_vaddr = ADDR(.idata);
|
||||
ape_idt_offset = ape_idt_vaddr - __executable_start;
|
||||
ape_idt_paddr = LOADADDR(.idata);
|
||||
ape_idt_filesz = ADDR(.eh_frame) - ADDR(.idata);
|
||||
ape_idt_memsz = ape_idt_filesz;
|
||||
ape_idt_align = CONSTANT(COMMONPAGESIZE);
|
||||
ape_idt_rva = RVA(ape_idt_vaddr);
|
||||
|
||||
ape_ram_vaddr = ADDR(.eh_frame);
|
||||
ape_ram_offset = ape_ram_vaddr - __executable_start;
|
||||
ape_ram_paddr = LOADADDR(.data);
|
||||
ape_ram_filesz = ADDR(.bss) - ADDR(.data);
|
||||
ape_ram_memsz = _end - ADDR(.data);
|
||||
ape_ram_paddr = LOADADDR(.eh_frame);
|
||||
ape_ram_filesz = ADDR(.bss) - ADDR(.eh_frame);
|
||||
ape_ram_memsz = _end - ADDR(.eh_frame);
|
||||
ape_ram_align = CONSTANT(COMMONPAGESIZE);
|
||||
ape_ram_rva = RVA(ape_ram_vaddr);
|
||||
|
||||
@@ -596,7 +623,7 @@ ape_stack_offset = 0;
|
||||
ape_stack_vaddr = DEFINED(ape_stack_vaddr) ? ape_stack_vaddr : 0x700000000000;
|
||||
ape_stack_paddr = ape_ram_paddr + ape_ram_filesz;
|
||||
ape_stack_filesz = 0;
|
||||
ape_stack_memsz = DEFINED(ape_stack_memsz) ? ape_stack_memsz : 8 * 1024 * 1024;
|
||||
ape_stack_memsz = DEFINED(ape_stack_memsz) ? ape_stack_memsz : 8 * 1024 * 1024; /* for gnu make */
|
||||
|
||||
ape_note_offset = ape_cod_offset + (ape_note - ape_cod_vaddr);
|
||||
ape_note_filesz = ape_note_end - ape_note;
|
||||
@@ -710,8 +737,10 @@ CHURN(ape_pe_sections);
|
||||
CHURN(ape_pe_sections_end);
|
||||
CHURN(ape_pe_shnum);
|
||||
CHURN(ape_phdrs_end);
|
||||
CHURN(WinMain);
|
||||
#endif /* SupportsWindows() */
|
||||
#if SupportsWindows()
|
||||
CHURN(WinMain);
|
||||
#endif
|
||||
#endif /* SupportsXnu() */
|
||||
|
||||
#if SupportsWindows() || SupportsMetal()
|
||||
|
||||
+2
-2
@@ -10,8 +10,8 @@ if [ ! -f ape/loader.c ]; then
|
||||
cd "$COSMO" || exit
|
||||
fi
|
||||
|
||||
if [ -x build/bootstrap/make ]; then
|
||||
MAKE=build/bootstrap/make
|
||||
if [ -x .cosmocc/current/bin/make ]; then
|
||||
MAKE=.cosmocc/current/bin/make
|
||||
else
|
||||
MAKE=make
|
||||
fi
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
*/.section ".piro.data.sort.iat.2.\name\().3","awG",@progbits,"\name",comdat
|
||||
.quad 0
|
||||
.previous
|
||||
.section .rodata.str1.1,"aSM",@progbits,1
|
||||
.section .dllstr,"aSM",@progbits,1
|
||||
".Lidata.str.\name":
|
||||
.asciz "\name\().dll"
|
||||
.previous
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
#define IMAGE_BASE_REAL 0x2000
|
||||
|
||||
#ifndef IMAGE_BASE_VIRTUAL
|
||||
#define IMAGE_BASE_VIRTUAL 0x400000
|
||||
#define IMAGE_BASE_VIRTUAL 0x4200000
|
||||
#endif
|
||||
|
||||
#ifndef IMAGE_BASE_PHYSICAL
|
||||
|
||||
@@ -6,6 +6,7 @@ extern const char __notices[] __attribute__((__weak__));
|
||||
extern unsigned char __executable_start[] __attribute__((__weak__));
|
||||
extern unsigned char __privileged_start[] __attribute__((__weak__));
|
||||
extern unsigned char _ehead[] __attribute__((__weak__));
|
||||
extern unsigned char _rodata[] __attribute__((__weak__));
|
||||
extern unsigned char _etext[] __attribute__((__weak__));
|
||||
extern unsigned char _edata[] __attribute__((__weak__));
|
||||
extern unsigned char _ezip[] __attribute__((__weak__));
|
||||
@@ -28,5 +29,8 @@ extern unsigned long __got_start[] __attribute__((__weak__));
|
||||
extern unsigned long __got_end[] __attribute__((__weak__));
|
||||
extern unsigned char ape_phdrs[] __attribute__((__weak__));
|
||||
|
||||
#define __rarechange _Section(".bss.rarechange")
|
||||
#define __data_rarechange _Section(".data.rarechange")
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* COSMOPOLITAN_APE_SECTIONS_INTERNAL_H_ */
|
||||
+48
-72
@@ -361,13 +361,13 @@ Here's the TLS memory layout on x86_64:
|
||||
```
|
||||
__get_tls()
|
||||
│
|
||||
%fs OpenBSD/NetBSD
|
||||
%fs Linux/FreeBSD/NetBSD/OpenBSD
|
||||
_Thread_local │
|
||||
┌───┬──────────┬──────────┼───┐
|
||||
│pad│ .tdata │ .tbss │tib│
|
||||
└───┴──────────┴──────────┼───┘
|
||||
│
|
||||
Linux/FreeBSD/Windows/Mac %gs
|
||||
Windows/Mac %gs
|
||||
```
|
||||
|
||||
Quite possibly the greatest challenge in Actually Portable Executable
|
||||
@@ -401,80 +401,56 @@ arises in the fact that our Linux-flavored GCC and Clang toolchains
|
||||
(which are used to produce cross-OS binaries) are also only capable of
|
||||
producing TLS instructions that use the %fs convention.
|
||||
|
||||
To solve these challenges, the `cosmocc` compiler will rewrite binary
|
||||
objects after they've been compiled by GCC, so that the `%gs` register
|
||||
is used, rather than `%fs`. Morphing x86-64 binaries after they've been
|
||||
compiled is normally difficult, due to the complexity of the machine
|
||||
instruction language. However GCC provides `-mno-tls-direct-seg-refs`
|
||||
which greatly reduces the complexity of this task. This flag forgoes
|
||||
some optimizations to make the generated code simpler. Rather than doing
|
||||
clever arithmetic with `%fs` prefixes, the compiler will always generate
|
||||
the thread information block address load as a separate instruction.
|
||||
To solve these challenges, the `cosmocc` compiler rewrites GCC generated
|
||||
assembly so that the `%fs` referencing instructions created by GCC when
|
||||
using the `-mno-tls-direct-seg-refs` flag are replaced by calls to the
|
||||
following functions:
|
||||
|
||||
```c
|
||||
// Change AMD code to use %gs:0x30 instead of %fs:0
|
||||
// We assume -mno-tls-direct-seg-refs has been used
|
||||
static void ChangeTlsFsToGs(unsigned char *p, size_t n) {
|
||||
unsigned char *e = p + n - 9;
|
||||
while (p <= e) {
|
||||
// we're checking for the following expression:
|
||||
// 0144 == p[0] && // %fs
|
||||
// 0110 == (p[1] & 0373) && // rex.w (and ignore rex.r)
|
||||
// (0213 == p[2] || // mov reg/mem → reg (word-sized)
|
||||
// 0003 == p[2]) && // add reg/mem → reg (word-sized)
|
||||
// 0004 == (p[3] & 0307) && // mod/rm (4,reg,0) means sib → reg
|
||||
// 0045 == p[4] && // sib (5,4,0) → (rbp,rsp,0) → disp32
|
||||
// 0000 == p[5] && // displacement (von Neumann endian)
|
||||
// 0000 == p[6] && // displacement
|
||||
// 0000 == p[7] && // displacement
|
||||
// 0000 == p[8] // displacement
|
||||
uint64_t w = READ64LE(p) & READ64LE("\377\373\377\307\377\377\377\377");
|
||||
if ((w == READ64LE("\144\110\213\004\045\000\000\000") ||
|
||||
w == READ64LE("\144\110\003\004\045\000\000\000")) &&
|
||||
!p[8]) {
|
||||
p[0] = 0145; // change %fs to %gs
|
||||
p[5] = 0x30; // change 0 to 0x30
|
||||
p += 9;
|
||||
} else {
|
||||
++p;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
- `__get_tls_rax`
|
||||
- `__get_tls_rbx`
|
||||
- `__get_tls_rcx`
|
||||
- `__get_tls_rdx`
|
||||
- `__get_tls_rdi`
|
||||
- `__get_tls_rsi`
|
||||
- `__get_tls_rbp`
|
||||
- `__get_tls_r8`
|
||||
- `__get_tls_r9`
|
||||
- `__get_tls_r10`
|
||||
- `__get_tls_r11`
|
||||
- `__get_tls_r12`
|
||||
- `__get_tls_r13`
|
||||
- `__get_tls_r14`
|
||||
- `__get_tls_r15`
|
||||
- `__add_tls_rax`
|
||||
- `__add_tls_rbx`
|
||||
- `__add_tls_rcx`
|
||||
- `__add_tls_rdx`
|
||||
- `__add_tls_rdi`
|
||||
- `__add_tls_rsi`
|
||||
- `__add_tls_rbp`
|
||||
- `__add_tls_r8`
|
||||
- `__add_tls_r9`
|
||||
- `__add_tls_r10`
|
||||
- `__add_tls_r11`
|
||||
- `__add_tls_r12`
|
||||
- `__add_tls_r13`
|
||||
- `__add_tls_r14`
|
||||
- `__add_tls_r15`
|
||||
|
||||
By favoring `%gs` we've now ensured friction-free compatibility for the
|
||||
APE runtime on MacOS, Linux, and FreeBSD which are all able to conform
|
||||
easily to this convention. However additional work needs to be done at
|
||||
runtime when an APE program is started on Windows, OpenBSD, and NetBSD.
|
||||
On these platforms, all executable pages must be faulted and morphed to
|
||||
fixup the TLS instructions.
|
||||
The `cosmocc` script internally uses a tool named `tlscc` which wraps a
|
||||
gcc or clang compilation so it generates intermediate assembly code that
|
||||
it can modify to call the above functions. In the process, it also adds
|
||||
the `-mno-tls-direct-seg-refs` and `-mno-red-zone` flags to the compile
|
||||
command to ensure the compiler only generates TLS assembly instructions
|
||||
that are understood by the tool.
|
||||
|
||||
On OpenBSD and NetBSD, this is as simple as undoing the example
|
||||
operation above. Earlier at compile-time we turned `%fs` into `%gs`.
|
||||
Now, at runtime, `%gs` must be turned back into `%fs`. Since the
|
||||
executable is morphing itself, this is easier said than done.
|
||||
The above functions do not clobber any registers. The function name
|
||||
specifies the register to which the result is stored or added. The only
|
||||
above function that can be called by C code is `__get_tls_rax`.
|
||||
|
||||
OpenBSD for example enforces a `W^X` invariant. Code that's executing
|
||||
can't modify itself at the same time. The way Cosmopolitan solves this
|
||||
is by defining a special part of the binary called `.text.privileged`.
|
||||
This section is aligned to page boundaries. A GNU ld linker script is
|
||||
used to ensure that code which morphs code is placed into this section,
|
||||
through the use of a header-defined cosmo-specific keyword `privileged`.
|
||||
Additionally, the `fixupobj` program is used by the Cosmo build system
|
||||
to ensure that compiled objects don't contain privileged functions that
|
||||
call non-privileged functions. Needless to say, `mprotect()` needs to be
|
||||
a privileged function, so that it can be used to disable the execute bit
|
||||
on all other parts of the executable except for the privileged section,
|
||||
thereby making it writable. Once this has been done, code can change.
|
||||
|
||||
On Windows the displacement bytes of the TLS instruction are changed to
|
||||
use the `%gs:0x1480+i*8` ABI where `i` is a number assigned by the WIN32
|
||||
`TlsAlloc()` API. This avoids the need to call `TlsGetValue()` which is
|
||||
implemented this exact same way under the hood. Even though 0x1480 isn't
|
||||
explicitly documented by MSDN, this ABI is believed to be stable because
|
||||
MSVC generates binaries that use this offset directly. The only caveat
|
||||
is that `TlsAlloc()` must be called as early in the runtime init as
|
||||
possible, to ensure an index less than 64 is returned.
|
||||
When Windows and MacOS aren't in the support vector, then the above
|
||||
assembly rewriting pass can be avoided entirely, and the binary can
|
||||
simply use the TLS opcodes that GCC and Clang generate for Linux.
|
||||
|
||||
### Thread Information Block (TIB)
|
||||
|
||||
|
||||
+10
@@ -61,6 +61,16 @@ netbsd.ident:
|
||||
4: .size netbsd.ident,.-netbsd.ident
|
||||
.type netbsd.ident,@object
|
||||
.balign 4
|
||||
freebsd.ident:
|
||||
.long 2f-1f
|
||||
.long 4f-3f
|
||||
.long 1 // NT_FREEBSD_ABI_TAG
|
||||
1: .asciz "FreeBSD"
|
||||
2: .balign 4
|
||||
3: .long 1300000 // v13.0+ c. 2020
|
||||
4: .size freebsd.ident,.-freebsd.ident
|
||||
.type freebsd.ident,@object
|
||||
.balign 4
|
||||
ape.ident:
|
||||
.long 2f-1f
|
||||
.long 4f-3f
|
||||
|
||||
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
@@ -5,6 +5,7 @@
|
||||
-Wa,-msse2avx
|
||||
-Werror=maybe-uninitialized
|
||||
-Wno-literal-suffix
|
||||
-Wno-prio-ctor-dtor
|
||||
-Wno-unused-but-set-variable
|
||||
-Wunsafe-loop-optimizations
|
||||
-fbranch-target-load-optimize
|
||||
|
||||
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
+56
-25
@@ -96,24 +96,22 @@ endif
|
||||
# - Turns off support for other operating systems
|
||||
#
|
||||
ifeq ($(MODE), optlinux)
|
||||
TLSCC =
|
||||
CONFIG_OFLAGS ?= -g -ggdb
|
||||
CONFIG_CPPFLAGS += -DNDEBUG -DSYSDEBUG -DSUPPORT_VECTOR=1
|
||||
CONFIG_CCFLAGS += -O3 -fmerge-all-constants
|
||||
CONFIG_COPTS += -mred-zone
|
||||
CONFIG_TARGET_ARCH ?= -march=native
|
||||
CONFIG_TARGET_ARCH ?= -mavx
|
||||
endif
|
||||
ifeq ($(MODE), x86_64-optlinux)
|
||||
CONFIG_OFLAGS ?= -g -ggdb
|
||||
TLSCC =
|
||||
CONFIG_CPPFLAGS += -DNDEBUG -DSYSDEBUG -DSUPPORT_VECTOR=1
|
||||
CONFIG_CCFLAGS += -O3 -fmerge-all-constants
|
||||
CONFIG_COPTS += -mred-zone
|
||||
CONFIG_TARGET_ARCH ?= -march=native
|
||||
CONFIG_TARGET_ARCH ?= -mavx
|
||||
endif
|
||||
ifeq ($(MODE), aarch64-optlinux)
|
||||
CONFIG_OFLAGS ?= -g -ggdb
|
||||
CONFIG_CPPFLAGS += -DNDEBUG -DSYSDEBUG -DSUPPORT_VECTOR=1
|
||||
CONFIG_CCFLAGS += -O3 -fmerge-all-constants
|
||||
CONFIG_COPTS += -mred-zone
|
||||
endif
|
||||
|
||||
# Release Mode
|
||||
@@ -153,7 +151,7 @@ OVERRIDE_CFLAGS += -O0
|
||||
OVERRIDE_CXXFLAGS += -O0
|
||||
CONFIG_CPPFLAGS += -DMODE_DBG -D__SANITIZE_UNDEFINED__ -Wno-unused-variable -Wno-unused-but-set-variable
|
||||
CONFIG_CCFLAGS += $(BACKTRACES) -DSYSDEBUG
|
||||
CONFIG_COPTS += -fsanitize=undefined
|
||||
CONFIG_COPTS += -fsanitize=undefined -fno-data-sections
|
||||
OVERRIDE_CCFLAGS += -fno-pie
|
||||
QUOTA ?= -C64 -L300
|
||||
endif
|
||||
@@ -164,7 +162,7 @@ OVERRIDE_CFLAGS += -O0
|
||||
OVERRIDE_CXXFLAGS += -O0
|
||||
CONFIG_CPPFLAGS += -DMODE_DBG -D__SANITIZE_UNDEFINED__ -Wno-unused-variable -Wno-unused-but-set-variable
|
||||
CONFIG_CCFLAGS += $(BACKTRACES) -DSYSDEBUG
|
||||
CONFIG_COPTS += -fsanitize=undefined
|
||||
CONFIG_COPTS += -fsanitize=undefined -fno-data-sections
|
||||
OVERRIDE_CCFLAGS += -fno-pie
|
||||
QUOTA ?= -C64 -L300
|
||||
endif
|
||||
@@ -175,7 +173,7 @@ OVERRIDE_CFLAGS += -O0 -fdce
|
||||
OVERRIDE_CXXFLAGS += -O0 -fdce
|
||||
CONFIG_CPPFLAGS += -DMODE_DBG -D__SANITIZE_UNDEFINED__ -Wno-unused-variable -Wno-unused-but-set-variable
|
||||
CONFIG_CCFLAGS += $(BACKTRACES) -DSYSDEBUG
|
||||
CONFIG_COPTS += -fsanitize=undefined
|
||||
CONFIG_COPTS += -fsanitize=undefined -fno-data-sections
|
||||
QUOTA ?= -C64 -L300
|
||||
endif
|
||||
|
||||
@@ -191,7 +189,16 @@ endif
|
||||
#
|
||||
ifeq ($(MODE), sysv)
|
||||
ENABLE_FTRACE = 1
|
||||
CONFIG_OFLAGS ?= -g -ggdb
|
||||
CONFIG_CCFLAGS += $(BACKTRACES) -O2
|
||||
CONFIG_CPPFLAGS += -DSYSDEBUG -DSUPPORT_VECTOR=121
|
||||
endif
|
||||
ifeq ($(MODE), x86_64-sysv)
|
||||
ENABLE_FTRACE = 1
|
||||
CONFIG_CCFLAGS += $(BACKTRACES) -O2
|
||||
CONFIG_CPPFLAGS += -DSYSDEBUG -DSUPPORT_VECTOR=121
|
||||
endif
|
||||
ifeq ($(MODE), aarch64-sysv)
|
||||
ENABLE_FTRACE = 1
|
||||
CONFIG_CCFLAGS += $(BACKTRACES) -O2
|
||||
CONFIG_CPPFLAGS += -DSYSDEBUG -DSUPPORT_VECTOR=121
|
||||
endif
|
||||
@@ -211,8 +218,7 @@ endif
|
||||
ifeq ($(MODE), tiny)
|
||||
CONFIG_CPPFLAGS += \
|
||||
-DTINY \
|
||||
-DNDEBUG \
|
||||
-DTRUSTWORTHY
|
||||
-DNDEBUG
|
||||
CONFIG_CCFLAGS += \
|
||||
-Os \
|
||||
-fno-align-functions \
|
||||
@@ -230,8 +236,7 @@ endif
|
||||
ifeq ($(MODE), x86_64-tiny)
|
||||
CONFIG_CPPFLAGS += \
|
||||
-DTINY \
|
||||
-DNDEBUG \
|
||||
-DTRUSTWORTHY
|
||||
-DNDEBUG
|
||||
CONFIG_CCFLAGS += \
|
||||
-Os \
|
||||
-fno-align-functions \
|
||||
@@ -250,8 +255,7 @@ ifeq ($(MODE), aarch64-tiny)
|
||||
# TODO(jart): -mcmodel=tiny
|
||||
CONFIG_CPPFLAGS += \
|
||||
-DTINY \
|
||||
-DNDEBUG \
|
||||
-DTRUSTWORTHY
|
||||
-DNDEBUG
|
||||
CONFIG_CCFLAGS += \
|
||||
-Os \
|
||||
-fno-align-functions \
|
||||
@@ -281,10 +285,37 @@ endif
|
||||
# - YOLO
|
||||
#
|
||||
ifeq ($(MODE), tinylinux)
|
||||
TLSCC =
|
||||
CONFIG_CPPFLAGS += \
|
||||
-DTINY \
|
||||
-DNDEBUG \
|
||||
-DSUPPORT_VECTOR=1 \
|
||||
-DDWARFLESS
|
||||
CONFIG_CCFLAGS += \
|
||||
-Os \
|
||||
-fno-align-functions \
|
||||
-fno-align-jumps \
|
||||
-fno-align-labels \
|
||||
-fno-align-loops
|
||||
endif
|
||||
ifeq ($(MODE), x86_64-tinylinux)
|
||||
TLSCC =
|
||||
CONFIG_CPPFLAGS += \
|
||||
-DTINY \
|
||||
-DNDEBUG \
|
||||
-DSUPPORT_VECTOR=1 \
|
||||
-DDWARFLESS
|
||||
CONFIG_CCFLAGS += \
|
||||
-Os \
|
||||
-fno-align-functions \
|
||||
-fno-align-jumps \
|
||||
-fno-align-labels \
|
||||
-fno-align-loops
|
||||
endif
|
||||
ifeq ($(MODE), aarch64-tinylinux)
|
||||
CONFIG_CPPFLAGS += \
|
||||
-DTINY \
|
||||
-DNDEBUG \
|
||||
-DTRUSTWORTHY \
|
||||
-DSUPPORT_VECTOR=1 \
|
||||
-DDWARFLESS
|
||||
CONFIG_CCFLAGS += \
|
||||
@@ -310,10 +341,10 @@ endif
|
||||
# - YOLO
|
||||
#
|
||||
ifeq ($(MODE), tinylinuxbsd)
|
||||
TLSCC =
|
||||
CONFIG_CPPFLAGS += \
|
||||
-DTINY \
|
||||
-DNDEBUG \
|
||||
-DTRUSTWORTHY \
|
||||
-DSUPPORT_VECTOR=113 \
|
||||
-DDWARFLESS
|
||||
CONFIG_CCFLAGS += \
|
||||
@@ -341,7 +372,6 @@ ifeq ($(MODE), tinysysv)
|
||||
CONFIG_CPPFLAGS += \
|
||||
-DTINY \
|
||||
-DNDEBUG \
|
||||
-DTRUSTWORTHY \
|
||||
-DSUPPORT_VECTOR=121 \
|
||||
-DDWARFLESS
|
||||
CONFIG_CCFLAGS += \
|
||||
@@ -369,7 +399,6 @@ ifeq ($(MODE), tinynowin)
|
||||
CONFIG_CPPFLAGS += \
|
||||
-DTINY \
|
||||
-DNDEBUG \
|
||||
-DTRUSTWORTHY \
|
||||
-DSUPPORT_VECTOR=251 \
|
||||
-DDWARFLESS
|
||||
CONFIG_CCFLAGS += \
|
||||
@@ -410,7 +439,7 @@ endif
|
||||
# The rest of the monorepo may not work with llvm.
|
||||
#
|
||||
ifeq ($(MODE), llvm)
|
||||
.STRICT = 0
|
||||
.SANDBOXED = 0
|
||||
CONFIG_CCFLAGS += $(BACKTRACES) -DSYSDEBUG -O2
|
||||
AS = clang
|
||||
CC = clang
|
||||
@@ -450,9 +479,9 @@ endif
|
||||
# like objconv to convert them to COFF or MachO. Then use ANSI mode to
|
||||
# rollup one header file that'll enable linkage with minimal issues.
|
||||
ifeq ($(MODE), ansi)
|
||||
CONFIG_CFLAGS += -std=c11
|
||||
CONFIG_CFLAGS += -std=c23
|
||||
#CONFIG_CPPFLAGS += -ansi
|
||||
CONFIG_CXXFLAGS += -std=c++11
|
||||
CONFIG_CXXFLAGS += -std=c++23
|
||||
endif
|
||||
|
||||
ifneq ($(ENABLE_FTRACE),)
|
||||
@@ -490,7 +519,7 @@ endif
|
||||
ifeq ($(ARCH), aarch64)
|
||||
# this flag causes gcc to generate functions like this
|
||||
#
|
||||
# nop nop nop nop nop nop
|
||||
# nop nop nop nop nop nop nop nop
|
||||
# func:
|
||||
# nop
|
||||
# ...
|
||||
@@ -507,7 +536,9 @@ ifeq ($(ARCH), aarch64)
|
||||
# b -5
|
||||
# ...
|
||||
#
|
||||
CONFIG_CCFLAGS += -fpatchable-function-entry=7,6
|
||||
# we only need -fpatchable-function-entry=7,6 but we round up to 32
|
||||
# bytes so we don't break function alignment.
|
||||
CONFIG_CCFLAGS += -fpatchable-function-entry=9,8
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
@@ -53,11 +53,7 @@
|
||||
# OVERRIDE_FOO set ~/.cosmo.mk and target-specific (use rarely)
|
||||
#
|
||||
|
||||
ifeq ($(LANDLOCKMAKE_VERSION),)
|
||||
TMPSAFE = $(join $(TMPDIR)/,$(subst /,_,$@)).tmp
|
||||
else
|
||||
TMPSAFE = $(TMPDIR)/
|
||||
endif
|
||||
|
||||
BACKTRACES = \
|
||||
-fno-schedule-insns2 \
|
||||
@@ -94,6 +90,7 @@ DEFAULT_COPTS ?= \
|
||||
-fstrict-overflow \
|
||||
-fno-semantic-interposition
|
||||
|
||||
ifneq ($(TLSCC),)
|
||||
ifeq ($(ARCH), x86_64)
|
||||
# Microsoft says "[a]ny memory below the stack beyond the red zone
|
||||
# [note: Windows defines the x64 red zone size as 0] is considered
|
||||
@@ -103,6 +100,7 @@ DEFAULT_COPTS += \
|
||||
-mno-red-zone \
|
||||
-mno-tls-direct-seg-refs
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH), aarch64)
|
||||
#
|
||||
|
||||
@@ -99,3 +99,8 @@ rm -f cosmocc.zip cosmocc.zip.sha256sum
|
||||
# commit output directory
|
||||
cd "${OLDPWD}" || die
|
||||
mv "${OUTPUT_TMP}" "${OUTPUT_DIR}" || die
|
||||
|
||||
# update current symlink
|
||||
BASE=$(basename "${OUTPUT_DIR}")
|
||||
DIR=$(dirname "${OUTPUT_DIR}")
|
||||
ln -sfn "$BASE" "$DIR/current"
|
||||
+8
-8
@@ -6,14 +6,14 @@ if [ -n "$OBJDUMP" ]; then
|
||||
fi
|
||||
|
||||
find_objdump() {
|
||||
if [ -x .cosmocc/3.8.0/bin/$1-linux-cosmo-objdump ]; then
|
||||
OBJDUMP=.cosmocc/3.8.0/bin/$1-linux-cosmo-objdump
|
||||
elif [ -x .cosmocc/3.8.0/bin/$1-linux-musl-objdump ]; then
|
||||
OBJDUMP=.cosmocc/3.8.0/bin/$1-linux-musl-objdump
|
||||
elif [ -x "$COSMO/.cosmocc/3.8.0/bin/$1-linux-cosmo-objdump" ]; then
|
||||
OBJDUMP="$COSMO/.cosmocc/3.8.0/bin/$1-linux-cosmo-objdump"
|
||||
elif [ -x "$COSMO/.cosmocc/3.8.0/bin/$1-linux-musl-objdump" ]; then
|
||||
OBJDUMP="$COSMO/.cosmocc/3.8.0/bin/$1-linux-musl-objdump"
|
||||
if [ -x .cosmocc/3.9.2/bin/$1-linux-cosmo-objdump ]; then
|
||||
OBJDUMP=.cosmocc/3.9.2/bin/$1-linux-cosmo-objdump
|
||||
elif [ -x .cosmocc/3.9.2/bin/$1-linux-musl-objdump ]; then
|
||||
OBJDUMP=.cosmocc/3.9.2/bin/$1-linux-musl-objdump
|
||||
elif [ -x "$COSMO/.cosmocc/3.9.2/bin/$1-linux-cosmo-objdump" ]; then
|
||||
OBJDUMP="$COSMO/.cosmocc/3.9.2/bin/$1-linux-cosmo-objdump"
|
||||
elif [ -x "$COSMO/.cosmocc/3.9.2/bin/$1-linux-musl-objdump" ]; then
|
||||
OBJDUMP="$COSMO/.cosmocc/3.9.2/bin/$1-linux-musl-objdump"
|
||||
else
|
||||
echo "error: toolchain not found (try running 'cosmocc --update' or 'make' in the cosmo monorepo)" >&2
|
||||
exit 1
|
||||
|
||||
+1
-2
@@ -23,8 +23,7 @@
|
||||
# - tool/build/runitd.c
|
||||
|
||||
.PRECIOUS: o/$(MODE)/%.ok
|
||||
o/$(MODE)/%.ok: private .PLEDGE = stdio rpath wpath cpath proc fattr inet dns
|
||||
o/$(MODE)/%.ok: private .UNVEIL += r:/etc/resolv.conf
|
||||
o/$(MODE)/%.ok: private .PLEDGE = fattr inet dns
|
||||
o/$(MODE)/%.ok: \
|
||||
o/$(MODE)/tool/build/runit \
|
||||
o/$(MODE)/tool/build/runitd \
|
||||
|
||||
+10
-19
@@ -145,16 +145,7 @@ o/%.zip.o: o/%
|
||||
o/$(MODE)/%.zip.o: %
|
||||
@$(COMPILE) -wAZIPOBJ $(ZIPOBJ) $(ZIPOBJ_FLAGS) $(OUTPUT_OPTION) $<
|
||||
|
||||
o/$(MODE)/%.zip.o: %
|
||||
@$(COMPILE) -wAZIPOBJ $(ZIPOBJ) $(ZIPOBJ_FLAGS) $(OUTPUT_OPTION) $<
|
||||
|
||||
# an issue with sandboxing arises when creating directory entries in the
|
||||
# zip file. we need the trailing slash (e.g. o//foo/.zip.o) but Landlock
|
||||
# Make avoids sandboxing directory names that have a trailing slash (so
|
||||
# they can be used to watch for deleted files, without creating overly
|
||||
# broad unveiling). such rules need to be written more explicitly.
|
||||
o/$(MODE)%/.zip.o: %
|
||||
@$(COMPILE) -wAZIPOBJ $(ZIPOBJ) $(ZIPOBJ_FLAGS) $(OUTPUT_OPTION) $<
|
||||
o/$(MODE)/%.zip.o: private .SANDBOXED = 0
|
||||
|
||||
################################################################################
|
||||
# STRICT HEADER CHECKING
|
||||
@@ -163,43 +154,43 @@ o/$(MODE)%/.zip.o: %
|
||||
# and it would be too costly in terms of make latency to have every
|
||||
# header file depend on $(HDRS) and $(INCS).
|
||||
|
||||
o/%.h.ok: private .UNSANDBOXED = 1
|
||||
o/%.h.ok: private .SANDBOXED = 0
|
||||
o/%.h.ok: %.h
|
||||
@$(COMPILE) -ACHECK.h $(COMPILE.c) -xc -g0 -o $@ $<
|
||||
|
||||
o/$(MODE)/%.h.ok: private .UNSANDBOXED = 1
|
||||
o/$(MODE)/%.h.ok: private .SANDBOXED = 0
|
||||
o/$(MODE)/%.h.ok: %.h
|
||||
@$(COMPILE) -ACHECK.h $(COMPILE.c) -xc -g0 -o $@ $<
|
||||
|
||||
o/$(MODE)/%.hh.ok: private .UNSANDBOXED = 1
|
||||
o/$(MODE)/%.hh.ok: private .SANDBOXED = 0
|
||||
o/$(MODE)/%.hh.ok: %.hh
|
||||
@$(COMPILE) -ACHECK.h $(COMPILE.cxx) -xc++ -g0 -o $@ $<
|
||||
|
||||
o/%.okk: .UNSANDBOXED = 1
|
||||
o/%.okk: .SANDBOXED = 0
|
||||
o/%.okk: %
|
||||
@$(COMPILE) -ACHECK.h $(COMPILE.cxx) -xc++ -g0 -o $@ $<
|
||||
|
||||
o/$(MODE)/%.okk: private .UNSANDBOXED = 1
|
||||
o/$(MODE)/%.okk: private .SANDBOXED = 0
|
||||
o/$(MODE)/%.okk: %
|
||||
@$(COMPILE) -ACHECK.h $(COMPILE.cxx) -xc++ -g0 -o $@ $<
|
||||
|
||||
################################################################################
|
||||
# EMACS ASSEMBLY GENERATION
|
||||
|
||||
o/$(MODE)/%-gcc.asm: private .UNSANDBOXED = 1
|
||||
o/$(MODE)/%-gcc.asm: private .SANDBOXED = 0
|
||||
o/$(MODE)/%-gcc.asm: %.c
|
||||
@$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.c) -S -g0 $(OUTPUT_OPTION) $<
|
||||
|
||||
o/$(MODE)/%-gcc.asm: private .UNSANDBOXED = 1
|
||||
o/$(MODE)/%-gcc.asm: private .SANDBOXED = 0
|
||||
o/$(MODE)/%-gcc.asm: %.cc
|
||||
@$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.cxx) -S -g0 $(OUTPUT_OPTION) $<
|
||||
|
||||
o/$(MODE)/%-clang.asm: private .UNSANDBOXED = 1
|
||||
o/$(MODE)/%-clang.asm: private .SANDBOXED = 0
|
||||
o/$(MODE)/%-clang.asm: %.c
|
||||
@$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.c) -S -g0 $(OUTPUT_OPTION) $<
|
||||
|
||||
# TODO(jart): Make intrinsics support Clang.
|
||||
o/$(MODE)/%-clang.asm: CC = $(CLANG)
|
||||
o/$(MODE)/%-clang.asm: private .UNSANDBOXED = 1
|
||||
o/$(MODE)/%-clang.asm: private .SANDBOXED = 0
|
||||
o/$(MODE)/%-clang.asm: %.cc
|
||||
@$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.cxx) -S -g0 $(OUTPUT_OPTION) $<
|
||||
@@ -0,0 +1,10 @@
|
||||
-nostdinc
|
||||
-iquote
|
||||
.
|
||||
-isystem
|
||||
libc/isystem
|
||||
-include
|
||||
libc/integral/normalize.inc
|
||||
-D_COSMO_SOURCE
|
||||
-DMODE="aarch64"
|
||||
-Wno-unknown-pragmas
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
|
||||
namespace ctl {
|
||||
|
||||
struct string;
|
||||
class string;
|
||||
|
||||
class istream : public ios
|
||||
{
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#define COSMOPOLITAN_CTL_NEW_H_
|
||||
|
||||
#ifndef TINY
|
||||
__static_yoink("__demangle");
|
||||
__static_yoink("cosmo_demangle");
|
||||
#endif
|
||||
|
||||
namespace ctl {
|
||||
|
||||
+112
-11
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "exception.h"
|
||||
#include "is_base_of.h"
|
||||
#include "is_constructible.h"
|
||||
#include "is_convertible.h"
|
||||
#include "remove_extent.h"
|
||||
#include "unique_ptr.h"
|
||||
@@ -96,12 +97,12 @@ class shared_ref
|
||||
|
||||
size_t use_count() const noexcept
|
||||
{
|
||||
return shared + 1;
|
||||
return __atomic_load_n(&shared, __ATOMIC_RELAXED) + 1;
|
||||
}
|
||||
|
||||
size_t weak_count() const noexcept
|
||||
{
|
||||
return weak;
|
||||
return __atomic_load_n(&weak, __ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -348,7 +349,7 @@ class shared_ptr
|
||||
template<typename U>
|
||||
bool owner_before(const weak_ptr<U>& r) const noexcept
|
||||
{
|
||||
return !r.owner_before(*this);
|
||||
return rc < r.rc;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -381,6 +382,34 @@ class weak_ptr
|
||||
rc->keep_weak();
|
||||
}
|
||||
|
||||
weak_ptr(const weak_ptr& r) noexcept : p(r.p), rc(r.rc)
|
||||
{
|
||||
if (rc)
|
||||
rc->keep_weak();
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
requires __::shared_ptr_compatible<T, U>
|
||||
weak_ptr(const weak_ptr<U>& r) noexcept : p(r.p), rc(r.rc)
|
||||
{
|
||||
if (rc)
|
||||
rc->keep_weak();
|
||||
}
|
||||
|
||||
weak_ptr(weak_ptr&& r) noexcept : p(r.p), rc(r.rc)
|
||||
{
|
||||
r.p = nullptr;
|
||||
r.rc = nullptr;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
requires __::shared_ptr_compatible<T, U>
|
||||
weak_ptr(weak_ptr<U>&& r) noexcept : p(r.p), rc(r.rc)
|
||||
{
|
||||
r.p = nullptr;
|
||||
r.rc = nullptr;
|
||||
}
|
||||
|
||||
~weak_ptr()
|
||||
{
|
||||
if (rc)
|
||||
@@ -409,6 +438,19 @@ class weak_ptr
|
||||
swap(rc, r.rc);
|
||||
}
|
||||
|
||||
weak_ptr& operator=(weak_ptr r) noexcept
|
||||
{
|
||||
swap(r);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
requires __::shared_ptr_compatible<T, U>
|
||||
weak_ptr& operator=(weak_ptr<U> r) noexcept
|
||||
{
|
||||
weak_ptr<T>(move(r)).swap(*this);
|
||||
}
|
||||
|
||||
shared_ptr<T> lock() const noexcept
|
||||
{
|
||||
if (expired())
|
||||
@@ -437,6 +479,9 @@ class weak_ptr
|
||||
template<typename U>
|
||||
friend class shared_ptr;
|
||||
|
||||
template<typename U, typename... Args>
|
||||
friend shared_ptr<U> make_shared(Args&&...);
|
||||
|
||||
element_type* p = nullptr;
|
||||
__::shared_ref* rc = nullptr;
|
||||
};
|
||||
@@ -497,19 +542,75 @@ shared_ptr<T>::shared_ptr(U* const p, D d)
|
||||
}
|
||||
}
|
||||
|
||||
// Our make_shared supports passing a weak self reference as the first parameter
|
||||
// to your constructor, e.g.:
|
||||
//
|
||||
// struct Tree : ctl::weak_self_base
|
||||
// {
|
||||
// ctl::shared_ptr<Tree> l, r;
|
||||
// ctl::weak_ptr<Tree> parent;
|
||||
// Tree(weak_ptr<Tree> const& self, auto&& l2, auto&& r2)
|
||||
// : l(ctl::forward<decltype(l2)>(l2)),
|
||||
// r(ctl::forward<decltype(r2)>(r2))
|
||||
// {
|
||||
// if (l) l->parent = self;
|
||||
// if (r) r->parent = self;
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// int main() {
|
||||
// auto t = ctl::make_shared<Tree>(
|
||||
// ctl::make_shared<Tree>(nullptr, nullptr), nullptr);
|
||||
// return t->l->parent.lock().get() == t.get() ? 0 : 1;
|
||||
// }
|
||||
//
|
||||
// As shown, passing the parameter at object construction time lets you complete
|
||||
// object construction without needing a separate Init method. But because we go
|
||||
// off spec as far as the STL is concerned, there is a potential ambiguity where
|
||||
// you might have a constructor with a weak_ptr first parameter that is intended
|
||||
// to be something other than a self-reference. So this feature is opt-in by way
|
||||
// of inheriting from the following struct.
|
||||
struct weak_self_base
|
||||
{};
|
||||
|
||||
template<typename T, typename... Args>
|
||||
shared_ptr<T>
|
||||
make_shared(Args&&... args)
|
||||
{
|
||||
auto rc = __::shared_emplace<T>::make();
|
||||
rc->construct(forward<Args>(args)...);
|
||||
shared_ptr<T> r;
|
||||
r.p = &rc->t;
|
||||
r.rc = rc.release();
|
||||
if constexpr (is_base_of_v<enable_shared_from_this<T>, T>) {
|
||||
r->weak_this = r;
|
||||
unique_ptr rc = __::shared_emplace<T>::make();
|
||||
if constexpr (is_base_of_v<weak_self_base, T> &&
|
||||
is_constructible_v<T, const weak_ptr<T>&, Args...>) {
|
||||
// A __::shared_ref has a virtual weak reference that is owned by all of
|
||||
// the shared references. We can avoid some unnecessary refcount changes
|
||||
// by "borrowing" that reference and passing it to the constructor, then
|
||||
// promoting it to a shared reference by swapping it with the shared_ptr
|
||||
// that we return.
|
||||
weak_ptr<T> w;
|
||||
w.p = &rc->t;
|
||||
w.rc = rc.get();
|
||||
try {
|
||||
rc->construct(const_cast<const weak_ptr<T>&>(w),
|
||||
forward<Args>(args)...);
|
||||
} catch (...) {
|
||||
w.p = nullptr;
|
||||
w.rc = nullptr;
|
||||
throw;
|
||||
}
|
||||
rc.release();
|
||||
shared_ptr<T> r;
|
||||
swap(r.p, w.p);
|
||||
swap(r.rc, w.rc);
|
||||
return r;
|
||||
} else {
|
||||
rc->construct(forward<Args>(args)...);
|
||||
shared_ptr<T> r;
|
||||
r.p = &rc->t;
|
||||
r.rc = rc.release();
|
||||
if constexpr (is_base_of_v<enable_shared_from_this<T>, T>) {
|
||||
r->weak_this = r;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
} // namespace ctl
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
// -*- mode:c++; indent-tabs-mode:nil; c-basic-offset:4; coding:utf-8 -*-
|
||||
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
|
||||
//
|
||||
// Copyright 2026 Justin Alexander 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 "sort.h"
|
||||
#include "libc/mem/alg.h"
|
||||
|
||||
namespace ctl {
|
||||
namespace detail {
|
||||
|
||||
// This lives out of line so that programs calling ctl::sort() only need
|
||||
// to depend on ctl, rather than on whichever library qsort_r() is in.
|
||||
void
|
||||
sort_r(void* a,
|
||||
size_t n,
|
||||
size_t es,
|
||||
int (*cmp)(const void*, const void*, void*),
|
||||
void* arg)
|
||||
{
|
||||
qsort_r(a, n, es, cmp, arg);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace ctl
|
||||
+71
-9
@@ -2,23 +2,54 @@
|
||||
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
|
||||
#ifndef CTL_SORT_H_
|
||||
#define CTL_SORT_H_
|
||||
#include "is_same.h"
|
||||
#include "iterator_traits.h"
|
||||
#include "less.h"
|
||||
#include "partition.h"
|
||||
#include "new.h"
|
||||
#include "utility.h"
|
||||
|
||||
namespace ctl {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename RandomIt, typename Compare>
|
||||
void
|
||||
quicksort(RandomIt first, RandomIt last, Compare comp)
|
||||
sort_r(void*, size_t, size_t, int (*)(const void*, const void*, void*), void*);
|
||||
|
||||
template<typename T, typename Compare>
|
||||
int
|
||||
sort_compare(const T& x, const T& y, Compare& comp)
|
||||
{
|
||||
if (first < last) {
|
||||
auto pivot = ctl::partition(first, last, comp);
|
||||
quicksort(first, pivot, comp);
|
||||
quicksort(pivot + 1, last, comp);
|
||||
}
|
||||
if (comp(x, y))
|
||||
return -1;
|
||||
if (comp(y, x))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename T, typename Compare>
|
||||
int
|
||||
sort_direct(const void* a, const void* b, void* arg)
|
||||
{
|
||||
return sort_compare(*static_cast<const T*>(a),
|
||||
*static_cast<const T*>(b),
|
||||
*static_cast<Compare*>(arg));
|
||||
}
|
||||
|
||||
template<typename RandomIt, typename Compare>
|
||||
struct sort_context
|
||||
{
|
||||
RandomIt first;
|
||||
Compare& comp;
|
||||
};
|
||||
|
||||
template<typename RandomIt, typename Compare>
|
||||
int
|
||||
sort_indirect(const void* a, const void* b, void* arg)
|
||||
{
|
||||
auto* ctx = static_cast<sort_context<RandomIt, Compare>*>(arg);
|
||||
return sort_compare(ctx->first[*static_cast<const size_t*>(a)],
|
||||
ctx->first[*static_cast<const size_t*>(b)],
|
||||
ctx->comp);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
@@ -27,7 +58,38 @@ template<typename RandomIt, typename Compare>
|
||||
void
|
||||
sort(RandomIt first, RandomIt last, Compare comp)
|
||||
{
|
||||
detail::quicksort(first, last, comp);
|
||||
using T = typename ctl::iterator_traits<RandomIt>::value_type;
|
||||
size_t n = last - first;
|
||||
if (n < 2)
|
||||
return;
|
||||
if constexpr (ctl::is_same_v<RandomIt, T*> && __is_trivially_copyable(T)) {
|
||||
detail::sort_r(
|
||||
first, n, sizeof(T), detail::sort_direct<T, Compare>, &comp);
|
||||
return;
|
||||
}
|
||||
size_t* idx = new size_t[n];
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
idx[i] = i;
|
||||
detail::sort_context<RandomIt, Compare> ctx{ first, comp };
|
||||
detail::sort_r(
|
||||
idx, n, sizeof(size_t), detail::sort_indirect<RandomIt, Compare>, &ctx);
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
if (idx[i] == i)
|
||||
continue;
|
||||
T tmp(ctl::move(first[i]));
|
||||
size_t j = i;
|
||||
for (;;) {
|
||||
size_t k = idx[j];
|
||||
idx[j] = j;
|
||||
if (k == i) {
|
||||
first[j] = ctl::move(tmp);
|
||||
break;
|
||||
}
|
||||
first[j] = ctl::move(first[k]);
|
||||
j = k;
|
||||
}
|
||||
}
|
||||
delete[] idx;
|
||||
}
|
||||
|
||||
template<typename RandomIt>
|
||||
|
||||
+69
-1
@@ -277,7 +277,7 @@ size_t
|
||||
string::find(const char ch, const size_t pos) const noexcept
|
||||
{
|
||||
char* q;
|
||||
if ((q = (char*)memchr(data(), ch, size())))
|
||||
if (pos < size() && (q = (char*)memchr(data() + pos, ch, size() - pos)))
|
||||
return q - data();
|
||||
return npos;
|
||||
}
|
||||
@@ -383,4 +383,72 @@ string::erase(const size_t pos, size_t count) noexcept
|
||||
return *this;
|
||||
}
|
||||
|
||||
void
|
||||
string::append(const ctl::string_view& s, size_t pos, size_t count) noexcept
|
||||
{
|
||||
append(s.substr(pos, count));
|
||||
}
|
||||
|
||||
size_t
|
||||
string::find_last_of(char c, size_t pos) const noexcept
|
||||
{
|
||||
const char* b = data();
|
||||
size_t n = size();
|
||||
if (pos > n)
|
||||
pos = n;
|
||||
const char* p = (const char*)memrchr(b, c, pos);
|
||||
return p ? p - b : npos;
|
||||
}
|
||||
|
||||
size_t
|
||||
string::find_last_of(ctl::string_view set, size_t pos) const noexcept
|
||||
{
|
||||
if (empty() || set.empty())
|
||||
return npos;
|
||||
bool lut[256] = {};
|
||||
for (char c : set)
|
||||
lut[c & 255] = true;
|
||||
const char* b = data();
|
||||
size_t last = size() - 1;
|
||||
if (pos > last)
|
||||
pos = last;
|
||||
for (;;) {
|
||||
if (lut[b[pos] & 255])
|
||||
return pos;
|
||||
if (!pos)
|
||||
return npos;
|
||||
--pos;
|
||||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
string::find_first_of(char c, size_t pos) const noexcept
|
||||
{
|
||||
size_t n = size();
|
||||
if (pos >= n)
|
||||
return npos;
|
||||
const char* b = data();
|
||||
const char* p = (const char*)memchr(b + pos, c, n - pos);
|
||||
return p ? p - b : npos;
|
||||
}
|
||||
|
||||
size_t
|
||||
string::find_first_of(ctl::string_view set, size_t pos) const noexcept
|
||||
{
|
||||
if (set.empty())
|
||||
return npos;
|
||||
bool lut[256] = {};
|
||||
for (char c : set)
|
||||
lut[c & 255] = true;
|
||||
const char* b = data();
|
||||
size_t n = size();
|
||||
for (;;) {
|
||||
if (pos >= n)
|
||||
return npos;
|
||||
if (lut[b[pos] & 255])
|
||||
return pos;
|
||||
++pos;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ctl
|
||||
+6
-1
@@ -125,6 +125,7 @@ class string
|
||||
void append(char, size_t) noexcept;
|
||||
void append(unsigned long) noexcept;
|
||||
void append(const void*, size_t) noexcept;
|
||||
void append(const ctl::string_view&, size_t, size_t = npos) noexcept;
|
||||
string& insert(size_t, ctl::string_view) noexcept;
|
||||
string& erase(size_t = 0, size_t = npos) noexcept;
|
||||
string substr(size_t = 0, size_t = npos) const noexcept;
|
||||
@@ -136,6 +137,10 @@ class string
|
||||
bool starts_with(ctl::string_view) const noexcept;
|
||||
size_t find(char, size_t = 0) const noexcept;
|
||||
size_t find(ctl::string_view, size_t = 0) const noexcept;
|
||||
size_t find_first_of(char, size_t = 0) const noexcept;
|
||||
size_t find_first_of(ctl::string_view, size_t = 0) const noexcept;
|
||||
size_t find_last_of(char, size_t = npos) const noexcept;
|
||||
size_t find_last_of(ctl::string_view, size_t = npos) const noexcept;
|
||||
|
||||
void swap(string& s) noexcept
|
||||
{
|
||||
@@ -302,7 +307,7 @@ class string
|
||||
append(ch);
|
||||
}
|
||||
|
||||
void append(const ctl::string_view s) noexcept
|
||||
void append(const ctl::string_view& s) noexcept
|
||||
{
|
||||
append(s.p, s.n);
|
||||
}
|
||||
|
||||
+63
-1
@@ -29,7 +29,7 @@ size_t
|
||||
string_view::find(const char ch, const size_t pos) const noexcept
|
||||
{
|
||||
char* q;
|
||||
if (n && (q = (char*)memchr(p, ch, n)))
|
||||
if (pos < n && (q = (char*)memchr(p + pos, ch, n - pos)))
|
||||
return q - p;
|
||||
return npos;
|
||||
}
|
||||
@@ -108,4 +108,66 @@ string_view::starts_with(const string_view s) const noexcept
|
||||
return !memcmp(p, s.p, s.n);
|
||||
}
|
||||
|
||||
size_t
|
||||
string_view::find_last_of(char c, size_t pos) const noexcept
|
||||
{
|
||||
const char* b = data();
|
||||
size_t n = size();
|
||||
if (pos > n)
|
||||
pos = n;
|
||||
const char* p = (const char*)memrchr(b, c, pos);
|
||||
return p ? p - b : npos;
|
||||
}
|
||||
|
||||
size_t
|
||||
string_view::find_last_of(ctl::string_view set, size_t pos) const noexcept
|
||||
{
|
||||
if (empty() || set.empty())
|
||||
return npos;
|
||||
bool lut[256] = {};
|
||||
for (char c : set)
|
||||
lut[c & 255] = true;
|
||||
const char* b = data();
|
||||
size_t last = size() - 1;
|
||||
if (pos > last)
|
||||
pos = last;
|
||||
for (;;) {
|
||||
if (lut[b[pos] & 255])
|
||||
return pos;
|
||||
if (!pos)
|
||||
return npos;
|
||||
--pos;
|
||||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
string_view::find_first_of(char c, size_t pos) const noexcept
|
||||
{
|
||||
size_t n = size();
|
||||
if (pos >= n)
|
||||
return npos;
|
||||
const char* b = data();
|
||||
const char* p = (const char*)memchr(b + pos, c, n - pos);
|
||||
return p ? p - b : npos;
|
||||
}
|
||||
|
||||
size_t
|
||||
string_view::find_first_of(ctl::string_view set, size_t pos) const noexcept
|
||||
{
|
||||
if (set.empty())
|
||||
return npos;
|
||||
bool lut[256] = {};
|
||||
for (char c : set)
|
||||
lut[c & 255] = true;
|
||||
const char* b = data();
|
||||
size_t n = size();
|
||||
for (;;) {
|
||||
if (pos >= n)
|
||||
return npos;
|
||||
if (lut[b[pos] & 255])
|
||||
return pos;
|
||||
++pos;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ctl
|
||||
+6
-2
@@ -45,6 +45,10 @@ struct string_view
|
||||
string_view substr(size_t = 0, size_t = npos) const noexcept;
|
||||
size_t find(char, size_t = 0) const noexcept;
|
||||
size_t find(string_view, size_t = 0) const noexcept;
|
||||
size_t find_first_of(char, size_t = 0) const noexcept;
|
||||
size_t find_first_of(ctl::string_view, size_t = 0) const noexcept;
|
||||
size_t find_last_of(char, size_t = npos) const noexcept;
|
||||
size_t find_last_of(ctl::string_view, size_t = npos) const noexcept;
|
||||
|
||||
constexpr string_view& operator=(const string_view s) noexcept
|
||||
{
|
||||
@@ -109,12 +113,12 @@ struct string_view
|
||||
return p[n - 1];
|
||||
}
|
||||
|
||||
constexpr const_iterator begin() noexcept
|
||||
constexpr const_iterator begin() const noexcept
|
||||
{
|
||||
return p;
|
||||
}
|
||||
|
||||
constexpr const_iterator end() noexcept
|
||||
constexpr const_iterator end() const noexcept
|
||||
{
|
||||
return p + n;
|
||||
}
|
||||
|
||||
@@ -6,4 +6,5 @@ o/$(MODE)/dsp: o/$(MODE)/dsp/audio \
|
||||
o/$(MODE)/dsp/core \
|
||||
o/$(MODE)/dsp/mpeg \
|
||||
o/$(MODE)/dsp/scale \
|
||||
o/$(MODE)/dsp/prog \
|
||||
o/$(MODE)/dsp/tty
|
||||
@@ -469,16 +469,16 @@ COSMOAUDIO_ABI int cosmoaudio_poll(struct CosmoAudio* ca,
|
||||
if (in_out_writeFrames && 1u + *in_out_writeFrames > ca->outputBufferFrames)
|
||||
return COSMOAUDIO_ENOBUF;
|
||||
for (;;) {
|
||||
int done = 0;
|
||||
int done = 1;
|
||||
ma_uint32 readable = 0;
|
||||
ma_uint32 writable = 0;
|
||||
if (in_out_readFrames) {
|
||||
readable = ma_pcm_rb_available_read(&ca->input);
|
||||
done |= readable >= (ma_uint32)*in_out_readFrames;
|
||||
done &= readable >= (ma_uint32)*in_out_readFrames;
|
||||
}
|
||||
if (in_out_writeFrames) {
|
||||
writable = ma_pcm_rb_available_write(&ca->output);
|
||||
done |= writable >= (ma_uint32)*in_out_writeFrames;
|
||||
done &= writable >= (ma_uint32)*in_out_writeFrames;
|
||||
}
|
||||
if (done) {
|
||||
if (in_out_readFrames)
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
Symlink
+1
@@ -0,0 +1 @@
|
||||
../../../third_party/miniaudio/miniaudio.h
|
||||
@@ -167,6 +167,7 @@ See below for detailed the API documentation.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <cosmotime.h>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
|
||||
#── vi: set noet ft=make ts=8 sw=8 fenc=utf-8 :vi ────────────────────┘
|
||||
|
||||
PKGS += DSP_PROG
|
||||
|
||||
DSP_PROG_FILES := $(wildcard dsp/prog/*)
|
||||
DSP_PROG_HDRS = $(filter %.h,$(DSP_PROG_FILES))
|
||||
DSP_PROG_SRCS = $(filter %.c,$(DSP_PROG_FILES))
|
||||
DSP_PROG_OBJS = $(DSP_PROG_SRCS:%.c=o/$(MODE)/%.o)
|
||||
DSP_PROG_COMS = $(DSP_PROG_SRCS:%.c=o/$(MODE)/%)
|
||||
DSP_PROG_BINS = $(DSP_PROG_COMS) $(DSP_PROG_COMS:%=%.dbg)
|
||||
|
||||
DSP_PROG_DIRECTDEPS = \
|
||||
DSP_AUDIO \
|
||||
LIBC_CALLS \
|
||||
LIBC_FMT \
|
||||
LIBC_INTRIN \
|
||||
LIBC_NEXGEN32E \
|
||||
LIBC_RUNTIME \
|
||||
LIBC_SOCK \
|
||||
LIBC_STDIO \
|
||||
LIBC_SYSV \
|
||||
LIBC_TINYMATH \
|
||||
THIRD_PARTY_GETOPT \
|
||||
THIRD_PARTY_MINIAUDIO \
|
||||
THIRD_PARTY_MUSL \
|
||||
|
||||
DSP_PROG_DEPS := \
|
||||
$(call uniq,$(foreach x,$(DSP_PROG_DIRECTDEPS),$($(x))))
|
||||
|
||||
o/$(MODE)/dsp/prog/prog.pkg: \
|
||||
$(DSP_PROG_OBJS) \
|
||||
$(foreach x,$(DSP_PROG_DIRECTDEPS),$($(x)_A).pkg)
|
||||
|
||||
o/$(MODE)/dsp/prog/%.dbg: \
|
||||
$(DSP_PROG_DEPS) \
|
||||
o/$(MODE)/dsp/prog/prog.pkg \
|
||||
o/$(MODE)/dsp/prog/%.o \
|
||||
$(CRT) \
|
||||
$(APE_NO_MODIFY_SELF)
|
||||
@$(APELINK)
|
||||
|
||||
$(DSP_PROG_OBJS): dsp/prog/BUILD.mk
|
||||
|
||||
.PHONY: o/$(MODE)/dsp/prog
|
||||
o/$(MODE)/dsp/prog: $(DSP_PROG_BINS)
|
||||
@@ -0,0 +1,41 @@
|
||||
#ifndef COSMOPOLITAN_DSP_PROG_LOUDNESS_H_
|
||||
#define COSMOPOLITAN_DSP_PROG_LOUDNESS_H_
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define MIN_DECIBEL -60
|
||||
#define MAX_DECIBEL 0
|
||||
|
||||
// computes root of mean squares
|
||||
static double rms(float *p, int n) {
|
||||
double s = 0;
|
||||
for (int i = 0; i < n; ++i)
|
||||
s += p[i] * p[i];
|
||||
return sqrt(s / n);
|
||||
}
|
||||
|
||||
// converts rms to decibel
|
||||
static double rms_to_db(double rms) {
|
||||
double db = 20 * log10(rms);
|
||||
db = fmin(db, MAX_DECIBEL);
|
||||
db = fmax(db, MIN_DECIBEL);
|
||||
return db;
|
||||
}
|
||||
|
||||
// char meter[21];
|
||||
// format_decibel_meter(meter, 20, rms_to_db(rms(samps, count)))
|
||||
static char *format_decibel_meter(char *meter, int width, double db) {
|
||||
double range = MAX_DECIBEL - MIN_DECIBEL;
|
||||
int filled = (db - MIN_DECIBEL) / range * width;
|
||||
for (int i = 0; i < width; ++i) {
|
||||
if (i < filled) {
|
||||
meter[i] = '=';
|
||||
} else {
|
||||
meter[i] = ' ';
|
||||
}
|
||||
}
|
||||
meter[width] = 0;
|
||||
return meter;
|
||||
}
|
||||
|
||||
#endif /* COSMOPOLITAN_DSP_PROG_LOUDNESS_H_ */
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2025 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/audio/cosmoaudio/cosmoaudio.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "third_party/getopt/getopt.internal.h"
|
||||
#include "third_party/miniaudio/miniaudio.h"
|
||||
|
||||
/**
|
||||
* @fileoverview audio file player
|
||||
*
|
||||
* This program provides a command line interface for playing .wav,
|
||||
* .mp3, .flac, and .ogg on the system speaker.
|
||||
*/
|
||||
|
||||
volatile int gotsig;
|
||||
|
||||
void onsig(int sig) {
|
||||
gotsig = sig;
|
||||
}
|
||||
|
||||
void usage(FILE *f) {
|
||||
fprintf(f, "usage: %s [-hrv] [-s 44100] [-c 2] FILE...\n",
|
||||
program_invocation_short_name);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
// parse flags
|
||||
int opt;
|
||||
int flag_repeat = 0;
|
||||
int flag_verbose = 0;
|
||||
int flag_channels = 2;
|
||||
int flag_samprate = 44100;
|
||||
while ((opt = getopt(argc, argv, "hrvc:s:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'c':
|
||||
flag_channels = atoi(optarg);
|
||||
if (flag_channels < 1) {
|
||||
fprintf(stderr, "error: invalid channel count\n");
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
flag_samprate = atoi(optarg);
|
||||
if (flag_samprate < 1) {
|
||||
fprintf(stderr, "error: invalid sampling rate\n");
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'v':
|
||||
++flag_verbose;
|
||||
break;
|
||||
case 'r':
|
||||
++flag_repeat;
|
||||
break;
|
||||
case 'h':
|
||||
usage(stdout);
|
||||
exit(0);
|
||||
default:
|
||||
usage(stderr);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// open speaker
|
||||
int status;
|
||||
struct CosmoAudio *ca;
|
||||
struct CosmoAudioOpenOptions cao = {0};
|
||||
cao.sizeofThis = sizeof(struct CosmoAudioOpenOptions);
|
||||
cao.deviceType = kCosmoAudioDeviceTypePlayback;
|
||||
cao.sampleRate = flag_samprate;
|
||||
cao.debugLog = flag_verbose > 1;
|
||||
cao.channels = flag_channels;
|
||||
status = cosmoaudio_open(&ca, &cao);
|
||||
if (status != COSMOAUDIO_SUCCESS) {
|
||||
fprintf(stderr, "failed to open speaker: %d\n", status);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// intercept ctrl-c
|
||||
signal(SIGINT, onsig);
|
||||
|
||||
// play files
|
||||
int exit_code = 0;
|
||||
do {
|
||||
for (int arg = optind; arg < argc; ++arg) {
|
||||
const char *fname = argv[arg];
|
||||
|
||||
if (flag_verbose)
|
||||
fprintf(stderr, "playing %s\n", fname);
|
||||
|
||||
// create decoder
|
||||
ma_decoder_config decoderConfig =
|
||||
ma_decoder_config_init(ma_format_f32, flag_channels, flag_samprate);
|
||||
decoderConfig.resampling.algorithm = ma_resample_algorithm_linear;
|
||||
decoderConfig.resampling.linear.lpfOrder = 8;
|
||||
|
||||
// open input file
|
||||
ma_decoder decoder;
|
||||
ma_result rc = ma_decoder_init_file(fname, &decoderConfig, &decoder);
|
||||
if (rc != MA_SUCCESS) {
|
||||
fprintf(stderr,
|
||||
"%s: failed to open audio file: %s (we support "
|
||||
".wav, .mp3, .flac, and .ogg)\n",
|
||||
fname, ma_result_description(rc));
|
||||
exit_code = 1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// decode audio and transfer to speaker
|
||||
while (!gotsig) {
|
||||
float buf[512];
|
||||
ma_uint64 got = 0;
|
||||
ma_uint64 max = sizeof(buf) / sizeof(*buf) / flag_channels;
|
||||
ma_decoder_read_pcm_frames(&decoder, buf, max, &got);
|
||||
if (!got)
|
||||
break;
|
||||
int can = got;
|
||||
cosmoaudio_poll(ca, NULL, &can);
|
||||
cosmoaudio_write(ca, buf, got);
|
||||
}
|
||||
|
||||
// cleanup
|
||||
ma_decoder_uninit(&decoder);
|
||||
}
|
||||
} while (!gotsig && flag_repeat);
|
||||
|
||||
// we're done
|
||||
exit:
|
||||
cosmoaudio_flush(ca);
|
||||
cosmoaudio_close(ca);
|
||||
exit(exit_code);
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
#if 0
|
||||
/*─────────────────────────────────────────────────────────────────╗
|
||||
│ To the extent possible under law, Justine Tunney has waived │
|
||||
│ all copyright and related or neighboring rights to this file, │
|
||||
│ as it is written in the following disclaimers: │
|
||||
│ • http://unlicense.org/ │
|
||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include <arpa/inet.h>
|
||||
#include <assert.h>
|
||||
#include <cosmoaudio.h>
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
#include <netinet/in.h>
|
||||
#include <signal.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include "loudness.h"
|
||||
|
||||
/**
|
||||
* @fileoverview plays audio from remote computer on speaker
|
||||
* @see dsp/prog/sendaudio.c
|
||||
*/
|
||||
|
||||
#define SAMPLING_RATE 44100
|
||||
#define FRAMES_PER_SECOND 60
|
||||
#define DEBUG_LOG 0
|
||||
#define PORT 9834
|
||||
|
||||
#define CHUNK_FRAMES (SAMPLING_RATE / FRAMES_PER_SECOND)
|
||||
|
||||
static_assert(CHUNK_FRAMES * sizeof(short) < 1472,
|
||||
"audio chunks won't fit in udp ethernet packet");
|
||||
|
||||
sig_atomic_t g_done;
|
||||
|
||||
void onsig(int sig) {
|
||||
g_done = 1;
|
||||
}
|
||||
|
||||
short toshort(float x) {
|
||||
return fmaxf(-1, fminf(1, x)) * 32767;
|
||||
}
|
||||
|
||||
float tofloat(short x) {
|
||||
return x / 32768.f;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
|
||||
// listen on udp port for audio
|
||||
int server;
|
||||
if ((server = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
|
||||
perror("socket");
|
||||
return 3;
|
||||
}
|
||||
struct sockaddr_in addr = {.sin_family = AF_INET, .sin_port = htons(PORT)};
|
||||
if (bind(server, (struct sockaddr*)&addr, sizeof(addr))) {
|
||||
perror("bind");
|
||||
return 4;
|
||||
}
|
||||
|
||||
// setup signals
|
||||
struct sigaction sa;
|
||||
sa.sa_flags = 0;
|
||||
sa.sa_handler = onsig;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sigaction(SIGINT, &sa, 0);
|
||||
|
||||
// configure cosmo audio
|
||||
struct CosmoAudioOpenOptions cao = {0};
|
||||
cao.sizeofThis = sizeof(struct CosmoAudioOpenOptions);
|
||||
cao.deviceType = kCosmoAudioDeviceTypePlayback;
|
||||
cao.sampleRate = SAMPLING_RATE;
|
||||
cao.bufferFrames = CHUNK_FRAMES * 2;
|
||||
cao.debugLog = DEBUG_LOG;
|
||||
cao.channels = 1;
|
||||
|
||||
// connect to microphone and speaker
|
||||
int status;
|
||||
struct CosmoAudio* ca;
|
||||
status = cosmoaudio_open(&ca, &cao);
|
||||
if (status != COSMOAUDIO_SUCCESS) {
|
||||
fprintf(stderr, "failed to open audio: %d\n", status);
|
||||
return 5;
|
||||
}
|
||||
|
||||
while (!g_done) {
|
||||
// read from network
|
||||
ssize_t got;
|
||||
short buf16[CHUNK_FRAMES];
|
||||
if ((got = read(server, buf16, CHUNK_FRAMES * sizeof(short))) == -1) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
perror("read");
|
||||
return 7;
|
||||
}
|
||||
if (got != CHUNK_FRAMES * sizeof(short)) {
|
||||
fprintf(stderr, "warning: got partial audio frame\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
// write to speaker
|
||||
float buf32[CHUNK_FRAMES];
|
||||
for (int i = 0; i < CHUNK_FRAMES; ++i)
|
||||
buf32[i] = tofloat(buf16[i]);
|
||||
cosmoaudio_poll(ca, 0, (int[]){CHUNK_FRAMES});
|
||||
cosmoaudio_write(ca, buf32, CHUNK_FRAMES);
|
||||
|
||||
// print loudness in ascii
|
||||
char meter[21];
|
||||
double db = rms_to_db(rms(buf32, CHUNK_FRAMES));
|
||||
format_decibel_meter(meter, 20, db);
|
||||
printf("\r%s| %+6.2f dB", meter, db);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
// clean up resources
|
||||
cosmoaudio_flush(ca);
|
||||
cosmoaudio_close(ca);
|
||||
close(server);
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
#if 0
|
||||
/*─────────────────────────────────────────────────────────────────╗
|
||||
│ To the extent possible under law, Justine Tunney has waived │
|
||||
│ all copyright and related or neighboring rights to this file, │
|
||||
│ as it is written in the following disclaimers: │
|
||||
│ • http://unlicense.org/ │
|
||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include <arpa/inet.h>
|
||||
#include <assert.h>
|
||||
#include <cosmoaudio.h>
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <signal.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include "loudness.h"
|
||||
|
||||
/**
|
||||
* @fileoverview sends audio from microphone to remote computer
|
||||
* @see dsp/prog/recvaudio.c
|
||||
*/
|
||||
|
||||
#define SAMPLING_RATE 44100
|
||||
#define FRAMES_PER_SECOND 60
|
||||
#define DEBUG_LOG 0
|
||||
#define PORT 9834
|
||||
|
||||
#define CHUNK_FRAMES (SAMPLING_RATE / FRAMES_PER_SECOND)
|
||||
|
||||
static_assert(CHUNK_FRAMES * sizeof(short) < 1472,
|
||||
"audio chunks won't fit in udp ethernet packet");
|
||||
|
||||
sig_atomic_t g_done;
|
||||
|
||||
void onsig(int sig) {
|
||||
g_done = 1;
|
||||
}
|
||||
|
||||
short toshort(float x) {
|
||||
return fmaxf(-1, fminf(1, x)) * 32767;
|
||||
}
|
||||
|
||||
float tofloat(short x) {
|
||||
return x / 32768.f;
|
||||
}
|
||||
|
||||
uint32_t host2ip(const char* host) {
|
||||
uint32_t ip;
|
||||
if ((ip = inet_addr(host)) != -1u)
|
||||
return ip;
|
||||
int rc;
|
||||
struct addrinfo* ai = NULL;
|
||||
struct addrinfo hint = {AI_NUMERICSERV, AF_INET, SOCK_STREAM, IPPROTO_TCP};
|
||||
if ((rc = getaddrinfo(host, "0", &hint, &ai))) {
|
||||
fprintf(stderr, "%s: %s\n", host, gai_strerror(rc));
|
||||
exit(50 + rc);
|
||||
}
|
||||
ip = ntohl(((struct sockaddr_in*)ai->ai_addr)->sin_addr.s_addr);
|
||||
freeaddrinfo(ai);
|
||||
return ip;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "%s: missing host argument\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// get host argument
|
||||
const char* remote_host = argv[1];
|
||||
uint32_t ip = host2ip(remote_host);
|
||||
|
||||
// connect to server
|
||||
int client;
|
||||
if ((client = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
|
||||
perror(remote_host);
|
||||
return 3;
|
||||
}
|
||||
struct sockaddr_in addr = {.sin_family = AF_INET,
|
||||
.sin_port = htons(PORT),
|
||||
.sin_addr.s_addr = htonl(ip)};
|
||||
if (connect(client, (struct sockaddr*)&addr, sizeof(addr))) {
|
||||
perror(remote_host);
|
||||
return 4;
|
||||
}
|
||||
|
||||
// setup signals
|
||||
struct sigaction sa;
|
||||
sa.sa_flags = 0;
|
||||
sa.sa_handler = onsig;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sigaction(SIGINT, &sa, 0);
|
||||
|
||||
// configure cosmo audio
|
||||
struct CosmoAudioOpenOptions cao = {0};
|
||||
cao.sizeofThis = sizeof(struct CosmoAudioOpenOptions);
|
||||
cao.deviceType = kCosmoAudioDeviceTypeCapture;
|
||||
cao.sampleRate = SAMPLING_RATE;
|
||||
cao.bufferFrames = CHUNK_FRAMES * 2;
|
||||
cao.debugLog = DEBUG_LOG;
|
||||
cao.channels = 1;
|
||||
|
||||
// connect to microphone and speaker
|
||||
int status;
|
||||
struct CosmoAudio* ca;
|
||||
status = cosmoaudio_open(&ca, &cao);
|
||||
if (status != COSMOAUDIO_SUCCESS) {
|
||||
fprintf(stderr, "failed to open audio: %d\n", status);
|
||||
return 5;
|
||||
}
|
||||
|
||||
while (!g_done) {
|
||||
// read from microphone
|
||||
float buf32[CHUNK_FRAMES];
|
||||
cosmoaudio_poll(ca, (int[]){CHUNK_FRAMES}, 0);
|
||||
cosmoaudio_read(ca, buf32, CHUNK_FRAMES);
|
||||
short buf16[CHUNK_FRAMES];
|
||||
for (int i = 0; i < CHUNK_FRAMES; ++i)
|
||||
buf16[i] = toshort(buf32[i]);
|
||||
|
||||
// send to server
|
||||
if (write(client, buf16, CHUNK_FRAMES * sizeof(short)) == -1) {
|
||||
if (errno == EINTR && g_done)
|
||||
break;
|
||||
perror(remote_host);
|
||||
return 7;
|
||||
}
|
||||
|
||||
// print loudness in ascii
|
||||
char meter[21];
|
||||
double db = rms_to_db(rms(buf32, CHUNK_FRAMES));
|
||||
format_decibel_meter(meter, 20, db);
|
||||
printf("\r%s| %+6.2f dB", meter, db);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
// clean up resources
|
||||
cosmoaudio_close(ca);
|
||||
close(client);
|
||||
}
|
||||
+11
-18
@@ -54,9 +54,8 @@ EXAMPLES_DIRECTDEPS = \
|
||||
LIBC_NEXGEN32E \
|
||||
LIBC_NT_ADVAPI32 \
|
||||
LIBC_NT_IPHLPAPI \
|
||||
LIBC_NT_MEMORY \
|
||||
LIBC_NT_KERNEL32 \
|
||||
LIBC_NT_NTDLL \
|
||||
LIBC_NT_MEMORY \
|
||||
LIBC_NT_USER32 \
|
||||
LIBC_NT_WS2_32 \
|
||||
LIBC_PROC \
|
||||
@@ -64,6 +63,7 @@ EXAMPLES_DIRECTDEPS = \
|
||||
LIBC_SOCK \
|
||||
LIBC_STDIO \
|
||||
LIBC_STR \
|
||||
LIBC_SYSTEM \
|
||||
LIBC_SYSV \
|
||||
LIBC_SYSV_CALLS \
|
||||
LIBC_TESTLIB \
|
||||
@@ -79,11 +79,15 @@ EXAMPLES_DIRECTDEPS = \
|
||||
THIRD_PARTY_DOUBLECONVERSION \
|
||||
THIRD_PARTY_GDTOA \
|
||||
THIRD_PARTY_GETOPT \
|
||||
THIRD_PARTY_HACLSTAR \
|
||||
THIRD_PARTY_HIREDIS \
|
||||
THIRD_PARTY_LIBCXX \
|
||||
THIRD_PARTY_LIBCXXABI \
|
||||
THIRD_PARTY_LIBUNWIND \
|
||||
THIRD_PARTY_LINENOISE \
|
||||
THIRD_PARTY_LUA \
|
||||
THIRD_PARTY_MBEDTLS \
|
||||
THIRD_PARTY_MINIAUDIO \
|
||||
THIRD_PARTY_MUSL \
|
||||
THIRD_PARTY_NSYNC \
|
||||
THIRD_PARTY_NSYNC_MEM \
|
||||
@@ -94,12 +98,10 @@ EXAMPLES_DIRECTDEPS = \
|
||||
THIRD_PARTY_TZ \
|
||||
THIRD_PARTY_VQSORT \
|
||||
THIRD_PARTY_XED \
|
||||
THIRD_PARTY_LIBCXXABI \
|
||||
THIRD_PARTY_LIBUNWIND \
|
||||
THIRD_PARTY_ZLIB \
|
||||
TOOL_ARGS \
|
||||
TOOL_BUILD_LIB \
|
||||
TOOL_VIZ_LIB
|
||||
TOOL_VIZ_LIB \
|
||||
|
||||
EXAMPLES_DEPS := \
|
||||
$(call uniq,$(foreach x,$(EXAMPLES_DIRECTDEPS),$($(x))))
|
||||
@@ -108,11 +110,6 @@ o/$(MODE)/examples/examples.pkg: \
|
||||
$(EXAMPLES_OBJS) \
|
||||
$(foreach x,$(EXAMPLES_DIRECTDEPS),$($(x)_A).pkg)
|
||||
|
||||
o/$(MODE)/examples/unbourne.o: private \
|
||||
CPPFLAGS += \
|
||||
-DSTACK_FRAME_UNLIMITED \
|
||||
-fpie
|
||||
|
||||
o/$(MODE)/examples/%.dbg: \
|
||||
$(EXAMPLES_DEPS) \
|
||||
o/$(MODE)/examples/%.o \
|
||||
@@ -128,14 +125,6 @@ o/$(MODE)/examples/hellolua.dbg: \
|
||||
$(EXAMPLES_BOOTLOADER)
|
||||
@$(APELINK)
|
||||
|
||||
o/$(MODE)/examples/ispell.dbg: \
|
||||
$(EXAMPLES_DEPS) \
|
||||
o/$(MODE)/examples/ispell.o \
|
||||
o/$(MODE)/usr/share/dict/words.zip.o \
|
||||
o/$(MODE)/examples/examples.pkg \
|
||||
$(EXAMPLES_BOOTLOADER)
|
||||
@$(APELINK)
|
||||
|
||||
o/$(MODE)/examples/nesemu1.dbg: \
|
||||
$(EXAMPLES_DEPS) \
|
||||
o/$(MODE)/examples/nesemu1.o \
|
||||
@@ -150,6 +139,10 @@ o/$(MODE)/examples/picol.o: private \
|
||||
CPPFLAGS += \
|
||||
-DSTACK_FRAME_UNLIMITED
|
||||
|
||||
o/$(MODE)/examples/nesemu1.o: private \
|
||||
CPPFLAGS += \
|
||||
-O3
|
||||
|
||||
o/$(MODE)/examples/picol.dbg: \
|
||||
$(EXAMPLES_DEPS) \
|
||||
o/$(MODE)/examples/picol.o \
|
||||
|
||||
+3
-3
@@ -60,10 +60,10 @@ struct List {
|
||||
int count;
|
||||
};
|
||||
|
||||
atomic_uintptr_t list;
|
||||
_Atomic(struct List*) list;
|
||||
|
||||
void push(struct List* elem) {
|
||||
uintptr_t tip;
|
||||
struct List* tip;
|
||||
assert(!TAG(elem));
|
||||
for (tip = atomic_load_explicit(&list, memory_order_relaxed);;) {
|
||||
elem->next = (struct List*)PTR(tip);
|
||||
@@ -76,7 +76,7 @@ void push(struct List* elem) {
|
||||
}
|
||||
|
||||
struct List* pop(void) {
|
||||
uintptr_t tip;
|
||||
struct List* tip;
|
||||
struct List* elem;
|
||||
tip = atomic_load_explicit(&list, memory_order_relaxed);
|
||||
while ((elem = (struct List*)PTR(tip))) {
|
||||
|
||||
+353
@@ -0,0 +1,353 @@
|
||||
#if 0
|
||||
/*─────────────────────────────────────────────────────────────────╗
|
||||
│ To the extent possible under law, Justine Tunney has waived │
|
||||
│ all copyright and related or neighboring rights to this file, │
|
||||
│ as it is written in the following disclaimers: │
|
||||
│ • http://unlicense.org/ │
|
||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <iconv.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <termios.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/**
|
||||
* @fileoverview program for viewing bbs art files
|
||||
* @see https://github.com/blocktronics/artpacks
|
||||
* @see http://www.textfiles.com/art/
|
||||
*/
|
||||
|
||||
#define HELP \
|
||||
"Usage:\n\
|
||||
art [-b %d] [-f %s] [-t %s] FILE...\n\
|
||||
\n\
|
||||
Flags:\n\
|
||||
-b NUMBER specifies simulated modem baud rate, which defaults to\n\
|
||||
2400 since that was the most common modem speed in the\n\
|
||||
later half of the 1980s during the BBS golden age; you\n\
|
||||
could also say 300 for the slowest experience possible\n\
|
||||
or you could say 14.4k to get more of a 90's feel, and\n\
|
||||
there's also the infamous 56k to bring you back to y2k\n\
|
||||
-f CHARSET specifies charset of input bytes, where the default is\n\
|
||||
cp347 which means IBM Code Page 347 a.k.a. DOS\n\
|
||||
-t CHARSET specifies output charset used by your terminal, and it\n\
|
||||
defaults to utf8 a.k.a. thompson-pike encoding\n\
|
||||
\n\
|
||||
Supported charsets:\n\
|
||||
utf8, ascii, wchar_t, ucs2be, ucs2le, utf16be, utf16le, ucs4be,\n\
|
||||
ucs4le, utf16, ucs4, ucs2, eucjp, shiftjis, iso2022jp, gb18030, gbk,\n\
|
||||
gb2312, big5, euckr, iso88591, latin1, iso88592, iso88593, iso88594,\n\
|
||||
iso88595, iso88596, iso88597, iso88598, iso88599, iso885910,\n\
|
||||
iso885911, iso885913, iso885914, iso885915, iso885916, cp1250,\n\
|
||||
windows1250, cp1251, windows1251, cp1252, windows1252, cp1253,\n\
|
||||
windows1253, cp1254, windows1254, cp1255, windows1255, cp1256,\n\
|
||||
windows1256, cp1257, windows1257, cp1258, windows1258, koi8r, koi8u,\n\
|
||||
cp437, cp850, cp866, ibm1047, cp1047.\n\
|
||||
\n\
|
||||
See also:\n\
|
||||
http://www.textfiles.com/art/\n\
|
||||
https://github.com/blocktronics/artpacks\n\
|
||||
\n"
|
||||
|
||||
#define INBUFSZ 256
|
||||
#define OUBUFSZ (INBUFSZ * 6)
|
||||
#define SLIT(s) ((unsigned)s[3] << 24 | s[2] << 16 | s[1] << 8 | s[0])
|
||||
|
||||
// "When new technology comes out, people don't all buy it right away.
|
||||
// If what they have works, some will wait until it doesn't. A few
|
||||
// people do get the latest though. In 1984 2400 baud modems became
|
||||
// available, so some people had them, but many didn't. A BBS list
|
||||
// from 1986 shows operators were mostly 300 and 1200, but some were
|
||||
// using 2400. The next 5 years were the hayday of the 2400."
|
||||
//
|
||||
// https://forum.vcfed.org/index.php?threads/the-2400-baud-modem.44241/
|
||||
|
||||
int baud_rate = 2400; // -b 2400
|
||||
const char* from_charset = "CP437"; // -f CP437
|
||||
const char* to_charset = "UTF-8"; // -t UTF-8
|
||||
|
||||
volatile sig_atomic_t done;
|
||||
|
||||
void on_signal(int sig) {
|
||||
done = 1;
|
||||
(void)sig;
|
||||
}
|
||||
|
||||
void print(const char* s) {
|
||||
(void)!write(STDOUT_FILENO, s, strlen(s));
|
||||
}
|
||||
|
||||
int encode_character(char output[8], const char* codec, wchar_t character) {
|
||||
size_t inbytesleft = sizeof(wchar_t);
|
||||
size_t outbytesleft = 7;
|
||||
char* inbuf = (char*)&character;
|
||||
char* outbuf = output;
|
||||
iconv_t cd = iconv_open(codec, "wchar_t");
|
||||
if (cd == (iconv_t)-1)
|
||||
return -1;
|
||||
size_t result = iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft);
|
||||
iconv_close(cd);
|
||||
if (result == (size_t)-1)
|
||||
return -1;
|
||||
*outbuf = '\0';
|
||||
return 7 - outbytesleft;
|
||||
}
|
||||
|
||||
void append_replacement_character(char** b) {
|
||||
int n = encode_character(*b, to_charset, 0xFFFD);
|
||||
if (n == -1)
|
||||
n = encode_character(*b, to_charset, '?');
|
||||
if (n != -1)
|
||||
*b += n;
|
||||
}
|
||||
|
||||
int compare_time(struct timespec a, struct timespec b) {
|
||||
int cmp;
|
||||
if (!(cmp = (a.tv_sec > b.tv_sec) - (a.tv_sec < b.tv_sec)))
|
||||
cmp = (a.tv_nsec > b.tv_nsec) - (a.tv_nsec < b.tv_nsec);
|
||||
return cmp;
|
||||
}
|
||||
|
||||
struct timespec add_time(struct timespec x, struct timespec y) {
|
||||
x.tv_sec += y.tv_sec;
|
||||
x.tv_nsec += y.tv_nsec;
|
||||
if (x.tv_nsec >= 1000000000) {
|
||||
x.tv_nsec -= 1000000000;
|
||||
x.tv_sec += 1;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
struct timespec subtract_time(struct timespec a, struct timespec b) {
|
||||
a.tv_sec -= b.tv_sec;
|
||||
if (a.tv_nsec < b.tv_nsec) {
|
||||
a.tv_nsec += 1000000000;
|
||||
a.tv_sec--;
|
||||
}
|
||||
a.tv_nsec -= b.tv_nsec;
|
||||
return a;
|
||||
}
|
||||
|
||||
struct timespec fromnanos(long long x) {
|
||||
struct timespec ts;
|
||||
ts.tv_sec = x / 1000000000;
|
||||
ts.tv_nsec = x % 1000000000;
|
||||
return ts;
|
||||
}
|
||||
|
||||
void process_file(const char* path, int fd, iconv_t cd) {
|
||||
size_t carry = 0;
|
||||
struct timespec next;
|
||||
char input_buffer[INBUFSZ];
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &next);
|
||||
|
||||
for (;;) {
|
||||
|
||||
// read from file
|
||||
ssize_t bytes_read = read(fd, input_buffer + carry, INBUFSZ - carry);
|
||||
if (!bytes_read)
|
||||
return;
|
||||
if (bytes_read == -1) {
|
||||
perror(path);
|
||||
done = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
// modernize character set
|
||||
char* input_ptr = input_buffer;
|
||||
size_t input_left = carry + bytes_read;
|
||||
char output_buffer[OUBUFSZ];
|
||||
char* output_ptr = output_buffer;
|
||||
size_t output_left = OUBUFSZ;
|
||||
size_t ir = iconv(cd, &input_ptr, &input_left, &output_ptr, &output_left);
|
||||
carry = 0;
|
||||
if (ir == (size_t)-1) {
|
||||
if (errno == EINVAL) {
|
||||
// incomplete multibyte sequence encountered
|
||||
memmove(input_buffer, input_ptr, input_left);
|
||||
carry = input_left;
|
||||
} else if (errno == EILSEQ && input_left) {
|
||||
// EILSEQ means either
|
||||
// 1. illegal input sequence encountered
|
||||
// 2. code not encodable in output codec
|
||||
//
|
||||
// so we skip one byte of input, and insert � or ? in the output
|
||||
// this isn't the most desirable behavior, but it is the best we
|
||||
// can do, since we don't know specifics about the codecs in use
|
||||
//
|
||||
// unlike glibc cosmo's iconv implementation may handle case (2)
|
||||
// automatically by inserting an asterisk in place of a sequence
|
||||
++input_ptr;
|
||||
--input_left;
|
||||
memmove(input_buffer, input_ptr, input_left);
|
||||
carry = input_left;
|
||||
if (output_left >= 8)
|
||||
append_replacement_character(&output_ptr);
|
||||
} else {
|
||||
perror(path);
|
||||
done = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// write to terminal
|
||||
for (char* p = output_buffer; p < output_ptr; p++) {
|
||||
if (done)
|
||||
return;
|
||||
|
||||
(void)!write(STDOUT_FILENO, p, 1);
|
||||
|
||||
// allow arrow keys to change baud rate
|
||||
int have;
|
||||
if (ioctl(STDIN_FILENO, FIONREAD, &have)) {
|
||||
perror("ioctl");
|
||||
done = 1;
|
||||
return;
|
||||
}
|
||||
if (have > 0) {
|
||||
char key[4] = {0};
|
||||
if (read(STDIN_FILENO, key, sizeof(key)) > 0) {
|
||||
if (SLIT(key) == SLIT("\33[A") || // up
|
||||
SLIT(key) == SLIT("\33[C")) { // right
|
||||
baud_rate *= 1.4;
|
||||
} else if (SLIT(key) == SLIT("\33[B") || // down
|
||||
SLIT(key) == SLIT("\33[D")) { // left
|
||||
baud_rate *= 0.6;
|
||||
}
|
||||
if (baud_rate < 3)
|
||||
baud_rate = 3;
|
||||
if (baud_rate > 1000000000)
|
||||
baud_rate = 1000000000;
|
||||
}
|
||||
}
|
||||
|
||||
// insert artificial delay for one byte. we divide by 10 to convert
|
||||
// bits to bytes, because that is how many bits 8-N-1 encoding used
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
next = add_time(next, fromnanos(1e9 / (baud_rate / 10.)));
|
||||
if (compare_time(next, now) > 0) {
|
||||
struct timespec sleep = subtract_time(next, now);
|
||||
nanosleep(&sleep, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "hb:f:t:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'b': {
|
||||
char* endptr;
|
||||
double rate = strtod(optarg, &endptr);
|
||||
if (*endptr == 'k') {
|
||||
rate *= 1e3;
|
||||
++endptr;
|
||||
} else if (*endptr == 'm') {
|
||||
rate *= 1e6;
|
||||
++endptr;
|
||||
}
|
||||
if (*endptr || baud_rate <= 0) {
|
||||
fprintf(stderr, "%s: invalid baud rate: %s\n", argv[0], optarg);
|
||||
exit(1);
|
||||
}
|
||||
baud_rate = rate;
|
||||
break;
|
||||
}
|
||||
case 'f':
|
||||
from_charset = optarg;
|
||||
break;
|
||||
case 't':
|
||||
to_charset = optarg;
|
||||
break;
|
||||
case 'h':
|
||||
fprintf(stderr, HELP, baud_rate, from_charset, to_charset);
|
||||
exit(0);
|
||||
default:
|
||||
fprintf(stderr, "protip: pass the -h flag for help\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if (optind == argc) {
|
||||
fprintf(stderr, "%s: missing operand\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// create character transcoder
|
||||
iconv_t cd = iconv_open(to_charset, from_charset);
|
||||
if (cd == (iconv_t)-1) {
|
||||
fprintf(stderr, "error: conversion from %s to %s not supported\n",
|
||||
from_charset, to_charset);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// catch ctrl-c
|
||||
signal(SIGINT, on_signal);
|
||||
|
||||
// don't wait until newline to read() keystrokes
|
||||
struct termios t;
|
||||
if (!tcgetattr(STDIN_FILENO, &t)) {
|
||||
struct termios t2 = t;
|
||||
t2.c_lflag &= ~(ICANON | ECHO);
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &t2);
|
||||
}
|
||||
|
||||
// Process each file specified on the command line
|
||||
for (int i = optind; i < argc && !done; i++) {
|
||||
|
||||
// open file
|
||||
int fd = open(argv[i], O_RDONLY);
|
||||
if (fd == -1) {
|
||||
perror(argv[i]);
|
||||
break;
|
||||
}
|
||||
|
||||
// wait between files
|
||||
if (i > optind)
|
||||
sleep(1);
|
||||
|
||||
print("\33[?25l"); // hide cursor
|
||||
print("\33[H"); // move cursor to top-left
|
||||
print("\33[J"); // erase display forward
|
||||
print("\33[1;24r"); // set scrolling region to first 24 lines
|
||||
print("\33[?7h"); // enable auto-wrap mode
|
||||
print("\33[?3l"); // 80 column mode (deccolm) vt100
|
||||
print("\33[H"); // move cursor to top-left, again
|
||||
|
||||
// get busy
|
||||
process_file(argv[i], fd, cd);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
// cleanup
|
||||
iconv_close(cd);
|
||||
|
||||
print("\33[s"); // save cursor position
|
||||
print("\33[?25h"); // show cursor
|
||||
print("\33[0m"); // reset text attributes (color, bold, etc.)
|
||||
print("\33[?1049l"); // exit alternate screen mode
|
||||
print("\33(B"); // exit line drawing and other alt charset modes
|
||||
print("\33[r"); // reset scrolling region
|
||||
print("\33[?2004l"); // turn off bracketed paste mode
|
||||
print("\33[4l"); // exit insert mode
|
||||
print("\33[?1l\33>"); // exit application keypad mode
|
||||
print("\33[?7h"); // reset text wrapping mode
|
||||
print("\33[?12l"); // reset cursor blinking mode
|
||||
print("\33[?6l"); // reset origin mode
|
||||
print("\33[20l"); // reset auto newline mode
|
||||
print("\33[u"); // restore cursor position
|
||||
|
||||
// restore terminal
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &t);
|
||||
}
|
||||
@@ -0,0 +1,353 @@
|
||||
// -*- mode:c; indent-tabs-mode:nil; c-basic-offset:4 -*-
|
||||
// vi: set et ft=c ts=4 sts=4 sw=4 fenc=utf-8
|
||||
|
||||
// asteroids by tsotchke
|
||||
// https://github.com/tsotchke/asteroids
|
||||
|
||||
// clang-format off
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <termios.h>
|
||||
#include <sys/select.h>
|
||||
|
||||
#define SCREEN_WIDTH 80
|
||||
#define SCREEN_HEIGHT 24
|
||||
#define MAX_ASTEROIDS 5
|
||||
#define MAX_BULLETS 5
|
||||
|
||||
typedef struct {
|
||||
float x, y;
|
||||
} Vector2;
|
||||
|
||||
typedef struct {
|
||||
Vector2 position;
|
||||
Vector2 velocity;
|
||||
float angle;
|
||||
float radius;
|
||||
} GameObject;
|
||||
|
||||
GameObject spaceship;
|
||||
GameObject asteroids[MAX_ASTEROIDS];
|
||||
GameObject bullets[MAX_BULLETS];
|
||||
|
||||
int score = 0;
|
||||
time_t startTime;
|
||||
int isGameOver = 0;
|
||||
int shouldExit = 0;
|
||||
int finalTime = 0; // To store final time at game over
|
||||
char display[SCREEN_HEIGHT][SCREEN_WIDTH];
|
||||
|
||||
// Function to clear the screen buffer
|
||||
void clearDisplay() {
|
||||
memset(display, ' ', sizeof(display));
|
||||
}
|
||||
|
||||
// Function to draw a pixel on the screen
|
||||
void drawPixel(int x, int y) {
|
||||
if (x >= 0 && x < SCREEN_WIDTH && y >= 0 && y < SCREEN_HEIGHT) {
|
||||
display[y][x] = '*';
|
||||
}
|
||||
}
|
||||
|
||||
// Function to draw a line using Bresenham's algorithm
|
||||
void drawLine(int x1, int y1, int x2, int y2) {
|
||||
int dx = abs(x2 - x1), sx = (x1 < x2) ? 1 : -1;
|
||||
int dy = -abs(y2 - y1), sy = (y1 < y2) ? 1 : -1;
|
||||
int error = dx + dy, e2;
|
||||
|
||||
while (1) {
|
||||
drawPixel(x1, y1);
|
||||
if (x1 == x2 && y1 == y2) break;
|
||||
e2 = 2 * error;
|
||||
if (e2 >= dy) { error += dy; x1 += sx; }
|
||||
if (e2 <= dx) { error += dx; y1 += sy; }
|
||||
}
|
||||
}
|
||||
|
||||
// Function to draw a circle
|
||||
void drawCircle(int centerX, int centerY, int radius) {
|
||||
int x = radius - 1, y = 0, dx = 1, dy = 1, err = dx - (radius << 1);
|
||||
while (x >= y) {
|
||||
drawPixel(centerX + x, centerY + y);
|
||||
drawPixel(centerX + y, centerY + x);
|
||||
drawPixel(centerX - y, centerY + x);
|
||||
drawPixel(centerX - x, centerY + y);
|
||||
drawPixel(centerX - x, centerY - y);
|
||||
drawPixel(centerX - y, centerY - x);
|
||||
drawPixel(centerX + y, centerY - x);
|
||||
drawPixel(centerX + x, centerY - y);
|
||||
|
||||
if (err <= 0) {
|
||||
y++;
|
||||
err += dy;
|
||||
dy += 2;
|
||||
}
|
||||
if (err > 0) {
|
||||
x--;
|
||||
dx += 2;
|
||||
err += dx - (radius << 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize a game object
|
||||
void initializeGameObject(GameObject *obj, float x, float y, float angle, float radius) {
|
||||
obj->position = (Vector2){x, y};
|
||||
obj->velocity = (Vector2){0, 0};
|
||||
obj->angle = angle;
|
||||
obj->radius = radius;
|
||||
}
|
||||
|
||||
// Wrap position of the spaceship and asteroids within screen bounds
|
||||
void wrapPosition(Vector2 *pos) {
|
||||
if (pos->x < 0) pos->x = SCREEN_WIDTH - 1;
|
||||
if (pos->x >= SCREEN_WIDTH) pos->x = 0;
|
||||
if (pos->y < 0) pos->y = SCREEN_HEIGHT - 1;
|
||||
if (pos->y >= SCREEN_HEIGHT) pos->y = 0;
|
||||
}
|
||||
|
||||
// Check if two game objects are colliding
|
||||
int checkCollision(GameObject *a, GameObject *b) {
|
||||
float deltaX = a->position.x - b->position.x;
|
||||
float deltaY = a->position.y - b->position.y;
|
||||
return sqrt(deltaX * deltaX + deltaY * deltaY) < (a->radius + b->radius);
|
||||
}
|
||||
|
||||
// Initialize game state
|
||||
void initGame() {
|
||||
score = 0; // Reset the score
|
||||
initializeGameObject(&spaceship, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 0, 2);
|
||||
|
||||
for (int i = 0; i < MAX_ASTEROIDS; i++) {
|
||||
initializeGameObject(&asteroids[i],
|
||||
rand() % SCREEN_WIDTH,
|
||||
rand() % SCREEN_HEIGHT,
|
||||
0,
|
||||
2 + rand() % 3);
|
||||
asteroids[i].velocity.x = ((float)rand() / RAND_MAX) * 2 - 1;
|
||||
asteroids[i].velocity.y = ((float)rand() / RAND_MAX) * 2 - 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_BULLETS; i++) {
|
||||
bullets[i].position.x = -1; // Mark bullet as inactive
|
||||
bullets[i].position.y = -1;
|
||||
}
|
||||
|
||||
startTime = time(NULL);
|
||||
isGameOver = 0;
|
||||
finalTime = 0; // Reset final time
|
||||
}
|
||||
|
||||
// Draw the spaceship on the screen
|
||||
void drawSpaceship() {
|
||||
int x = (int)spaceship.position.x;
|
||||
int y = (int)spaceship.position.y;
|
||||
int size = 3;
|
||||
|
||||
float cosAngle = cos(spaceship.angle);
|
||||
float sinAngle = sin(spaceship.angle);
|
||||
|
||||
int x1 = x + size * cosAngle;
|
||||
int y1 = y + size * sinAngle;
|
||||
int x2 = x + size * cos(spaceship.angle + 2.5);
|
||||
int y2 = y + size * sin(spaceship.angle + 2.5);
|
||||
int x3 = x + size * cos(spaceship.angle - 2.5);
|
||||
int y3 = y + size * sin(spaceship.angle - 2.5);
|
||||
|
||||
drawLine(x1, y1, x2, y2);
|
||||
drawLine(x2, y2, x3, y3);
|
||||
drawLine(x3, y3, x1, y1);
|
||||
}
|
||||
|
||||
// Draw all entities on the screen
|
||||
void drawEntities(GameObject *entities, int count, void (*drawFunc)(GameObject *)) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
drawFunc(&entities[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw a bullet on the screen
|
||||
void drawBullet(GameObject *bullet) { // Changed to non-const
|
||||
if (bullet->position.x >= 0) {
|
||||
drawPixel((int)bullet->position.x, (int)bullet->position.y);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw an asteroid on the screen
|
||||
void drawAsteroid(GameObject *asteroid) { // Changed to non-const
|
||||
drawCircle((int)asteroid->position.x, (int)asteroid->position.y, (int)asteroid->radius);
|
||||
}
|
||||
|
||||
// Refresh the display
|
||||
void updateDisplay() {
|
||||
clearDisplay();
|
||||
if (!isGameOver) {
|
||||
drawSpaceship();
|
||||
drawEntities(asteroids, MAX_ASTEROIDS, drawAsteroid);
|
||||
drawEntities(bullets, MAX_BULLETS, drawBullet);
|
||||
}
|
||||
|
||||
// Print the screen buffer
|
||||
printf("\033[H");
|
||||
for (int y = 0; y < SCREEN_HEIGHT; y++) {
|
||||
for (int x = 0; x < SCREEN_WIDTH; x++) {
|
||||
putchar(display[y][x]);
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
// Display score and elapsed time
|
||||
time_t currentTime = time(NULL);
|
||||
int elapsedTime = isGameOver ? finalTime : (currentTime - startTime);
|
||||
printf("Score: %d | Time: %02d:%02d | %s\n", score, elapsedTime / 60, elapsedTime % 60, isGameOver ? "Game Over!" : " ");
|
||||
}
|
||||
|
||||
// Update the position of game objects
|
||||
void updateGameObject(GameObject *obj, int isBullet) {
|
||||
obj->position.x += obj->velocity.x;
|
||||
obj->position.y += obj->velocity.y;
|
||||
|
||||
// If it's a bullet, check if it's out of bounds
|
||||
if (isBullet) {
|
||||
if (obj->position.x < 0 || obj->position.x >= SCREEN_WIDTH || obj->position.y < 0 || obj->position.y >= SCREEN_HEIGHT) {
|
||||
obj->position.x = -1; // Deactivate bullet
|
||||
obj->position.y = -1;
|
||||
}
|
||||
} else {
|
||||
wrapPosition(&obj->position);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the game state
|
||||
void updateGame() {
|
||||
if (isGameOver) return;
|
||||
|
||||
// Update spaceship and apply friction
|
||||
updateGameObject(&spaceship, 0); // 0 indicates it's not a bullet
|
||||
spaceship.velocity.x *= 0.98;
|
||||
spaceship.velocity.y *= 0.98;
|
||||
|
||||
// Move asteroids and check for collisions
|
||||
for (int i = 0; i < MAX_ASTEROIDS; i++) {
|
||||
updateGameObject(&asteroids[i], 0);
|
||||
if (checkCollision(&spaceship, &asteroids[i])) {
|
||||
isGameOver = 1;
|
||||
finalTime = time(NULL) - startTime;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Update bullet positions
|
||||
for (int i = 0; i < MAX_BULLETS; i++) {
|
||||
if (bullets[i].position.x >= 0) {
|
||||
updateGameObject(&bullets[i], 1); // 1 indicates it's a bullet
|
||||
}
|
||||
}
|
||||
|
||||
// Check for bullet collisions with asteroids
|
||||
for (int i = 0; i < MAX_BULLETS; i++) {
|
||||
if (bullets[i].position.x >= 0) {
|
||||
for (int j = 0; j < MAX_ASTEROIDS; j++) {
|
||||
if (checkCollision(&bullets[i], &asteroids[j])) {
|
||||
bullets[i].position.x = -1; // Deactivate bullet
|
||||
bullets[i].position.y = -1;
|
||||
asteroids[j].position.x = rand() % SCREEN_WIDTH;
|
||||
asteroids[j].position.y = rand() % SCREEN_HEIGHT;
|
||||
score += 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fire a bullet
|
||||
void shootBullet() {
|
||||
for (int i = 0; i < MAX_BULLETS; i++) {
|
||||
if (bullets[i].position.x < 0) {
|
||||
bullets[i].position = spaceship.position;
|
||||
bullets[i].velocity.x = cos(spaceship.angle) * 2;
|
||||
bullets[i].velocity.y = sin(spaceship.angle) * 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if a key was hit
|
||||
int isKeyHit() {
|
||||
struct timeval tv = { 0L, 0L };
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(0, &fds);
|
||||
return select(1, &fds, NULL, NULL, &tv);
|
||||
}
|
||||
|
||||
// Configure terminal settings
|
||||
void configureTerminal(struct termios *old_tio, struct termios *new_tio) {
|
||||
tcgetattr(STDIN_FILENO, old_tio);
|
||||
*new_tio = *old_tio;
|
||||
new_tio->c_lflag &= (~ICANON & ~ECHO);
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, new_tio);
|
||||
}
|
||||
|
||||
// Restore terminal settings
|
||||
void restoreTerminal(struct termios *old_tio) {
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, old_tio);
|
||||
}
|
||||
|
||||
void onSignal(int sig) {
|
||||
shouldExit = 1;
|
||||
}
|
||||
|
||||
// Main game loop
|
||||
int main() {
|
||||
signal(SIGINT, onSignal); // Capture ^C
|
||||
srand(time(NULL)); // Seed the random number generator
|
||||
initGame(); // Initialize the game state
|
||||
|
||||
struct termios old_tio, new_tio;
|
||||
configureTerminal(&old_tio, &new_tio);
|
||||
|
||||
printf("\033[?25l"); // Hide the cursor
|
||||
|
||||
while (!shouldExit) {
|
||||
if (isKeyHit()) {
|
||||
char input = getchar();
|
||||
if (input == 27) { // ESC key
|
||||
if (getchar() == '[') { // Handle arrow keys
|
||||
switch (getchar()) {
|
||||
case 'A': // Up arrow
|
||||
spaceship.velocity.x += cos(spaceship.angle) * 0.2;
|
||||
spaceship.velocity.y += sin(spaceship.angle) * 0.2;
|
||||
break;
|
||||
case 'B': // Down arrow
|
||||
spaceship.velocity.x -= cos(spaceship.angle) * 0.2;
|
||||
spaceship.velocity.y -= sin(spaceship.angle) * 0.2;
|
||||
break;
|
||||
case 'D': spaceship.angle -= 0.2; break; // Left arrow
|
||||
case 'C': spaceship.angle += 0.2; break; // Right arrow
|
||||
}
|
||||
}
|
||||
} else if (input == ' ') {
|
||||
shootBullet(); // Fire a bullet
|
||||
} else if (input == 'q') {
|
||||
break; // Quit the game
|
||||
} else if (input == 'r' && isGameOver) {
|
||||
initGame(); // Restart the game
|
||||
}
|
||||
}
|
||||
|
||||
updateGame(); // Update game state
|
||||
updateDisplay(); // Refresh the display
|
||||
usleep(50000); // Wait for 50ms (20 FPS)
|
||||
}
|
||||
|
||||
printf("\033[?25h"); // Show the cursor
|
||||
restoreTerminal(&old_tio); // Restore terminal settings
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
// clang-format off
|
||||
//
|
||||
// this program implements ancient roman cryptography
|
||||
//
|
||||
// example usage:
|
||||
//
|
||||
// make -j o//examples/caesar # compile
|
||||
// o//examples/caesar 23 <file.txt | # encrypt
|
||||
// o//examples/caesar # decrypt
|
||||
//
|
||||
|
||||
/*
|
||||
* Copyright (c) 1989, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Rick Adams.
|
||||
*
|
||||
* Authors:
|
||||
* Stan King, John Eldridge, based on algorithm suggested by
|
||||
* Bob Morris
|
||||
* 29-Sep-82
|
||||
*
|
||||
* 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.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define LINELENGTH 2048
|
||||
#define ROTATE(ch, perm) \
|
||||
isascii(ch) ? ( \
|
||||
isupper(ch) ? ('A' + (ch - 'A' + perm) % 26) : \
|
||||
islower(ch) ? ('a' + (ch - 'a' + perm) % 26) : ch) : ch
|
||||
|
||||
/*
|
||||
* letter frequencies (taken from some unix(tm) documentation)
|
||||
* (unix is a trademark of Bell Laboratories)
|
||||
*/
|
||||
static double stdf[26] = {
|
||||
7.97, 1.35, 3.61, 4.78, 12.37, 2.01, 1.46, 4.49, 6.39, 0.04,
|
||||
0.42, 3.81, 2.69, 5.92, 6.96, 2.91, 0.08, 6.63, 8.77, 9.68,
|
||||
2.62, 0.81, 1.88, 0.23, 2.07, 0.06,
|
||||
};
|
||||
|
||||
static void
|
||||
printit(char *arg)
|
||||
{
|
||||
int ch, rot;
|
||||
|
||||
if ((rot = atoi(arg)) < 0) {
|
||||
fprintf(stderr, "caesar: bad rotation value.\n");
|
||||
exit(1);
|
||||
}
|
||||
while ((ch = getchar()) != EOF)
|
||||
putchar(ROTATE(ch, rot));
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int ch, dot, i, nread, winnerdot = 0;
|
||||
unsigned char inbuf[LINELENGTH];
|
||||
int obs[26], try, winner;
|
||||
|
||||
if (argc > 1)
|
||||
printit(argv[1]);
|
||||
|
||||
/* adjust frequency table to weight low probs REAL low */
|
||||
for (i = 0; i < 26; ++i)
|
||||
stdf[i] = log(stdf[i]) + log(26.0 / 100.0);
|
||||
|
||||
/* zero out observation table */
|
||||
bzero(obs, 26 * sizeof(int));
|
||||
|
||||
if ((nread = read(STDIN_FILENO, inbuf, LINELENGTH)) < 0) {
|
||||
perror("caesar");
|
||||
exit(1);
|
||||
}
|
||||
for (i = nread; i--;) {
|
||||
ch = inbuf[i];
|
||||
if (isascii(ch)) {
|
||||
if (islower(ch))
|
||||
++obs[ch - 'a'];
|
||||
else if (isupper(ch))
|
||||
++obs[ch - 'A'];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* now "dot" the freqs with the observed letter freqs
|
||||
* and keep track of best fit
|
||||
*/
|
||||
for (try = winner = 0; try < 26; ++try) { /* += 13) { */
|
||||
dot = 0;
|
||||
for (i = 0; i < 26; i++)
|
||||
dot += obs[i] * stdf[(i + try) % 26];
|
||||
/* initialize winning score */
|
||||
if (try == 0)
|
||||
winnerdot = dot;
|
||||
if (dot > winnerdot) {
|
||||
/* got a new winner! */
|
||||
winner = try;
|
||||
winnerdot = dot;
|
||||
}
|
||||
}
|
||||
|
||||
while (nread) {
|
||||
for (i = 0; i < nread; ++i) {
|
||||
ch = inbuf[i];
|
||||
putchar(ROTATE(ch, winner));
|
||||
}
|
||||
if ((nread = read(STDIN_FILENO, inbuf, LINELENGTH)) < 0) {
|
||||
perror("caesar");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -89,7 +89,7 @@ int main(int argc, char *argv[]) {
|
||||
// not the case when you use sigprocmask() to block signals which is
|
||||
// useful for kicking the can down the road.
|
||||
WRITE("doing cpu task...\n");
|
||||
for (volatile int i = 0; i < INT_MAX / 3; ++i) {
|
||||
for (volatile int i = 0; i < (int)(INT_MAX * .4); ++i) {
|
||||
if (gotsig) {
|
||||
WRITE("\rgot ctrl+c asynchronously\n");
|
||||
exit(0);
|
||||
|
||||
@@ -259,7 +259,7 @@ int main(int argc, char *argv[]) {
|
||||
unassert(!sigaction(SIGTERM, &sa, 0));
|
||||
|
||||
// you can pass the number of threads you want as the first command arg
|
||||
threads = argc > 1 ? atoi(argv[1]) : __get_cpu_count();
|
||||
threads = argc > 1 ? atoi(argv[1]) : cosmo_cpu_count();
|
||||
if (!(1 <= threads && threads <= 100000)) {
|
||||
tinyprint(2, "error: invalid number of threads\n", NULL);
|
||||
exit(1);
|
||||
@@ -337,7 +337,7 @@ int main(int argc, char *argv[]) {
|
||||
sigaddset(&block, SIGQUIT);
|
||||
pthread_attr_t attr;
|
||||
unassert(!pthread_attr_init(&attr));
|
||||
unassert(!pthread_attr_setstacksize(&attr, 65536));
|
||||
unassert(!pthread_attr_setstacksize(&attr, 65536 - getpagesize()));
|
||||
unassert(!pthread_attr_setguardsize(&attr, getpagesize()));
|
||||
unassert(!pthread_attr_setsigmask_np(&attr, &block));
|
||||
unassert(!pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0));
|
||||
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
#/*─────────────────────────────────────────────────────────────────╗
|
||||
│ To the extent possible under law, Justine Tunney has waived │
|
||||
│ all copyright and related or neighboring rights to this file, │
|
||||
│ as it is written in the following disclaimers: │
|
||||
│ • http://unlicense.org/ │
|
||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// POSIX kill command
|
||||
//
|
||||
// - kill -l
|
||||
// - kill -9 123 456
|
||||
// - kill -KILL 123 456
|
||||
// - kill -SIGKILL 123 456
|
||||
// - kill -s SIGKILL 123 456
|
||||
//
|
||||
|
||||
const char kSignals[] = {
|
||||
SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGBUS,
|
||||
SIGFPE, SIGKILL, SIGUSR1, SIGSEGV, SIGUSR2, SIGPIPE, SIGALRM,
|
||||
SIGTERM, SIGCHLD, SIGCONT, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU,
|
||||
SIGURG, SIGXCPU, SIGXFSZ, SIGVTALRM, SIGPROF, SIGWINCH, SIGSYS,
|
||||
};
|
||||
|
||||
const char kSignalNames[][8] = {
|
||||
"HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "BUS",
|
||||
"FPE", "KILL", "USR1", "SEGV", "USR2", "PIPE", "ALRM",
|
||||
"TERM", "CHLD", "CONT", "STOP", "TSTP", "TTIN", "TTOU",
|
||||
"URG", "XCPU", "XFSZ", "VTALRM", "PROF", "WINCH", "SYS",
|
||||
};
|
||||
|
||||
void list_signals(void) {
|
||||
for (int i = 0; i < sizeof(kSignals); i += 4) {
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
if (i + j < sizeof(kSignals)) {
|
||||
int sig = kSignals[i + j];
|
||||
printf("%2d) SIG%-7s", sig, kSignalNames[i + j]);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
const char *prog = argv[0];
|
||||
if (!prog)
|
||||
prog = "kill";
|
||||
|
||||
int arg = 1;
|
||||
const char *sigstr = 0;
|
||||
if (arg < argc) {
|
||||
if (argv[arg][0] == '-') {
|
||||
if (argv[arg][1] == 's' && !argv[arg][2]) {
|
||||
if (++arg < argc)
|
||||
sigstr = argv[arg++];
|
||||
} else if (argv[arg][1] == 'l' && !argv[arg][2]) {
|
||||
list_signals();
|
||||
exit(0);
|
||||
} else {
|
||||
sigstr = argv[arg++] + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (arg == argc) {
|
||||
fprintf(stderr, "%s: missing operand\n", prog);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int sig;
|
||||
if (!sigstr) {
|
||||
sig = SIGTERM;
|
||||
} else {
|
||||
sig = atoi(sigstr);
|
||||
if (sig) {
|
||||
if (!(1 <= sig && sig <= NSIG)) {
|
||||
fprintf(stderr, "%s: bad signal number: %d\n", prog, sig);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
if (toupper(sigstr[0]) == 'S' && //
|
||||
toupper(sigstr[1]) == 'I' && //
|
||||
toupper(sigstr[2]) == 'G')
|
||||
sigstr += 3;
|
||||
for (int i = 0; i < sizeof(kSignals); ++i)
|
||||
if (!strcasecmp(sigstr, kSignalNames[i]))
|
||||
sig = kSignals[i];
|
||||
if (!sig) {
|
||||
fprintf(stderr, "%s: bad signal name: %s\n", prog, sigstr);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int rc = 0;
|
||||
for (int i = arg; i < argc; ++i) {
|
||||
int pid = atoi(argv[i]);
|
||||
if (kill(pid, sig)) {
|
||||
fprintf(stderr, "%s: %d: %s\n", prog, pid, strerror(errno));
|
||||
rc = 1;
|
||||
}
|
||||
}
|
||||
exit(rc);
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
#if 0
|
||||
/*─────────────────────────────────────────────────────────────────╗
|
||||
│ To the extent possible under law, Justine Tunney has waived │
|
||||
│ all copyright and related or neighboring rights to this file, │
|
||||
│ as it is written in the following disclaimers: │
|
||||
│ • http://unlicense.org/ │
|
||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include <cosmo.h>
|
||||
#include <malloc.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// cosmo bulitin memory leak detector demo
|
||||
//
|
||||
// you can use cosmo's memory leak detector to track backtraces for your
|
||||
// memory allocations and then print error messages using addr2line. you
|
||||
// generally want to have your app override malloc with your own malloc.
|
||||
// you can use libc's builtin dlmalloc implementation like this as well.
|
||||
// using this leak detector makes malloc() go 10x slower and allocations
|
||||
// will each use 200 bytes of additional memory. if you don't care about
|
||||
// backtraces, then you can create a leak detector for free by using the
|
||||
// CheckForMemoryLeaks() function at the end of main().
|
||||
//
|
||||
// $ make -j o//examples/memleak_backtrace
|
||||
// $ o//examples/memleak_backtrace
|
||||
// let's demo the builtin memory leak detector
|
||||
// error: memory leak detected 0x778dd0979fc0
|
||||
// 42175f4 in malloc at /home/jart/cosmo/examples/memleak_backtrace.c:34
|
||||
// 42049b7 in main at /home/jart/cosmo/examples/memleak_backtrace.c:67
|
||||
// 4204db4 in cosmo at /home/jart/cosmo/libc/runtime/cosmo.S:103
|
||||
// 42045d9 in _start at /home/jart/cosmo/libc/crt/crt.S:128
|
||||
// error: memory leak detected 0x778dd0979fe0
|
||||
// 42175f4 in malloc at /home/jart/cosmo/examples/memleak_backtrace.c:34
|
||||
// 42049aa in main at /home/jart/cosmo/examples/memleak_backtrace.c:66
|
||||
// 4204db4 in cosmo at /home/jart/cosmo/libc/runtime/cosmo.S:103
|
||||
// 42045d9 in _start at /home/jart/cosmo/libc/crt/crt.S:128
|
||||
//
|
||||
|
||||
// wrap cosmopolitan's memory allocator to track allocations
|
||||
static pthread_mutex_t mu = PTHREAD_MUTEX_INITIALIZER;
|
||||
void *malloc(size_t n) {
|
||||
pthread_mutex_lock(&mu);
|
||||
void *r = cosmo_leak_track(__dlmalloc(n));
|
||||
pthread_mutex_unlock(&mu);
|
||||
return r;
|
||||
}
|
||||
void *calloc(size_t n, size_t z) {
|
||||
pthread_mutex_lock(&mu);
|
||||
void *r = cosmo_leak_track(__dlcalloc(n, z));
|
||||
pthread_mutex_unlock(&mu);
|
||||
return r;
|
||||
}
|
||||
void *realloc(void *p, size_t n) {
|
||||
pthread_mutex_lock(&mu);
|
||||
void *t = cosmo_leak_track(__dlrealloc(p, n));
|
||||
if (t && t != p)
|
||||
cosmo_leak_untrack(p);
|
||||
pthread_mutex_unlock(&mu);
|
||||
return t;
|
||||
}
|
||||
void *memalign(size_t a, size_t n) {
|
||||
pthread_mutex_lock(&mu);
|
||||
void *r = cosmo_leak_track(__dlmemalign(a, n));
|
||||
pthread_mutex_unlock(&mu);
|
||||
return r;
|
||||
}
|
||||
void free(void *p) {
|
||||
pthread_mutex_lock(&mu);
|
||||
__dlfree(cosmo_leak_untrack(p));
|
||||
pthread_mutex_unlock(&mu);
|
||||
}
|
||||
|
||||
// the c standard says this function can't be optimized away
|
||||
void *identity(void *arg) {
|
||||
return arg;
|
||||
}
|
||||
void *(*pIdentity)(void *) = identity;
|
||||
|
||||
int main(void) {
|
||||
kprintf("let's demo the builtin memory leak detector\n");
|
||||
|
||||
// leak two allocations, one small, one large
|
||||
pIdentity(malloc(1));
|
||||
pIdentity(malloc(257));
|
||||
|
||||
// wait for any detached posix threads to exit
|
||||
while (!pthread_orphan_np())
|
||||
pthread_decimate_np();
|
||||
|
||||
// this runs atexit functions and thread/process finalizers
|
||||
cosmo_leak_finalize();
|
||||
|
||||
// print information about memory leaks with backtraces
|
||||
if (cosmo_leak_print(0) > 0)
|
||||
_Exit(73);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#/*─────────────────────────────────────────────────────────────────╗
|
||||
│ To the extent possible under law, Justine Tunney has waived │
|
||||
│ all copyright and related or neighboring rights to this file, │
|
||||
│ as it is written in the following disclaimers: │
|
||||
│ • http://unlicense.org/ │
|
||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#include <cosmo.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// zero-overhead memory leak detector demo
|
||||
//
|
||||
// $ make -j o//examples/memleak_zerocost
|
||||
// $ o//examples/memleak_zerocost
|
||||
// error: leaked 128 byte allocation at 0x7a1483eabf50
|
||||
// error: leaked 16 byte allocation at 0x7a1483eacfe0
|
||||
// error: leaked 1'008 byte allocation at 0x42671e0
|
||||
// you forgot to call free 3 times
|
||||
//
|
||||
|
||||
void NoOp(void *p) {
|
||||
}
|
||||
|
||||
void (*pNoOp)(void *) = NoOp;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
// run program, which leaks memory
|
||||
pNoOp(malloc(10));
|
||||
pNoOp(malloc(100));
|
||||
pNoOp(malloc(1000));
|
||||
|
||||
// check for memory leaks. this has zero overhead, because it crawls
|
||||
// the data structures used by cosmopolitan's malloc implementation.
|
||||
// this is great thing to always use so you know when there's a leak
|
||||
// and if you have trouble tracking down where it came from then you
|
||||
// can swap this out with the code in examples/memleak_backtrace.c
|
||||
CheckForMemoryLeaks();
|
||||
}
|
||||
-143
@@ -1,143 +0,0 @@
|
||||
#if 0
|
||||
/*─────────────────────────────────────────────────────────────────╗
|
||||
│ To the extent possible under law, Justine Tunney has waived │
|
||||
│ all copyright and related or neighboring rights to this file, │
|
||||
│ as it is written in the following disclaimers: │
|
||||
│ • http://unlicense.org/ │
|
||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include <cosmo.h>
|
||||
#include <getopt.h>
|
||||
#include <netdb.h>
|
||||
#include <poll.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
// clang-format off
|
||||
|
||||
/**
|
||||
* @fileoverview netcat clone
|
||||
*
|
||||
* Implemented because BusyBox's netcat doesn't detect remote close and
|
||||
* lingers in the CLOSE_WAIT wait possibly due to file descriptor leaks
|
||||
*
|
||||
* Here's an example usage:
|
||||
*
|
||||
* make -j8 o//examples/nc.com
|
||||
* printf 'GET /\r\nHost: justine.lol\r\n\r\n' | o//examples/nc.com justine.lol 80
|
||||
*
|
||||
* Once upon time we called this command basically "telnet"
|
||||
*/
|
||||
|
||||
#define ARRAYLEN(A) \
|
||||
((sizeof(A) / sizeof(*(A))) / ((unsigned)!(sizeof(A) % sizeof(*(A)))))
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
ssize_t rc;
|
||||
size_t i, got;
|
||||
int opt, sock;
|
||||
char buf[1500];
|
||||
bool halfclose = true;
|
||||
const char *host, *port;
|
||||
struct addrinfo *ai = NULL;
|
||||
struct linger linger = {true, 1};
|
||||
struct pollfd fds[2] = {{-1, POLLIN}, {-1, POLLIN}};
|
||||
struct addrinfo hint = {AI_NUMERICSERV, AF_INET, SOCK_STREAM, IPPROTO_TCP};
|
||||
|
||||
while ((opt = getopt(argc, argv, "hH")) != -1) {
|
||||
switch (opt) {
|
||||
case 'H':
|
||||
halfclose = false;
|
||||
break;
|
||||
case 'h':
|
||||
tinyprint(1, "Usage: ", argv[0], " [-hH] IP PORT\n", NULL);
|
||||
exit(0);
|
||||
default:
|
||||
fprintf(stderr, "bad option %d\n", opt);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if (argc - optind != 2) {
|
||||
fputs("missing args\n", stderr);
|
||||
exit(1);
|
||||
}
|
||||
host = argv[optind + 0];
|
||||
port = argv[optind + 1];
|
||||
|
||||
if ((rc = getaddrinfo(host, port, &hint, &ai))) {
|
||||
tinyprint(2, host, ": ", gai_strerror(rc), "\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ((sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) == -1) {
|
||||
perror("socket");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger)) == -1) {
|
||||
perror("SO_LINGER");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
|
||||
perror(host);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fds[0].fd = 0;
|
||||
fds[1].fd = sock;
|
||||
for (;;) {
|
||||
fds[0].revents = 0;
|
||||
fds[1].revents = 0;
|
||||
if (poll(fds, ARRAYLEN(fds), -1) == -1) {
|
||||
perror("poll");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (fds[0].revents & (POLLIN | POLLERR | POLLHUP)) {
|
||||
if ((rc = read(0, buf, 1400)) == -1) {
|
||||
perror("read(stdin)");
|
||||
exit(1);
|
||||
}
|
||||
if (!(got = rc)) {
|
||||
if (halfclose) {
|
||||
shutdown(sock, SHUT_WR);
|
||||
}
|
||||
fds[0].fd = -1;
|
||||
}
|
||||
for (i = 0; i < got; i += rc) {
|
||||
if ((rc = write(sock, buf + i, got - i)) == -1) {
|
||||
perror("write(sock)");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fds[1].revents & (POLLIN | POLLERR | POLLHUP)) {
|
||||
if ((rc = read(sock, buf, 1500)) == -1) {
|
||||
perror("read(sock)");
|
||||
exit(1);
|
||||
}
|
||||
if (!(got = rc)) {
|
||||
break;
|
||||
}
|
||||
for (i = 0; i < got; i += rc) {
|
||||
if ((rc = write(1, buf + i, got - i)) == -1) {
|
||||
perror("write(stdout)");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (close(sock) == -1) {
|
||||
perror("close");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
freeaddrinfo(ai);
|
||||
return 0;
|
||||
}
|
||||
+122
-134
@@ -13,10 +13,10 @@
|
||||
#include "dsp/tty/tty.h"
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/itimerval.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/struct/winsize.h"
|
||||
#include "libc/calls/termios.h"
|
||||
#include "libc/cosmotime.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
@@ -36,20 +36,17 @@
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/ex.h"
|
||||
#include "libc/sysv/consts/exit.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
#include "libc/sysv/consts/fileno.h"
|
||||
#include "libc/sysv/consts/itimer.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/poll.h"
|
||||
#include "libc/sysv/consts/prio.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/consts/w.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/time.h"
|
||||
#include "libc/x/xasprintf.h"
|
||||
#include "libc/x/xsigaction.h"
|
||||
#include "libc/zip.h"
|
||||
#include "third_party/getopt/getopt.internal.h"
|
||||
#include "third_party/libcxx/__atomic/atomic.h"
|
||||
#include "third_party/libcxx/vector"
|
||||
#include "tool/viz/lib/knobs.h"
|
||||
|
||||
@@ -124,15 +121,6 @@ typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
|
||||
static const struct itimerval kNesFps = {
|
||||
{0, 1. / FPS * 1e6},
|
||||
{0, 1. / FPS * 1e6},
|
||||
};
|
||||
|
||||
struct Frame {
|
||||
char *p, *w, *mem;
|
||||
};
|
||||
|
||||
struct Action {
|
||||
int code;
|
||||
int wait;
|
||||
@@ -148,15 +136,13 @@ struct ZipGames {
|
||||
char** p;
|
||||
};
|
||||
|
||||
static int frame_;
|
||||
static size_t vtsize_;
|
||||
static const struct timespec kNesFps = {0, 1. / FPS * 1e9};
|
||||
|
||||
static bool artifacts_;
|
||||
static long tyn_, txn_;
|
||||
static struct Frame vf_[2];
|
||||
static const char* inputfn_;
|
||||
static struct Status status_;
|
||||
static volatile bool exited_;
|
||||
static volatile bool timeout_;
|
||||
static volatile bool resized_;
|
||||
static struct CosmoAudio* ca_;
|
||||
static struct TtyRgb* ttyrgb_;
|
||||
@@ -165,7 +151,7 @@ static struct ZipGames zipgames_;
|
||||
static struct Action arrow_, button_;
|
||||
static struct SamplingSolution* ssy_;
|
||||
static struct SamplingSolution* ssx_;
|
||||
static unsigned char pixels_[3][DYN][DXN];
|
||||
static unsigned char (*pixels_)[3][DYN][DXN];
|
||||
static unsigned char palette_[3][64][512][3];
|
||||
static int joy_current_[2], joy_next_[2], joypos_[2];
|
||||
|
||||
@@ -173,6 +159,11 @@ static int keyframes_ = 10;
|
||||
static enum TtyBlocksSelection blocks_ = kTtyBlocksUnicode;
|
||||
static enum TtyQuantizationAlgorithm quant_ = kTtyQuantXterm256;
|
||||
|
||||
static struct timespec deadline_;
|
||||
static std::atomic<void*> pixels_ready_;
|
||||
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
|
||||
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static int Clamp(int v) {
|
||||
return MAX(0, MIN(255, v));
|
||||
}
|
||||
@@ -232,18 +223,8 @@ void InitPalette(void) {
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t Write(int fd, const void* p, size_t n) {
|
||||
int rc;
|
||||
sigset_t ss, oldss;
|
||||
sigfillset(&ss);
|
||||
sigprocmask(SIG_SETMASK, &ss, &oldss);
|
||||
rc = write(fd, p, n);
|
||||
sigprocmask(SIG_SETMASK, &oldss, 0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void WriteString(const char* s) {
|
||||
Write(STDOUT_FILENO, s, strlen(s));
|
||||
write(STDOUT_FILENO, s, strlen(s));
|
||||
}
|
||||
|
||||
void Exit(int rc) {
|
||||
@@ -264,28 +245,10 @@ void OnCtrlC(void) {
|
||||
exited_ = true;
|
||||
}
|
||||
|
||||
void OnTimer(void) {
|
||||
timeout_ = true;
|
||||
}
|
||||
|
||||
void OnResize(void) {
|
||||
resized_ = true;
|
||||
}
|
||||
|
||||
void OnPiped(void) {
|
||||
exited_ = true;
|
||||
}
|
||||
|
||||
void OnSigChld(void) {
|
||||
waitpid(-1, 0, WNOHANG);
|
||||
cosmoaudio_close(ca_);
|
||||
ca_ = 0;
|
||||
}
|
||||
|
||||
void InitFrame(struct Frame* f) {
|
||||
f->p = f->w = f->mem = (char*)realloc(f->mem, vtsize_);
|
||||
}
|
||||
|
||||
long ChopAxis(long dn, long sn) {
|
||||
while (HALF(sn) > dn) {
|
||||
sn = HALF(sn);
|
||||
@@ -308,11 +271,6 @@ void GetTermSize(void) {
|
||||
G = (unsigned char*)realloc(G, tyn_ * txn_);
|
||||
B = (unsigned char*)realloc(B, tyn_ * txn_);
|
||||
ttyrgb_ = (struct TtyRgb*)realloc(ttyrgb_, tyn_ * txn_ * 4);
|
||||
vtsize_ = ((tyn_ * txn_ * strlen("\e[48;2;255;48;2;255m▄")) +
|
||||
(tyn_ * strlen("\e[0m\r\n")) + 128);
|
||||
frame_ = 0;
|
||||
InitFrame(&vf_[0]);
|
||||
InitFrame(&vf_[1]);
|
||||
WriteString("\e[0m\e[H\e[J");
|
||||
resized_ = false;
|
||||
}
|
||||
@@ -320,11 +278,7 @@ void GetTermSize(void) {
|
||||
void IoInit(void) {
|
||||
GetTermSize();
|
||||
xsigaction(SIGINT, (void*)OnCtrlC, 0, 0, NULL);
|
||||
xsigaction(SIGPIPE, (void*)OnPiped, 0, 0, NULL);
|
||||
xsigaction(SIGWINCH, (void*)OnResize, 0, 0, NULL);
|
||||
xsigaction(SIGALRM, (void*)OnTimer, 0, 0, NULL);
|
||||
xsigaction(SIGCHLD, (void*)OnSigChld, 0, 0, NULL);
|
||||
setitimer(ITIMER_REAL, &kNesFps, NULL);
|
||||
ttyhidecursor(STDOUT_FILENO);
|
||||
ttyraw(kTtySigs);
|
||||
ttyquantsetup(quant_, kTtyQuantRgb, blocks_);
|
||||
@@ -461,57 +415,29 @@ ssize_t ReadKeyboard(void) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool HasVideo(struct Frame* f) {
|
||||
return f->w < f->p;
|
||||
}
|
||||
|
||||
bool HasPendingVideo(void) {
|
||||
return HasVideo(&vf_[0]) || HasVideo(&vf_[1]);
|
||||
}
|
||||
|
||||
struct Frame* FlipFrameBuffer(void) {
|
||||
frame_ = !frame_;
|
||||
return &vf_[frame_];
|
||||
}
|
||||
|
||||
void TransmitVideo(void) {
|
||||
ssize_t rc;
|
||||
struct Frame* f;
|
||||
f = &vf_[frame_];
|
||||
if (!HasVideo(f))
|
||||
f = FlipFrameBuffer();
|
||||
if ((rc = Write(STDOUT_FILENO, f->w, f->p - f->w)) != -1) {
|
||||
f->w += rc;
|
||||
} else if (errno == EAGAIN) {
|
||||
// slow teletypewriter
|
||||
} else if (errno == EPIPE) {
|
||||
Exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
void ScaleVideoFrameToTeletypewriter(void) {
|
||||
void ScaleVideoFrameToTeletypewriter(unsigned char (*pixels)[3][DYN][DXN]) {
|
||||
long y, x, yn, xn;
|
||||
yn = DYN, xn = DXN;
|
||||
while (HALF(yn) > tyn_ || HALF(xn) > txn_) {
|
||||
if (HALF(xn) > txn_) {
|
||||
Magikarp2xX(DYN, DXN, pixels_[0], yn, xn);
|
||||
Magikarp2xX(DYN, DXN, pixels_[1], yn, xn);
|
||||
Magikarp2xX(DYN, DXN, pixels_[2], yn, xn);
|
||||
Magikarp2xX(DYN, DXN, (*pixels)[0], yn, xn);
|
||||
Magikarp2xX(DYN, DXN, (*pixels)[1], yn, xn);
|
||||
Magikarp2xX(DYN, DXN, (*pixels)[2], yn, xn);
|
||||
xn = HALF(xn);
|
||||
}
|
||||
if (HALF(yn) > tyn_) {
|
||||
Magikarp2xY(DYN, DXN, pixels_[0], yn, xn);
|
||||
Magikarp2xY(DYN, DXN, pixels_[1], yn, xn);
|
||||
Magikarp2xY(DYN, DXN, pixels_[2], yn, xn);
|
||||
Magikarp2xY(DYN, DXN, (*pixels)[0], yn, xn);
|
||||
Magikarp2xY(DYN, DXN, (*pixels)[1], yn, xn);
|
||||
Magikarp2xY(DYN, DXN, (*pixels)[2], yn, xn);
|
||||
yn = HALF(yn);
|
||||
}
|
||||
}
|
||||
GyaradosUint8(tyn_, txn_, R, DYN, DXN, pixels_[0], tyn_, txn_, yn, xn, 0, 255,
|
||||
ssy_, ssx_, true);
|
||||
GyaradosUint8(tyn_, txn_, G, DYN, DXN, pixels_[1], tyn_, txn_, yn, xn, 0, 255,
|
||||
ssy_, ssx_, true);
|
||||
GyaradosUint8(tyn_, txn_, B, DYN, DXN, pixels_[2], tyn_, txn_, yn, xn, 0, 255,
|
||||
ssy_, ssx_, true);
|
||||
GyaradosUint8(tyn_, txn_, R, DYN, DXN, (*pixels)[0], tyn_, txn_, yn, xn, 0,
|
||||
255, ssy_, ssx_, true);
|
||||
GyaradosUint8(tyn_, txn_, G, DYN, DXN, (*pixels)[1], tyn_, txn_, yn, xn, 0,
|
||||
255, ssy_, ssx_, true);
|
||||
GyaradosUint8(tyn_, txn_, B, DYN, DXN, (*pixels)[2], tyn_, txn_, yn, xn, 0,
|
||||
255, ssy_, ssx_, true);
|
||||
for (y = 0; y < tyn_; ++y) {
|
||||
for (x = 0; x < txn_; ++x) {
|
||||
ttyrgb_[y * txn_ + x] =
|
||||
@@ -528,55 +454,104 @@ void KeyCountdown(struct Action* a) {
|
||||
}
|
||||
}
|
||||
|
||||
void PollAndSynchronize(void) {
|
||||
do {
|
||||
if (ReadKeyboard() == -1) {
|
||||
if (errno != EINTR)
|
||||
Exit(1);
|
||||
if (exited_)
|
||||
Exit(0);
|
||||
if (resized_)
|
||||
GetTermSize();
|
||||
}
|
||||
} while (!timeout_);
|
||||
TransmitVideo();
|
||||
timeout_ = false;
|
||||
KeyCountdown(&arrow_);
|
||||
KeyCountdown(&button_);
|
||||
joy_next_[0] = arrow_.code | button_.code;
|
||||
joy_next_[1] = arrow_.code | button_.code;
|
||||
}
|
||||
|
||||
void Raster(void) {
|
||||
struct Frame* f;
|
||||
void Raster(unsigned char (*pixels)[3][DYN][DXN]) {
|
||||
struct TtyRgb bg = {0x12, 0x34, 0x56, 0};
|
||||
struct TtyRgb fg = {0x12, 0x34, 0x56, 0};
|
||||
ScaleVideoFrameToTeletypewriter();
|
||||
f = &vf_[!frame_];
|
||||
f->p = f->w = f->mem;
|
||||
f->p = stpcpy(f->p, "\e[0m\e[H");
|
||||
f->p = ttyraster(f->p, ttyrgb_, tyn_, txn_, bg, fg);
|
||||
ScaleVideoFrameToTeletypewriter(pixels);
|
||||
char* ansi = (char*)malloc((tyn_ * txn_ * strlen("\e[48;2;255;48;2;255m▄")) +
|
||||
(tyn_ * strlen("\e[0m\r\n")) + 128);
|
||||
char* p = ansi;
|
||||
p = stpcpy(p, "\e[0m\e[H");
|
||||
p = ttyraster(p, ttyrgb_, tyn_, txn_, bg, fg);
|
||||
free(pixels);
|
||||
if (status_.wait) {
|
||||
status_.wait--;
|
||||
f->p = stpcpy(f->p, "\e[0m\e[H");
|
||||
f->p = stpcpy(f->p, status_.text);
|
||||
p = stpcpy(p, "\e[0m\e[H");
|
||||
p = stpcpy(p, status_.text);
|
||||
}
|
||||
size_t n = p - ansi;
|
||||
ssize_t wrote;
|
||||
for (size_t i = 0; i < n; i += wrote) {
|
||||
if ((wrote = write(STDOUT_FILENO, ansi + i, n - i)) == -1) {
|
||||
exited_ = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(ansi);
|
||||
}
|
||||
|
||||
void* RasterThread(void* arg) {
|
||||
sigset_t ss;
|
||||
sigemptyset(&ss);
|
||||
sigaddset(&ss, SIGINT);
|
||||
sigaddset(&ss, SIGHUP);
|
||||
sigaddset(&ss, SIGQUIT);
|
||||
sigaddset(&ss, SIGTERM);
|
||||
sigaddset(&ss, SIGPIPE);
|
||||
sigprocmask(SIG_SETMASK, &ss, 0);
|
||||
for (;;) {
|
||||
unsigned char(*pixels)[3][DYN][DXN];
|
||||
pthread_mutex_lock(&lock);
|
||||
while (!(pixels = (unsigned char(*)[3][DYN][DXN])pixels_ready_.load()))
|
||||
pthread_cond_wait(&cond, &lock);
|
||||
pixels_ready_.store(0);
|
||||
pthread_mutex_unlock(&lock);
|
||||
if (resized_)
|
||||
GetTermSize();
|
||||
Raster(pixels);
|
||||
}
|
||||
PollAndSynchronize();
|
||||
}
|
||||
|
||||
void FlushScanline(unsigned py) {
|
||||
if (py == DYN - 1) {
|
||||
if (!timeout_)
|
||||
Raster();
|
||||
timeout_ = false;
|
||||
if (py != DYN - 1)
|
||||
return;
|
||||
pthread_mutex_lock(&lock);
|
||||
if (!pixels_ready_) {
|
||||
pixels_ready_.store(pixels_);
|
||||
pixels_ = 0;
|
||||
pthread_cond_signal(&cond);
|
||||
}
|
||||
pthread_mutex_unlock(&lock);
|
||||
if (!pixels_)
|
||||
pixels_ = (unsigned char(*)[3][DYN][DXN])malloc(3 * DYN * DXN);
|
||||
if (exited_)
|
||||
Exit(0);
|
||||
do {
|
||||
struct timespec now = timespec_mono();
|
||||
struct timespec remain = timespec_subz(deadline_, now);
|
||||
int remain_ms = timespec_tomillis(remain);
|
||||
struct pollfd fds[] = {{STDIN_FILENO, POLLIN}};
|
||||
int got = poll(fds, 1, remain_ms);
|
||||
if (got == -1) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
Exit(1);
|
||||
}
|
||||
if (got == 1) {
|
||||
do {
|
||||
if (ReadKeyboard() == -1) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
Exit(1);
|
||||
}
|
||||
} while (0);
|
||||
}
|
||||
KeyCountdown(&arrow_);
|
||||
KeyCountdown(&button_);
|
||||
joy_next_[0] = arrow_.code | button_.code;
|
||||
joy_next_[1] = arrow_.code | button_.code;
|
||||
now = timespec_mono();
|
||||
do
|
||||
deadline_ = timespec_add(deadline_, kNesFps);
|
||||
while (timespec_cmp(deadline_, now) <= 0);
|
||||
} while (0);
|
||||
}
|
||||
|
||||
static void PutPixel(unsigned px, unsigned py, unsigned pixel, int offset) {
|
||||
static unsigned prev;
|
||||
pixels_[0][py][px] = palette_[offset][prev % 64][pixel][2];
|
||||
pixels_[1][py][px] = palette_[offset][prev % 64][pixel][1];
|
||||
pixels_[2][py][px] = palette_[offset][prev % 64][pixel][0];
|
||||
(*pixels_)[0][py][px] = palette_[offset][prev % 64][pixel][2];
|
||||
(*pixels_)[1][py][px] = palette_[offset][prev % 64][pixel][1];
|
||||
(*pixels_)[2][py][px] = palette_[offset][prev % 64][pixel][0];
|
||||
prev = pixel;
|
||||
}
|
||||
|
||||
@@ -1732,8 +1707,18 @@ int PlayGame(const char* romfile, const char* opt_tasfile) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
// initialize screen
|
||||
pixels_ = (unsigned char(*)[3][DYN][DXN])malloc(3 * DYN * DXN);
|
||||
InitPalette();
|
||||
|
||||
// start raster thread
|
||||
errno_t err;
|
||||
pthread_t th;
|
||||
if ((err = pthread_create(&th, 0, RasterThread, 0))) {
|
||||
fprintf(stderr, "pthread_create: %s\n", strerror(err));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// open speaker
|
||||
struct CosmoAudioOpenOptions cao = {};
|
||||
cao.sizeofThis = sizeof(struct CosmoAudioOpenOptions);
|
||||
@@ -1742,6 +1727,9 @@ int PlayGame(const char* romfile, const char* opt_tasfile) {
|
||||
cao.channels = 1;
|
||||
cosmoaudio_open(&ca_, &cao);
|
||||
|
||||
// initialize time
|
||||
deadline_ = timespec_add(timespec_mono(), kNesFps);
|
||||
|
||||
// Read the ROM file header
|
||||
u8 rom16count = fgetc(fp);
|
||||
u8 vrom8count = fgetc(fp);
|
||||
|
||||
+1
-1
@@ -11,5 +11,5 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
printf("%d\n", __get_cpu_count());
|
||||
printf("%d\n", cosmo_cpu_count());
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
#include "libc/cosmotime.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/fmt/magnumstrs.internal.h"
|
||||
|
||||
-1022
File diff suppressed because it is too large.
Load diff
+322
@@ -0,0 +1,322 @@
|
||||
#/*────────────────────────────────────────────────────────────────╗
|
||||
┌┘ To the extent possible under law, Justine Tunney has waived │
|
||||
│ all copyright and related or neighboring rights to this file, │
|
||||
│ as it is written in the following disclaimers: │
|
||||
│ • http://unlicense.org/ │
|
||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#include <ctype.h>
|
||||
#include <signal.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <termios.h>
|
||||
|
||||
/**
|
||||
* @fileoverview cosmopolitan flash cards viewer
|
||||
*/
|
||||
|
||||
struct Card {
|
||||
char* qa[2];
|
||||
};
|
||||
|
||||
atomic_int g_done;
|
||||
|
||||
void onsig(int sig) {
|
||||
g_done = 1;
|
||||
}
|
||||
|
||||
void* xmalloc(int n) {
|
||||
void* p;
|
||||
if ((p = malloc(n)))
|
||||
return p;
|
||||
perror("malloc");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void* xrealloc(void* p, int n) {
|
||||
if ((p = realloc(p, n)))
|
||||
return p;
|
||||
perror("realloc");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
char* xstrcat(const char* a, const char* b) {
|
||||
char* p;
|
||||
size_t n, m;
|
||||
n = strlen(a);
|
||||
m = strlen(b);
|
||||
p = xmalloc(n + m + 1);
|
||||
memcpy(p, a, n);
|
||||
memcpy(p + n, b, m + 1);
|
||||
return p;
|
||||
}
|
||||
|
||||
void shuffle(struct Card* a, int n) {
|
||||
while (n > 1) {
|
||||
int i = rand() % n--;
|
||||
struct Card t = a[i];
|
||||
a[i] = a[n];
|
||||
a[n] = t;
|
||||
}
|
||||
}
|
||||
|
||||
char* trim(char* s) {
|
||||
int i;
|
||||
if (s) {
|
||||
while (isspace(*s))
|
||||
++s;
|
||||
for (i = strlen(s); i--;) {
|
||||
if (isspace(s[i])) {
|
||||
s[i] = 0;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
char* readline(FILE* f) {
|
||||
for (;;) {
|
||||
char* line = trim(fgetln(f, 0));
|
||||
if (!line)
|
||||
return 0;
|
||||
if (*line != '#')
|
||||
if (*line)
|
||||
return line;
|
||||
}
|
||||
}
|
||||
|
||||
char* fill(const char* text, int max_line_width, int* out_line_count) {
|
||||
int text_len = strlen(text);
|
||||
char* result = xmalloc(text_len * 2 + 1);
|
||||
int result_pos = 0;
|
||||
int line_start = 0;
|
||||
int line_count = 1;
|
||||
int i = 0;
|
||||
while (i < text_len && isspace(text[i]))
|
||||
i++;
|
||||
while (i < text_len) {
|
||||
int word_end = i;
|
||||
while (word_end < text_len && !isspace(text[word_end]))
|
||||
word_end++;
|
||||
int word_length = word_end - i;
|
||||
if ((result_pos - line_start) + (result_pos > line_start ? 1 : 0) +
|
||||
word_length >
|
||||
max_line_width) {
|
||||
if (result_pos > line_start) {
|
||||
++line_count;
|
||||
result[result_pos++] = '\n';
|
||||
line_start = result_pos;
|
||||
}
|
||||
} else if (result_pos > line_start) {
|
||||
result[result_pos++] = ' ';
|
||||
}
|
||||
memcpy(result + result_pos, text + i, word_length);
|
||||
result_pos += word_length;
|
||||
i = word_end;
|
||||
while (i < text_len && isspace(text[i]))
|
||||
i++;
|
||||
}
|
||||
result[result_pos] = '\0';
|
||||
result = xrealloc(result, result_pos + 1);
|
||||
if (out_line_count)
|
||||
*out_line_count = line_count;
|
||||
return result;
|
||||
}
|
||||
|
||||
void show(const char* text, int i, int n) {
|
||||
|
||||
// get pseudoteletypewriter dimensions
|
||||
struct winsize ws = {80, 25};
|
||||
tcgetwinsize(1, &ws);
|
||||
int width = ws.ws_col;
|
||||
if (width > (int)(ws.ws_col * .9))
|
||||
width = ws.ws_col * .9;
|
||||
if (width > 80)
|
||||
width = 80;
|
||||
width &= -2;
|
||||
|
||||
// clear display
|
||||
printf("\033[H\033[J");
|
||||
|
||||
// display flash card text in middle of display
|
||||
char buf[32];
|
||||
int line_count;
|
||||
char* lines = fill(text, width, &line_count);
|
||||
sprintf(buf, "%d/%d\r\n\r\n", i + 1, n);
|
||||
line_count += 2;
|
||||
char* extra = xstrcat(buf, lines);
|
||||
free(lines);
|
||||
char* tokens = extra;
|
||||
for (int j = 0;; ++j) {
|
||||
char* line = strtok(tokens, "\n");
|
||||
tokens = 0;
|
||||
if (!line)
|
||||
break;
|
||||
printf("\033[%d;%dH%s", ws.ws_row / 2 - line_count / 2 + j + 1,
|
||||
ws.ws_col / 2 - strlen(line) / 2 + 1, line);
|
||||
}
|
||||
free(extra);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void usage(FILE* f, const char* prog) {
|
||||
fprintf(f,
|
||||
"usage: %s FILE\n"
|
||||
"\n"
|
||||
"here's an example of what your file should look like:\n"
|
||||
"\n"
|
||||
" # cosmopolitan flash cards\n"
|
||||
" # california dmv drivers test\n"
|
||||
" \n"
|
||||
" which of the following point totals could result in "
|
||||
"your license being suspended by the dmv?\n"
|
||||
" 4 points in 12 months (middle)\n"
|
||||
" \n"
|
||||
" at 55 mph under good conditions a passenger vehicle can stop "
|
||||
"within\n"
|
||||
" 300 feet (not 200, not 400, middle)\n"
|
||||
" \n"
|
||||
" two sets of solid double yellow lines spaced two or more feet "
|
||||
"apart indicate\n"
|
||||
" a BARRIER (do not cross unless there's an opening)\n"
|
||||
"\n"
|
||||
"more specifically, empty lines are ignored, lines starting with\n"
|
||||
"a hash are ignored, then an even number of lines must remain,\n"
|
||||
"where each two lines is a card, holding question and answer.\n",
|
||||
prog);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
|
||||
// show help
|
||||
if (argc != 2) {
|
||||
usage(stderr, argv[0]);
|
||||
return 1;
|
||||
}
|
||||
if (!strcmp(argv[1], "-?") || //
|
||||
!strcmp(argv[1], "-h") || //
|
||||
!strcmp(argv[1], "--help")) {
|
||||
usage(stdout, argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// teletypewriter is required
|
||||
if (!isatty(0) || !isatty(1)) {
|
||||
perror("isatty");
|
||||
return 2;
|
||||
}
|
||||
|
||||
// load cards
|
||||
FILE* f = fopen(argv[1], "r");
|
||||
if (!f) {
|
||||
perror(argv[1]);
|
||||
return 3;
|
||||
}
|
||||
int count = 0;
|
||||
struct Card* cards = 0;
|
||||
for (;;) {
|
||||
struct Card card;
|
||||
if (!(card.qa[0] = readline(f)))
|
||||
break;
|
||||
card.qa[0] = strdup(card.qa[0]);
|
||||
if (!(card.qa[1] = readline(f))) {
|
||||
fprintf(stderr, "%s: flash card file has odd number of lines\n", argv[1]);
|
||||
exit(1);
|
||||
}
|
||||
card.qa[1] = strdup(card.qa[1]);
|
||||
cards = xrealloc(cards, (count + 1) * sizeof(struct Card));
|
||||
cards[count++] = card;
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
// randomize
|
||||
srand(time(0));
|
||||
shuffle(cards, count);
|
||||
|
||||
// catch ctrl-c
|
||||
struct sigaction sa;
|
||||
sa.sa_flags = 0;
|
||||
sa.sa_handler = onsig;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sigaction(SIGINT, &sa, 0);
|
||||
|
||||
// enter raw mode
|
||||
struct termios ot;
|
||||
tcgetattr(1, &ot);
|
||||
struct termios nt = ot;
|
||||
cfmakeraw(&nt);
|
||||
nt.c_lflag |= ISIG;
|
||||
tcsetattr(1, TCSANOW, &nt);
|
||||
printf("\033[?25l");
|
||||
|
||||
// show flash cards
|
||||
int i = 0;
|
||||
while (!g_done) {
|
||||
show(cards[i / 2].qa[i % 2], i / 2, count);
|
||||
|
||||
// press any key
|
||||
char b[8] = {0};
|
||||
read(0, b, sizeof(b));
|
||||
|
||||
// q quits
|
||||
if (b[0] == 'q')
|
||||
break;
|
||||
|
||||
// b or ctrl-b goes backward
|
||||
if (b[0] == 'b' || //
|
||||
b[0] == ('B' ^ 0100)) {
|
||||
if (--i < 0)
|
||||
i = count * 2 - 1;
|
||||
i &= -2;
|
||||
continue;
|
||||
}
|
||||
|
||||
// p or ctrl-p goes backward
|
||||
if (b[0] == 'p' || //
|
||||
b[0] == ('P' ^ 0100)) {
|
||||
if (--i < 0)
|
||||
i = count * 2 - 1;
|
||||
i &= -2;
|
||||
continue;
|
||||
}
|
||||
|
||||
// up arrow goes backward
|
||||
if (b[0] == 033 && //
|
||||
b[1] == '[' && //
|
||||
b[2] == 'A') {
|
||||
if (--i < 0)
|
||||
i = count * 2 - 1;
|
||||
i &= -2;
|
||||
continue;
|
||||
}
|
||||
|
||||
// left arrow goes backward
|
||||
if (b[0] == 033 && //
|
||||
b[1] == '[' && //
|
||||
b[2] == 'D') {
|
||||
if (--i < 0)
|
||||
i = count * 2 - 1;
|
||||
i &= -2;
|
||||
continue;
|
||||
}
|
||||
|
||||
// only advance
|
||||
if (++i == count * 2)
|
||||
i = 0;
|
||||
}
|
||||
|
||||
// free memory
|
||||
for (int i = 0; i < count; ++i)
|
||||
for (int j = 0; j < 2; ++j)
|
||||
free(cards[i].qa[j]);
|
||||
free(cards);
|
||||
|
||||
// cleanup terminal and show cursor
|
||||
tcsetattr(1, TCSANOW, &ot);
|
||||
printf("\033[?25h");
|
||||
printf("\n");
|
||||
}
|
||||
+4
-2
@@ -13,7 +13,9 @@
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/cosmotime.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/log/appendresourcereport.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/append.h"
|
||||
@@ -27,7 +29,7 @@ int main(int argc, char *argv[]) {
|
||||
prog = "rusage";
|
||||
|
||||
if (argc < 2) {
|
||||
tinyprint(2, prog, ": missing command\n", NULL);
|
||||
fprintf(stderr, "%s: missing command\n", prog);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -42,7 +44,7 @@ int main(int argc, char *argv[]) {
|
||||
struct timespec started = timespec_real();
|
||||
|
||||
// launch subprocess
|
||||
int child = fork();
|
||||
int child = vfork();
|
||||
if (child == -1) {
|
||||
perror(prog);
|
||||
exit(1);
|
||||
|
||||
+15
-5
@@ -49,10 +49,10 @@
|
||||
* @fileoverview Terminal Screencast Recorder / Player, e.g.
|
||||
*
|
||||
* make o//examples/script.com
|
||||
* o//examples/script.com -r
|
||||
* o//examples/script.com -w80 -h24 -r recording.tty
|
||||
* # type stuff..
|
||||
* # CTRL-D
|
||||
* o//examples/script.com -p typescript
|
||||
* o//examples/script.com -p recording.tty
|
||||
*
|
||||
* @note works on Linux, OpenBSD, NetBSD, FreeBSD, MacOS
|
||||
* @see https://asciinema.org/
|
||||
@@ -103,9 +103,9 @@ main(int argc, char *argv[])
|
||||
fd_set rfd;
|
||||
int fm_fd;
|
||||
int aflg, Fflg, kflg, pflg, ch, k, n;
|
||||
int flushtime, readstdin;
|
||||
int flushtime, readstdin, width, height;
|
||||
|
||||
aflg = Fflg = kflg = pflg = 0;
|
||||
aflg = Fflg = kflg = pflg = height = width = 0;
|
||||
usesleep = 1;
|
||||
rawout = 0;
|
||||
flushtime = 30;
|
||||
@@ -115,7 +115,7 @@ main(int argc, char *argv[])
|
||||
|
||||
(void)fm_fd;
|
||||
|
||||
while ((ch = getopt(argc, argv, "adeFfkpqrt:")) != -1)
|
||||
while ((ch = getopt(argc, argv, "adeFfkpqrt:w:h:")) != -1)
|
||||
switch(ch) {
|
||||
case 'a':
|
||||
aflg = 1;
|
||||
@@ -145,6 +145,12 @@ main(int argc, char *argv[])
|
||||
if (flushtime < 0)
|
||||
err(1, "invalid flush time %d", flushtime);
|
||||
break;
|
||||
case 'w':
|
||||
width = atoi(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
height = atoi(optarg);
|
||||
break;
|
||||
case '?':
|
||||
default:
|
||||
usage();
|
||||
@@ -172,6 +178,10 @@ main(int argc, char *argv[])
|
||||
if (openpty(&master, &slave, NULL, NULL, NULL) == -1)
|
||||
err(1, "openpty");
|
||||
} else {
|
||||
if (width)
|
||||
win.ws_col = width;
|
||||
if (height)
|
||||
win.ws_row = height;
|
||||
if (openpty(&master, &slave, NULL, &tt, &win) == -1)
|
||||
err(1, "openpty");
|
||||
ttyflg = 1;
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
#if 0
|
||||
/*─────────────────────────────────────────────────────────────────╗
|
||||
│ To the extent possible under law, Justine Tunney has waived │
|
||||
│ all copyright and related or neighboring rights to this file, │
|
||||
│ as it is written in the following disclaimers: │
|
||||
│ • http://unlicense.org/ │
|
||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define PHILOSOPHERS 9 // number of hungry philosophers
|
||||
#define THOUGHTS 3 // number of breaks for thinking
|
||||
#define SEATS 3 // number of seats at the tables
|
||||
|
||||
atomic_int occupied;
|
||||
sem_t sem;
|
||||
|
||||
void *philosopher(void *arg) {
|
||||
int tid = (long)arg;
|
||||
|
||||
printf("philosopher#%d arrives\n", tid);
|
||||
|
||||
for (int i = 0; i < THOUGHTS; ++i) {
|
||||
|
||||
sem_wait(&sem);
|
||||
printf("philosopher#%d sat down to eat (%d occupied)\n", tid, ++occupied);
|
||||
usleep(rand() % 1000000);
|
||||
|
||||
printf("philosopher#%d gets up to think (%d occupied)\n", tid, --occupied);
|
||||
sem_post(&sem);
|
||||
usleep(rand() % 1000000);
|
||||
}
|
||||
|
||||
printf("philosopher#%d leaves\n", tid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
printf("inviting %d philosophers to %d person dinner\n", PHILOSOPHERS, SEATS);
|
||||
sem_init(&sem, 0, SEATS);
|
||||
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
|
||||
for (long i = 0; i < PHILOSOPHERS; ++i) {
|
||||
pthread_t th;
|
||||
pthread_create(&th, &attr, philosopher, (void *)i);
|
||||
}
|
||||
|
||||
pthread_exit(0);
|
||||
}
|
||||
@@ -9,7 +9,9 @@
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ucontext.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/**
|
||||
* @fileoverview swapcontext() and makecontext() example
|
||||
@@ -20,7 +22,7 @@ static ucontext_t uctx_main;
|
||||
static ucontext_t uctx_func1;
|
||||
static ucontext_t uctx_func2;
|
||||
|
||||
#define say(s) write(1, s, strlen(s))
|
||||
#define say(s) (void)!write(1, s, strlen(s))
|
||||
#define handle_error(msg) \
|
||||
do { \
|
||||
perror(msg); \
|
||||
|
||||
@@ -7,9 +7,13 @@
|
||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/maps.h"
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/runtime/winargs.internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/x/xasprintf.h"
|
||||
|
||||
@@ -67,8 +71,18 @@ int main(int argc, char *argv[]) {
|
||||
Append((uintptr_t)&__auxv[i + 1],
|
||||
xasprintf("&auxv[%d] = %#lx", i + 1, __auxv[i + 1]));
|
||||
}
|
||||
qsort(things.p, things.n, sizeof(*things.p), Compare);
|
||||
for (int i = 0; i < things.n; ++i) {
|
||||
printf("%012lx %s\n", things.p[i].i, things.p[i].s);
|
||||
if (!IsWindows()) {
|
||||
struct AddrSize stak = __get_main_stack();
|
||||
Append((intptr_t)stak.addr + stak.size, "top of stack");
|
||||
Append((intptr_t)stak.addr, "bottom of stack");
|
||||
} else {
|
||||
#ifdef __x86_64__
|
||||
Append(GetStaticStackAddr(0) + GetStaticStackSize(), "top of stack");
|
||||
Append(GetStaticStackAddr(0) + GetGuardSize(), "bottom of stack");
|
||||
Append(GetStaticStackAddr(0), "bottom of guard region");
|
||||
#endif
|
||||
}
|
||||
qsort(things.p, things.n, sizeof(*things.p), Compare);
|
||||
for (int i = 0; i < things.n; ++i)
|
||||
printf("%012lx %s\n", things.p[i].i, things.p[i].s);
|
||||
}
|
||||
+13
-10
@@ -12,6 +12,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <time.h>
|
||||
|
||||
/**
|
||||
@@ -62,7 +63,6 @@ const char *DescribeFileType(unsigned mode) {
|
||||
|
||||
void PrintFileMetadata(const char *pathname, struct stat *st) {
|
||||
int fd;
|
||||
printf("\n%s:", pathname);
|
||||
if (numeric) {
|
||||
fd = atoi(pathname);
|
||||
if (fstat(fd, st)) {
|
||||
@@ -70,15 +70,16 @@ void PrintFileMetadata(const char *pathname, struct stat *st) {
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
if (stat(pathname, st)) {
|
||||
if (lstat(pathname, st)) {
|
||||
perror(pathname);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
printf("\n"
|
||||
"%-32s%,ld\n"
|
||||
"%-32s%,ld\n"
|
||||
"%-32s%#lx\n"
|
||||
"%s:\n"
|
||||
"%-32s%,ld\n" // bytes in file
|
||||
"%-32s%,ld\n" // physical bytes
|
||||
"%-32s%u,%u\n" // device id
|
||||
"%-32s%#lx\n"
|
||||
"%-32s%ld\n"
|
||||
"%-32s%#o (%s)\n"
|
||||
@@ -92,11 +93,13 @@ void PrintFileMetadata(const char *pathname, struct stat *st) {
|
||||
"%-32s%s\n"
|
||||
"%-32s%s\n"
|
||||
"%-32s%s\n",
|
||||
"bytes in file:", st->st_size, "physical bytes:", st->st_blocks * 512,
|
||||
"device id w/ file:", st->st_dev, "inode:", st->st_ino,
|
||||
"hard link count:", st->st_nlink, "mode / permissions:", st->st_mode,
|
||||
DescribeFileType(st->st_mode), "owner id:", st->st_uid,
|
||||
"group id:", st->st_gid, "flags:", st->st_flags, "gen:", st->st_gen,
|
||||
pathname, "bytes in file:", st->st_size,
|
||||
"physical bytes:", st->st_blocks * 512,
|
||||
"device id w/ file:", major(st->st_dev), minor(st->st_dev),
|
||||
"inode:", st->st_ino, "hard link count:", st->st_nlink,
|
||||
"mode / permissions:", st->st_mode, DescribeFileType(st->st_mode),
|
||||
"owner id:", st->st_uid, "group id:", st->st_gid,
|
||||
"flags:", st->st_flags, "gen:", st->st_gen,
|
||||
"device id (if special):", st->st_rdev, "block size:", st->st_blksize,
|
||||
"access time:", gc(xiso8601(st->st_atim)),
|
||||
"modified time:", gc(xiso8601(st->st_mtim)),
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// how to spawn a thread
|
||||
|
||||
void *my_thread(void *arg) {
|
||||
printf("my_thread(%p) is running\n", arg);
|
||||
return (void *)0x456L;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
void *res;
|
||||
pthread_t th;
|
||||
pthread_create(&th, 0, my_thread, (void *)0x123L);
|
||||
pthread_join(th, &res);
|
||||
printf("my_thread() returned %p\n", res);
|
||||
}
|
||||
@@ -45,4 +45,7 @@ int main(int argc, char *argv[]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
int main(int argc, char *argv[]) {
|
||||
}
|
||||
#endif /* __x86_64__ */
|
||||
@@ -61,4 +61,7 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
int main(int argc, char *argv[]) {
|
||||
}
|
||||
#endif /* __x86_64__ */
|
||||
@@ -43,4 +43,7 @@ int main(int argc, char *argv[]) {
|
||||
;
|
||||
}
|
||||
|
||||
#else
|
||||
int main(int argc, char *argv[]) {
|
||||
}
|
||||
#endif /* __x86_64__ */
|
||||
@@ -0,0 +1,22 @@
|
||||
#/*─────────────────────────────────────────────────────────────────╗
|
||||
│ To the extent possible under law, Justine Tunney has waived │
|
||||
│ all copyright and related or neighboring rights to this file, │
|
||||
│ as it is written in the following disclaimers: │
|
||||
│ • http://unlicense.org/ │
|
||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void onsig(int sig) {
|
||||
fprintf(stderr, "got %s\n", strsignal(sig));
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
fprintf(stderr, "my pid is %d\n", getpid());
|
||||
for (int sig = 1; sig <= NSIG; ++sig)
|
||||
signal(sig, onsig);
|
||||
pause();
|
||||
}
|
||||
+10
-1
@@ -13,12 +13,17 @@ libc/isystem/arm_fp16.h \
|
||||
libc/isystem/arm_neon.h \
|
||||
libc/isystem/arpa/inet.h \
|
||||
libc/isystem/arpa/nameser.h \
|
||||
libc/isystem/arpa/telnet.h \
|
||||
libc/isystem/assert.h \
|
||||
libc/isystem/bsd/readpassphrase.h \
|
||||
libc/isystem/bsd/stdlib.h \
|
||||
libc/isystem/bsd/string.h \
|
||||
libc/isystem/byteswap.h \
|
||||
libc/isystem/clzerointrin.h \
|
||||
libc/isystem/complex.h \
|
||||
libc/isystem/cosmo.h \
|
||||
libc/isystem/cosmoaudio.h \
|
||||
libc/isystem/cosmotime.h \
|
||||
libc/isystem/cpio.h \
|
||||
libc/isystem/cpuid.h \
|
||||
libc/isystem/crypt.h \
|
||||
@@ -49,9 +54,12 @@ libc/isystem/langinfo.h \
|
||||
libc/isystem/libgen.h \
|
||||
libc/isystem/limits.h \
|
||||
libc/isystem/link.h \
|
||||
libc/isystem/linux/f2fs.h \
|
||||
libc/isystem/linux/futex.h \
|
||||
libc/isystem/linux/ioctl.h \
|
||||
libc/isystem/linux/limits.h \
|
||||
libc/isystem/linux/param.h \
|
||||
libc/isystem/linux/rseq.h \
|
||||
libc/isystem/linux/types.h \
|
||||
libc/isystem/locale.h \
|
||||
libc/isystem/malloc.h \
|
||||
@@ -68,6 +76,7 @@ libc/isystem/net/if.h \
|
||||
libc/isystem/net/if_arp.h \
|
||||
libc/isystem/netdb.h \
|
||||
libc/isystem/netinet/in.h \
|
||||
libc/isystem/netinet/in_systm.h \
|
||||
libc/isystem/netinet/ip.h \
|
||||
libc/isystem/netinet/tcp.h \
|
||||
libc/isystem/netinet/udp.h \
|
||||
@@ -94,6 +103,7 @@ libc/isystem/popcntintrin.h \
|
||||
libc/isystem/pthread.h \
|
||||
libc/isystem/pty.h \
|
||||
libc/isystem/pwd.h \
|
||||
libc/isystem/readpassphrase.h \
|
||||
libc/isystem/regex.h \
|
||||
libc/isystem/resolv.h \
|
||||
libc/isystem/sched.h \
|
||||
@@ -135,7 +145,6 @@ libc/isystem/sys/param.h \
|
||||
libc/isystem/sys/poll.h \
|
||||
libc/isystem/sys/prctl.h \
|
||||
libc/isystem/sys/procfs.h \
|
||||
libc/isystem/sys/ptrace.h \
|
||||
libc/isystem/sys/random.h \
|
||||
libc/isystem/sys/reboot.h \
|
||||
libc/isystem/sys/resource.h \
|
||||
|
||||
+2
-1
@@ -23,7 +23,8 @@ void unassert(const char *, const char *, int) libcesque;
|
||||
#define assert(x) ((void)((x) || (__assert_fail(#x, __FILE__, __LINE__), 0)))
|
||||
#endif
|
||||
|
||||
#if __STDC_VERSION__ >= 201112L && !defined(__cplusplus)
|
||||
#if !defined(__cplusplus) && defined(__STDC_VERSION__) && \
|
||||
__STDC_VERSION__ >= 201112L && __STDC_VERSION__ <= 201710L
|
||||
#undef static_assert
|
||||
#define static_assert _Static_assert
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef COSMOPOLITAN_LIBC_BSDSTDLIB_H_
|
||||
#define COSMOPOLITAN_LIBC_BSDSTDLIB_H_
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
uint32_t arc4random(void);
|
||||
void arc4random_buf(void *, size_t);
|
||||
uint32_t arc4random_uniform(uint32_t);
|
||||
|
||||
void *reallocarray(void *, size_t, size_t);
|
||||
long long strtonum(const char *, long long, long long, const char **);
|
||||
int heapsort(void *, size_t, size_t, int (*)(const void *, const void *));
|
||||
int mergesort(void *, size_t, size_t, int (*)(const void *, const void *));
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* COSMOPOLITAN_LIBC_BSDSTDLIB_H_ */
|
||||
+7
-8
@@ -50,6 +50,7 @@ LIBC_CALLS_A_DIRECTDEPS = \
|
||||
LIBC_NT_PSAPI \
|
||||
LIBC_NT_REALTIME \
|
||||
LIBC_NT_SYNCHRONIZATION \
|
||||
LIBC_NT_WINMM \
|
||||
LIBC_NT_WS2_32 \
|
||||
LIBC_STR \
|
||||
LIBC_SYSV \
|
||||
@@ -111,7 +112,7 @@ o//libc/calls/prctl.o: \
|
||||
|
||||
# we always want -Os because:
|
||||
# it's early runtime mandatory and quite huge without it
|
||||
o//libc/calls/getcwd.greg.o \
|
||||
o//libc/calls/getcwd.o \
|
||||
o//libc/calls/statfs2cosmo.o: private \
|
||||
CFLAGS += \
|
||||
-Os
|
||||
@@ -174,7 +175,6 @@ o//libc/calls/gettimeofday.o \
|
||||
o//libc/calls/ioctl.o \
|
||||
o//libc/calls/lutimes.o \
|
||||
o//libc/calls/metaflock.o \
|
||||
o//libc/calls/ntaccesscheck.o \
|
||||
o//libc/calls/ntspawn.o \
|
||||
o//libc/calls/open-nt.o \
|
||||
o//libc/calls/pledge-linux.o \
|
||||
@@ -215,13 +215,12 @@ o//libc/calls/writev.o: private \
|
||||
COPTS += \
|
||||
-mgeneral-regs-only
|
||||
|
||||
# it's too early to use memmove() due to __vex and WinMain
|
||||
o/$(MODE)/libc/calls/mkunixpath.o: private \
|
||||
CFLAGS += \
|
||||
-ffreestanding
|
||||
|
||||
# these assembly files are safe to build on aarch64
|
||||
o/$(MODE)/libc/calls/getcontext.o: libc/calls/getcontext.S
|
||||
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
||||
o/$(MODE)/libc/calls/swapcontext.o: libc/calls/swapcontext.S
|
||||
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
||||
o/$(MODE)/libc/calls/tailcontext.o: libc/calls/tailcontext.S
|
||||
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
||||
o/$(MODE)/libc/calls/stackjump.o: libc/calls/stackjump.S
|
||||
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
||||
|
||||
|
||||
+42
-6
@@ -17,10 +17,20 @@
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
alignas(64) static struct {
|
||||
pthread_mutex_t lock;
|
||||
int phase;
|
||||
} __abort = {
|
||||
PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP,
|
||||
};
|
||||
|
||||
/**
|
||||
* Terminates program abnormally.
|
||||
@@ -33,10 +43,36 @@
|
||||
* @noreturn
|
||||
*/
|
||||
wontreturn void abort(void) {
|
||||
sigset_t m = 1ull << (SIGABRT - 1);
|
||||
sigprocmask(SIG_UNBLOCK, &m, 0);
|
||||
raise(SIGABRT);
|
||||
signal(SIGABRT, SIG_DFL);
|
||||
raise(SIGABRT);
|
||||
_Exit(128 + SIGABRT);
|
||||
sigset_t mask;
|
||||
if (!__spawned)
|
||||
pthread_mutex_lock(&__abort.lock);
|
||||
switch (__abort.phase) {
|
||||
case 0:
|
||||
mask = 1ull << (SIGABRT - 1);
|
||||
sigprocmask(SIG_UNBLOCK, &mask, 0);
|
||||
__abort.phase = 1;
|
||||
// fallthrough
|
||||
case 1:
|
||||
__abort.phase = 0;
|
||||
if (!__spawned)
|
||||
pthread_mutex_unlock(&__abort.lock);
|
||||
raise(SIGABRT);
|
||||
if (!__spawned)
|
||||
pthread_mutex_lock(&__abort.lock);
|
||||
__abort.phase = 2;
|
||||
// fallthrough
|
||||
case 2:
|
||||
__abort.phase = 3;
|
||||
signal(SIGABRT, SIG_DFL);
|
||||
// fallthrough
|
||||
case 3:
|
||||
__abort.phase = 4;
|
||||
raise(SIGABRT);
|
||||
// fallthrough
|
||||
case 4:
|
||||
__abort.phase = 5;
|
||||
_Exit(128 + SIGABRT);
|
||||
}
|
||||
for (;;)
|
||||
notpossible;
|
||||
}
|
||||
+4
-2
@@ -20,12 +20,14 @@
|
||||
#include "libc/sysv/consts/at.h"
|
||||
|
||||
/**
|
||||
* Checks if effective user can access path in particular ways.
|
||||
* Checks if file can be accessed in particular way.
|
||||
*
|
||||
* This is equivalent to saying:
|
||||
* This function is equivalent to:
|
||||
*
|
||||
* faccessat(AT_FDCWD, path, mode, 0);
|
||||
*
|
||||
* Please see faccessat() for further documentation.
|
||||
*
|
||||
* @param path is a filename or directory
|
||||
* @param mode can be R_OK, W_OK, X_OK, F_OK
|
||||
* @return 0 if ok, or -1 w/ errno
|
||||
|
||||
@@ -0,0 +1,330 @@
|
||||
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright (c) 1996, David Mazieres <[email protected]> │
|
||||
│ Copyright (c) 2008, Damien Miller <[email protected]> │
|
||||
│ Copyright (c) 2013, Markus Friedl <[email protected]> │
|
||||
│ Copyright (c) 2014, Theo de Raadt <[email protected]> │
|
||||
│ Copyright (c) Robert Clausecker <[email protected]> │
|
||||
│ │
|
||||
│ 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/state.internal.h"
|
||||
#include "libc/calls/typedef/u.h"
|
||||
#include "libc/cosmo.h"
|
||||
#include "libc/stdio/rand.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
// clang-format off
|
||||
|
||||
#define FAIR_SHARE 1024
|
||||
#define USE_CURRENT_CPU 1
|
||||
#define USE_THREAD_COUNT 1
|
||||
|
||||
__notice(arc4random_notice, "arc4random (ISC)\n\
|
||||
Copyright (c) 1996, David Mazieres <[email protected]>\n\
|
||||
Copyright (c) 2008, Damien Miller <[email protected]>\n\
|
||||
Copyright (c) 2013, Markus Friedl <[email protected]>\n\
|
||||
Copyright (c) 2014, Theo de Raadt <[email protected]>\n\
|
||||
Copyright (c) 2024, Robert Clausecker <[email protected]>\n\
|
||||
Copyright (c) 2025, Justine Tunney <[email protected]>\n\
|
||||
Based upon D. J. Bernstein's public domain ChaCha code\n\
|
||||
Based upon Daniel Lemire's arc4random_uniform research");
|
||||
|
||||
#define KEYSTREAM_ONLY
|
||||
#include "libc/str/chacha.inc"
|
||||
|
||||
#define minimum(a, b) ((a) < (b) ? (a) : (b))
|
||||
|
||||
#define KEYSZ 32
|
||||
#define IVSZ 8
|
||||
#define BLOCKSZ 64
|
||||
#define RSBUFSZ (16*BLOCKSZ)
|
||||
|
||||
#define REKEY_BASE (1024*1024) /* NB. should be a power of 2 */
|
||||
|
||||
/* This is zero'd out in forked children. */
|
||||
struct _rs {
|
||||
size_t rs_have; /* valid bytes at end of rs_buf */
|
||||
size_t rs_count; /* bytes till reseed */
|
||||
};
|
||||
|
||||
/* Optional to preserve in forked children. */
|
||||
struct _rsx {
|
||||
chacha_ctx rs_chacha; /* chacha context for random keystream */
|
||||
u_char rs_buf[RSBUFSZ]; /* keystream blocks */
|
||||
};
|
||||
|
||||
struct _rng {
|
||||
alignas(64) pthread_mutex_t mtx;
|
||||
bool once;
|
||||
struct _rs rs;
|
||||
struct _rsx rsx;
|
||||
};
|
||||
|
||||
static struct _rng _rs_state[COSMO_SHARDS];
|
||||
|
||||
void
|
||||
__arc4random_fork_prepare(void)
|
||||
{
|
||||
if (__isthreaded < 2)
|
||||
return;
|
||||
for (int i = 0; i < COSMO_SHARDS; ++i)
|
||||
if (_rs_state[i].once)
|
||||
pthread_mutex_lock(&_rs_state[i].mtx);
|
||||
}
|
||||
|
||||
void
|
||||
__arc4random_fork_parent(void)
|
||||
{
|
||||
if (__isthreaded < 2)
|
||||
return;
|
||||
for (int i = COSMO_SHARDS; i--;)
|
||||
if (_rs_state[i].once)
|
||||
pthread_mutex_unlock(&_rs_state[i].mtx);
|
||||
}
|
||||
|
||||
void
|
||||
__arc4random_fork_child(void)
|
||||
{
|
||||
for (int i = 0; i < COSMO_SHARDS; ++i) {
|
||||
if (!_rs_state[i].once)
|
||||
continue;
|
||||
pthread_mutex_wipe_np(&_rs_state[i].mtx);
|
||||
memset(&_rs_state[i].rs, 0, sizeof(_rs_state[i].rs));
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
_rs_init(struct _rng *rng, u_char *buf, size_t n)
|
||||
{
|
||||
if (n < KEYSZ + IVSZ)
|
||||
return;
|
||||
chacha_keysetup(&rng->rsx.rs_chacha, buf, KEYSZ * 8);
|
||||
chacha_ivsetup(&rng->rsx.rs_chacha, buf + KEYSZ);
|
||||
rng->once = true;
|
||||
}
|
||||
|
||||
static inline void
|
||||
_rs_rekey(struct _rng *rng, u_char *dat, size_t datlen)
|
||||
{
|
||||
#ifndef KEYSTREAM_ONLY
|
||||
memset(rng->rsx.rs_buf, 0, sizeof(rng->rsx.rs_buf));
|
||||
#endif
|
||||
/* fill rs_buf with the keystream */
|
||||
chacha_encrypt_bytes(&rng->rsx.rs_chacha, rng->rsx.rs_buf,
|
||||
rng->rsx.rs_buf, sizeof(rng->rsx.rs_buf));
|
||||
/* mix in optional user provided data */
|
||||
if (dat) {
|
||||
size_t i, m;
|
||||
|
||||
m = minimum(datlen, KEYSZ + IVSZ);
|
||||
for (i = 0; i < m; i++)
|
||||
rng->rsx.rs_buf[i] ^= dat[i];
|
||||
}
|
||||
/* immediately reinit for backtracking resistance */
|
||||
_rs_init(rng, rng->rsx.rs_buf, KEYSZ + IVSZ);
|
||||
memset(rng->rsx.rs_buf, 0, KEYSZ + IVSZ);
|
||||
rng->rs.rs_have = sizeof(rng->rsx.rs_buf) - KEYSZ - IVSZ;
|
||||
}
|
||||
|
||||
static void
|
||||
_rs_stir(struct _rng *rng)
|
||||
{
|
||||
u_char rnd[KEYSZ + IVSZ];
|
||||
uint32_t rekey_fuzz = 0;
|
||||
|
||||
if (getentropy(rnd, sizeof rnd) == -1)
|
||||
notpossible;
|
||||
|
||||
if (!rng->once) {
|
||||
_rs_init(rng, rnd, sizeof(rnd));
|
||||
} else {
|
||||
_rs_rekey(rng, rnd, sizeof(rnd));
|
||||
}
|
||||
explicit_bzero(rnd, sizeof(rnd)); /* discard source seed */
|
||||
|
||||
/* invalidate rs_buf */
|
||||
rng->rs.rs_have = 0;
|
||||
memset(rng->rsx.rs_buf, 0, sizeof(rng->rsx.rs_buf));
|
||||
|
||||
/* rekey interval should not be predictable */
|
||||
chacha_encrypt_bytes(&rng->rsx.rs_chacha, (uint8_t *)&rekey_fuzz,
|
||||
(uint8_t *)&rekey_fuzz, sizeof(rekey_fuzz));
|
||||
rng->rs.rs_count = REKEY_BASE + (rekey_fuzz % REKEY_BASE);
|
||||
}
|
||||
|
||||
static inline void
|
||||
_rs_stir_if_needed(struct _rng *rng, size_t len)
|
||||
{
|
||||
if (!rng->once || rng->rs.rs_count <= len)
|
||||
_rs_stir(rng);
|
||||
if (rng->rs.rs_count <= len)
|
||||
rng->rs.rs_count = 0;
|
||||
else
|
||||
rng->rs.rs_count -= len;
|
||||
}
|
||||
|
||||
static inline void
|
||||
_rs_random_buf(struct _rng *rng, void *_buf, size_t n)
|
||||
{
|
||||
u_char *buf = (u_char *)_buf;
|
||||
u_char *keystream;
|
||||
size_t m;
|
||||
|
||||
_rs_stir_if_needed(rng, n);
|
||||
while (n > 0) {
|
||||
if (rng->rs.rs_have > 0) {
|
||||
m = minimum(n, rng->rs.rs_have);
|
||||
keystream = rng->rsx.rs_buf + sizeof(rng->rsx.rs_buf)
|
||||
- rng->rs.rs_have;
|
||||
memcpy(buf, keystream, m);
|
||||
memset(keystream, 0, m);
|
||||
buf += m;
|
||||
n -= m;
|
||||
rng->rs.rs_have -= m;
|
||||
}
|
||||
if (rng->rs.rs_have == 0)
|
||||
_rs_rekey(rng, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills buffer with random data.
|
||||
*
|
||||
* This is the best random number generator provided by cosmo libc.
|
||||
*
|
||||
* This RNG is simple. Unlike getrandom() which can fail in numerous
|
||||
* ways, this API always succeeds and returns the full number of bytes.
|
||||
*
|
||||
* This RNG is cryptographic grade. It uses D.J. Bernstein's ChaCha
|
||||
* algorithm. The code for this implementation was originally copied
|
||||
* from the OpenBSD and FreeBSD codebases.
|
||||
*
|
||||
* This RNG is fast. arc4random runs in userspace and occasionally
|
||||
* reseeds itself using getentropy(). This gives you a 10x performance
|
||||
* boost by avoiding costly system calls.
|
||||
*
|
||||
* This RNG is scalable. The Cosmopolitan Libc authors have modified the
|
||||
* original OpenBSD source code so it doesn't use a GIL. We instead load
|
||||
* balance threads across a fixed number of arc4random states which are
|
||||
* indexed based on the current cpu if possible otherwise the thread id.
|
||||
* This way threads needn't wait in sequence and can work concurrently.
|
||||
* The only other fast cryptographic userspace PRNG we've seen that can
|
||||
* do this is Microsoft's ProcessPrng() in Windows 10+.
|
||||
*
|
||||
* This RNG makes the single-threaded case go fast. Similar to FreeBSD
|
||||
* our integration with the C library enables us to know when it's safe
|
||||
* to avoid mutexes entirely. If your app never creates a thread, then
|
||||
* arc4random() will never need to pay the cost of locks.
|
||||
*
|
||||
* This RNG is fork() safe. Naive random number generators oftentimes
|
||||
* have an issue where forked child processes are likely to generate the
|
||||
* same numbers as the parent. Our libc system call wrapper ensures the
|
||||
* RNG state is appropriately wiped. However if you talk directly to the
|
||||
* kernel, using functions like sys_fork(), you're on your own. vfork()
|
||||
* should be safe, since the child process is actually just a thread.
|
||||
*/
|
||||
void
|
||||
arc4random_buf(void *buf, size_t n)
|
||||
{
|
||||
size_t i, w;
|
||||
struct _rng *rng;
|
||||
|
||||
FORBIDDEN_IN_POSIX_SPAWN;
|
||||
|
||||
/* load balance threads across fixed number of shards */
|
||||
rng = &_rs_state[cosmo_shard()];
|
||||
|
||||
/* avoid one thread hogging shard */
|
||||
for (i = 0; i < n; i += w) {
|
||||
w = n - i;
|
||||
if (w > FAIR_SHARE)
|
||||
w = FAIR_SHARE;
|
||||
if (__isthreaded >= 2)
|
||||
pthread_mutex_lock(&rng->mtx);
|
||||
_rs_random_buf(rng, (char *)buf + i, w);
|
||||
if (__isthreaded >= 2)
|
||||
pthread_mutex_unlock(&rng->mtx);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns single random 32-bit value.
|
||||
*
|
||||
* The arc4random() function returns pseudo-random numbers in the range
|
||||
* of 0 to (2**32)−1, and has twice the range of rand(3) and random(3).
|
||||
*
|
||||
* This function is a wrapper around arc4random_buf() which can be used
|
||||
* to obtain an arbitrary number of bytes.
|
||||
*
|
||||
* Be careful about using the euclidean remainder operator (`%`) on the
|
||||
* result of this function. It's only safe to do that if you keep the
|
||||
* result unsigned and the denominator is a two power. What you almost
|
||||
* certainly want instead is arc4random_uniform() to do safe modulus.
|
||||
*/
|
||||
uint32_t
|
||||
arc4random(void)
|
||||
{
|
||||
uint32_t val;
|
||||
arc4random_buf(&val, sizeof(val));
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns random number less than `upper_bound`.
|
||||
*
|
||||
* This is like arc4random() except the arc4random_uniform() function
|
||||
* will calculate a uniformly distributed random number that's less than
|
||||
* `upper_bound` while avoiding "modulo bias".
|
||||
*
|
||||
* This implementation uses the optimized technique discovered by Daniel
|
||||
* Lemire in his paper "Fast Random Integer Generation in an Interval",
|
||||
* Association for Computing Machinery, ACM Trans. Model. Comput.
|
||||
* Simul., no. 1, vol. 29, pp. 1--12, New York, NY, USA, January 2019.
|
||||
* Available: https://r-libre.teluq.ca/1437/1/rangedrng.pdf
|
||||
*/
|
||||
uint32_t
|
||||
arc4random_uniform(uint32_t upper_bound)
|
||||
{
|
||||
uint64_t product;
|
||||
|
||||
/*
|
||||
* The paper uses these variable names:
|
||||
*
|
||||
* L -- log2(UINT32_MAX+1)
|
||||
* s -- upper_bound
|
||||
* x -- arc4random() return value
|
||||
* m -- product
|
||||
* l -- (uint32_t)product
|
||||
* t -- threshold
|
||||
*/
|
||||
|
||||
if (upper_bound <= 1)
|
||||
return 0;
|
||||
|
||||
product = upper_bound * (uint64_t)arc4random();
|
||||
|
||||
if ((uint32_t)product < upper_bound) {
|
||||
uint32_t threshold;
|
||||
|
||||
/* threshold = (2**32 - upper_bound) % upper_bound */
|
||||
threshold = -upper_bound % upper_bound;
|
||||
while ((uint32_t)product < threshold)
|
||||
product = upper_bound * (uint64_t)arc4random();
|
||||
}
|
||||
|
||||
return product >> 32;
|
||||
}
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/describebacktrace.h"
|
||||
#include "libc/log/internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/symbols.internal.h"
|
||||
|
||||
@@ -32,8 +33,9 @@ void __assert_fail(const char *expr, const char *file, int line) {
|
||||
char ibuf[12];
|
||||
sigset_t m = __sig_block();
|
||||
FormatInt32(ibuf, line);
|
||||
tinyprint(2, file, ":", ibuf, ": \e[31;1massert(", expr,
|
||||
") failed\e[0m (cosmoaddr2line ", FindDebugBinary(), " ",
|
||||
tinyprint(2, file, ":", ibuf, ": ", __nocolor ? "" : "\e[31;1m", "assert(",
|
||||
expr, ") failed", __nocolor ? "" : "\e[0m", " (cosmoaddr2line ",
|
||||
FindDebugBinary(), " ",
|
||||
DescribeBacktrace(__builtin_frame_address(0)), ")\n", NULL);
|
||||
__sig_unblock(m);
|
||||
abort();
|
||||
|
||||
+24
-47
@@ -42,28 +42,15 @@
|
||||
|
||||
#define MAP_FAILED ((void *)-1)
|
||||
|
||||
#define WTERMSIG(x) (127 & (x))
|
||||
#define WCOREDUMP(x) (128 & (x))
|
||||
#define WIFEXITED(x) (!WTERMSIG(x))
|
||||
#define WEXITSTATUS(x) ((x) >> 8)
|
||||
#define WSTOPSIG(x) ((0xff00 & (x)) >> 8)
|
||||
#define WIFSTOPPED(x) __wifstopped(x)
|
||||
#define WIFSIGNALED(x) __wifsignaled(x)
|
||||
#define WIFCONTINUED(x) __wifcontinued(x)
|
||||
#define W_STOPCODE(x) ((x) << 8 | 0177)
|
||||
|
||||
#ifdef _COSMO_SOURCE
|
||||
#define clone __clone
|
||||
#define commandv __commandv
|
||||
#define fileexists __fileexists
|
||||
#define ischardev __ischardev
|
||||
#define isdirectory __isdirectory
|
||||
#define isexecutable __isexecutable
|
||||
#define isregularfile __isregularfile
|
||||
#define issymlink __issymlink
|
||||
#define makedirs __makedirs
|
||||
#define tmpfd __tmpfd
|
||||
#endif
|
||||
#define WEXITSTATUS(s) ((s) >> 8)
|
||||
#define WTERMSIG(s) ((s) & 0x7f)
|
||||
#define WSTOPSIG(s) (((s) & 0xff00) >> 8)
|
||||
#define WIFEXITED(s) (!WTERMSIG(s))
|
||||
#define WIFSTOPPED(s) (((s) & 0xff) == 0x7f)
|
||||
#define WIFSIGNALED(s) (((signed char)(((s) & 0x7f) + 1) >> 1) > 0)
|
||||
#define WCOREDUMP(s) ((s) & 0x80)
|
||||
#define WIFCONTINUED(s) (((s) & 0xffff) == 0xffff)
|
||||
#define W_STOPCODE(s) ((s) << 8 | 0x7f)
|
||||
|
||||
COSMOPOLITAN_C_START_
|
||||
/*───────────────────────────────────────────────────────────────────────────│─╗
|
||||
@@ -83,8 +70,6 @@ int chmod(const char *, unsigned) libcesque __read_only(1);
|
||||
int chown(const char *, unsigned, unsigned) libcesque __read_only(1);
|
||||
int chroot(const char *) libcesque __read_only(1);
|
||||
int close(int) libcesque;
|
||||
int close_range(unsigned, unsigned, unsigned) libcesque;
|
||||
int closefrom(int) libcesque;
|
||||
int creat(const char *, unsigned) libcesque __read_only(1);
|
||||
int dup(int) libcesque;
|
||||
int dup2(int, int) libcesque;
|
||||
@@ -101,7 +86,6 @@ int fchmod(int, unsigned) libcesque;
|
||||
int fchmodat(int, const char *, unsigned, int) libcesque __read_only(2);
|
||||
int fchown(int, unsigned, unsigned) libcesque;
|
||||
int fchownat(int, const char *, unsigned, unsigned, int) libcesque __read_only(2);
|
||||
int fcntl(int, int, ...) libcesque;
|
||||
int fdatasync(int) libcesque;
|
||||
int fexecve(int, char *const[], char *const[]) libcesque __read_only(2) __read_only(3);
|
||||
int flock(int, int) libcesque;
|
||||
@@ -171,7 +155,7 @@ int ttyname_r(int, char *, size_t) libcesque __write_only(2, 3);
|
||||
int unlink(const char *) libcesque __read_only(1);
|
||||
int unlinkat(int, const char *, int) libcesque __read_only(2);
|
||||
int usleep(uint64_t) libcesque;
|
||||
int vfork(void) libcesque returnstwice;
|
||||
int vfork(void) dontthrow returnstwice;
|
||||
int wait(int *) libcesque __write_only(1);
|
||||
int waitpid(int, int *, int) libcesque __write_only(2);
|
||||
int64_t clock(void) libcesque;
|
||||
@@ -194,6 +178,11 @@ unsigned ualarm(unsigned, unsigned) libcesque;
|
||||
unsigned umask(unsigned) libcesque;
|
||||
void sync(void) libcesque;
|
||||
|
||||
#if defined(_COSMO_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE)
|
||||
int madvise(void *, size_t, int);
|
||||
void closefrom(int) libcesque;
|
||||
#endif
|
||||
|
||||
#if defined(_COSMO_SOURCE) || defined(_GNU_SOURCE)
|
||||
int syncfs(int) libcesque;
|
||||
int prctl(int, ...) libcesque;
|
||||
@@ -204,26 +193,21 @@ int getresgid(unsigned *, unsigned *, unsigned *) libcesque __write_only(1) __wr
|
||||
int getresuid(unsigned *, unsigned *, unsigned *) libcesque __write_only(1) __write_only(2) __write_only(3);
|
||||
char *get_current_dir_name(void) libcesque __wur;
|
||||
ssize_t splice(int, int64_t *, int, int64_t *, size_t, unsigned) libcesque __read_write(2) __read_write(4);
|
||||
int memfd_create(const char *, unsigned int) libcesque __read_only(1);
|
||||
int execvpe(const char *, char *const[], char *const[]) libcesque __read_only(1) __read_only(2) __read_only(3);
|
||||
int euidaccess(const char *, int) libcesque __read_only(1);
|
||||
int eaccess(const char *, int) libcesque __read_only(1);
|
||||
int madvise(void *, uint64_t, int) libcesque __read_write(1);
|
||||
int getcpu(unsigned *, unsigned *) libcesque __write_only(1) __write_only(2);
|
||||
int close_range(unsigned, unsigned, unsigned) libcesque;
|
||||
#endif
|
||||
|
||||
#ifdef _COSMO_SOURCE
|
||||
bool32 fdexists(int) libcesque;
|
||||
bool32 fileexists(const char *) libcesque __read_only(1);
|
||||
bool32 ischardev(int) libcesque;
|
||||
bool32 isdirectory(const char *) libcesque __read_only(1);
|
||||
bool32 isexecutable(const char *) libcesque __read_only(1);
|
||||
bool32 isregularfile(const char *) libcesque __read_only(1);
|
||||
bool32 issymlink(const char *) libcesque __read_only(1);
|
||||
#if defined(_COSMO_SOURCE) || defined(_BSD_SOURCE)
|
||||
#define commandv __commandv
|
||||
#define copyfd __copyfd
|
||||
#define makedirs __makedirs
|
||||
#define tmpfd __tmpfd
|
||||
char *commandv(const char *, char *, size_t) libcesque __read_only(1) __write_only(2, 3);
|
||||
int __getcwd(char *, size_t) libcesque __write_only(1, 2);
|
||||
int clone(void *, void *, size_t, int, void *, void *, void *, void *);
|
||||
int fadvise(int, uint64_t, uint64_t, int) libcesque;
|
||||
int __clone(void *, void *, size_t, int, void *, void *, void *, void *);
|
||||
int __getcwd(char *, size_t) libcesque;
|
||||
int makedirs(const char *, unsigned) libcesque __read_only(1);
|
||||
int pivot_root(const char *, const char *) libcesque __read_only(1) __read_only(2);
|
||||
int pledge(const char *, const char *) libcesque __read_only(1) __read_only(2);
|
||||
@@ -237,26 +221,19 @@ int sys_mlockall(int) libcesque;
|
||||
int sys_munlock(const void *, size_t) libcesque __read_only(1);
|
||||
int sys_munlockall(void) libcesque;
|
||||
int sys_personality(uint64_t) libcesque;
|
||||
int sys_ptrace(int, ...) libcesque;
|
||||
int sysctl(int *, unsigned, void *, size_t *, void *, size_t) libcesque __read_write(1) __read_write(4) __read_write(5);
|
||||
int sysctlbyname(const char *, void *, size_t *, void *, size_t) libcesque __read_only(1) __write_only(2) __read_write(3) __read_only(4);
|
||||
int sysctlnametomib(const char *, int *, size_t *) libcesque __read_only(1) __write_only(2) __read_write(3);
|
||||
int tmpfd(void) libcesque;
|
||||
int touch(const char *, unsigned) libcesque __read_only(1);
|
||||
int unveil(const char *, const char *) libcesque __read_only(1);
|
||||
long ptrace(int, ...) libcesque;
|
||||
ssize_t copyfd(int, int, size_t) libcesque;
|
||||
ssize_t readansi(int, char *, size_t) libcesque;
|
||||
ssize_t tinyprint(int, const char *, ...) libcesque nullterminated();
|
||||
void shm_path_np(const char *, char[hasatleast 78]) libcesque;
|
||||
#endif /* _COSMO_SOURCE */
|
||||
#endif
|
||||
|
||||
int system(const char *) libcesque;
|
||||
|
||||
int __wifstopped(int) libcesque pureconst;
|
||||
int __wifcontinued(int) libcesque pureconst;
|
||||
int __wifsignaled(int) libcesque pureconst;
|
||||
|
||||
#if defined(_LARGEFILE64_SOURCE) || defined(_GNU_SOURCE)
|
||||
#define lseek64 lseek
|
||||
#define pread64 pread
|
||||
|
||||
+40
-37
@@ -16,48 +16,53 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/process.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/sysv/errno.h"
|
||||
|
||||
textwindows int sys_chdir_nt_impl(char16_t path[hasatleast PATH_MAX],
|
||||
uint32_t len) {
|
||||
uint32_t n;
|
||||
char16_t var[4];
|
||||
if (len && path[len - 1] != u'\\') {
|
||||
if (len + 2 > PATH_MAX)
|
||||
return enametoolong();
|
||||
path[len + 0] = u'\\';
|
||||
path[len + 1] = u'\0';
|
||||
}
|
||||
if (SetCurrentDirectory(path)) {
|
||||
/*
|
||||
* Now we need to set a magic environment variable.
|
||||
*/
|
||||
if ((n = GetCurrentDirectory(PATH_MAX, path))) {
|
||||
if (n < PATH_MAX) {
|
||||
if (!((path[0] == '/' && path[1] == '/') ||
|
||||
(path[0] == '\\' && path[1] == '\\'))) {
|
||||
var[0] = '=';
|
||||
var[1] = path[0];
|
||||
var[2] = ':';
|
||||
var[3] = 0;
|
||||
if (!SetEnvironmentVariable(var, path))
|
||||
return __winerr();
|
||||
}
|
||||
return 0;
|
||||
textwindows int sys_chdir_nt16(char16_t path[hasatleast PATH_MAX],
|
||||
uint32_t len) {
|
||||
|
||||
// the win32 SetCurrentDirectory() API wants us to put back a trailing
|
||||
// slash that was removed earlier, when __mkntpath() normalized things
|
||||
if (len + 2 > PATH_MAX)
|
||||
return enametoolong();
|
||||
if (SetCurrentDirectory(path))
|
||||
return 0;
|
||||
|
||||
// remap win32 error codes to unix
|
||||
uint32_t dwErrorCode;
|
||||
switch ((dwErrorCode = GetLastError())) {
|
||||
case kNtErrorFileNotFound: // 2 a.k.a. ENOENT
|
||||
// it does this when the parent components exist but file didn't
|
||||
errno = ENOENT;
|
||||
break;
|
||||
case kNtErrorPathNotFound: // 3 a.k.a. ENOTDIR
|
||||
// with Windows, this means that the file doesn't exist, because
|
||||
// a parent component didn't exist. however unix still considers
|
||||
// that to be an ENOENT error. on UNIX this only becomes ENOTDIR
|
||||
// if a parent component DID exist, but was not a real directory
|
||||
if (__hasregularparent(path)) {
|
||||
errno = ENOTDIR;
|
||||
} else {
|
||||
return enametoolong();
|
||||
errno = ENOENT;
|
||||
}
|
||||
} else {
|
||||
return __winerr();
|
||||
}
|
||||
} else {
|
||||
return __fix_enotdir(__winerr(), path);
|
||||
break;
|
||||
case kNtErrorDirectory: // 267 a.k.a. EISDIR
|
||||
// it'll do this when try to chdir to a regular file that exists
|
||||
// it could also be an ELOOP error although not worth supporting
|
||||
errno = ENOTDIR;
|
||||
break;
|
||||
default:
|
||||
errno = __errno_windows2linux(dwErrorCode);
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
textwindows int sys_chdir_nt(const char *path) {
|
||||
@@ -65,7 +70,5 @@ textwindows int sys_chdir_nt(const char *path) {
|
||||
char16_t path16[PATH_MAX];
|
||||
if ((len = __mkntpath(path, path16)) == -1)
|
||||
return -1;
|
||||
if (!len)
|
||||
return enoent();
|
||||
return sys_chdir_nt_impl(path16, len);
|
||||
return sys_chdir_nt16(path16, len);
|
||||
}
|
||||
+11
-8
@@ -20,6 +20,7 @@
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/log/log.h"
|
||||
@@ -33,14 +34,19 @@
|
||||
* The current directory is shared by all threads in a process. This
|
||||
* does not update the `PWD` environment variable.
|
||||
*
|
||||
* On Windows it's important to consider that when you chdir() into a
|
||||
* directory, any attempt to rmdir() that directory will raise EACCES.
|
||||
*
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @raise ELOOP if a loop was detected resolving components of `path`
|
||||
* @raise EACCES if search permission was denied on directory
|
||||
* @raise ENOTDIR if component of `path` isn't a directory
|
||||
* @raise ENOTDIR if parent of `path` exists as non-directory
|
||||
* @raise ENOTDIR if `path` is a regular file that exists
|
||||
* @raise ENOMEM if insufficient memory was available
|
||||
* @raise EFAULT if `path` points to invalid memory
|
||||
* @raise ENOTSUP if `path` is a `/zip/...` file
|
||||
* @raise ENAMETOOLONG if `path` was too long
|
||||
* @raise ENOENT if `path` is empty string
|
||||
* @raise ENOENT if `path` doesn't exist
|
||||
* @raise EIO if an i/o error happened
|
||||
* @asyncsignalsafe
|
||||
@@ -49,16 +55,13 @@
|
||||
int chdir(const char *path) {
|
||||
int rc;
|
||||
struct ZiposUri zipname;
|
||||
if (_weaken(__get_tmpdir)) {
|
||||
if (_weaken(__get_tmpdir))
|
||||
_weaken(__get_tmpdir)();
|
||||
}
|
||||
if (_weaken(GetAddr2linePath)) {
|
||||
if (_weaken(GetAddr2linePath))
|
||||
_weaken(GetAddr2linePath)();
|
||||
}
|
||||
if (_weaken(GetProgramExecutableName)) {
|
||||
if (_weaken(GetProgramExecutableName))
|
||||
_weaken(GetProgramExecutableName)();
|
||||
}
|
||||
if (!path) {
|
||||
if (kisdangerous(path)) {
|
||||
rc = efault();
|
||||
} else if (_weaken(__zipos_parseuri) &&
|
||||
_weaken(__zipos_parseuri)(path, &zipname) != -1) {
|
||||
|
||||
@@ -18,9 +18,10 @@
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#ifdef __x86_64__
|
||||
#if SupportsWindows()
|
||||
|
||||
textwindows int _check_signal(bool restartable) {
|
||||
int status;
|
||||
|
||||
+2
-1
@@ -19,6 +19,7 @@
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
@@ -48,7 +49,7 @@
|
||||
*/
|
||||
int chroot(const char *path) {
|
||||
int rc;
|
||||
if (!path) {
|
||||
if (kisdangerous(path)) {
|
||||
rc = efault();
|
||||
} else {
|
||||
rc = sys_chroot(path);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timespec.internal.h"
|
||||
#include "libc/cosmotime.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/describeflags.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
|
||||
@@ -17,32 +17,28 @@
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/calls/struct/timespec.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/cosmotime.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/stdio/sysparam.h"
|
||||
#include "libc/sysv/consts/timer.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#ifdef __x86_64__
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
#include "libc/nt/winmm.h"
|
||||
#if SupportsWindows()
|
||||
|
||||
static textwindows int sys_clock_nanosleep_nt_impl(int clock,
|
||||
struct timespec abs,
|
||||
sigset_t waitmask) {
|
||||
uint32_t msdelay;
|
||||
struct timespec now;
|
||||
for (;;) {
|
||||
if (sys_clock_gettime_nt(clock, &now))
|
||||
return -1;
|
||||
if (timespec_cmp(now, abs) >= 0)
|
||||
return 0;
|
||||
msdelay = timespec_tomillis(timespec_sub(abs, now));
|
||||
msdelay = MIN(msdelay, -1u);
|
||||
if (_park_norestart(msdelay, waitmask))
|
||||
return -1;
|
||||
}
|
||||
struct timespec now, wall;
|
||||
sys_clock_gettime_nt(0, &wall);
|
||||
if (sys_clock_gettime_nt(clock, &now))
|
||||
return -1;
|
||||
if (timespec_cmp(abs, now) > 0)
|
||||
wall = timespec_add(wall, timespec_sub(abs, now));
|
||||
return _park_norestart(wall, waitmask);
|
||||
}
|
||||
|
||||
textwindows int sys_clock_nanosleep_nt(int clock, int flags,
|
||||
@@ -70,4 +66,9 @@ BailOut:
|
||||
return rc;
|
||||
}
|
||||
|
||||
// called by WinMain() if clock_nanosleep() is linked
|
||||
__msabi textwindows dontinstrument void sys_clock_nanosleep_nt_init(void) {
|
||||
__imp_timeBeginPeriod(1); // make sleep(1ms) not take 15ms
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
||||
@@ -19,13 +19,14 @@
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/calls/struct/timespec.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/cosmotime.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
int sys_clock_nanosleep_openbsd(int clock, int flags,
|
||||
const struct timespec *req,
|
||||
struct timespec *rem) {
|
||||
relegated int sys_clock_nanosleep_openbsd(int clock, int flags,
|
||||
const struct timespec *req,
|
||||
struct timespec *rem) {
|
||||
int res;
|
||||
struct timespec start, relative, remainder;
|
||||
if (!flags) {
|
||||
|
||||
@@ -54,8 +54,5 @@ int sys_clock_nanosleep(int clock, int flags, //
|
||||
rc = ecanceled();
|
||||
}
|
||||
END_CANCELATION_POINT;
|
||||
STRACE("sys_clock_nanosleep(%s, %s, %s, [%s]) → %d% m",
|
||||
DescribeClockName(clock), DescribeSleepFlags(flags),
|
||||
DescribeTimespec(0, req), DescribeTimespec(rc, rem), rc);
|
||||
return rc;
|
||||
}
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "libc/calls/struct/timespec.internal.h"
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/cosmotime.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/runtime/syslib.internal.h"
|
||||
|
||||
@@ -17,11 +17,16 @@
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/calls/struct/timespec.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/describeflags.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
#include "libc/sysv/consts/timer.h"
|
||||
|
||||
int sys_clock_nanosleep(int, int, const struct timespec *, struct timespec *);
|
||||
|
||||
/**
|
||||
* Sleeps for particular amount of time.
|
||||
*
|
||||
@@ -57,6 +62,7 @@
|
||||
*
|
||||
* @param clock may be
|
||||
* - `CLOCK_REALTIME`
|
||||
* - `CLOCK_BOOTTIME`
|
||||
* - `CLOCK_MONOTONIC`
|
||||
* - `CLOCK_REALTIME_COARSE` but is likely to sleep negative time
|
||||
* - `CLOCK_MONTONIC_COARSE` but is likely to sleep negative time
|
||||
@@ -98,5 +104,8 @@ errno_t clock_nanosleep(int clock, int flags, //
|
||||
rc = sys_clock_nanosleep(clock, flags, req, rem);
|
||||
err = !rc ? 0 : errno;
|
||||
errno = old;
|
||||
STRACE("clock_nanosleep(%s, %s, %s, [%s]) → %s", DescribeClockName(clock),
|
||||
DescribeSleepFlags(flags), DescribeTimespec(0, req),
|
||||
DescribeTimespec(rc, rem), DescribeErrno(err));
|
||||
return err;
|
||||
}
|
||||
@@ -16,10 +16,9 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/calls/struct/timespec.internal.h"
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
#include "libc/calls/struct/timeval.internal.h"
|
||||
#include "libc/cosmotime.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/describeflags.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
|
||||
+74
-42
@@ -16,54 +16,100 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/flocks.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/fds.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/runtime/zipos.internal.h"
|
||||
#include "libc/sock/syscall_fd.internal.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
// for performance reasons we want to avoid holding __fds_lock()
|
||||
// while sys_close() is happening. this leaves the kernel / libc
|
||||
// having a temporarily inconsistent state. routines that obtain
|
||||
// file descriptors the way __zipos_open() does need to retry if
|
||||
// there's indication this race condition happened.
|
||||
#include "libc/sysv/pib.h"
|
||||
|
||||
static int close_impl(int fd) {
|
||||
|
||||
if (fd < 0) {
|
||||
// handle obvious case
|
||||
if (fd < 0)
|
||||
return ebadf();
|
||||
}
|
||||
|
||||
// give kprintf() the opportunity to dup() stderr
|
||||
if (fd == 2 && _weaken(kloghandle)) {
|
||||
if (fd == 2 && !__vforked && _weaken(kloghandle))
|
||||
_weaken(kloghandle)();
|
||||
}
|
||||
|
||||
if (__isfdkind(fd, kFdZip)) {
|
||||
unassert(_weaken(__zipos_close));
|
||||
return _weaken(__zipos_close)(fd);
|
||||
}
|
||||
|
||||
if (!IsWindows() && !IsMetal()) {
|
||||
// unix isn't required to track fds
|
||||
if (fd >= __get_pib()->fds.n) {
|
||||
if (IsWindows() || IsMetal())
|
||||
return ebadf();
|
||||
return sys_close(fd);
|
||||
}
|
||||
|
||||
if (IsWindows()) {
|
||||
return sys_close_nt(fd, fd);
|
||||
// if we're vforked on unix then we can't even modify f->kind
|
||||
if (__vforked && !(IsWindows() || IsMetal()))
|
||||
return sys_close(fd);
|
||||
|
||||
// atomically close file descriptor table entry
|
||||
int rc = 0;
|
||||
struct Fd *f = __get_pib()->fds.p + fd;
|
||||
switch (atomic_exchange(&f->kind, kFdReserved)) {
|
||||
case kFdEmpty:
|
||||
if (IsWindows() || IsMetal()) {
|
||||
rc = ebadf();
|
||||
} else {
|
||||
rc = sys_close(fd);
|
||||
}
|
||||
break;
|
||||
case kFdReserved:
|
||||
return ebadf();
|
||||
case kFdZip:
|
||||
if (_weaken(__zipos_close))
|
||||
rc = _weaken(__zipos_close)(fd);
|
||||
break;
|
||||
#if SupportsMetal()
|
||||
case kFdSerial:
|
||||
break;
|
||||
#endif
|
||||
#if SupportsWindows()
|
||||
case kFdDevRandom:
|
||||
break;
|
||||
case kFdFile:
|
||||
if (!__vforked)
|
||||
if (_weaken(__flocks_close))
|
||||
_weaken(__flocks_close)(f);
|
||||
if (!__vforked || f->was_created_during_vfork) {
|
||||
if (f->cursor)
|
||||
__cursor_unref(f->cursor);
|
||||
if (!CloseHandle(f->handle))
|
||||
rc = __winerr();
|
||||
}
|
||||
break;
|
||||
case kFdDevNull:
|
||||
case kFdConsole:
|
||||
if (!__vforked || f->was_created_during_vfork)
|
||||
if (!CloseHandle(f->handle))
|
||||
rc = __winerr();
|
||||
break;
|
||||
case kFdSocket:
|
||||
if (!__vforked || f->was_created_during_vfork)
|
||||
if (_weaken(sys_closesocket_nt))
|
||||
rc = _weaken(sys_closesocket_nt)(f);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
rc = ebadf();
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
// wipe fd and update lowest file descriptor number
|
||||
// we do this even if there was an underlying os error
|
||||
__releasefd(fd);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,6 +120,8 @@ static int close_impl(int fd) {
|
||||
* - openat()
|
||||
* - socket()
|
||||
* - accept()
|
||||
* - pipe()
|
||||
* - socketpair()
|
||||
* - landlock_create_ruleset()
|
||||
*
|
||||
* This function should never be reattempted if an error is returned;
|
||||
@@ -82,29 +130,13 @@ static int close_impl(int fd) {
|
||||
*
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @raise EINTR if signal was delivered; do *not* retry
|
||||
* @raise EBADF if `fd` is negative or not open; however, an exception
|
||||
* is made by Cosmopolitan Libc for `close(-1)` which returns zero
|
||||
* and does nothing, in order to assist with code that may wish to
|
||||
* close the same resource multiple times without dirtying `errno`
|
||||
* @raise EBADF if `fd` is negative or not open
|
||||
* @raise EIO if a low-level i/o error occurred
|
||||
* @asyncsignalsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
int close(int fd) {
|
||||
int rc;
|
||||
if (__isfdkind(fd, kFdZip)) { // XXX IsWindows()?
|
||||
BLOCK_SIGNALS;
|
||||
__fds_lock();
|
||||
rc = close_impl(fd);
|
||||
if (!__vforked)
|
||||
__releasefd(fd);
|
||||
__fds_unlock();
|
||||
ALLOW_SIGNALS;
|
||||
} else {
|
||||
rc = close_impl(fd);
|
||||
if (!__vforked)
|
||||
__releasefd(fd);
|
||||
}
|
||||
int rc = close_impl(fd);
|
||||
STRACE("close(%d) → %d% m", fd, rc);
|
||||
return rc;
|
||||
}
|
||||
@@ -23,29 +23,24 @@
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Closes inclusive range of file descriptors, e.g.
|
||||
* Closes inclusive range of file descriptors.
|
||||
*
|
||||
* // close all non-stdio file descriptors
|
||||
* if (close_range(3, -1, 0) == -1) {
|
||||
* for (int i = 3; i < 256; ++i) {
|
||||
* close(i);
|
||||
* }
|
||||
* }
|
||||
* This system call is only supported on Linux 5.9+ and FreeBSD 13+.
|
||||
* Consider using closefrom() which will work on all platforms.
|
||||
*
|
||||
* The following flags are available:
|
||||
*
|
||||
* - `CLOSE_RANGE_UNSHARE` (Linux-only)
|
||||
* - `CLOSE_RANGE_CLOEXEC` (Linux-only)
|
||||
* - `CLOSE_RANGE_CLOEXEC` to mark file descriptors as close-on-exec
|
||||
* instead of actually closing them.
|
||||
*
|
||||
* This is only supported on Linux 5.9+ and FreeBSD 13+. Consider using
|
||||
* closefrom() which will work on OpenBSD too.
|
||||
* - `CLOSE_RANGE_UNSHARE` (Linux only) can improve performance in
|
||||
* situations where threads are in play.
|
||||
*
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @error EINVAL if flags are bad or first is greater than last
|
||||
* @error EMFILE if a weird race condition happens on Linux
|
||||
* @error ENOSYS if not Linux 5.9+ or FreeBSD 13+
|
||||
* @error ENOMEM on Linux maybe
|
||||
* @see closefrom()
|
||||
*/
|
||||
int close_range(unsigned int first, unsigned int last, unsigned int flags) {
|
||||
int rc;
|
||||
|
||||
+36
-29
@@ -18,47 +18,54 @@
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/blockcancel.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/rlimit.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/sysv/pib.h"
|
||||
|
||||
/**
|
||||
* Closes extra file descriptors, e.g.
|
||||
* Closes extra file descriptors.
|
||||
*
|
||||
* if (closefrom(3))
|
||||
* for (int i = 3; i < 256; ++i)
|
||||
* close(i);
|
||||
*
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @raise EBADF if `first` is negative
|
||||
* @raise ENOSYS if not Linux 5.9+, FreeBSD 8+, OpenBSD, or NetBSD
|
||||
* @raise EBADF on OpenBSD if `first` is greater than highest fd
|
||||
* @raise EINVAL if flags are bad or first is greater than last
|
||||
* @raise EMFILE if a weird race condition happens on Linux
|
||||
* @raise EINTR possibly on OpenBSD
|
||||
* @raise ENOMEM on Linux maybe
|
||||
* This system call is always successful.
|
||||
*/
|
||||
int closefrom(int first) {
|
||||
int rc;
|
||||
void closefrom(int minfd) {
|
||||
BLOCK_CANCELATION;
|
||||
if (first < 0) {
|
||||
// consistent with openbsd
|
||||
// freebsd allows this but it's dangerous
|
||||
// necessary on linux due to type signature
|
||||
rc = ebadf();
|
||||
} else if (IsFreebsd() || IsOpenbsd()) {
|
||||
rc = sys_closefrom(first);
|
||||
|
||||
// fix arg
|
||||
if (minfd < 0)
|
||||
minfd = 0;
|
||||
|
||||
// try syscall
|
||||
int rc;
|
||||
int olderr = errno;
|
||||
if (IsFreebsd() || IsOpenbsd()) {
|
||||
rc = sys_closefrom(minfd);
|
||||
} else if (IsLinux()) {
|
||||
rc = sys_close_range(first, 0xffffffffu, 0);
|
||||
rc = sys_close_range(minfd, 0xffffffffu, 0);
|
||||
} else if (IsNetbsd()) {
|
||||
rc = __sys_fcntl(first, 10 /*F_CLOSEM*/, first);
|
||||
rc = __sys_fcntl(minfd, 10 /*F_CLOSEM*/, minfd);
|
||||
} else {
|
||||
rc = enosys();
|
||||
rc = -1;
|
||||
}
|
||||
|
||||
// try polyfill
|
||||
if (rc == -1) {
|
||||
long maxfd = 1024;
|
||||
if (IsWindows() || IsMetal()) {
|
||||
maxfd = __get_pib()->fds.n;
|
||||
} else {
|
||||
struct rlimit rl;
|
||||
if (!getrlimit(RLIMIT_NOFILE, &rl))
|
||||
if (rl.rlim_cur <= 0x7fffffffu)
|
||||
maxfd = rl.rlim_cur;
|
||||
}
|
||||
for (long fd = minfd; fd < maxfd; ++fd)
|
||||
close(fd);
|
||||
errno = olderr;
|
||||
}
|
||||
|
||||
ALLOW_CANCELATION;
|
||||
STRACE("closefrom(%d) → %d% m", first, rc);
|
||||
return rc;
|
||||
STRACE("closefrom(%d)", minfd);
|
||||
}
|
||||
@@ -83,9 +83,11 @@ static void copy_file_range_init(void) {
|
||||
* @return number of bytes transferred, or -1 w/ errno
|
||||
* @raise EXDEV if source and destination are on different filesystems
|
||||
* @raise EBADF if `infd` or `outfd` aren't open files or append-only
|
||||
* @raise EOPNOTSUPP if filesystem doesn't support this operation
|
||||
* @raise EPERM if `fdout` refers to an immutable file on Linux
|
||||
* @raise ECANCELED if thread was cancelled in masked mode
|
||||
* @raise EINVAL if ranges overlap or `flags` is non-zero
|
||||
* @raise EINVAL on eCryptFs filesystems that have a bug
|
||||
* @raise EFBIG if `setrlimit(RLIMIT_FSIZE)` is exceeded
|
||||
* @raise EFAULT if one of the pointers memory is bad
|
||||
* @raise ERANGE if overflow happens computing ranges
|
||||
|
||||
+3
-1
@@ -23,10 +23,12 @@
|
||||
/**
|
||||
* Creates file.
|
||||
*
|
||||
* This is equivalent to saying:
|
||||
* This function is equivalent to:
|
||||
*
|
||||
* int fd = openat(AT_FDCWD, file, O_CREAT | O_WRONLY | O_TRUNC, mode);
|
||||
*
|
||||
* Please see openat() for further documentation.
|
||||
*
|
||||
* @param file specifies filesystem path to create
|
||||
* @param mode is octal bits, e.g. 0644 usually
|
||||
* @return file descriptor, or -1 w/ errno
|
||||
|
||||
Loaded 100 of 3334 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user