Compare commits
39
Commits
bcb4e259c3
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d07a914a1
|
||
|
|
49968bdbfc
|
||
|
|
1f39e217d4
|
||
|
|
b731a02db4
|
||
|
|
c4c64a4eb5
|
||
|
|
9eb32c4010
|
||
|
|
523045b5ae
|
||
|
|
9b576757cf
|
||
|
|
fefd17241f
|
||
|
|
3bf875030a
|
||
|
|
f9a055f547
|
||
|
|
9ffe86fc55
|
||
|
|
8c0f23fbd0
|
||
|
|
e9aa002453
|
||
|
|
3ab43c7f10
|
||
|
|
c4156efeff
|
||
|
|
ea0482e9ff
|
||
|
|
c67e890b17
|
||
|
|
d495d3eb89
|
||
|
|
ed996ff4c4
|
||
|
|
3eff4c97fd
|
||
|
|
ddcf8f39be
|
||
|
|
5af4197ee9
|
||
|
|
a2f7c33601
|
||
|
|
14c6fd6cf6
|
||
|
|
c9e9676d38
|
||
|
|
6eaacd4448
|
||
|
|
98f4c3169c
|
||
|
|
87ab610a9b
|
||
|
|
7b11452b87
|
||
|
|
34408380f0
|
||
|
|
22587bd545
|
||
|
|
5355574121
|
||
|
|
b53ccaa1e8
|
||
|
|
bff8fe9c86
|
||
|
|
99eff26467
|
||
|
|
8bf754419f
|
||
|
|
14b0136a8e
|
||
|
|
26786e9027
|
+379
-11
@@ -15,9 +15,23 @@ AM_CFLAGS = @VLIBC_CFLAGS@
|
||||
# appended to libvlibc_la_SOURCES WHEN ITS TODO LANDS — the list grows
|
||||
# incrementally and never references a directory that does not exist yet:
|
||||
#
|
||||
# W1 (wired now): src/internal, src/start, src/errno,
|
||||
# src/setjmp/x86_64, src/string, crt/ (crt1.o)
|
||||
# todo 7 src/malloc todo 39-42 src/math todo 50 src/multibyte
|
||||
# Wired so far (W1-W3 + W5/W6 slices, each wired when its todo landed):
|
||||
# src/internal, src/start, src/errno,
|
||||
# src/setjmp/x86_64, src/string, crt/ (crt1.o),
|
||||
# src/malloc (todo 7), src/ctype (todo 9,51),
|
||||
# src/stdlib (todo 11-14, incl. the 13 env slice),
|
||||
# src/unistd (todo 19), src/process (todo 20,23),
|
||||
# src/fcntl (todo 21), src/stat (todo 22),
|
||||
# src/dirent (todo 24), src/select (todo 25),
|
||||
# src/time (todo 26 + todo 27's sys/time
|
||||
# slice — gettimeofday/settimeofday/
|
||||
# getitimer/setitimer/utimes.c + sys/time.h —
|
||||
# all wired), src/stdio (todo 15-18 FILE core
|
||||
# + popen; vfprintf.c/vfprintf_float.c now
|
||||
# wired with the printf test),
|
||||
# src/math (todo 39-42 basic set — all L1,
|
||||
# wired with the consolidated math test)
|
||||
# todo 39-42 src/math todo 50 src/multibyte
|
||||
# todo 11-14 src/stdlib todo 43 src/complex todo 53-54 src/regex
|
||||
# todo 15-18 src/stdio todo 9,51 src/ctype todo 57-58 src/network
|
||||
# todo 19,38 src/unistd todo 55 src/search todo 59 src/mq
|
||||
@@ -41,6 +55,15 @@ AM_CFLAGS = @VLIBC_CFLAGS@
|
||||
# standalone crt1.o below (the sole crt owner; todo 64 reuses, never rebuilds).
|
||||
|
||||
# Level-independent L1 core (wired today + the pre-plan string slice).
|
||||
# W5/W6 additions follow the established split rule: whole-content-L2 TUs
|
||||
# (lockf, mknod, readdir_r/scandir/alphasort, ppoll, strptime/timer) go to
|
||||
# VLIBC_LEVEL2_SRCS; L1 bodies with L2 sections self-gated inside the .c
|
||||
# (fork.c vfork, groups.c setgroups, session.c tail, wait.c wait3/wait4,
|
||||
# time ctime/mktime/nanosleep/strftime/tzset L2 tails, stdio.c L2 tail,
|
||||
# unistd lseek64/dup3/pipe2/truncate) stay in CORE. task-19's unistd dir is
|
||||
# CORE throughout (self-gated L2 sections, no whole-file gate). T27's
|
||||
# sys/time slice (gettimeofday/settimeofday/getitimer/setitimer/utimes) is
|
||||
# whole-file L2, so it lives in VLIBC_LEVEL2_SRCS.
|
||||
VLIBC_CORE_SRCS = \
|
||||
src/vlibc.c \
|
||||
src/internal/assert_fail.c \
|
||||
@@ -50,6 +73,10 @@ VLIBC_CORE_SRCS = \
|
||||
src/start/exit.c \
|
||||
src/start/environ.c \
|
||||
src/start/tls.c \
|
||||
src/thread/futex.c \
|
||||
src/thread/pthread_self.c \
|
||||
src/thread/tls.c \
|
||||
src/thread/x86_64/__set_thread_area.s \
|
||||
src/errno/strerror.c \
|
||||
src/setjmp/x86_64/setjmp.s \
|
||||
src/setjmp/x86_64/longjmp.s \
|
||||
@@ -75,7 +102,126 @@ VLIBC_CORE_SRCS = \
|
||||
src/string/strstr.c \
|
||||
src/string/strtok.c \
|
||||
src/malloc/malloc.c \
|
||||
src/ctype/ctype.c
|
||||
src/ctype/ctype.c \
|
||||
src/stdlib/strtox.c \
|
||||
src/stdlib/strtox_impl.c \
|
||||
src/stdlib/strtod.c \
|
||||
src/stdlib/rand.c \
|
||||
src/stdlib/qsort.c \
|
||||
src/stdlib/div.c \
|
||||
src/stdlib/inttypes.c \
|
||||
src/stdlib/env.c \
|
||||
src/stdlib/multibyte.c \
|
||||
src/stdlib/mkstemp.c \
|
||||
src/stdio/stdio.c \
|
||||
src/stdio/fmemopen.c \
|
||||
src/stdio/getline.c \
|
||||
src/stdio/locking.c \
|
||||
src/stdio/popen.c \
|
||||
src/stdio/vfscanf.c \
|
||||
src/stdio/vfprintf.c \
|
||||
src/unistd/access.c \
|
||||
src/unistd/close.c \
|
||||
src/unistd/dup.c \
|
||||
src/unistd/fsync.c \
|
||||
src/unistd/lseek.c \
|
||||
src/unistd/open.c \
|
||||
src/unistd/pipe.c \
|
||||
src/unistd/pread.c \
|
||||
src/unistd/rw.c \
|
||||
src/unistd/sync.c \
|
||||
src/unistd/truncate.c \
|
||||
src/unistd/chdir.c \
|
||||
src/unistd/getcwd.c \
|
||||
src/unistd/getlogin.c \
|
||||
src/unistd/isatty.c \
|
||||
src/unistd/link.c \
|
||||
src/unistd/readlink.c \
|
||||
src/unistd/renameat.c \
|
||||
src/unistd/rmdir.c \
|
||||
src/unistd/symlink.c \
|
||||
src/unistd/ttyname.c \
|
||||
src/unistd/unlink.c \
|
||||
src/unistd/getopt.c \
|
||||
src/process/atfork.c \
|
||||
src/process/execl.c \
|
||||
src/process/execle.c \
|
||||
src/process/execlp.c \
|
||||
src/process/execv.c \
|
||||
src/process/execve.c \
|
||||
src/process/execvp.c \
|
||||
src/process/fork.c \
|
||||
src/process/groups.c \
|
||||
src/process/pid.c \
|
||||
src/process/session.c \
|
||||
src/process/system.c \
|
||||
src/process/uid.c \
|
||||
src/process/wait.c \
|
||||
src/fcntl/creat.c \
|
||||
src/fcntl/fcntl.c \
|
||||
src/fcntl/posix_fadvise.c \
|
||||
src/fcntl/posix_fallocate.c \
|
||||
src/stat/chmod.c \
|
||||
src/stat/chown.c \
|
||||
src/stat/fstatat.c \
|
||||
src/stat/mkdir.c \
|
||||
src/stat/mkfifo.c \
|
||||
src/stat/umask.c \
|
||||
src/stat/utimensat.c \
|
||||
src/dirent/closedir.c \
|
||||
src/dirent/dirfd.c \
|
||||
src/dirent/fdopendir.c \
|
||||
src/dirent/opendir.c \
|
||||
src/dirent/readdir.c \
|
||||
src/dirent/rewinddir.c \
|
||||
src/dirent/seekdir.c \
|
||||
src/dirent/telldir.c \
|
||||
src/select/poll.c \
|
||||
src/select/pselect.c \
|
||||
src/select/select.c \
|
||||
src/time/time.c \
|
||||
src/time/clock.c \
|
||||
src/time/timespec_get.c \
|
||||
src/time/sleep.c \
|
||||
src/time/nanosleep.c \
|
||||
src/time/mktime.c \
|
||||
src/time/ctime.c \
|
||||
src/time/tzset.c \
|
||||
src/time/strftime.c \
|
||||
src/mman/mmap.c \
|
||||
src/passwd/passwd.c \
|
||||
src/group/group.c \
|
||||
src/math/cbrt.c \
|
||||
src/math/ceil.c \
|
||||
src/math/copysign.c \
|
||||
src/math/exp.c \
|
||||
src/math/fabs.c \
|
||||
src/math/fdim.c \
|
||||
src/math/floor.c \
|
||||
src/math/fmax.c \
|
||||
src/math/fmin.c \
|
||||
src/math/fmod.c \
|
||||
src/math/frexp.c \
|
||||
src/math/hypot.c \
|
||||
src/math/ilogb.c \
|
||||
src/math/ldexp.c \
|
||||
src/math/llrint.c \
|
||||
src/math/llround.c \
|
||||
src/math/log.c \
|
||||
src/math/log1p.c \
|
||||
src/math/logb.c \
|
||||
src/math/lrint.c \
|
||||
src/math/lround.c \
|
||||
src/math/modf.c \
|
||||
src/math/nearbyint.c \
|
||||
src/math/pow.c \
|
||||
src/math/remainder.c \
|
||||
src/math/remquo.c \
|
||||
src/math/rint.c \
|
||||
src/math/round.c \
|
||||
src/math/scalbn.c \
|
||||
src/math/sqrt.c \
|
||||
src/math/trunc.c
|
||||
|
||||
# Profile-gated sources, EXISTING gates preserved (strlcpy/strlcat stay L2
|
||||
# BSD, strcasestr stays L3 GNU — NOT promoted). Task 8/9 additions: stpcpy.c
|
||||
@@ -87,11 +233,32 @@ VLIBC_CORE_SRCS = \
|
||||
# ffsl/ffsll/bcmp/bcopy/bzero/index/rindex — nothing is POSIX.1-2008 base), so
|
||||
# it also joins the GE_2 conditional list. Files with L1
|
||||
# bodies plus small L2 sections (strcoll.c, strdup.c) stay in VLIBC_CORE_SRCS
|
||||
# and self-gate at level 1.
|
||||
# and self-gate at level 1. Likewise rand.c (its L2 rand_r/drand48 section)
|
||||
# and the strtox/strtod/inttypes/div/qsort C files (their L2 gates live in
|
||||
# include/stdlib.h / include/inttypes.h declarations) stay in VLIBC_CORE_SRCS.
|
||||
# W5/W6 whole-file-L2 joins (verified by reading each .c — the #if
|
||||
# VLIBC_LEVEL_GE(2) wraps the whole body after the includes): lockf.c (XSI),
|
||||
# mknod.c (XSI), readdir_r.c/scandir.c/alphasort.c (XSI dirent), ppoll.c
|
||||
# (Linux), strptime.c/timer.c (POSIX timers), plus task 27's sys/time slice
|
||||
# (gettimeofday.c/settimeofday.c/getitimer.c/setitimer.c/utimes.c — each
|
||||
# whole-file L2, wired with the systime test), plus the earlier strings
|
||||
# slice.
|
||||
VLIBC_LEVEL2_SRCS = src/string/strlcpy.c src/string/strlcat.c \
|
||||
src/string/stpcpy.c src/string/memccpy.c \
|
||||
src/ctype/isascii.c src/ctype/toascii.c \
|
||||
src/string/strings_impl.c
|
||||
src/string/strings_impl.c \
|
||||
src/stdlib/pty.c src/stdlib/getsubopt.c \
|
||||
src/fcntl/lockf.c \
|
||||
src/stat/mknod.c \
|
||||
src/dirent/readdir_r.c src/dirent/scandir.c src/dirent/alphasort.c \
|
||||
src/select/ppoll.c \
|
||||
src/time/strptime.c src/time/timer.c \
|
||||
src/time/gettimeofday.c src/time/settimeofday.c \
|
||||
src/time/getitimer.c src/time/setitimer.c src/time/utimes.c \
|
||||
src/stat/statvfs.c src/sys/times.c src/misc/syslog.c \
|
||||
src/unistd/basename.c src/unistd/dirname.c src/unistd/realpath.c \
|
||||
src/unistd/chroot.c src/unistd/swab.c src/unistd/confstr.c \
|
||||
src/unistd/getwd.c
|
||||
VLIBC_LEVEL3_SRCS = src/string/strcasestr.c
|
||||
|
||||
vlibc_lib_LTLIBRARIES = libvlibc.la
|
||||
@@ -129,13 +296,38 @@ vlibc_include_HEADERS = \
|
||||
include/stdbool.h \
|
||||
include/stdckdint.h \
|
||||
include/stdint.h \
|
||||
include/inttypes.h \
|
||||
include/stdlib.h \
|
||||
include/stdnoreturn.h \
|
||||
include/tar.h \
|
||||
include/math.h \
|
||||
include/tgmath.h \
|
||||
include/threads.h \
|
||||
include/uchar.h \
|
||||
include/sys/types.h
|
||||
include/dirent.h \
|
||||
include/fcntl.h \
|
||||
include/poll.h \
|
||||
include/stdio.h \
|
||||
include/time.h \
|
||||
include/unistd.h \
|
||||
include/syslog.h \
|
||||
include/pwd.h \
|
||||
include/grp.h \
|
||||
include/libgen.h
|
||||
|
||||
# sys/* headers use their own primary so the sys/ subpath survives install:
|
||||
# automake's flat _HEADERS install strips directory components, which would
|
||||
# both drop the sys/ prefix for <sys/*.h> consumers AND collide on shared
|
||||
# basenames (time.h vs sys/time.h, GNU install refuses the second copy).
|
||||
vlibc_include_sys_HEADERS = \
|
||||
include/sys/select.h \
|
||||
include/sys/stat.h \
|
||||
include/sys/time.h \
|
||||
include/sys/types.h \
|
||||
include/sys/wait.h \
|
||||
include/sys/mman.h \
|
||||
include/sys/statvfs.h \
|
||||
include/sys/times.h
|
||||
|
||||
# Install location depends on the install method (see configure.ac).
|
||||
if INSTALL_OVERWRITE
|
||||
@@ -146,6 +338,11 @@ vlibc_includedir = $(prefix)/lib/vlibc/include
|
||||
vlibc_libdir = $(prefix)/lib/vlibc/lib
|
||||
endif
|
||||
|
||||
# sys/* headers live under $(vlibc_includedir)/sys (see the sys primary
|
||||
# above; must follow the INSTALL_OVERWRITE conditional to inherit the
|
||||
# resolved vlibc_includedir).
|
||||
vlibc_include_sysdir = $(vlibc_includedir)/sys
|
||||
|
||||
# Generated per-profile feature header (see configure.ac); installed under
|
||||
# $(vlibc_includedir)/vlibc/. A dedicated primary is used because
|
||||
# nodist_nobase_vlibc_include_HEADERS is rejected by automake (the "nobase"
|
||||
@@ -263,7 +460,13 @@ VLIBC_TEST_CFLAGS = -DVLIBC_LEVEL=$(VLIBC_TEST_LEVEL) -fno-stack-protector \
|
||||
-fno-pic -fno-pie
|
||||
|
||||
check_PROGRAMS = test_syscall test_strerror test_setjmp test_headers \
|
||||
test_startup test_malloc test_string test_ctype test_strings
|
||||
test_startup test_malloc test_string test_ctype test_strings \
|
||||
test_strtol test_qsort test_inttypes test_env \
|
||||
test_process test_fcntl test_stat test_wait test_dirent \
|
||||
test_poll test_time test_scanf test_getline \
|
||||
test_systime test_printf test_stdio test_unistd_file \
|
||||
test_mman test_statvfs test_times test_syslog test_pwdgrp \
|
||||
test_unistd_misc test_math_basic
|
||||
|
||||
test_syscall_SOURCES = tests/syscall_test.c
|
||||
test_syscall_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
@@ -319,13 +522,167 @@ test_strings_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_strings_LINK = $(VLIBC_TEST_LINK)
|
||||
test_strings_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_strtol_SOURCES = tests/test_strtol.c
|
||||
test_strtol_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_strtol_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_strtol_LINK = $(VLIBC_TEST_LINK)
|
||||
test_strtol_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_qsort_SOURCES = tests/test_qsort.c
|
||||
test_qsort_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_qsort_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_qsort_LINK = $(VLIBC_TEST_LINK)
|
||||
test_qsort_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_inttypes_SOURCES = tests/test_inttypes.c
|
||||
test_inttypes_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_inttypes_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_inttypes_LINK = $(VLIBC_TEST_LINK)
|
||||
test_inttypes_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_env_SOURCES = tests/test_env.c
|
||||
test_env_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_env_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_env_LINK = $(VLIBC_TEST_LINK)
|
||||
test_env_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_process_SOURCES = tests/test_process.c
|
||||
test_process_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_process_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_process_LINK = $(VLIBC_TEST_LINK)
|
||||
test_process_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_fcntl_SOURCES = tests/test_fcntl.c
|
||||
test_fcntl_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_fcntl_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_fcntl_LINK = $(VLIBC_TEST_LINK)
|
||||
test_fcntl_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_stat_SOURCES = tests/test_stat.c
|
||||
test_stat_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_stat_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_stat_LINK = $(VLIBC_TEST_LINK)
|
||||
test_stat_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_wait_SOURCES = tests/test_wait.c
|
||||
test_wait_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_wait_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_wait_LINK = $(VLIBC_TEST_LINK)
|
||||
test_wait_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_dirent_SOURCES = tests/test_dirent.c
|
||||
test_dirent_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_dirent_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_dirent_LINK = $(VLIBC_TEST_LINK)
|
||||
test_dirent_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_poll_SOURCES = tests/test_poll.c
|
||||
test_poll_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_poll_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_poll_LINK = $(VLIBC_TEST_LINK)
|
||||
test_poll_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_time_SOURCES = tests/test_time.c
|
||||
test_time_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_time_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_time_LINK = $(VLIBC_TEST_LINK)
|
||||
test_time_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_scanf_SOURCES = tests/test_scanf.c
|
||||
test_scanf_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_scanf_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_scanf_LINK = $(VLIBC_TEST_LINK)
|
||||
test_scanf_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_getline_SOURCES = tests/test_getline.c
|
||||
test_getline_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_getline_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_getline_LINK = $(VLIBC_TEST_LINK)
|
||||
test_getline_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_systime_SOURCES = tests/test_systime.c
|
||||
test_systime_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_systime_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_systime_LINK = $(VLIBC_TEST_LINK)
|
||||
test_systime_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_printf_SOURCES = tests/test_printf.c
|
||||
test_printf_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_printf_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_printf_LINK = $(VLIBC_TEST_LINK)
|
||||
test_printf_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_stdio_SOURCES = tests/test_stdio.c
|
||||
test_stdio_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_stdio_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_stdio_LINK = $(VLIBC_TEST_LINK)
|
||||
test_stdio_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_unistd_file_SOURCES = tests/test_unistd_file.c
|
||||
test_unistd_file_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_unistd_file_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_unistd_file_LINK = $(VLIBC_TEST_LINK)
|
||||
test_unistd_file_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_mman_SOURCES = tests/test_mman.c
|
||||
test_mman_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_mman_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_mman_LINK = $(VLIBC_TEST_LINK)
|
||||
test_mman_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_statvfs_SOURCES = tests/test_statvfs.c
|
||||
test_statvfs_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_statvfs_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_statvfs_LINK = $(VLIBC_TEST_LINK)
|
||||
test_statvfs_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_times_SOURCES = tests/test_times.c
|
||||
test_times_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_times_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_times_LINK = $(VLIBC_TEST_LINK)
|
||||
test_times_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_syslog_SOURCES = tests/test_syslog.c
|
||||
test_syslog_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_syslog_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_syslog_LINK = $(VLIBC_TEST_LINK)
|
||||
test_syslog_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_pwdgrp_SOURCES = tests/test_pwdgrp.c
|
||||
test_pwdgrp_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_pwdgrp_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_pwdgrp_LINK = $(VLIBC_TEST_LINK)
|
||||
test_pwdgrp_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_unistd_misc_SOURCES = tests/test_unistd_misc.c
|
||||
test_unistd_misc_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_unistd_misc_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_unistd_misc_LINK = $(VLIBC_TEST_LINK)
|
||||
test_unistd_misc_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_math_basic_SOURCES = tests/test_math_basic.c
|
||||
test_math_basic_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_math_basic_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_math_basic_LINK = $(VLIBC_TEST_LINK)
|
||||
test_math_basic_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
# test_startup's default mode exits 42 by design, so it is NOT a bare TESTS
|
||||
# entry; tests/startup_modes.sh runs its full mode matrix and the -f failure
|
||||
# scenarios of the other binaries. test_malloc/test_string/test_ctype/
|
||||
# test_strings default to the happy mode (exit 0 on all-pass) and are bare
|
||||
# TESTS entries; their -f/-p modes stay runnable by hand.
|
||||
# test_strings/test_strtol/test_qsort/test_inttypes/test_env and the W5/W6
|
||||
# wave tests (test_process/test_fcntl/test_stat/test_wait/test_dirent/
|
||||
# test_poll/test_time/test_scanf/test_getline) default to the happy mode
|
||||
# (exit 0 on all-pass) and are bare TESTS entries; their -f/-p modes stay
|
||||
# runnable by hand.
|
||||
TESTS = test_syscall test_strerror test_setjmp test_headers \
|
||||
test_malloc test_string test_ctype test_strings \
|
||||
test_strtol test_qsort test_inttypes test_env \
|
||||
test_process test_fcntl test_stat test_wait test_dirent \
|
||||
test_poll test_time test_scanf test_getline \
|
||||
test_systime test_printf test_stdio test_unistd_file \
|
||||
test_mman test_statvfs test_times test_syslog test_pwdgrp \
|
||||
test_unistd_misc \
|
||||
test_math_basic \
|
||||
tests/startup_modes.sh
|
||||
|
||||
CLEANFILES = crt1.o libvlibc-check.a $(VLIBC_CHECK_OBJECTS)
|
||||
@@ -340,9 +697,20 @@ EXTRA_DIST = \
|
||||
arch/x86_64/syscall_arch.h \
|
||||
src/internal/atomic.h \
|
||||
src/internal/errno.h \
|
||||
src/internal/futex.h \
|
||||
src/internal/libc.h \
|
||||
src/internal/malloc.h \
|
||||
src/internal/strtox.h \
|
||||
src/internal/syscall.h \
|
||||
src/internal/types.h \
|
||||
src/start/start.h \
|
||||
src/start/tcb.h
|
||||
src/start/tcb.h \
|
||||
src/thread/pthread_impl.h \
|
||||
src/dirent/dirent_impl.h \
|
||||
src/process/atfork_impl.h \
|
||||
src/stat/stat_impl.h \
|
||||
src/stdio/stdio_impl.h \
|
||||
src/stdio/vfprintf_float.c \
|
||||
src/time/time_impl.h \
|
||||
src/unistd/unistd_impl.h \
|
||||
src/math/math_impl.h
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
#ifndef VLIBC_DIRENT_H
|
||||
#define VLIBC_DIRENT_H
|
||||
|
||||
/*
|
||||
* vlibc — <dirent.h>.
|
||||
*
|
||||
* Directory streams: opendir/fdopendir/readdir/rewinddir/closedir,
|
||||
* seekdir/telldir/dirfd (POSIX.1-2008 base) iterate a directory via the
|
||||
* SYS_getdents64 kernel ABI, exposing one entry at a time.
|
||||
*
|
||||
* Level 1 (onlyposix): the base family above.
|
||||
* Level 2 (muslmimic): scandir, alphasort (XSI) and readdir_r
|
||||
* (obsolescent). versionsort is GNU and is not
|
||||
* provided.
|
||||
*
|
||||
* struct dirent mirrors the x86_64 getdents64 record layout (verified by
|
||||
* the static assertions below): d_ino/d_off are 64-bit, d_reclen is the
|
||||
* kernel record length, d_type carries the DT_* file type, and d_name is a
|
||||
* 256-byte NUL-terminated name buffer (NAME_MAX 255 + NUL).
|
||||
*
|
||||
* DIR is an opaque handle whose layout lives in the internal header
|
||||
* src/dirent/dirent_impl.h. None of these declarations carries an intent
|
||||
* attribute: every function performs I/O with side effects and reports
|
||||
* failures through errno, so const/pure would be unsound (the same
|
||||
* rationale sys/stat.h documents for its I/O family).
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* File type values for the d_type member of struct dirent (kernel UAPI). */
|
||||
#define DT_UNKNOWN 0
|
||||
#define DT_FIFO 1
|
||||
#define DT_CHR 2
|
||||
#define DT_DIR 4
|
||||
#define DT_BLK 6
|
||||
#define DT_REG 8
|
||||
#define DT_LNK 10
|
||||
#define DT_SOCK 12
|
||||
#define DT_WHT 14
|
||||
|
||||
/*
|
||||
* One directory entry. Layout equals the x86_64 struct linux_dirent64
|
||||
* fields 0..18 verbatim; the name then follows at offset 19, stored in the
|
||||
* conventional 256-byte buffer.
|
||||
*/
|
||||
struct dirent
|
||||
{
|
||||
ino_t d_ino; /* 0: inode number */
|
||||
off_t d_off; /* 8: offset of the next entry (seek cookie) */
|
||||
unsigned short d_reclen; /* 16: length of the kernel record */
|
||||
unsigned char d_type; /* 18: DT_* file type */
|
||||
char d_name[256]; /* 19: NUL-terminated file name */
|
||||
};
|
||||
|
||||
/* Pin the layout to the x86_64 kernel ABI. */
|
||||
_Static_assert(sizeof(struct dirent) == 280, "struct dirent must match the x86_64 getdents64 layout");
|
||||
_Static_assert(offsetof(struct dirent, d_off) == 8, "d_off must sit at offset 8");
|
||||
_Static_assert(offsetof(struct dirent, d_type) == 18, "d_type must sit at offset 18");
|
||||
_Static_assert(offsetof(struct dirent, d_name) == 19, "d_name must sit at offset 19");
|
||||
|
||||
/*
|
||||
* Opaque directory stream handle. The struct tag stays usable from the
|
||||
* internal layout header, which defines struct vlibc_DIR (see
|
||||
* src/dirent/dirent_impl.h).
|
||||
*/
|
||||
typedef struct vlibc_DIR DIR;
|
||||
|
||||
/*
|
||||
* Open the directory named by path for reading and return a directory
|
||||
* stream positioned at its first entry, or NULL with errno set (a
|
||||
* non-directory path fails with ENOTDIR). The stream owns a descriptor
|
||||
* that closedir() releases.
|
||||
*/
|
||||
DIR *
|
||||
opendir(const char *path);
|
||||
|
||||
/*
|
||||
* Like opendir(), but over the already-open descriptor fd, which must
|
||||
* refer to a directory (validated with fstat). On failure NULL is returned
|
||||
* with errno set and fd is left open and owned by the caller.
|
||||
*/
|
||||
DIR *
|
||||
fdopendir(int fd);
|
||||
|
||||
/*
|
||||
* Return the next directory entry of dir, or NULL at the end of the
|
||||
* directory (errno untouched) or on error (errno set). The result points
|
||||
* at storage owned by dir and is valid until the next call to readdir,
|
||||
* rewinddir, seekdir, or closedir on the same stream.
|
||||
*/
|
||||
struct dirent *
|
||||
readdir(DIR *dir);
|
||||
|
||||
/*
|
||||
* Reset dir to the beginning of the directory: the next readdir returns
|
||||
* the first entry again. Never fails.
|
||||
*/
|
||||
void
|
||||
rewinddir(DIR *dir);
|
||||
|
||||
/*
|
||||
* Close dir, releasing its descriptor and storage. Return 0, or -1 with
|
||||
* errno set if the underlying close fails.
|
||||
*/
|
||||
int
|
||||
closedir(DIR *dir);
|
||||
|
||||
/*
|
||||
* Reposition dir so the next readdir resumes at the location loc, which
|
||||
* must be a value previously returned by telldir (a getdents64 seek
|
||||
* cookie). Never fails.
|
||||
*/
|
||||
void
|
||||
seekdir(DIR *dir, long loc);
|
||||
|
||||
/*
|
||||
* Return the current location of dir, for a later seekdir. The location is
|
||||
* the point after the entry most recently returned by readdir; it becomes
|
||||
* indeterminate after rewinddir or closedir.
|
||||
*/
|
||||
long
|
||||
telldir(DIR *dir);
|
||||
|
||||
/*
|
||||
* Return the descriptor underlying dir. The descriptor stays owned by the
|
||||
* stream and remains valid until closedir.
|
||||
*/
|
||||
int
|
||||
dirfd(DIR *dir);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/* Level 2 (muslmimic): XSI and obsolescent. */
|
||||
|
||||
/*
|
||||
* Reentrant readdir: store the next entry in *buf and set *result to buf;
|
||||
* at the end of the directory *result is NULL. Returns 0 at end of
|
||||
* directory, an error number on failure (errno is not used for the error
|
||||
* report), and leaves errno unmodified on success. Obsolescent.
|
||||
*/
|
||||
int
|
||||
readdir_r(DIR *restrict dir, struct dirent *restrict buf,
|
||||
struct dirent **restrict result);
|
||||
|
||||
/*
|
||||
* Read the whole directory named by path and store a malloc'd array of
|
||||
* malloc'd struct dirent copies in *res (both released with free; the
|
||||
* array is NULL-terminated with one extra NULL pointer). Only entries for
|
||||
* which sel is NULL or returns nonzero are kept; cmp, when non-NULL, sorts
|
||||
* the array (alphasort is the strcmp-on-name comparator). Returns the
|
||||
* number of entries, or -1 with errno set. XSI.
|
||||
*/
|
||||
int
|
||||
scandir(const char *path, struct dirent ***res,
|
||||
int (*sel)(const struct dirent *),
|
||||
int (*cmp)(const struct dirent **, const struct dirent **));
|
||||
|
||||
/*
|
||||
* Lexicographic comparator over the d_name fields of two struct dirent
|
||||
* pointers, for use as scandir's cmp argument. XSI.
|
||||
*/
|
||||
int
|
||||
alphasort(const struct dirent **a, const struct dirent **b);
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_DIRENT_H */
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
#ifndef VLIBC_FCNTL_H
|
||||
#define VLIBC_FCNTL_H
|
||||
|
||||
/*
|
||||
* vlibc — <fcntl.h>.
|
||||
*
|
||||
* File control, record locks, and the open-flag constants (POSIX.1-2008 plus
|
||||
* the Linux x86_64 additions). Every O_*, F_*, AT_* and POSIX_FADV_* value
|
||||
* below is a Linux kernel UAPI fact for x86_64 (asm-generic/fcntl.h), transcribed in
|
||||
* hex with the kernel's octal form in a comment — not an invented number.
|
||||
* O_TMPFILE and O_SYNC are defined as flag combinations exactly as the
|
||||
* kernel defines them.
|
||||
*
|
||||
* Note on the *at constants: on x86_64 AT_EACCESS == AT_REMOVEDIR == 0x200;
|
||||
* the kernel disambiguates by syscall (faccessat vs unlinkat), so both names
|
||||
* carry the same value here.
|
||||
*
|
||||
* open/openat are deliberately NOT declared here: POSIX places them in this
|
||||
* header, but this project's single declaration site is <unistd.h> (todo 19;
|
||||
* see its header comment). creat() lives here — its canonical POSIX home.
|
||||
*
|
||||
* Level 2 (muslmimic/XSI): lockf plus F_LOCK/F_TLOCK/F_ULOCK/F_TEST. POSIX
|
||||
* puts lockf in <unistd.h>; that file is todo 19's and out of scope here,
|
||||
* and musl declares lockf in <fcntl.h> as well — the constants accompany the
|
||||
* declaration so the header stays self-contained. A later integration pass
|
||||
* may re-export the same declaration from <unistd.h> (identical, harmless).
|
||||
*
|
||||
* None of these declarations carry an intent attribute: every function
|
||||
* performs I/O with side effects and reports failures through errno (or,
|
||||
* for posix_fadvise/posix_fallocate, an error-number return), so const/pure
|
||||
* would be unsound — the same rationale unistd.h documents for its I/O
|
||||
* family.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ---- open(2) flag bits ---- */
|
||||
|
||||
/* Mask for the O_RDONLY/O_WRONLY/O_RDWR access mode. */
|
||||
#define O_ACCMODE 0x3
|
||||
|
||||
#define O_RDONLY 0x0 /* 00000000 */
|
||||
#define O_WRONLY 0x1 /* 00000001 */
|
||||
#define O_RDWR 0x2 /* 00000002 */
|
||||
|
||||
#define O_CREAT 0x40 /* 00000100 */
|
||||
#define O_EXCL 0x80 /* 00000200 */
|
||||
#define O_NOCTTY 0x100 /* 00000400 */
|
||||
#define O_TRUNC 0x200 /* 00001000 */
|
||||
#define O_APPEND 0x400 /* 00002000 */
|
||||
#define O_NONBLOCK 0x800 /* 00004000 */
|
||||
#define O_DSYNC 0x1000 /* 00010000 */
|
||||
#define O_ASYNC 0x2000 /* 00020000 */
|
||||
#define O_DIRECT 0x4000 /* 00040000 */
|
||||
#define O_LARGEFILE 0x8000 /* 00100000 */
|
||||
|
||||
#define O_DIRECTORY 0x10000 /* 00200000 */
|
||||
#define O_NOFOLLOW 0x20000 /* 00400000 */
|
||||
#define O_NOATIME 0x40000 /* 01000000 */
|
||||
#define O_CLOEXEC 0x80000 /* 02000000 */
|
||||
|
||||
/*
|
||||
* __O_SYNC/__O_TMPFILE are Linux-kernel names in the implementation-reserved
|
||||
* namespace (this libc IS "the implementation", and the kernel UAPI mandates
|
||||
* the exact spellings) — the NOLINT below is the house waiver for that.
|
||||
*/
|
||||
#define __O_SYNC 0x100000 /* NOLINT(bugprone-reserved-identifier) 04000000 */
|
||||
#define O_SYNC (__O_SYNC | O_DSYNC)
|
||||
#define O_PATH 0x200000 /* 010000000 */
|
||||
#define __O_TMPFILE 0x400000 /* NOLINT(bugprone-reserved-identifier) 020000000 */
|
||||
#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY)
|
||||
|
||||
/* ---- fcntl(2) commands ---- */
|
||||
|
||||
#define F_DUPFD 0
|
||||
#define F_GETFD 1
|
||||
#define F_SETFD 2
|
||||
#define F_GETFL 3
|
||||
#define F_SETFL 4
|
||||
#define F_GETLK 5
|
||||
#define F_SETLK 6
|
||||
#define F_SETLKW 7
|
||||
#define F_SETOWN 8
|
||||
#define F_GETOWN 9
|
||||
#define F_SETSIG 10
|
||||
#define F_GETSIG 11
|
||||
|
||||
/* Linux-specific owner-identity commands (constants only; struct f_owner_ex
|
||||
* is out of POSIX scope and not provided). */
|
||||
#define F_SETOWN_EX 15
|
||||
#define F_GETOWN_EX 16
|
||||
#define F_GETOWNER_UIDS 17
|
||||
|
||||
/* Open-file-description (OFD) locks, Linux 3.15+. */
|
||||
#define F_OFD_GETLK 36
|
||||
#define F_OFD_SETLK 37
|
||||
#define F_OFD_SETLKW 38
|
||||
|
||||
#define F_DUPFD_CLOEXEC 1030 /* F_LINUX_SPECIFIC_BASE (1024) + 6 */
|
||||
|
||||
/* Close the descriptor on exec — the F_SETFD/F_GETFD flag. */
|
||||
#define FD_CLOEXEC 1
|
||||
|
||||
/* Record-lock types (struct flock l_type). */
|
||||
#define F_RDLCK 0
|
||||
#define F_WRLCK 1
|
||||
#define F_UNLCK 2
|
||||
|
||||
/* ---- *at(2) base-directory and behavior flags ---- */
|
||||
|
||||
#define AT_FDCWD (-100)
|
||||
#define AT_SYMLINK_NOFOLLOW 0x100
|
||||
#define AT_REMOVEDIR 0x200
|
||||
#define AT_SYMLINK_FOLLOW 0x400
|
||||
#define AT_EACCESS 0x200
|
||||
|
||||
/* ---- posix_fadvise(2) advice values ---- */
|
||||
|
||||
#define POSIX_FADV_NORMAL 0
|
||||
#define POSIX_FADV_RANDOM 1
|
||||
#define POSIX_FADV_SEQUENTIAL 2
|
||||
#define POSIX_FADV_WILLNEED 3
|
||||
#define POSIX_FADV_DONTNEED 4
|
||||
#define POSIX_FADV_NOREUSE 5
|
||||
|
||||
/*
|
||||
* Record-lock descriptor, ABI-identical to the x86_64 kernel layout
|
||||
* (asm-generic/fcntl.h: short, short, long, long, int — 32 bytes with
|
||||
* natural padding). On x86_64 the LFS and non-LFS layouts are one struct,
|
||||
* so there is no separate flock64 here.
|
||||
*/
|
||||
struct flock
|
||||
{
|
||||
short l_type; /* F_RDLCK, F_WRLCK, or F_UNLCK */
|
||||
short l_whence; /* SEEK_SET, SEEK_CUR, or SEEK_END (from <unistd.h>) */
|
||||
off_t l_start; /* relative offset of the locked region */
|
||||
off_t l_len; /* region length; 0 means through EOF */
|
||||
pid_t l_pid; /* PID of the process holding the lock (F_GETLK) */
|
||||
};
|
||||
|
||||
/*
|
||||
* Perform one of the F_* control operations on fildes. Only the commands
|
||||
* that take a third argument read it from the varargs; all others pass 0,
|
||||
* which the kernel ignores. Return the command-specific result, or -1 with
|
||||
* errno set.
|
||||
*/
|
||||
int
|
||||
fcntl(int fildes, int cmd, ...);
|
||||
|
||||
/*
|
||||
* Equivalent to open(path, O_WRONLY | O_CREAT | O_TRUNC, mode): create
|
||||
* path for writing, truncating any existing file, with mode masked by the
|
||||
* process umask. Return a file descriptor, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
creat(const char *path, mode_t mode);
|
||||
|
||||
/*
|
||||
* Announce an expected access pattern for the range [offset, offset+len) of
|
||||
* fd (len 0 means through EOF). Unlike the rest of the family this returns
|
||||
* an error NUMBER directly — 0 on success, else the positive errno value
|
||||
* (e.g. EBADF, ESPIPE) — and errno is untouched (POSIX).
|
||||
*/
|
||||
int
|
||||
posix_fadvise(int fd, off_t offset, off_t len, int advice);
|
||||
|
||||
/*
|
||||
* Ensure storage is allocated for the range [offset, offset+len) of fd,
|
||||
* growing the file as needed. Returns an error number directly (0 on
|
||||
* success) and leaves errno untouched, like posix_fadvise (POSIX).
|
||||
*/
|
||||
int
|
||||
posix_fallocate(int fd, off_t offset, off_t len);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/* lockf() commands (POSIX XSI; the kernel has no lockf syscall — these are
|
||||
* userspace cmd values). */
|
||||
#define F_ULOCK 0 /* unlock a previously locked region */
|
||||
#define F_LOCK 1 /* lock a region, blocking until available */
|
||||
#define F_TLOCK 2 /* try to lock; -1 with EACCES/EAGAIN if held */
|
||||
#define F_TEST 3 /* test a region for another process's lock */
|
||||
|
||||
/*
|
||||
* Apply or remove an exclusive POSIX record lock on [current offset,
|
||||
* current offset + len) of fd (len 0 means through EOF). XSI. Return 0, or
|
||||
* -1 with errno set.
|
||||
*/
|
||||
int
|
||||
lockf(int fd, int cmd, off_t len);
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_FCNTL_H */
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
#ifndef VLIBC_GRP_H
|
||||
#define VLIBC_GRP_H
|
||||
|
||||
/*
|
||||
* vlibc — <grp.h>.
|
||||
*
|
||||
* The group database (todo 37). struct group mirrors /etc/group, whose
|
||||
* records are name:passwd:gid:member,member,... — the member list is a
|
||||
* comma-separated tail, empty when the group has no members. Everything
|
||||
* here is POSIX.1-2008 base (Level 1).
|
||||
*
|
||||
* The non-reentrant forms getgrnam/getgrgid/getgrent return a pointer to a
|
||||
* static structure whose contents — including the strings and the member
|
||||
* array it points at — are overwritten by the next call to any of the
|
||||
* three. getgrnam/getgrgid open and scan the whole file per call; getgrent
|
||||
* walks a cursor that setgrent rewinds and endgrent closes.
|
||||
*
|
||||
* The _r forms behave like their <pwd.h> counterparts: the entry is written
|
||||
* into caller storage (the gr_mem pointer array and the member name strings
|
||||
* both live inside the supplied buffer) and the return value is 0 on
|
||||
* success, ERANGE when the buffer is too small, or 0 with *result NULL when
|
||||
* no entry matches. errno is never touched: the error number IS the return
|
||||
* value.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* One group database entry (/etc/group record). */
|
||||
struct group
|
||||
{
|
||||
char *gr_name; /* group name */
|
||||
char *gr_passwd; /* encrypted password; usually empty */
|
||||
gid_t gr_gid; /* numeric group id */
|
||||
char **gr_mem; /* NULL-terminated member-name array */
|
||||
};
|
||||
|
||||
/*
|
||||
* Look up the first entry whose name matches; NULL when absent. The result
|
||||
* points into shared static storage valid only until the next grp call.
|
||||
*/
|
||||
struct group *
|
||||
getgrnam(const char *name);
|
||||
|
||||
/*
|
||||
* Look up the first entry whose gid matches; NULL when absent. Result
|
||||
* storage is shared with getgrnam/getgrent as described above.
|
||||
*/
|
||||
struct group *
|
||||
getgrgid(gid_t gid);
|
||||
|
||||
/*
|
||||
* Return the next entry from the group stream, opening it on the first
|
||||
* call; NULL at end of file. The stream cursor advances per call.
|
||||
*/
|
||||
struct group *
|
||||
getgrent(void);
|
||||
|
||||
/* Rewind the group stream to its first entry. */
|
||||
void
|
||||
setgrent(void);
|
||||
|
||||
/* Close the group stream; a later getgrent reopens from the start. */
|
||||
void
|
||||
endgrent(void);
|
||||
|
||||
int
|
||||
getgrnam_r(const char *name, struct group *gr, char *buf, size_t bufsize, struct group **result);
|
||||
|
||||
int
|
||||
getgrgid_r(gid_t gid, struct group *gr, char *buf, size_t bufsize, struct group **result);
|
||||
|
||||
int
|
||||
getgrent_r(struct group *gr, char *buf, size_t bufsize, struct group **result);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): XSI. */
|
||||
|
||||
#include <stdio.h> /* FILE */
|
||||
|
||||
/*
|
||||
* Return the next entry read from the given open stream, or NULL at end of
|
||||
* file or on a malformed/unopenable record; the entry is parsed as an
|
||||
* /etc/group record. Result storage is shared with getgrnam/getgrgid/
|
||||
* getgrent as described above, so the stream must not be closed until the
|
||||
* caller has copied the result. XSI.
|
||||
*/
|
||||
struct group *
|
||||
fgetgrent(FILE *stream);
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_GRP_H */
|
||||
@@ -0,0 +1,314 @@
|
||||
#ifndef VLIBC_INTTYPES_H
|
||||
#define VLIBC_INTTYPES_H
|
||||
|
||||
/*
|
||||
* vlibc — <inttypes.h>.
|
||||
*
|
||||
* Extended integer type names, the imax* conversions of the widest signed
|
||||
* type, and the PRI and SCN formatted-I/O macros (C23 7.8). Every macro below
|
||||
* expands to a string literal holding the complete conversion specifier
|
||||
* (length modifier included) for the corresponding <stdint.h> type on the
|
||||
* x86_64 LP64 target, where the compiler predefined types are:
|
||||
*
|
||||
* int8_t/uint8_t signed char / unsigned char
|
||||
* int16_t/uint16_t short / unsigned short
|
||||
* int32_t/uint32_t int / unsigned int
|
||||
* int64_t/uint64_t long / unsigned long
|
||||
* int_leastN_t identical to the exact-width types
|
||||
* int_fast8_t signed char; int_fast16/32/64_t are long
|
||||
* intmax_t/uintmax_t long / unsigned long
|
||||
* intptr_t/uintptr_t long / unsigned long
|
||||
*
|
||||
* Design rule (per plan): the fprintf macros name the length modifier of
|
||||
* the PROMOTED argument — printf receives an int for every sub-int type,
|
||||
* so PRId8 is "d", not "hhd" — while the fscanf macros name the
|
||||
* exact-width modifier — scanf receives a pointer, so SCNd8 is "hhd".
|
||||
* Both forms convert the corresponding type correctly under C23 7.8.1;
|
||||
* glibc and musl instead use the exact-width modifier in both families,
|
||||
* which is equally conformant and indistinguishable in behavior for
|
||||
* correctly-typed arguments.
|
||||
*
|
||||
* This header is ISO C core and is present in every profile.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Quotient and remainder of the widest integer division (C23 7.8.1). */
|
||||
typedef struct imaxdiv_t
|
||||
{
|
||||
intmax_t quot;
|
||||
intmax_t rem;
|
||||
} imaxdiv_t;
|
||||
|
||||
/*
|
||||
* fprintf macros for the signed exact-width types (argument promotes to
|
||||
* int, or stays long for the 64-bit width).
|
||||
*/
|
||||
#define PRId8 "d"
|
||||
#define PRId16 "d"
|
||||
#define PRId32 "d"
|
||||
#define PRId64 "ld"
|
||||
|
||||
#define PRIi8 "i"
|
||||
#define PRIi16 "i"
|
||||
#define PRIi32 "i"
|
||||
#define PRIi64 "li"
|
||||
|
||||
/* fprintf macros for the unsigned exact-width types. */
|
||||
#define PRIo8 "o"
|
||||
#define PRIo16 "o"
|
||||
#define PRIo32 "o"
|
||||
#define PRIo64 "lo"
|
||||
|
||||
#define PRIu8 "u"
|
||||
#define PRIu16 "u"
|
||||
#define PRIu32 "u"
|
||||
#define PRIu64 "lu"
|
||||
|
||||
#define PRIx8 "x"
|
||||
#define PRIx16 "x"
|
||||
#define PRIx32 "x"
|
||||
#define PRIx64 "lx"
|
||||
|
||||
#define PRIX8 "X"
|
||||
#define PRIX16 "X"
|
||||
#define PRIX32 "X"
|
||||
#define PRIX64 "lX"
|
||||
|
||||
/* fprintf macros for the signed least-width types (same typedefs as the
|
||||
* exact-width types on x86_64). */
|
||||
#define PRIdLEAST8 "d"
|
||||
#define PRIdLEAST16 "d"
|
||||
#define PRIdLEAST32 "d"
|
||||
#define PRIdLEAST64 "ld"
|
||||
|
||||
#define PRIiLEAST8 "i"
|
||||
#define PRIiLEAST16 "i"
|
||||
#define PRIiLEAST32 "i"
|
||||
#define PRIiLEAST64 "li"
|
||||
|
||||
/* fprintf macros for the unsigned least-width types. */
|
||||
#define PRIoLEAST8 "o"
|
||||
#define PRIoLEAST16 "o"
|
||||
#define PRIoLEAST32 "o"
|
||||
#define PRIoLEAST64 "lo"
|
||||
|
||||
#define PRIuLEAST8 "u"
|
||||
#define PRIuLEAST16 "u"
|
||||
#define PRIuLEAST32 "u"
|
||||
#define PRIuLEAST64 "lu"
|
||||
|
||||
#define PRIxLEAST8 "x"
|
||||
#define PRIxLEAST16 "x"
|
||||
#define PRIxLEAST32 "x"
|
||||
#define PRIxLEAST64 "lx"
|
||||
|
||||
#define PRIXLEAST8 "X"
|
||||
#define PRIXLEAST16 "X"
|
||||
#define PRIXLEAST32 "X"
|
||||
#define PRIXLEAST64 "lX"
|
||||
|
||||
/* fprintf macros for the signed fastest types (int_fast8_t is signed char,
|
||||
* which promotes to int; int_fast16/32/64_t are long). */
|
||||
#define PRIdFAST8 "d"
|
||||
#define PRIdFAST16 "ld"
|
||||
#define PRIdFAST32 "ld"
|
||||
#define PRIdFAST64 "ld"
|
||||
|
||||
#define PRIiFAST8 "i"
|
||||
#define PRIiFAST16 "li"
|
||||
#define PRIiFAST32 "li"
|
||||
#define PRIiFAST64 "li"
|
||||
|
||||
/* fprintf macros for the unsigned fastest types. */
|
||||
#define PRIoFAST8 "o"
|
||||
#define PRIoFAST16 "lo"
|
||||
#define PRIoFAST32 "lo"
|
||||
#define PRIoFAST64 "lo"
|
||||
|
||||
#define PRIuFAST8 "u"
|
||||
#define PRIuFAST16 "lu"
|
||||
#define PRIuFAST32 "lu"
|
||||
#define PRIuFAST64 "lu"
|
||||
|
||||
#define PRIxFAST8 "x"
|
||||
#define PRIxFAST16 "lx"
|
||||
#define PRIxFAST32 "lx"
|
||||
#define PRIxFAST64 "lx"
|
||||
|
||||
#define PRIXFAST8 "X"
|
||||
#define PRIXFAST16 "lX"
|
||||
#define PRIXFAST32 "lX"
|
||||
#define PRIXFAST64 "lX"
|
||||
|
||||
/* fprintf macros for the widest (intmax_t/uintmax_t) and pointer-width
|
||||
* (intptr_t/uintptr_t) types; both families are long/unsigned long. */
|
||||
#define PRIdMAX "ld"
|
||||
#define PRIiMAX "li"
|
||||
#define PRIoMAX "lo"
|
||||
#define PRIuMAX "lu"
|
||||
#define PRIxMAX "lx"
|
||||
#define PRIXMAX "lX"
|
||||
|
||||
#define PRIdPTR "ld"
|
||||
#define PRIiPTR "li"
|
||||
#define PRIoPTR "lo"
|
||||
#define PRIuPTR "lu"
|
||||
#define PRIxPTR "lx"
|
||||
#define PRIXPTR "lX"
|
||||
|
||||
/*
|
||||
* fscanf macros for the signed exact-width types: the pointer targets the
|
||||
* exact type, so the modifier is hh/h/(none)/l by width.
|
||||
*/
|
||||
#define SCNd8 "hhd"
|
||||
#define SCNd16 "hd"
|
||||
#define SCNd32 "d"
|
||||
#define SCNd64 "ld"
|
||||
|
||||
#define SCNi8 "hhi"
|
||||
#define SCNi16 "hi"
|
||||
#define SCNi32 "i"
|
||||
#define SCNi64 "li"
|
||||
|
||||
/* fscanf macros for the unsigned exact-width types. */
|
||||
#define SCNo8 "hho"
|
||||
#define SCNo16 "ho"
|
||||
#define SCNo32 "o"
|
||||
#define SCNo64 "lo"
|
||||
|
||||
#define SCNu8 "hhu"
|
||||
#define SCNu16 "hu"
|
||||
#define SCNu32 "u"
|
||||
#define SCNu64 "lu"
|
||||
|
||||
#define SCNx8 "hhx"
|
||||
#define SCNx16 "hx"
|
||||
#define SCNx32 "x"
|
||||
#define SCNx64 "lx"
|
||||
|
||||
/* fscanf macros for the signed least-width types (same typedefs as the
|
||||
* exact-width types on x86_64). */
|
||||
#define SCNdLEAST8 "hhd"
|
||||
#define SCNdLEAST16 "hd"
|
||||
#define SCNdLEAST32 "d"
|
||||
#define SCNdLEAST64 "ld"
|
||||
|
||||
#define SCNiLEAST8 "hhi"
|
||||
#define SCNiLEAST16 "hi"
|
||||
#define SCNiLEAST32 "i"
|
||||
#define SCNiLEAST64 "li"
|
||||
|
||||
/* fscanf macros for the unsigned least-width types. */
|
||||
#define SCNoLEAST8 "hho"
|
||||
#define SCNoLEAST16 "ho"
|
||||
#define SCNoLEAST32 "o"
|
||||
#define SCNoLEAST64 "lo"
|
||||
|
||||
#define SCNuLEAST8 "hhu"
|
||||
#define SCNuLEAST16 "hu"
|
||||
#define SCNuLEAST32 "u"
|
||||
#define SCNuLEAST64 "lu"
|
||||
|
||||
#define SCNxLEAST8 "hhx"
|
||||
#define SCNxLEAST16 "hx"
|
||||
#define SCNxLEAST32 "x"
|
||||
#define SCNxLEAST64 "lx"
|
||||
|
||||
/* fscanf macros for the signed fastest types (int_fast8_t is signed char;
|
||||
* int_fast16/32/64_t are long). */
|
||||
#define SCNdFAST8 "hhd"
|
||||
#define SCNdFAST16 "ld"
|
||||
#define SCNdFAST32 "ld"
|
||||
#define SCNdFAST64 "ld"
|
||||
|
||||
#define SCNiFAST8 "hhi"
|
||||
#define SCNiFAST16 "li"
|
||||
#define SCNiFAST32 "li"
|
||||
#define SCNiFAST64 "li"
|
||||
|
||||
/* fscanf macros for the unsigned fastest types. */
|
||||
#define SCNoFAST8 "hho"
|
||||
#define SCNoFAST16 "lo"
|
||||
#define SCNoFAST32 "lo"
|
||||
#define SCNoFAST64 "lo"
|
||||
|
||||
#define SCNuFAST8 "hhu"
|
||||
#define SCNuFAST16 "lu"
|
||||
#define SCNuFAST32 "lu"
|
||||
#define SCNuFAST64 "lu"
|
||||
|
||||
#define SCNxFAST8 "hhx"
|
||||
#define SCNxFAST16 "lx"
|
||||
#define SCNxFAST32 "lx"
|
||||
#define SCNxFAST64 "lx"
|
||||
|
||||
/* fscanf macros for the widest and pointer-width types. */
|
||||
#define SCNdMAX "ld"
|
||||
#define SCNiMAX "li"
|
||||
#define SCNoMAX "lo"
|
||||
#define SCNuMAX "lu"
|
||||
#define SCNxMAX "lx"
|
||||
|
||||
#define SCNdPTR "ld"
|
||||
#define SCNiPTR "li"
|
||||
#define SCNoPTR "lo"
|
||||
#define SCNuPTR "lu"
|
||||
#define SCNxPTR "lx"
|
||||
|
||||
/*
|
||||
* Return the absolute value of n. abs(INTMAX_MIN) is not representable;
|
||||
* it wraps (implementation-defined, no errno), matching GCC's two's
|
||||
* complement semantics.
|
||||
* const: result depends only on the argument (GCC predeclares imaxabs
|
||||
* with this attribute, so no weaker one may be used).
|
||||
*/
|
||||
__attribute__((const)) intmax_t
|
||||
imaxabs(intmax_t n);
|
||||
|
||||
/*
|
||||
* Return { quot, rem } of numer / denom, both truncated toward zero with
|
||||
* the remainder taking the sign of numer (C division semantics).
|
||||
* const: result depends only on the arguments.
|
||||
*/
|
||||
__attribute__((const)) imaxdiv_t
|
||||
imaxdiv(intmax_t numer, intmax_t denom);
|
||||
|
||||
/*
|
||||
* Convert the initial portion of nptr to intmax_t, as strtol with the
|
||||
* widest signed type. intmax_t is long on x86_64 LP64, so this is an
|
||||
* ABI-identical delegation to strtol (same engine, same errno policy:
|
||||
* ERANGE clamps, EINVAL for a base outside {0} union [2, 36]).
|
||||
*/
|
||||
intmax_t
|
||||
strtoimax(const char *restrict nptr, char **restrict endptr, int base);
|
||||
|
||||
/* As strtoimax, converted to uintmax_t (unsigned long); negative subject
|
||||
* sequences wrap modulo 2^64 and are never a range error (C23 7.8.2.3). */
|
||||
uintmax_t
|
||||
strtoumax(const char *restrict nptr, char **restrict endptr, int base);
|
||||
|
||||
/*
|
||||
* Wide-character forms of strtoimax/strtoumax (C23 7.8.2.4-5). The subject
|
||||
* sequence is scanned from nptr under the same rules as strtol: C-locale
|
||||
* whitespace (L' ' and L'\t'..L'\r'), optional sign, base 0/2..36 with the
|
||||
* 0x/0 prefix rules, ASCII digits only. errno and endptr (of type
|
||||
* wchar_t **) behave exactly as the narrow forms.
|
||||
*/
|
||||
intmax_t
|
||||
wcstoimax(const wchar_t *restrict nptr, wchar_t **restrict endptr, int base);
|
||||
|
||||
uintmax_t
|
||||
wcstoumax(const wchar_t *restrict nptr, wchar_t **restrict endptr, int base);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_INTTYPES_H */
|
||||
@@ -0,0 +1,58 @@
|
||||
#ifndef VLIBC_LIBGEN_H
|
||||
#define VLIBC_LIBGEN_H
|
||||
|
||||
/*
|
||||
* vlibc — <libgen.h>.
|
||||
*
|
||||
* basename() and dirname() split a pathname into its final component and
|
||||
* the directory that contains it (XSI, declared when _XOPEN_SOURCE is
|
||||
* enabled in glibc — level 2 here). They are the XSI forms: the input
|
||||
* path is modified in place (callers must pass a writable buffer), and
|
||||
* both are allowed to return pointers into a shared static area, so
|
||||
* repeated calls overwrite earlier results.
|
||||
*
|
||||
* The two functions are deliberately declared here and nowhere else:
|
||||
* POSIX places them in <libgen.h> only.
|
||||
*
|
||||
* Neither declaration carries an intent attribute: both functions write
|
||||
* to and return pointers into their argument or a shared static buffer,
|
||||
* so const/pure would be unsound.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* Return a pointer to the final component of path, modifying path in
|
||||
* place: trailing '/' characters (other than a leading root '/') are
|
||||
* overwritten with NUL so basename("/usr/lib") == "lib", basename("/")
|
||||
* == "/" and basename("") == ".". The result may be a suffix of path or
|
||||
* point into a shared static buffer (POSIX permits either); the special
|
||||
* cases "/" and "" return static strings.
|
||||
*/
|
||||
char *
|
||||
basename(char *path);
|
||||
|
||||
/*
|
||||
* Return a pointer to the directory portion of path, modifying path in
|
||||
* place: the last '/' that is not the final character is overwritten
|
||||
* with NUL so dirname("/usr/lib") == "/usr", dirname("/") == "/" and
|
||||
* dirname("") == ".". The result may be a suffix of path or point into
|
||||
* a shared static buffer (POSIX permits either); the special cases
|
||||
* return static strings.
|
||||
*/
|
||||
char *
|
||||
dirname(char *path);
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_LIBGEN_H */
|
||||
+634
@@ -0,0 +1,634 @@
|
||||
#ifndef VLIBC_MATH_H
|
||||
#define VLIBC_MATH_H
|
||||
|
||||
/*
|
||||
* vlibc — <math.h>.
|
||||
*
|
||||
* This header is ISO C core and is present in every profile. It carries the
|
||||
* whole todo-39 arithmetic inventory: the classification macros
|
||||
* (fpclassify/isnan/isinf/isfinite/isnormal/signbit), the constants
|
||||
* (HUGE_VAL/INFINITY/NAN, FP_*, math_errhandling), and the 25 basic
|
||||
* real-function families (fabs, copysign, floor, ceil, trunc, round, rint,
|
||||
* nearbyint, lrint, llrint, frexp, ldexp, modf, scalbn, scalbln, fmin,
|
||||
* fmax, fdim, fmod, remainder, remquo, ilogb, logb, lround, llround), each
|
||||
* spelled in all three precisions (float `...f`, double, long double
|
||||
* `...l`).
|
||||
*
|
||||
* Every declaration below lands NOW so that the header is stable across the
|
||||
* todo-39 implementation slices; the slices (wip commits) fill in the
|
||||
* src/math/ definitions family by family and never touch this header again.
|
||||
* Later math todos extend this header in place the same way stdlib.h is
|
||||
* extended: todo 40 adds the exp/log/pow families, todo 41 the trig and
|
||||
* hyperbolic families, todo 42 erf/erfc/lgamma/tgamma/fma/nextafter/nan,
|
||||
* and todo 43 provides <complex.h> separately.
|
||||
*
|
||||
* Edge-case behavior (IEEE 754-2008): floor/ceil/trunc round exact results
|
||||
* with correct signed-zero and NaN/Inf handling; fabs/copysign/fmin/fmax/
|
||||
* fdim never raise an exception on their own and preserve NaN payloads
|
||||
* where the standard allows; the classification macros are
|
||||
* exception-free. Functions that per POSIX can set errno (ldexp, scalbn,
|
||||
* scalbln, fmod, remainder, remquo, and the fraction-splitters frexp/modf
|
||||
* with their pointer outputs) carry no const attribute below so the
|
||||
* compiler never hoists or elides an errno-setting call. Functions that
|
||||
* are pure (never set errno in their defined domain — fabs, copysign,
|
||||
* floor, ceil, trunc, round, rint, nearbyint, fmin, fmax, fdim, ilogb,
|
||||
* logb) are declared const and fold away in static links.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ---- Constants ---- */
|
||||
|
||||
/*
|
||||
* Positive infinity, as double/float/long double (C23 7.12.1p4). HUGE_VAL
|
||||
* is also the "overflowed" return value of the strto* conversions.
|
||||
*/
|
||||
#define HUGE_VAL __builtin_huge_val()
|
||||
#define HUGE_VALF __builtin_huge_valf()
|
||||
#define HUGE_VALL __builtin_huge_vall()
|
||||
|
||||
/* Positive infinity as a float (C23 7.12.1p5). */
|
||||
#define INFINITY __builtin_inff()
|
||||
|
||||
/* A quiet NaN as a float (C23 7.12.1p6); the payload is implementation
|
||||
* defined but always a NaN of the quiet kind. */
|
||||
#define NAN __builtin_nanf("")
|
||||
|
||||
/*
|
||||
* Return values of fpclassify (C23 7.12.3.1): the numbers need only be
|
||||
* distinct positive values; the classification macros below hand these
|
||||
* exact constants to __builtin_fpclassify so the two always agree.
|
||||
*/
|
||||
#define FP_NAN 0
|
||||
#define FP_INFINITE 1
|
||||
#define FP_ZERO 2
|
||||
#define FP_SUBNORMAL 3
|
||||
#define FP_NORMAL 4
|
||||
|
||||
/*
|
||||
* Return values of ilogb (C23 7.12.6.5p4): FP_ILOGB0 for a zero argument
|
||||
* and FP_ILOGBNAN for a NaN or infinite argument, each the indicated
|
||||
* <limits.h> sentinel.
|
||||
*/
|
||||
#define FP_ILOGB0 INT_MIN
|
||||
#define FP_ILOGBNAN INT_MAX
|
||||
|
||||
/*
|
||||
* Which error mechanisms the library reports through (C23 7.12.1p3):
|
||||
* MATH_ERRNO and MATH_ERREXCEPT are both in effect (the domain/range
|
||||
* errno values are set where POSIX requires and the corresponding
|
||||
* floating-point exceptions are raised by the hardware).
|
||||
*/
|
||||
#define MATH_ERRNO 1
|
||||
#define MATH_ERREXCEPT 2
|
||||
#define math_errhandling 3
|
||||
|
||||
/* ---- Classification macros (type-generic, exception-free) ---- */
|
||||
|
||||
/*
|
||||
* Classify x as NaN, infinite, zero, subnormal, or normal, returning the
|
||||
* matching FP_* constant. __builtin_fpclassify is a GCC type-generic
|
||||
* builtin that evaluates its floating argument exactly once and never
|
||||
* traps, so no _Generic dispatch is needed here.
|
||||
*/
|
||||
#define fpclassify(x) \
|
||||
__builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, (x))
|
||||
|
||||
/*
|
||||
* The predicate macros below are thin wrappers over the corresponding
|
||||
* GCC type-generic builtins. Each builtin evaluates its argument once and
|
||||
* returns an int; integer arguments classify as finite, non-zero, and
|
||||
* normal (they convert exactly), so isnan(3) is 0 and isinf(3) is 0.
|
||||
*/
|
||||
#define isnan(x) __builtin_isnan(x)
|
||||
#define isinf(x) __builtin_isinf(x)
|
||||
#define isfinite(x) __builtin_isfinite(x)
|
||||
#define isnormal(x) __builtin_isnormal(x)
|
||||
#define signbit(x) __builtin_signbit(x)
|
||||
|
||||
/* ---- Basic arithmetic families (todo 39) ---- */
|
||||
|
||||
/*
|
||||
* Absolute value of x (C23 7.12.7.2). fabs(±0) is +0, fabs(±Inf) is +Inf,
|
||||
* and fabs(NaN) is a NaN. Pure: no domain, no exception.
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
fabsf(float x);
|
||||
|
||||
__attribute__((const)) double
|
||||
fabs(double x);
|
||||
|
||||
__attribute__((const)) long double
|
||||
fabsl(long double x);
|
||||
|
||||
/*
|
||||
* A value with the magnitude of x and the sign of y (C23 7.12.7.3).
|
||||
* copysign(±0, y) carries y's sign; a NaN x keeps its payload but takes
|
||||
* y's sign bit. Pure.
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
copysignf(float x, float y);
|
||||
|
||||
__attribute__((const)) double
|
||||
copysign(double x, double y);
|
||||
|
||||
__attribute__((const)) long double
|
||||
copysignl(long double x, long double y);
|
||||
|
||||
/*
|
||||
* The largest integral value not greater than x (C23 7.12.9.2).
|
||||
* floor(±0) is ±0, floor(-0.5) is -1.0, floor(±Inf) and floor(NaN)
|
||||
* return their argument unchanged. Pure and exact (no inexact
|
||||
* exception), so it folds.
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
floorf(float x);
|
||||
|
||||
__attribute__((const)) double
|
||||
floor(double x);
|
||||
|
||||
__attribute__((const)) long double
|
||||
floorl(long double x);
|
||||
|
||||
/*
|
||||
* The smallest integral value not less than x (C23 7.12.9.1).
|
||||
* ceil(±0) is ±0, ceil(-0.5) is -0.0 (a real negative zero), ceil(±Inf)
|
||||
* and ceil(NaN) return their argument unchanged. Pure and exact.
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
ceilf(float x);
|
||||
|
||||
__attribute__((const)) double
|
||||
ceil(double x);
|
||||
|
||||
__attribute__((const)) long double
|
||||
ceill(long double x);
|
||||
|
||||
/*
|
||||
* The integral value nearest to x in the direction of zero (C23 7.12.9.3).
|
||||
* trunc(-0.7) is -0.0, trunc(0.7) is 0.0, and ±Inf/NaN pass through.
|
||||
* Pure and exact.
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
truncf(float x);
|
||||
|
||||
__attribute__((const)) double
|
||||
trunc(double x);
|
||||
|
||||
__attribute__((const)) long double
|
||||
truncl(long double x);
|
||||
|
||||
/*
|
||||
* The integral value nearest to x, with halfway cases rounded away from
|
||||
* zero (C23 7.12.9.6). round(±0) is ±0, round(-0.5) is -1.0, and ±Inf/
|
||||
* NaN pass through. Pure: round never raises the inexact exception.
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
roundf(float x);
|
||||
|
||||
__attribute__((const)) double
|
||||
round(double x);
|
||||
|
||||
__attribute__((const)) long double
|
||||
roundl(long double x);
|
||||
|
||||
/*
|
||||
* The integral value nearest to x in the current rounding direction
|
||||
* (C23 7.12.9.4). rint may raise the inexact exception; it is still pure
|
||||
* in the const sense because it never sets errno and reads no memory.
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
rintf(float x);
|
||||
|
||||
__attribute__((const)) double
|
||||
rint(double x);
|
||||
|
||||
__attribute__((const)) long double
|
||||
rintl(long double x);
|
||||
|
||||
/*
|
||||
* As rint, but guaranteed never to raise the inexact exception (C23
|
||||
* 7.12.9.5). Pure and exact.
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
nearbyintf(float x);
|
||||
|
||||
__attribute__((const)) double
|
||||
nearbyint(double x);
|
||||
|
||||
__attribute__((const)) long double
|
||||
nearbyintl(long double x);
|
||||
|
||||
/*
|
||||
* The nearest integral value to x in the current rounding direction,
|
||||
* returned as long (C23 7.12.9.7). A result outside the range of long is
|
||||
* a range error (the return value is unspecified and errno may be set),
|
||||
* so no const attribute.
|
||||
*/
|
||||
long
|
||||
lrintf(float x);
|
||||
|
||||
long
|
||||
lrint(double x);
|
||||
|
||||
long
|
||||
lrintl(long double x);
|
||||
|
||||
/*
|
||||
* As lrint, returned as long long (C23 7.12.9.8). Range errors as for
|
||||
* lrint; no const attribute.
|
||||
*/
|
||||
long long
|
||||
llrintf(float x);
|
||||
|
||||
long long
|
||||
llrint(double x);
|
||||
|
||||
long long
|
||||
llrintl(long double x);
|
||||
|
||||
/*
|
||||
* Split x into a fraction f in [1/2, 1) (or 0) and an integer exponent
|
||||
* *exp such that x == f * 2^(*exp) (C23 7.12.6.4). Zero returns ±0 with
|
||||
* *exp 0; Inf/NaN return x with an unspecified *exp. Writes *exp, so no
|
||||
* const attribute.
|
||||
*/
|
||||
float
|
||||
frexpf(float x, int *exp);
|
||||
|
||||
double
|
||||
frexp(double x, int *exp);
|
||||
|
||||
long double
|
||||
frexpl(long double x, int *exp);
|
||||
|
||||
/*
|
||||
* x times 2^n (C23 7.12.6.3): the inverse of frexp. A result too large
|
||||
* to represent is a range error returning ±HUGE_VAL with errno ERANGE;
|
||||
* no const attribute.
|
||||
*/
|
||||
float
|
||||
ldexpf(float x, int n);
|
||||
|
||||
double
|
||||
ldexp(double x, int n);
|
||||
|
||||
long double
|
||||
ldexpl(long double x, int n);
|
||||
|
||||
/*
|
||||
* Split x into an integral part stored in *iptr and a fractional part
|
||||
* returned (C23 7.12.6.5); both have x's sign, so modf(-1.5, &i) puts
|
||||
* -1.0 in i and returns -0.5. Writes *iptr, so no const attribute.
|
||||
*/
|
||||
float
|
||||
modff(float x, float *iptr);
|
||||
|
||||
double
|
||||
modf(double x, double *iptr);
|
||||
|
||||
long double
|
||||
modfl(long double x, long double *iptr);
|
||||
|
||||
/*
|
||||
* x * FLT_RADIX^n with FLT_RADIX 2 (C23 7.12.6.6): the scalb* functions
|
||||
* differ from ldexp only in the exponent argument's type. A result too
|
||||
* large to represent is a range error returning ±HUGE_VAL* with errno
|
||||
* ERANGE; no const attribute.
|
||||
*/
|
||||
float
|
||||
scalbnf(float x, int n);
|
||||
|
||||
double
|
||||
scalbn(double x, int n);
|
||||
|
||||
long double
|
||||
scalbnl(long double x, int n);
|
||||
|
||||
float
|
||||
scalblnf(float x, long n);
|
||||
|
||||
double
|
||||
scalbln(double x, long n);
|
||||
|
||||
long double
|
||||
scalblnl(long double x, long n);
|
||||
|
||||
/*
|
||||
* The smaller of x and y (C23 7.12.12.4), returning -0.0 when the
|
||||
* arguments are +0.0 and -0.0. A NaN argument is ignored in favor of the
|
||||
* numeric one; two NaNs return a NaN. Pure.
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
fminf(float x, float y);
|
||||
|
||||
__attribute__((const)) double
|
||||
fmin(double x, double y);
|
||||
|
||||
__attribute__((const)) long double
|
||||
fminl(long double x, long double y);
|
||||
|
||||
/*
|
||||
* The larger of x and y (C23 7.12.12.3), returning +0.0 when the
|
||||
* arguments are +0.0 and -0.0. NaN handling as fmin. Pure.
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
fmaxf(float x, float y);
|
||||
|
||||
__attribute__((const)) double
|
||||
fmax(double x, double y);
|
||||
|
||||
__attribute__((const)) long double
|
||||
fmaxl(long double x, long double y);
|
||||
|
||||
/*
|
||||
* The positive difference x - y when x > y and +0.0 otherwise (C23
|
||||
* 7.12.12.2); fdim(x, NaN) and fdim(NaN, x) return a NaN. Pure in the
|
||||
* domain-error-free sense (an overflow may still raise an exception
|
||||
* through the hardware, which const does not model).
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
fdimf(float x, float y);
|
||||
|
||||
__attribute__((const)) double
|
||||
fdim(double x, double y);
|
||||
|
||||
__attribute__((const)) long double
|
||||
fdiml(long double x, long double y);
|
||||
|
||||
/*
|
||||
* The floating-point remainder x - n*y, where n is x/y truncated toward
|
||||
* zero (C23 7.12.10.1); the result therefore has x's sign. fmod(x, ±0) is
|
||||
* a domain error returning NaN with errno EDOM; ±Inf/x is likewise a
|
||||
* domain error. errno can be set, so no const attribute.
|
||||
*/
|
||||
float
|
||||
fmodf(float x, float y);
|
||||
|
||||
double
|
||||
fmod(double x, double y);
|
||||
|
||||
long double
|
||||
fmodl(long double x, long double y);
|
||||
|
||||
/*
|
||||
* The IEEE remainder x - n*y, where n is x/y rounded to the nearest
|
||||
* integer (ties to even) (C23 7.12.10.2); |result| <= |y|/2. Domain
|
||||
* errors as fmod; errno can be set, so no const attribute.
|
||||
*/
|
||||
float
|
||||
remainderf(float x, float y);
|
||||
|
||||
double
|
||||
remainder(double x, double y);
|
||||
|
||||
long double
|
||||
remainderl(long double x, long double y);
|
||||
|
||||
/*
|
||||
* As remainder, additionally storing the low bits of the integer
|
||||
* quotient n in *quo (C23 7.12.10.3). Writes *quo, so no const
|
||||
* attribute.
|
||||
*/
|
||||
float
|
||||
remquof(float x, float y, int *quo);
|
||||
|
||||
double
|
||||
remquo(double x, double y, int *quo);
|
||||
|
||||
long double
|
||||
remquol(long double x, long double y, int *quo);
|
||||
|
||||
/*
|
||||
* The signed exponent of x as an int (C23 7.12.6.5p1): ilogb(x) is
|
||||
* floor(log2 |x|) for a nonzero finite x. ilogb(±0) returns FP_ILOGB0,
|
||||
* ilogb(±Inf) and ilogb(NaN) return FP_ILOGBNAN. Pure: the sentinels are
|
||||
* returned without touching errno.
|
||||
*/
|
||||
__attribute__((const)) int
|
||||
ilogbf(float x);
|
||||
|
||||
__attribute__((const)) int
|
||||
ilogb(double x);
|
||||
|
||||
__attribute__((const)) int
|
||||
ilogbl(long double x);
|
||||
|
||||
/*
|
||||
* The signed exponent of x as a floating-point value (C23 7.12.6.6):
|
||||
* logb(±0) is -Inf and logb(±Inf) is +Inf, each raising the division-
|
||||
* by-zero/invalid exception through the hardware but without an errno
|
||||
* path in the representable domain. Pure.
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
logbf(float x);
|
||||
|
||||
__attribute__((const)) double
|
||||
logb(double x);
|
||||
|
||||
__attribute__((const)) long double
|
||||
logbl(long double x);
|
||||
|
||||
/*
|
||||
* The nearest integral value to x, with halfway cases rounded away from
|
||||
* zero, returned as long (C23 7.12.9.9). A result outside the range of
|
||||
* long is a range error; no const attribute.
|
||||
*/
|
||||
long
|
||||
lroundf(float x);
|
||||
|
||||
long
|
||||
lround(double x);
|
||||
|
||||
long
|
||||
lroundl(long double x);
|
||||
|
||||
/*
|
||||
* As lround, returned as long long (C23 7.12.9.10). Range errors as for
|
||||
* lround; no const attribute.
|
||||
*/
|
||||
long long
|
||||
llroundf(float x);
|
||||
|
||||
long long
|
||||
llround(double x);
|
||||
|
||||
long long
|
||||
llroundl(long double x);
|
||||
|
||||
/*
|
||||
* e raised to the power x (C23 7.12.6.1). exp(+0) == exp(-0) == 1;
|
||||
* exp(-Inf) == +0; exp(+Inf) == +Inf; exp(NaN) == NaN. A result too
|
||||
* large or too small is a range error and sets errno to ERANGE, so no
|
||||
* const attribute.
|
||||
*/
|
||||
double
|
||||
exp(double x);
|
||||
|
||||
float
|
||||
expf(float x);
|
||||
|
||||
long double
|
||||
expl(long double x);
|
||||
|
||||
/*
|
||||
* 2 raised to the power x (C23 7.12.6.2). exp2(+0) == exp2(-0) == 1;
|
||||
* exp2(-Inf) == +0; exp2(+Inf) == +Inf; exp2(NaN) == NaN. A result too
|
||||
* large or too small is a range error and sets errno to ERANGE, so no
|
||||
* const attribute.
|
||||
*/
|
||||
double
|
||||
exp2(double x);
|
||||
|
||||
float
|
||||
exp2f(float x);
|
||||
|
||||
long double
|
||||
exp2l(long double x);
|
||||
|
||||
/*
|
||||
* e raised to the power x minus 1 (C23 7.12.6.3). expm1(+0) == +0;
|
||||
* expm1(-0) == -0; expm1(-Inf) == -1; expm1(+Inf) == +Inf;
|
||||
* expm1(NaN) == NaN. A result too large is a range error and sets
|
||||
* errno to ERANGE, so no const attribute.
|
||||
*/
|
||||
double
|
||||
expm1(double x);
|
||||
|
||||
float
|
||||
expm1f(float x);
|
||||
|
||||
long double
|
||||
expm1l(long double x);
|
||||
|
||||
/*
|
||||
* Natural logarithm of x (C23 7.12.6.7). log(+0) == log(-0) == -Inf;
|
||||
* log(1) == 0; log(-x) == NaN (domain error); log(+Inf) == +Inf;
|
||||
* log(NaN) == NaN. A negative argument is a domain error setting errno
|
||||
* to EDOM and a zero argument is a range error setting errno to ERANGE,
|
||||
* so no const attribute.
|
||||
*/
|
||||
double
|
||||
log(double x);
|
||||
|
||||
float
|
||||
logf(float x);
|
||||
|
||||
long double
|
||||
logl(long double x);
|
||||
|
||||
/*
|
||||
* Base-2 logarithm of x (C23 7.12.6.8). log2(1) == 0; log2(2) == 1.
|
||||
* Special values, domain errors, and range errors as for log, so no
|
||||
* const attribute.
|
||||
*/
|
||||
double
|
||||
log2(double x);
|
||||
|
||||
float
|
||||
log2f(float x);
|
||||
|
||||
long double
|
||||
log2l(long double x);
|
||||
|
||||
/*
|
||||
* Base-10 logarithm of x (C23 7.12.6.9). log10(1) == 0; log10(10) == 1.
|
||||
* Special values, domain errors, and range errors as for log, so no
|
||||
* const attribute.
|
||||
*/
|
||||
double
|
||||
log10(double x);
|
||||
|
||||
float
|
||||
log10f(float x);
|
||||
|
||||
long double
|
||||
log10l(long double x);
|
||||
|
||||
/*
|
||||
* Natural logarithm of 1 + x (C23 7.12.6.13). log1p(+-0) == +-0;
|
||||
* log1p(-1) == -Inf (a pole error setting errno to ERANGE); log1p(x) for
|
||||
* x < -1 is a domain error setting errno to EDOM; log1p(+Inf) == +Inf;
|
||||
* log1p(NaN) == NaN. Accurate for x close to 0, so no const attribute.
|
||||
*/
|
||||
double
|
||||
log1p(double x);
|
||||
|
||||
float
|
||||
log1pf(float x);
|
||||
|
||||
long double
|
||||
log1pl(long double x);
|
||||
|
||||
/*
|
||||
* Principal square root of x (C23 7.12.5.4). sqrt(+-0) == +-0; sqrt(x)
|
||||
* for x < 0 is a domain error returning NaN and setting errno to EDOM;
|
||||
* sqrt(+Inf) == +Inf; sqrt(NaN) == NaN. Correctly rounded; no const
|
||||
* attribute because of the errno path.
|
||||
*/
|
||||
double
|
||||
sqrt(double x);
|
||||
|
||||
float
|
||||
sqrtf(float x);
|
||||
|
||||
long double
|
||||
sqrtl(long double x);
|
||||
|
||||
/*
|
||||
* Cube root of x (C23 7.12.5.5). cbrt(+-0) == +-0; cbrt(+-Inf) == +-Inf;
|
||||
* cbrt(NaN) == NaN; cbrt(-x) == -cbrt(x). Defined for all reals and never
|
||||
* sets errno.
|
||||
*/
|
||||
double
|
||||
cbrt(double x);
|
||||
|
||||
float
|
||||
cbrtf(float x);
|
||||
|
||||
long double
|
||||
cbrtl(long double x);
|
||||
|
||||
/*
|
||||
* Square root of x*x + y*y without undue overflow or underflow
|
||||
* (C23 7.12.5.6). hypot(+-0, +-0) == +0; hypot(+-Inf, y) == +Inf even for
|
||||
* NaN y; hypot(x, NaN) == NaN for finite x. The internal scaling avoids
|
||||
* spurious overflow, so no errno path exists.
|
||||
*/
|
||||
double
|
||||
hypot(double x, double y);
|
||||
|
||||
float
|
||||
hypotf(float x, float y);
|
||||
|
||||
long double
|
||||
hypotl(long double x, long double y);
|
||||
|
||||
/*
|
||||
* x raised to the power y (C23 7.12.6.11). pow(x, 0) == 1; pow(1, y) == 1
|
||||
* even for NaN y; pow(NaN, 0) == 1; pow(+-0, negative y) == +-Inf (a pole
|
||||
* error setting errno to ERANGE); pow(negative finite x, non-integer y) is
|
||||
* a domain error setting errno to EDOM; overflow and underflow set errno to
|
||||
* ERANGE. No const attribute.
|
||||
*/
|
||||
double
|
||||
pow(double x, double y);
|
||||
|
||||
float
|
||||
powf(float x, float y);
|
||||
|
||||
long double
|
||||
powl(long double x, long double y);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_MATH_H */
|
||||
@@ -0,0 +1,99 @@
|
||||
#ifndef VLIBC_POLL_H
|
||||
#define VLIBC_POLL_H
|
||||
|
||||
/*
|
||||
* vlibc — <poll.h>.
|
||||
*
|
||||
* Poll multiple file descriptors for readiness. Every declaration here is a
|
||||
* thin pass-through to the kernel: poll() blocks per its millisecond
|
||||
* timeout, ppoll() takes a struct timespec and an optional signal mask
|
||||
* directly, and both return the count of ready descriptors (0 on timeout,
|
||||
* -1 with errno set on error).
|
||||
*
|
||||
* Level 1 (onlyposix): poll, nfds_t, struct pollfd, the POLL* event bits.
|
||||
* Level 2 (muslmimic): ppoll (Linux-specific; POSIX has only poll).
|
||||
*
|
||||
* struct pollfd and the POLL* values mirror the kernel ABI (x86_64
|
||||
* asm-generic/poll.h) exactly: the wrappers pass them to SYS_ppoll
|
||||
* unmodified. POLLERR, POLLHUP and POLLNVAL are report-only — they can
|
||||
* appear in revents even when not requested in events.
|
||||
*
|
||||
* The signal mask type ppoll takes is declared under the shared guard
|
||||
* below; <signal.h> is its canonical POSIX home and will own the full sig*
|
||||
* API.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Requestable event bits (poll()'s events field). */
|
||||
#define POLLIN 0x001 /* readable */
|
||||
#define POLLPRI 0x002 /* urgent readable (out-of-band) */
|
||||
#define POLLOUT 0x004 /* writable */
|
||||
|
||||
/* Report-only event bits (poll()'s revents field). */
|
||||
#define POLLERR 0x008 /* error condition */
|
||||
#define POLLHUP 0x010 /* hung up */
|
||||
#define POLLNVAL 0x020 /* invalid fd */
|
||||
|
||||
/* The number of struct pollfd entries poll()/ppoll() watch. */
|
||||
typedef unsigned long nfds_t;
|
||||
|
||||
/*
|
||||
* One descriptor watched by poll()/ppoll(). fd is the descriptor (negative
|
||||
* to ignore), events the requested bits (POLLIN/POLLOUT), revents the bits
|
||||
* the kernel reports (the requested bits plus any POLLERR/POLLHUP/POLLNVAL).
|
||||
*/
|
||||
struct pollfd
|
||||
{
|
||||
int fd;
|
||||
short events;
|
||||
short revents;
|
||||
};
|
||||
|
||||
/*
|
||||
* Wait for readiness on the first nfds entries of fds, blocking up to
|
||||
* timeout milliseconds (timeout < 0 blocks indefinitely, 0 never blocks).
|
||||
* Return the number of entries with a nonzero revents, 0 on timeout, or -1
|
||||
* with errno set on error.
|
||||
*/
|
||||
int
|
||||
poll(struct pollfd *fds, nfds_t nfds, int timeout);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): Linux-specific. */
|
||||
|
||||
#ifndef VLIBC_SIGSET_T_DEFINED
|
||||
#define VLIBC_SIGSET_T_DEFINED
|
||||
/*
|
||||
* Signal mask type: a single 64-bit word — the x86_64 Linux sigset_t (see
|
||||
* setjmp.h). <signal.h> is the canonical POSIX home for sigset_t and builds
|
||||
* the sig* API on this same layout; the typedef is repeated in
|
||||
* <sys/select.h> under this guard so the two headers stay consistent.
|
||||
*/
|
||||
typedef unsigned long sigset_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Like poll(), but the timeout is a struct timespec and, when sigmask is
|
||||
* not NULL, the given signal mask is atomically installed for the duration
|
||||
* of the wait (the previous mask is restored before returning).
|
||||
* Linux-specific.
|
||||
*/
|
||||
int
|
||||
ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout, const sigset_t *sigmask);
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_POLL_H */
|
||||
@@ -0,0 +1,90 @@
|
||||
#ifndef VLIBC_PWD_H
|
||||
#define VLIBC_PWD_H
|
||||
|
||||
/*
|
||||
* vlibc — <pwd.h>.
|
||||
*
|
||||
* The password database (todo 37). struct passwd mirrors /etc/passwd, whose
|
||||
* records are name:passwd:uid:gid:gecos:dir:shell — seven ':'-separated
|
||||
* fields. Everything here is POSIX.1-2008 base (Level 1).
|
||||
*
|
||||
* The non-reentrant forms getpwnam/getpwuid/getpwent return a pointer to a
|
||||
* static structure whose contents — including the strings it points at —
|
||||
* are overwritten by the next call to any of the three, so the result must
|
||||
* be copied out before the next database call. getpwnam/getpwuid open and
|
||||
* scan the whole file per call; getpwent walks a cursor that setpwent
|
||||
* rewinds and endpwent closes.
|
||||
*
|
||||
* The getpwnam_r/getpwuid_r/getpwent_r forms write the entry into
|
||||
* caller-supplied storage (struct, char buffer of bufsize bytes) and
|
||||
* return 0 on success with *result pointing at the filled struct, or an
|
||||
* error number as the return value with *result left NULL: ERANGE when the
|
||||
* buffer is too small for the entry, 0 with *result NULL when no entry
|
||||
* matches (or the stream is exhausted). errno is never touched by any _r
|
||||
* function: the error number IS the return value.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* One password database entry (/etc/passwd record). */
|
||||
struct passwd
|
||||
{
|
||||
char *pw_name; /* user's login name */
|
||||
char *pw_passwd; /* encrypted password; often "x" with shadow files */
|
||||
uid_t pw_uid; /* numeric user id */
|
||||
gid_t pw_gid; /* numeric group id */
|
||||
char *pw_gecos; /* user information (comment) field */
|
||||
char *pw_dir; /* home directory */
|
||||
char *pw_shell; /* login shell */
|
||||
};
|
||||
|
||||
/*
|
||||
* Look up the first entry whose name matches; NULL when absent. The result
|
||||
* points into shared static storage valid only until the next pwd call.
|
||||
*/
|
||||
struct passwd *
|
||||
getpwnam(const char *name);
|
||||
|
||||
/*
|
||||
* Look up the first entry whose uid matches; NULL when absent. Result
|
||||
* storage is shared with getpwnam/getpwent as described above.
|
||||
*/
|
||||
struct passwd *
|
||||
getpwuid(uid_t uid);
|
||||
|
||||
/*
|
||||
* Return the next entry from the password stream, opening it on the first
|
||||
* call; NULL at end of file. The stream cursor advances per call.
|
||||
*/
|
||||
struct passwd *
|
||||
getpwent(void);
|
||||
|
||||
/* Rewind the password stream to its first entry. */
|
||||
void
|
||||
setpwent(void);
|
||||
|
||||
/* Close the password stream; a later getpwent reopens from the start. */
|
||||
void
|
||||
endpwent(void);
|
||||
|
||||
int
|
||||
getpwnam_r(const char *name, struct passwd *pw, char *buf, size_t bufsize, struct passwd **result);
|
||||
|
||||
int
|
||||
getpwuid_r(uid_t uid, struct passwd *pw, char *buf, size_t bufsize, struct passwd **result);
|
||||
|
||||
int
|
||||
getpwent_r(struct passwd *pw, char *buf, size_t bufsize, struct passwd **result);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_PWD_H */
|
||||
@@ -0,0 +1,436 @@
|
||||
#ifndef VLIBC_SIGNAL_H
|
||||
#define VLIBC_SIGNAL_H
|
||||
|
||||
/*
|
||||
* vlibc — <signal.h>.
|
||||
*
|
||||
* Signal handling (POSIX.1-2008 base) plus the XSI/obsolete conveniences.
|
||||
* The core calls ride the kernel's rt_* signal ABI (SYS_rt_sigaction,
|
||||
* SYS_rt_sigprocmask, SYS_rt_sigpending, SYS_rt_sigsuspend,
|
||||
* SYS_rt_sigtimedwait, SYS_rt_sigqueueinfo) with the x86_64 kernel
|
||||
* sigset size of one 64-bit word.
|
||||
*
|
||||
* Level 1 (onlyposix): signal numbers, SIG_DFL/SIG_IGN/SIG_ERR, the
|
||||
* sigset_t manipulation and query calls, sigaction,
|
||||
* sigprocmask, sigpending, sigsuspend, sigwait,
|
||||
* sigwaitinfo, sigtimedwait, kill, killpg, raise,
|
||||
* pause, alarm, abort, sigqueue, and the signal()
|
||||
* convenience.
|
||||
* Level 2 (muslmimic): sigaltstack (and stack_t / SS_* / SIGSTKSZ),
|
||||
* ualarm, siginterrupt, psignal, psiginfo (XSI /
|
||||
* obsolete — sigaction/sigprocmask/alarm are POSIX
|
||||
* base and stay at level 1).
|
||||
*
|
||||
* The layout facts below are x86_64 kernel-ABI:
|
||||
*
|
||||
* - The kernel sigset_t is 8 bytes (one word); signal N occupies bit
|
||||
* N-1, signals 1..64 are usable (32..64 are the realtime signals).
|
||||
* The same single-word sigset_t is declared by <sys/select.h> and
|
||||
* <poll.h> under the shared VLIBC_SIGSET_T_DEFINED guard so pselect/
|
||||
* ppoll and the sig* API agree on the type no matter the include order.
|
||||
* - SIG_DFL/SIG_IGN/SIG_ERR are the magic handler values 0/1/-1 cast to
|
||||
* the handler type; the kernel dispatches on the raw value.
|
||||
* - The PUBLIC struct sigaction is the POSIX layout (handler, mask,
|
||||
* int flags, restorer). The kernel's rt_sigaction ABI layout is
|
||||
* DIFFERENT (handler, flags, restorer, mask — flags is a full word);
|
||||
* src/signal/sigaction.c converts between the two.
|
||||
* - SA_RESTORER (0x04000000) is the kernel-private bit that makes the
|
||||
* kernel jump to the user-supplied sa_restorer trampoline when the
|
||||
* handler returns; every real (non-DFL/IGN) handler install sets it
|
||||
* (see sigaction.c). It is masked out of the flags reported back.
|
||||
*
|
||||
* siginfo_t is the minimal 128-byte layout shared with <sys/wait.h>
|
||||
* under the VLIBC_INTERNAL_SIGINFO_DEFINED guard (see the block below);
|
||||
* either header may be included first. No SIGEV_* / SI_* constants are
|
||||
* defined: the signal-handling details behind them are owned by a later
|
||||
* todo and nothing here needs the names.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* struct timespec is defined by <time.h>; a forward declaration is enough
|
||||
* for sigtimedwait()'s pointer parameter, and the two headers may be
|
||||
* included in either order.
|
||||
*/
|
||||
struct timespec;
|
||||
|
||||
/* Signal numbers (x86_64 asm-generic values; kernel-ABI facts). */
|
||||
#define SIGHUP 1 /* hangup detected on controlling terminal */
|
||||
#define SIGINT 2 /* interactive attention (^C) */
|
||||
#define SIGQUIT 3 /* interactive quit (^\) */
|
||||
#define SIGILL 4 /* illegal instruction */
|
||||
#define SIGTRAP 5 /* trace/breakpoint trap */
|
||||
#define SIGABRT 6 /* abnormal termination (abort()) */
|
||||
#define SIGBUS 7 /* bus error */
|
||||
#define SIGFPE 8 /* floating-point exception */
|
||||
#define SIGKILL 9 /* kill (cannot be caught or ignored) */
|
||||
#define SIGUSR1 10 /* user-defined signal 1 */
|
||||
#define SIGSEGV 11 /* invalid memory reference */
|
||||
#define SIGUSR2 12 /* user-defined signal 2 */
|
||||
#define SIGPIPE 13 /* write on a pipe with no reader */
|
||||
#define SIGALRM 14 /* real-time timer expired (alarm/ualarm/setitimer) */
|
||||
#define SIGTERM 15 /* termination request */
|
||||
#define SIGSTKFLT 16 /* stack fault (unused) */
|
||||
#define SIGCHLD 17 /* child stopped or terminated */
|
||||
#define SIGCONT 18 /* continue if stopped */
|
||||
#define SIGSTOP 19 /* stop (cannot be caught or ignored) */
|
||||
#define SIGTSTP 20 /* interactive stop (^Z) */
|
||||
#define SIGTTIN 21 /* background process read from terminal */
|
||||
#define SIGTTOU 22 /* background process write to terminal */
|
||||
#define SIGURG 23 /* urgent condition on a socket */
|
||||
#define SIGXCPU 24 /* CPU time limit exceeded */
|
||||
#define SIGXFSZ 25 /* file size limit exceeded */
|
||||
#define SIGVTALRM 26 /* virtual timer expired */
|
||||
#define SIGPROF 27 /* profiling timer expired */
|
||||
#define SIGWINCH 28 /* window size change */
|
||||
#define SIGIO 29 /* I/O now possible */
|
||||
#define SIGPWR 30 /* power failure */
|
||||
#define SIGSYS 31 /* bad system call */
|
||||
|
||||
/* Realtime signals: the 32..64 range, usable for user-defined purposes. */
|
||||
#define SIGRTMIN 32
|
||||
#define SIGRTMAX 64
|
||||
|
||||
/* One more than the highest signal number; signals 1..64 are usable. */
|
||||
#define NSIG 65
|
||||
|
||||
/* The magic handler values. The kernel dispatches on the raw value. */
|
||||
#define SIG_DFL ((void (*)(int))0) /* default action */
|
||||
#define SIG_IGN ((void (*)(int))1) /* ignore the signal */
|
||||
#define SIG_ERR ((void (*)(int)) - 1) /* signal() error return */
|
||||
|
||||
/*
|
||||
* Integer type that can be accessed as an atomic entity even when an
|
||||
* asynchronous signal interrupts the access (volatile sig_atomic_t is the
|
||||
* conventional spelling). int is atomic on x86_64.
|
||||
*/
|
||||
typedef int sig_atomic_t;
|
||||
|
||||
/* sigprocmask()/pthread_sigmask() how values (kernel-identical). */
|
||||
#define SIG_BLOCK 0 /* add the given set to the blocked mask */
|
||||
#define SIG_UNBLOCK 1 /* remove the given set from the blocked mask */
|
||||
#define SIG_SETMASK 2 /* replace the blocked mask with the given set */
|
||||
|
||||
#ifndef VLIBC_SIGSET_T_DEFINED
|
||||
#define VLIBC_SIGSET_T_DEFINED
|
||||
/*
|
||||
* Signal mask type: a single 64-bit word — the x86_64 Linux sigset_t,
|
||||
* where signal N is bit N-1 (see setjmp.h, whose sigsetjmp stores this
|
||||
* same word). The typedef is shared with <sys/select.h> and <poll.h>
|
||||
* under this guard, so pselect()/ppoll() and the sig* API agree on the
|
||||
* type regardless of include order.
|
||||
*/
|
||||
typedef unsigned long sigset_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Value carried with a queued (realtime) signal. sival_int and sival_ptr
|
||||
* are alternative views of the same 64-bit payload.
|
||||
*/
|
||||
union sigval
|
||||
{
|
||||
int sival_int; /* integer payload */
|
||||
void *sival_ptr; /* pointer payload */
|
||||
};
|
||||
|
||||
/* POSIX spelling of the payload type. */
|
||||
typedef union sigval sigval_t;
|
||||
|
||||
/*
|
||||
* Description of a delivered signal: number, errno, code and one of the
|
||||
* per-code unions. This is the minimal x86_64 kernel layout (128 bytes)
|
||||
* shared with <sys/wait.h>, which fills si_pid/si_uid/si_status/si_utime/
|
||||
* si_stime for waitid(); the two headers define this exact block under the
|
||||
* common VLIBC_INTERNAL_SIGINFO_DEFINED guard, so whichever is included
|
||||
* first wins and the other skips — the type is identical either way. The
|
||||
* kernel copies the full 128 bytes on signal delivery (sigaction with
|
||||
* SA_SIGINFO, sigwaitinfo, sigtimedwait), so the size must stay 128 and
|
||||
* the pad member guarantees it; the fields behind each si_code value
|
||||
* (si_addr, si_value, timers, ...) are owned by a later todo.
|
||||
*/
|
||||
#ifndef VLIBC_INTERNAL_SIGINFO_DEFINED
|
||||
#define VLIBC_INTERNAL_SIGINFO_DEFINED
|
||||
typedef struct
|
||||
{
|
||||
int si_signo;
|
||||
int si_errno;
|
||||
int si_code;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
pid_t si_pid; /* 16 */
|
||||
uid_t si_uid; /* 20 */
|
||||
int si_status; /* 24 */
|
||||
long si_utime; /* 32 — 8-byte clock_t, matches the kernel/glibc ABI */
|
||||
long si_stime; /* 40 */
|
||||
};
|
||||
int vlibc_siginfo_pad[28]; /* union sized 112 so the struct stays 128 */
|
||||
};
|
||||
} siginfo_t;
|
||||
|
||||
_Static_assert(sizeof(siginfo_t) == 128, "siginfo_t must match the kernel size");
|
||||
_Static_assert(offsetof(siginfo_t, si_pid) == 16, "si_pid must sit at offset 16");
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Action taken when signal sig is delivered. sa_handler and sa_sigaction
|
||||
* are two views of the same slot (POSIX: sa_sigaction is used when
|
||||
* SA_SIGINFO is set and receives (sig, siginfo_t *, void *) instead of
|
||||
* just sig). The layout — handler, mask, flags, restorer — is the PUBLIC
|
||||
* POSIX order; the kernel ABI uses a different order (see the file-top
|
||||
* note) that sigaction() translates.
|
||||
*/
|
||||
struct sigaction
|
||||
{
|
||||
union
|
||||
{
|
||||
void (*sa_handler)(int); /* SIG_DFL/SIG_IGN/a handler */
|
||||
void (*sa_sigaction)(int, siginfo_t *, void *); /* SA_SIGINFO form */
|
||||
};
|
||||
sigset_t sa_mask; /* signals additionally blocked in the handler */
|
||||
int sa_flags; /* SA_* bits below */
|
||||
void (*sa_restorer)(void); /* kernel-private; do not set (see top) */
|
||||
};
|
||||
|
||||
_Static_assert(sizeof(struct sigaction) == 32, "struct sigaction must be 32 bytes");
|
||||
_Static_assert(offsetof(struct sigaction, sa_mask) == 8, "sa_mask must sit at offset 8");
|
||||
_Static_assert(offsetof(struct sigaction, sa_flags) == 16, "sa_flags must sit at offset 16");
|
||||
_Static_assert(offsetof(struct sigaction, sa_restorer) == 24, "sa_restorer must sit at offset 24");
|
||||
|
||||
/* sa_flags bits (x86_64 kernel values; SA_RESTORER is kernel-private). */
|
||||
#define SA_NOCLDSTOP 1 /* do not generate SIGCHLD when children stop */
|
||||
#define SA_NOCLDWAIT 2 /* do not leave zombies on child exit */
|
||||
#define SA_SIGINFO 4 /* call the handler with the siginfo form */
|
||||
#define SA_RESTORER 0x04000000 /* handler returns via sa_restorer (kernel) */
|
||||
#define SA_ONSTACK 0x08000000 /* run the handler on the alternate stack */
|
||||
#define SA_RESTART 0x10000000 /* restart interrupted syscalls after the handler */
|
||||
#define SA_NODEFER 0x40000000 /* do not block the delivered signal in its handler */
|
||||
#define SA_RESETHAND 0x80000000 /* reset the disposition to SIG_DFL on entry */
|
||||
|
||||
/*
|
||||
* Install the action act for signal sig (NULL leaves it unchanged) and
|
||||
* store the previous action through oact (NULL skips the store). Return
|
||||
* 0, or -1 with errno set. sig must be in 1..64 and (for a catching
|
||||
* action) not SIGKILL/SIGSTOP; the kernel rejects invalid values with
|
||||
* EINVAL. Real handler pointers are installed with SA_RESTORER and the
|
||||
* internal return trampoline so the handler may return normally.
|
||||
*/
|
||||
int
|
||||
sigaction(int sig, const struct sigaction *restrict act, struct sigaction *restrict oact);
|
||||
|
||||
/*
|
||||
* Inspect or change the calling thread's blocked-signal mask. how is
|
||||
* SIG_BLOCK/SIG_UNBLOCK/SIG_SETMASK; set is the mask operand (NULL
|
||||
* queries without changing: how is then ignored) and the previous mask is
|
||||
* stored through oldset (NULL skips the store). Return 0, or -1 with
|
||||
* errno set.
|
||||
*/
|
||||
int
|
||||
sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oldset);
|
||||
|
||||
/* Store the set of blocked-and-pending signals through set. 0 or -1. */
|
||||
int
|
||||
sigpending(sigset_t *set);
|
||||
|
||||
/*
|
||||
* Atomically install mask as the blocked mask and wait until a signal
|
||||
* whose delivery is not blocked by mask is caught; when its handler
|
||||
* returns, the previous mask is restored and sigsuspend returns -1 with
|
||||
* errno EINTR.
|
||||
*/
|
||||
int
|
||||
sigsuspend(const sigset_t *mask);
|
||||
|
||||
/* Empty set: no signal blocked, and no member set. Always 0. */
|
||||
int
|
||||
sigemptyset(sigset_t *set);
|
||||
|
||||
/* Set every signal 1..64 blocked/member. Always 0. */
|
||||
int
|
||||
sigfillset(sigset_t *set);
|
||||
|
||||
/*
|
||||
* Add/remove signal sig to/from set. Return 0, or -1 with errno EINVAL
|
||||
* when sig is outside 1..64.
|
||||
*/
|
||||
int
|
||||
sigaddset(sigset_t *set, int sig);
|
||||
int
|
||||
sigdelset(sigset_t *set, int sig);
|
||||
|
||||
/*
|
||||
* Nonzero when sig is a member of set, 0 otherwise, or -1 with errno
|
||||
* EINVAL when sig is outside 1..64.
|
||||
*/
|
||||
int
|
||||
sigismember(const sigset_t *set, int sig);
|
||||
|
||||
/*
|
||||
* Synchronously wait for one of the signals in set (which should be
|
||||
* blocked in the calling thread) and store the delivered signal number
|
||||
* through sig. Return 0 on success, or the error number (EINTR is
|
||||
* retried internally). The signal is consumed and never delivered to a
|
||||
* handler.
|
||||
*/
|
||||
int
|
||||
sigwait(const sigset_t *restrict set, int *restrict sig);
|
||||
|
||||
/*
|
||||
* Like sigwait, but return the delivered signal number directly (or -1
|
||||
* with errno set) and, when info is not NULL, store the full 128-byte
|
||||
* kernel siginfo through it.
|
||||
*/
|
||||
int
|
||||
sigwaitinfo(const sigset_t *restrict set, siginfo_t *restrict info);
|
||||
|
||||
/*
|
||||
* sigwaitinfo bounded by timeout (relative; NULL waits indefinitely).
|
||||
* Returns the signal number, 0 is never returned for a successful wait,
|
||||
* or -1 with errno set (EAGAIN on timeout, EINTR if a handler ran).
|
||||
*/
|
||||
int
|
||||
sigtimedwait(const sigset_t *restrict set, siginfo_t *restrict info,
|
||||
const struct timespec *restrict timeout);
|
||||
|
||||
/*
|
||||
* Send signal sig to the process pid (negative pid targets a process
|
||||
* group; 0 targets the caller's process group; see killpg). Return 0, or
|
||||
* -1 with errno set. Permission, existence and signal validity are
|
||||
* checked by the kernel.
|
||||
*/
|
||||
int
|
||||
kill(pid_t pid, int sig);
|
||||
|
||||
/*
|
||||
* Send signal sig to every process in the process group pgrp (0 selects
|
||||
* the caller's process group). Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
killpg(pid_t pgrp, int sig);
|
||||
|
||||
/*
|
||||
* Send signal sig to the calling thread (thread-directed, so it is
|
||||
* delivered even when another thread of the process has it blocked).
|
||||
* Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
raise(int sig);
|
||||
|
||||
/*
|
||||
* Wait until a signal is caught, then return -1 with errno EINTR. If the
|
||||
* process is terminated by the signal instead, pause never returns.
|
||||
*/
|
||||
int
|
||||
pause(void);
|
||||
|
||||
/*
|
||||
* Schedule delivery of SIGALRM to the calling process after seconds
|
||||
* seconds (0 cancels any pending alarm). Return the number of seconds
|
||||
* remaining on any previously scheduled alarm, or 0.
|
||||
*/
|
||||
unsigned
|
||||
alarm(unsigned seconds);
|
||||
|
||||
/*
|
||||
* Abnormally terminate the calling process: raise SIGABRT with default
|
||||
* disposition — if SIGABRT is blocked it is unblocked first, and if it
|
||||
* is caught or ignored the disposition is reset to SIG_DFL and the raise
|
||||
* repeated. If that still returns, terminate with exit status 134 as a
|
||||
* last resort. abort never returns.
|
||||
*/
|
||||
__attribute__((noreturn)) void
|
||||
abort(void);
|
||||
|
||||
/*
|
||||
* Send signal sig with payload value to process pid, as if by a kernel
|
||||
* queue operation (the kernel sees a negative si_code, SI_QUEUE). Return
|
||||
* 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
sigqueue(pid_t pid, int sig, const union sigval value);
|
||||
|
||||
/*
|
||||
* Install handler as the action for sig with SA_RESTART (BSD semantics:
|
||||
* syscalls interrupted by the signal are restarted). Return the previous
|
||||
* handler, or SIG_ERR with errno set.
|
||||
*/
|
||||
void (*signal(int sig, void (*handler)(int)))(int);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): XSI and obsolete conveniences. */
|
||||
|
||||
/*
|
||||
* Alternate signal stack descriptor. ss_sp/ss_size name a region the
|
||||
* kernel switches to when delivering a handler installed with SA_ONSTACK
|
||||
* (see sigaltstack below). stack_t is the POSIX spelling.
|
||||
*/
|
||||
typedef struct sigaltstack
|
||||
{
|
||||
void *ss_sp; /* stack base or current stack base */
|
||||
int ss_flags; /* SS_ONSTACK/SS_DISABLE */
|
||||
size_t ss_size; /* stack bytes */
|
||||
} stack_t;
|
||||
|
||||
/* ss_flags values. */
|
||||
#define SS_ONSTACK 1 /* the process is currently executing on this stack */
|
||||
#define SS_DISABLE 2 /* the alternate stack is currently disabled */
|
||||
|
||||
/* Minimum / default alternate stack sizes (x86_64 values). */
|
||||
#define MINSIGSTKSZ 2048
|
||||
#define SIGSTKSZ 8192
|
||||
|
||||
/*
|
||||
* Install ss as the alternate signal stack (NULL leaves it unchanged)
|
||||
* and store the previous descriptor through oss (NULL skips the store).
|
||||
* Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
sigaltstack(const stack_t *restrict ss, stack_t *restrict oss);
|
||||
|
||||
/*
|
||||
* Schedule SIGALRM after usecs microseconds, repeating every interval
|
||||
* microseconds (0 fires once). Return the number of microseconds
|
||||
* remaining on any previously scheduled alarm, or -1 on error.
|
||||
*/
|
||||
useconds_t
|
||||
ualarm(useconds_t usecs, useconds_t interval);
|
||||
|
||||
/*
|
||||
* Toggle the SA_RESTART bit of sig's disposition: flag nonzero removes
|
||||
* it (interrupted syscalls return EINTR, System V semantics), flag zero
|
||||
* restores it. Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
siginterrupt(int sig, int flag);
|
||||
|
||||
/*
|
||||
* Write s (when non-NULL and nonempty), ": " and strsignal(sig) to
|
||||
* stderr. The output is a diagnostic only.
|
||||
*/
|
||||
void
|
||||
psignal(int sig, const char *s);
|
||||
|
||||
/*
|
||||
* Like psignal, but the signal number and details are taken from the
|
||||
* siginfo si (si_signo names the signal). si may be NULL, in which case
|
||||
* only the message prefix is written.
|
||||
*/
|
||||
void
|
||||
psiginfo(const siginfo_t *si, const char *s);
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_SIGNAL_H */
|
||||
+463
@@ -0,0 +1,463 @@
|
||||
#ifndef VLIBC_STDIO_H
|
||||
#define VLIBC_STDIO_H
|
||||
|
||||
/*
|
||||
* vlibc — <stdio.h>.
|
||||
*
|
||||
* Buffered stream I/O (todo 15). The FILE object is opaque to consumers;
|
||||
* the layout lives in src/stdio/stdio_impl.h. stdin/stdout/stderr are
|
||||
* pre-wired streams over fds 0/1/2 and are initialized lazily on first
|
||||
* use. On Linux there is no text/binary distinction, so the 'b' mode
|
||||
* character is accepted and ignored.
|
||||
*
|
||||
* Buffering modes for setvbuf: _IOFBF (fully buffered), _IOLBF (line
|
||||
* buffered: flush on newline), _IONBF (unbuffered). A stream opened on a
|
||||
* terminal defaults to line buffering for stdout and full buffering for
|
||||
* stdin; stderr is always unbuffered.
|
||||
*
|
||||
* getc/putc/getchar/putchar are declared as functions (address-taking and
|
||||
* #undef work) and additionally defined as macros over fgetc/fputc; the
|
||||
* macro arguments are evaluated exactly once.
|
||||
*
|
||||
* Level 2 (muslmimic) adds tmpnam/ctermid/setbuffer/setlinebuf and the
|
||||
* fopen64 name alias (identical ABI on LP64).
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The opaque stream object. The underlying struct tag is vlibc_FILE (see
|
||||
* src/stdio/stdio_impl.h); consumers only ever use FILE *.
|
||||
*/
|
||||
typedef struct vlibc_FILE FILE;
|
||||
|
||||
/* The three pre-wired standard streams (fds 0, 1, 2). */
|
||||
extern FILE *stdin;
|
||||
extern FILE *stdout;
|
||||
extern FILE *stderr;
|
||||
|
||||
/* End of file indicator for character functions. */
|
||||
#define EOF (-1)
|
||||
|
||||
/* Minimum number of simultaneously open files. */
|
||||
#define FOPEN_MAX 16
|
||||
|
||||
/* Default buffer size for setvbuf/setbuf. */
|
||||
#define BUFSIZ 8192
|
||||
|
||||
/* Maximum length of a path argument for stdio functions. */
|
||||
#define FILENAME_MAX 4096
|
||||
|
||||
/* Minimum number of distinct tmpnam-generated names. */
|
||||
#define TMP_MAX 238328
|
||||
|
||||
/* Buffer sizes for tmpnam and ctermid results. */
|
||||
#define L_tmpnam 20
|
||||
#define L_ctermid 9
|
||||
|
||||
/* Seek positions for fseek/fseeko. */
|
||||
#define SEEK_SET 0
|
||||
#define SEEK_CUR 1
|
||||
#define SEEK_END 2
|
||||
|
||||
/* setvbuf modes. */
|
||||
#define _IOFBF 0 // NOLINT(bugprone-reserved-identifier)
|
||||
#define _IOLBF 1 // NOLINT(bugprone-reserved-identifier)
|
||||
#define _IONBF 2 // NOLINT(bugprone-reserved-identifier)
|
||||
|
||||
/* Opaque file position type for fgetpos/fsetpos. */
|
||||
typedef off_t fpos_t;
|
||||
|
||||
/*
|
||||
* Open the file at path with the given mode. The mode is 'r', 'w', or 'a',
|
||||
* optionally followed by '+' (update: read and write), 'b' (ignored on
|
||||
* Linux), and/or 'x' (exclusive create, C11). Returns NULL with errno set
|
||||
* on failure.
|
||||
*/
|
||||
FILE *
|
||||
fopen(const char *restrict path, const char *restrict mode);
|
||||
|
||||
/*
|
||||
* Wrap an existing descriptor in a stream. The requested mode must be
|
||||
* compatible with the descriptor's access mode (basic check via fcntl).
|
||||
* The descriptor is not duplicated; fclose closes it.
|
||||
*/
|
||||
FILE *
|
||||
fdopen(int fd, const char *mode);
|
||||
|
||||
/*
|
||||
* Rebind stream to path. The old descriptor is flushed and closed first.
|
||||
* With path == NULL only the mode changes and the descriptor stays open.
|
||||
* Returns NULL with errno set on failure.
|
||||
*/
|
||||
FILE *
|
||||
freopen(const char *restrict path, const char *restrict mode, FILE *restrict stream);
|
||||
|
||||
/*
|
||||
* Flush pending output, close the descriptor, and release the stream.
|
||||
* Returns EOF if flushing or closing failed.
|
||||
*/
|
||||
int
|
||||
fclose(FILE *stream);
|
||||
|
||||
/*
|
||||
* Flush pending output of stream. fflush(NULL) flushes all open streams
|
||||
* with pending output. On a stream with no pending writes (read mode) the
|
||||
* unread buffered data is discarded and the position rewound; this never
|
||||
* corrupts the stream. Returns EOF on error.
|
||||
*/
|
||||
int
|
||||
fflush(FILE *stream);
|
||||
|
||||
/*
|
||||
* Set the buffer of stream to buf. buf == NULL selects unbuffered I/O;
|
||||
* otherwise the buffer is used with full buffering and BUFSIZ size. Must
|
||||
* be called before the first operation on the stream.
|
||||
*/
|
||||
void
|
||||
setbuf(FILE *restrict stream, char *restrict buf);
|
||||
|
||||
/*
|
||||
* Set the buffering mode of stream: _IOFBF, _IOLBF, or _IONBF. With
|
||||
* buf != NULL the caller supplies size bytes of storage; with buf == NULL
|
||||
* the buffer is allocated lazily on first use (size is then ignored,
|
||||
* except that _IONBF needs no buffer). Returns 0, or -1 with errno EINVAL
|
||||
* for an invalid mode or a non-NULL buf with size 0 (except _IONBF).
|
||||
*/
|
||||
int
|
||||
setvbuf(FILE *restrict stream, char *restrict buf, int mode, size_t size);
|
||||
|
||||
/*
|
||||
* Read up to size * nmemb bytes in items of size bytes each. Returns the
|
||||
* number of complete items read; fewer than nmemb means end of file or an
|
||||
* error (distinguishable via feof/ferror). size or nmemb zero returns 0
|
||||
* without touching the stream.
|
||||
*/
|
||||
size_t
|
||||
fread(void *restrict ptr, size_t size, size_t nmemb, FILE *restrict stream);
|
||||
|
||||
/*
|
||||
* Write size * nmemb bytes in items of size bytes each. Returns the number
|
||||
* of complete items written; fewer than nmemb means an error (see
|
||||
* ferror).
|
||||
*/
|
||||
size_t
|
||||
fwrite(const void *restrict ptr, size_t size, size_t nmemb, FILE *restrict stream);
|
||||
|
||||
/* Read the next character as unsigned char, or EOF. */
|
||||
int
|
||||
fgetc(FILE *stream);
|
||||
|
||||
/* Write c as unsigned char; returns it, or EOF on error. */
|
||||
int
|
||||
fputc(int c, FILE *stream);
|
||||
|
||||
/* Same as fgetc/fputc; also provided as macros (arguments evaluated once). */
|
||||
int
|
||||
getc(FILE *stream);
|
||||
|
||||
int
|
||||
putc(int c, FILE *stream);
|
||||
|
||||
int
|
||||
getchar(void);
|
||||
|
||||
int
|
||||
putchar(int c);
|
||||
|
||||
/*
|
||||
* Read at most n-1 characters into s, stopping after (and keeping) a
|
||||
* newline, then NUL-terminate. Returns s, or NULL if no character was read
|
||||
* (end of file or error).
|
||||
*/
|
||||
char *
|
||||
fgets(char *restrict s, int n, FILE *restrict stream);
|
||||
|
||||
/*
|
||||
* Write the NUL-terminated string s to stream (no trailing newline is
|
||||
* added). Returns a non-negative value, or EOF on error.
|
||||
*/
|
||||
int
|
||||
fputs(const char *restrict s, FILE *restrict stream);
|
||||
|
||||
/*
|
||||
* Push c back onto the input stream; the next read returns it again. One
|
||||
* byte of pushback is guaranteed. Returns c, or EOF on error (also for
|
||||
* ungetc(EOF)). A successful seek discards the pushed-back character.
|
||||
*/
|
||||
int
|
||||
ungetc(int c, FILE *stream);
|
||||
|
||||
/* Position the stream (see SEEK_SET/SEEK_CUR/SEEK_END); clears feof. */
|
||||
int
|
||||
fseek(FILE *stream, long offset, int whence);
|
||||
|
||||
int
|
||||
fseeko(FILE *stream, off_t offset, int whence);
|
||||
|
||||
/* Current stream position, -1 with errno set on error. */
|
||||
long
|
||||
ftell(FILE *stream);
|
||||
|
||||
/*
|
||||
* Not marked pure: the implementation can allocate the stream buffer on
|
||||
* first use (an observable side effect), so the compiler must not elide
|
||||
* or reorder the call. (feof/ferror/fileno below are genuinely read-only
|
||||
* and keep their pure attribute.)
|
||||
*/
|
||||
off_t
|
||||
ftello(FILE *stream);
|
||||
|
||||
/* Rewind to the start and clear feof/ferror (equivalent to
|
||||
* fseeko(stream, 0, SEEK_SET) + clearerr). */
|
||||
void
|
||||
rewind(FILE *stream);
|
||||
|
||||
/* Get/set the opaque position via fpos_t. */
|
||||
int
|
||||
fgetpos(FILE *restrict stream, fpos_t *restrict pos);
|
||||
|
||||
int
|
||||
fsetpos(FILE *stream, const fpos_t *pos);
|
||||
|
||||
/* End-of-file and error indicators. */
|
||||
__attribute__((pure)) int
|
||||
feof(FILE *stream);
|
||||
|
||||
__attribute__((pure)) int
|
||||
ferror(FILE *stream);
|
||||
|
||||
void
|
||||
clearerr(FILE *stream);
|
||||
|
||||
/*
|
||||
* Remove the file at path (a directory is removed like rmdir). Returns 0,
|
||||
* or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
remove(const char *path);
|
||||
|
||||
/* Rename oldpath to newpath. Returns 0, or -1 with errno set. */
|
||||
int
|
||||
rename(const char *oldpath, const char *newpath);
|
||||
|
||||
/*
|
||||
* Create an anonymous temporary file ("w+b"): the file is created in /tmp
|
||||
* and unlinked immediately, so it disappears on close. Returns the stream,
|
||||
* or NULL with errno set.
|
||||
*/
|
||||
FILE *
|
||||
tmpfile(void);
|
||||
|
||||
/* The descriptor underlying the stream. */
|
||||
__attribute__((pure)) int
|
||||
fileno(FILE *stream);
|
||||
|
||||
/*
|
||||
* Run command in a subshell ("sh -c command") with a pipe attached to
|
||||
* its standard output (mode "r") or standard input (mode "w"). Only the
|
||||
* two POSIX modes are accepted (the glibc "re"/"we" close-on-exec
|
||||
* extension is not). Returns the stream, or NULL with errno set.
|
||||
* pclose closes the stream, waits for the shell, and returns its
|
||||
* termination status (the raw wait status, e.g. 0 for "exit 0"), or -1
|
||||
* when the stream was not opened by popen or the wait failed.
|
||||
*/
|
||||
FILE *
|
||||
popen(const char *command, const char *mode);
|
||||
|
||||
int
|
||||
pclose(FILE *stream);
|
||||
|
||||
/* getc/putc/getchar/putchar as macros over fgetc/fputc (see above). */
|
||||
#define getc(stream) fgetc(stream)
|
||||
#define putc(c, stream) fputc((c), (stream))
|
||||
#define getchar() fgetc(stdin)
|
||||
#define putchar(c) fputc((c), stdout)
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* Generate a name for a temporary file ("/tmp/vlibcXXXXXX" form; the file
|
||||
* is NOT created). With s == NULL a static buffer is used. Not thread-safe,
|
||||
* obsolescent.
|
||||
*/
|
||||
char *
|
||||
tmpnam(char *s);
|
||||
|
||||
/* Controlling terminal path: copies "/dev/tty" into s (or a static
|
||||
* buffer when s == NULL) and returns it. */
|
||||
char *
|
||||
ctermid(char *s);
|
||||
|
||||
/* BSD: like setvbuf with _IOFBF/_IONBF and the given size. */
|
||||
void
|
||||
setbuffer(FILE *stream, char *buf, size_t size);
|
||||
|
||||
/* BSD: select line buffering (setvbuf with _IOLBF and NULL buffer). */
|
||||
void
|
||||
setlinebuf(FILE *stream);
|
||||
|
||||
/* glibc LFS name alias: identical to fopen on LP64. */
|
||||
FILE *
|
||||
fopen64(const char *restrict path, const char *restrict mode);
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/* formatted output (todo 16) */
|
||||
|
||||
int
|
||||
printf(const char *restrict format, ...);
|
||||
int
|
||||
fprintf(FILE *restrict stream, const char *restrict format, ...);
|
||||
int
|
||||
sprintf(char *restrict s, const char *restrict format, ...);
|
||||
int
|
||||
snprintf(char *restrict s, size_t n, const char *restrict format, ...);
|
||||
int
|
||||
vprintf(const char *restrict format, va_list ap);
|
||||
int
|
||||
vfprintf(FILE *restrict stream, const char *restrict format, va_list ap);
|
||||
int
|
||||
vsprintf(char *restrict s, const char *restrict format, va_list ap);
|
||||
int
|
||||
vsnprintf(char *restrict s, size_t n, const char *restrict format, va_list ap);
|
||||
int
|
||||
dprintf(int fd, const char *restrict format, ...);
|
||||
int
|
||||
vdprintf(int fd, const char *restrict format, va_list ap);
|
||||
void
|
||||
perror(const char *s);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
int
|
||||
asprintf(char **restrict strp, const char *restrict format, ...);
|
||||
int
|
||||
vasprintf(char **restrict strp, const char *restrict format, va_list ap);
|
||||
#endif
|
||||
|
||||
/* line input, stream locking, and memory streams (todo 18) */
|
||||
|
||||
/*
|
||||
* Read one line from stream into a malloc'd, NUL-terminated buffer. On the
|
||||
* first call *lineptr may be NULL (a buffer is allocated); *n is the buffer
|
||||
* capacity and grows as needed. The newline is kept in the output; embedded
|
||||
* NUL bytes are preserved (the length comes from the return value, not
|
||||
* strlen). Returns the number of bytes read including the delimiter, or -1
|
||||
* at end of file (nothing read) or on error.
|
||||
*/
|
||||
ssize_t
|
||||
getline(char **restrict lineptr, size_t *restrict n, FILE *restrict stream);
|
||||
|
||||
/*
|
||||
* Like getline, but reads up to the given delimiter byte (which is kept in
|
||||
* the output). Reading stops at end of file without the delimiter; a partial
|
||||
* final line is still returned with its byte count.
|
||||
*/
|
||||
ssize_t
|
||||
getdelim(char **restrict lineptr, size_t *restrict n, int delim, FILE *restrict stream);
|
||||
|
||||
/* Write s to stdout followed by a newline. Returns a non-negative value,
|
||||
* or EOF on error. */
|
||||
int
|
||||
puts(const char *s);
|
||||
|
||||
/*
|
||||
* Per-stream advisory locks (POSIX.1-2008 base). flockfile is recursive:
|
||||
* the owning thread may lock the same stream any number of times, and
|
||||
* funlockfile must be called once per successful flockfile. ftrylockfile
|
||||
* acquires without blocking: 0 on success, nonzero when the stream is
|
||||
* already locked. Single-threaded today; the lock fields become real
|
||||
* blocking locks when the thread runtime lands.
|
||||
*/
|
||||
void
|
||||
flockfile(FILE *stream);
|
||||
|
||||
int
|
||||
ftrylockfile(FILE *stream);
|
||||
|
||||
void
|
||||
funlockfile(FILE *stream);
|
||||
|
||||
/*
|
||||
* Unlocked character I/O: the stream must already be locked by the caller
|
||||
* (see flockfile). Identical to fgetc/fputc over stdin/stdout without
|
||||
* taking the per-stream lock.
|
||||
*/
|
||||
int
|
||||
getc_unlocked(FILE *stream);
|
||||
|
||||
int
|
||||
getchar_unlocked(void);
|
||||
|
||||
int
|
||||
putc_unlocked(int c, FILE *stream);
|
||||
|
||||
int
|
||||
putchar_unlocked(int c);
|
||||
|
||||
/*
|
||||
* Open a stream over the memory region buf of size bytes. With buf == NULL
|
||||
* the stream allocates and owns a growable buffer of its own that is freed
|
||||
* on fclose. Modes are fopen-like ('r', 'w', 'a', optional '+' and 'b',
|
||||
* where 'b' is accepted but on Linux only disables the string semantics):
|
||||
* 'w' truncates the current length to zero, 'a' positions at the end of the
|
||||
* data currently in the buffer (the first NUL byte in string mode), and
|
||||
* writing past the buffer size is an error. Returns the stream, or NULL
|
||||
* with errno set.
|
||||
*/
|
||||
FILE *
|
||||
fmemopen(void *buf, size_t size, const char *mode);
|
||||
|
||||
/*
|
||||
* Open a write-only dynamic memory stream. The stream owns a growable
|
||||
* buffer; after fflush or fclose the buffer is NUL-terminated at the current
|
||||
* data length, *ptr points at it (it may have moved), and *sizeloc holds the
|
||||
* length. The caller releases the buffer with free() after fclose.
|
||||
*/
|
||||
FILE *
|
||||
open_memstream(char **ptr, size_t *sizeloc);
|
||||
|
||||
/* formatted input (todo 17) */
|
||||
|
||||
/*
|
||||
* Read formatted input. scanf reads from stdin, fscanf from the stream,
|
||||
* sscanf from the string. The conversion directives are %d %i %u %o %x %X
|
||||
* %f %F %e %E %g %G %a %A %c %s %[ %p %n and %% (with assignment
|
||||
* suppression '*', a decimal field width, and the length modifiers hh h l
|
||||
* ll j z t, plus L for the float conversions). A conversion fails to match
|
||||
* without consuming its offending character; the return value is the
|
||||
* number of assigned (non-suppressed) input items, or EOF if an input
|
||||
* failure occurs before the first one. Integer overflow stores a saturated
|
||||
* value and sets errno = ERANGE (nothing else touches errno).
|
||||
*/
|
||||
int
|
||||
scanf(const char *restrict format, ...);
|
||||
|
||||
int
|
||||
fscanf(FILE *restrict stream, const char *restrict format, ...);
|
||||
|
||||
int
|
||||
sscanf(const char *restrict s, const char *restrict format, ...);
|
||||
|
||||
int
|
||||
vscanf(const char *restrict format, va_list ap);
|
||||
|
||||
int
|
||||
vfscanf(FILE *restrict stream, const char *restrict format, va_list ap);
|
||||
|
||||
int
|
||||
vsscanf(const char *restrict s, const char *restrict format, va_list ap);
|
||||
|
||||
#endif /* VLIBC_STDIO_H */
|
||||
@@ -105,6 +105,460 @@ __attribute__((pure)) size_t
|
||||
malloc_usable_size(void *ptr);
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
/* numeric conversions (todo 11) */
|
||||
|
||||
/*
|
||||
* String-to-number conversions (C23 7.24.1). All are ISO C core / POSIX
|
||||
* base and present in every profile.
|
||||
*
|
||||
* The ato* wrappers carry no error reporting: their behavior is undefined
|
||||
* if the converted value cannot be represented (C23 7.24.1.1-2), so they
|
||||
* read their input and nothing else — hence pure.
|
||||
*
|
||||
* The strto* functions report range errors through errno and store the
|
||||
* scan position through endptr; both are real side effects, so they carry
|
||||
* no intent attribute. errno behavior follows C23 7.24.1.4-5: ERANGE when
|
||||
* the subject sequence is outside the representable range (the clamped
|
||||
* maximum/minimum is returned); EINVAL when base is not 0 and not in
|
||||
* [2, 36] (endptr left at nptr, 0 returned); endptr points at nptr when
|
||||
* no subject sequence is present.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Convert the initial decimal digits of nptr to int, discarding leading
|
||||
* whitespace and an optional sign. Equivalent to (int)strtol(nptr, 0, 10).
|
||||
* pure: reads memory, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) int
|
||||
atoi(const char *nptr);
|
||||
|
||||
/*
|
||||
* As atoi, converted to long: strtol(nptr, 0, 10).
|
||||
* pure: reads memory, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) long
|
||||
atol(const char *nptr);
|
||||
|
||||
/*
|
||||
* As atoi, converted to long long: strtoll(nptr, 0, 10).
|
||||
* pure: reads memory, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) long long
|
||||
atoll(const char *nptr);
|
||||
|
||||
/*
|
||||
* As atoi, converted to double: strtod(nptr, 0).
|
||||
* pure: reads memory, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) double
|
||||
atof(const char *nptr);
|
||||
|
||||
/*
|
||||
* Convert the initial portion of nptr to long, stopping at the first
|
||||
* character that is not part of the subject sequence and storing its
|
||||
* position in *endptr (if endptr is not NULL). See the family comment for
|
||||
* base, errno, and endptr semantics.
|
||||
*/
|
||||
long
|
||||
strtol(const char *restrict nptr, char **restrict endptr, int base);
|
||||
|
||||
/*
|
||||
* As strtol, converted to unsigned long. A subject sequence with a minus
|
||||
* sign yields the negated value computed in the return type (modulo
|
||||
* ULONG_MAX + 1) without a range error (C23 7.24.1.4p8).
|
||||
*/
|
||||
unsigned long
|
||||
strtoul(const char *restrict nptr, char **restrict endptr, int base);
|
||||
|
||||
/*
|
||||
* As strtol, converted to long long.
|
||||
*/
|
||||
long long
|
||||
strtoll(const char *restrict nptr, char **restrict endptr, int base);
|
||||
|
||||
/*
|
||||
* As strtol, converted to unsigned long long. Negative subject sequences
|
||||
* wrap modulo ULLONG_MAX + 1 as for strtoul.
|
||||
*/
|
||||
unsigned long long
|
||||
strtoull(const char *restrict nptr, char **restrict endptr, int base);
|
||||
|
||||
/*
|
||||
* Convert the initial portion of nptr to float, double, or long double
|
||||
* (C23 7.24.1.3): optional whitespace, optional sign, then either an
|
||||
* "inf"/"infinity" or "nan"/"nan(n-char-sequence)" subject (case-
|
||||
* insensitive), a hexadecimal floating subject ("0x1.8p1"), or a decimal
|
||||
* floating subject with optional exponent. Overflow returns ±HUGE_VAL*
|
||||
* with errno ERANGE; results too small to represent return a subnormal or
|
||||
* zero value with errno ERANGE. no-conversion stores nptr in *endptr.
|
||||
*/
|
||||
double
|
||||
strtod(const char *restrict nptr, char **restrict endptr);
|
||||
|
||||
float
|
||||
strtof(const char *restrict nptr, char **restrict endptr);
|
||||
|
||||
long double
|
||||
strtold(const char *restrict nptr, char **restrict endptr);
|
||||
|
||||
/* pseudo-random numbers, search, and integer arithmetic (todo 12) */
|
||||
|
||||
/*
|
||||
* C23 7.22.2/7.22.5/7.22.6: rand/srand, qsort/bsearch, the abs family, and
|
||||
* the div family are ISO C core and present in every profile. The abs and
|
||||
* div functions compute a pure function of their arguments, so they are
|
||||
* declared const (the compiler folds and eliminates the calls in static
|
||||
* links; GCC's own builtin declarations of abs/labs/llabs are const, and
|
||||
* the attribute here matches them). rand and srand carry state, and
|
||||
* qsort/bsearch call a caller-supplied comparator, so none of those four
|
||||
* carries an intent attribute.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The largest value rand() returns: 2^31 - 1, the largest int.
|
||||
*/
|
||||
#define RAND_MAX 2147483647
|
||||
|
||||
/*
|
||||
* Quotient/remainder pair from div/ldiv/lldiv: quot is the algebraic
|
||||
* quotient truncated toward zero, rem the remainder of the same sign as
|
||||
* the dividend, and quot*denom + rem == num.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int quot;
|
||||
int rem;
|
||||
} div_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
long quot;
|
||||
long rem;
|
||||
} ldiv_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
long long quot;
|
||||
long long rem;
|
||||
} lldiv_t;
|
||||
|
||||
/*
|
||||
* Absolute value of n. The most-negative value returns itself: the
|
||||
* negation happens in the unsigned type, which wraps, so the result is
|
||||
* never undefined behavior and errno is never set.
|
||||
*/
|
||||
__attribute__((const)) int
|
||||
abs(int n);
|
||||
|
||||
__attribute__((const)) long
|
||||
labs(long n);
|
||||
|
||||
__attribute__((const)) long long
|
||||
llabs(long long n);
|
||||
|
||||
/*
|
||||
* Quotient and remainder of num/denom, truncated toward zero.
|
||||
*/
|
||||
__attribute__((const)) div_t
|
||||
div(int num, int denom);
|
||||
|
||||
__attribute__((const)) ldiv_t
|
||||
ldiv(long num, long denom);
|
||||
|
||||
__attribute__((const)) lldiv_t
|
||||
lldiv(long long num, long long denom);
|
||||
|
||||
/*
|
||||
* Pseudo-random integer in [0, RAND_MAX], deterministic for a given
|
||||
* srand seed.
|
||||
*/
|
||||
int
|
||||
rand(void);
|
||||
|
||||
/*
|
||||
* Seed the rand() sequence with seed.
|
||||
*/
|
||||
void
|
||||
srand(unsigned int seed);
|
||||
|
||||
/*
|
||||
* Sort the array of nmemb elements of size bytes at base into ascending
|
||||
* order according to compar (C23 7.22.5.2). compar receives two pointers
|
||||
* to distinct elements and returns negative/zero/positive. The sort is
|
||||
* not stable.
|
||||
*/
|
||||
void
|
||||
qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
|
||||
|
||||
/*
|
||||
* Binary-search the array of nmemb sorted elements of size bytes at base
|
||||
* for *key, calling compar(key, element) (C23 7.22.5.1). Returns a
|
||||
* pointer to the matching element, or NULL when there is none.
|
||||
*/
|
||||
void *
|
||||
bsearch(const void *key, const void *base, size_t nmemb, size_t size,
|
||||
int (*compar)(const void *, const void *));
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): XSI and obsolescent extensions. */
|
||||
|
||||
/*
|
||||
* Reentrant rand: the state lives in the caller's *seedp, which is
|
||||
* updated on every call, so the sequence is independent of rand()'s own
|
||||
* global state.
|
||||
*/
|
||||
int
|
||||
rand_r(unsigned int *seedp);
|
||||
|
||||
/*
|
||||
* random/srandom family (XSI): a 31-bit pseudo-random sequence in
|
||||
* [0, 2^31). initstate installs state (size bytes, at least
|
||||
* sizeof(long)) as the current state buffer, seeds it, and returns the
|
||||
* previous buffer; setstate installs the buffer and returns the previous
|
||||
* one. srandom reseeds the current buffer.
|
||||
*/
|
||||
long
|
||||
random(void);
|
||||
|
||||
void
|
||||
srandom(unsigned int seed);
|
||||
|
||||
char *
|
||||
initstate(unsigned int seed, char *state, size_t size);
|
||||
|
||||
char *
|
||||
setstate(char *state);
|
||||
|
||||
/*
|
||||
* drand48 family (XSI): a 48-bit linear congruential sequence carried in
|
||||
* three unsigned shorts, least-significant first. drand48/erand48 return
|
||||
* the current value divided by 2^48 as a double in [0, 1); lrand48/
|
||||
* nrand48 return the high 31 bits; mrand48/jrand48 the high 32 bits
|
||||
* sign-extended. The erand48/nrand48/jrand48 forms step the caller's
|
||||
* xsubi in place; srand48/seed48/lcong48 manage the shared state and the
|
||||
* multiplier/addend.
|
||||
*/
|
||||
double
|
||||
drand48(void);
|
||||
|
||||
double
|
||||
erand48(unsigned short xsubi[3]);
|
||||
|
||||
long
|
||||
lrand48(void);
|
||||
|
||||
long
|
||||
nrand48(unsigned short xsubi[3]);
|
||||
|
||||
long
|
||||
mrand48(void);
|
||||
|
||||
long
|
||||
jrand48(unsigned short xsubi[3]);
|
||||
|
||||
void
|
||||
srand48(long seedval);
|
||||
|
||||
unsigned short *
|
||||
seed48(unsigned short seed16v[3]);
|
||||
|
||||
void
|
||||
lcong48(unsigned short param[7]);
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
/* environment, multibyte characters, and temporary files (todo 13) */
|
||||
|
||||
/*
|
||||
* getenv/setenv/unsetenv (POSIX base), the C-locale multibyte conversions
|
||||
* (C23 7.24.7), and mkstemp/mkdtemp are level 1. The XSI/BSD extras —
|
||||
* putenv, clearenv, mktemp, the PTY helpers, and getsubopt — are level 2.
|
||||
*
|
||||
* None of these functions carries an intent attribute. getenv and the
|
||||
* environment mutators all read or write the shared global environ, so
|
||||
* pure/const would let the compiler hoist a stale getenv result across a
|
||||
* setenv call. The multibyte conversions are locale-stateful in general,
|
||||
* the temporary-file functions create filesystem entries, and ptsname
|
||||
* writes a shared static buffer — none of them is side-effect-free.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The process environment: a NULL-terminated array of "name=value"
|
||||
* strings. The array is installed by the startup code before main; the
|
||||
* functions below mutate it (setenv/unsetenv copy strings into malloc'd
|
||||
* storage). Reassigning environ directly is permitted by POSIX, but
|
||||
* calling setenv/unsetenv/putenv/clearenv afterwards is undefined, as it
|
||||
* is everywhere (the array is then no longer guaranteed to be owned by
|
||||
* the library).
|
||||
*/
|
||||
extern char **environ;
|
||||
|
||||
/*
|
||||
* Return a pointer to the value part (the bytes after '=') of the
|
||||
* environment entry whose name matches name, or NULL when the variable
|
||||
* is not set. The pointer is valid until the next setenv, unsetenv,
|
||||
* putenv, or clearenv call. No intent attribute: the result depends on
|
||||
* the mutable global environ.
|
||||
*/
|
||||
char *
|
||||
getenv(const char *name);
|
||||
|
||||
/*
|
||||
* Set name to value. A copy of "name=value" is made, so the caller may
|
||||
* reuse or free its buffers once the call returns. With overwrite
|
||||
* nonzero an existing entry is replaced; with overwrite zero an existing
|
||||
* entry is left unchanged and 0 is returned. Returns -1 with errno
|
||||
* EINVAL when name is NULL, empty, or contains '=', and -1 with errno
|
||||
* ENOMEM on allocation failure. A NULL value is treated as the empty
|
||||
* string.
|
||||
*/
|
||||
int
|
||||
setenv(const char *name, const char *value, int overwrite);
|
||||
|
||||
/*
|
||||
* Remove every entry whose name is name, compacting the array. Returns
|
||||
* -1 with errno EINVAL when name is NULL, empty, or contains '='.
|
||||
*/
|
||||
int
|
||||
unsetenv(const char *name);
|
||||
|
||||
/*
|
||||
* C-locale multibyte conversions (C23 7.24.7). Only the "C" locale
|
||||
* exists today (locale support is a later todo), where every multibyte
|
||||
* character is a single byte: byte c corresponds to the wide character
|
||||
* (unsigned char)c. The encoding is therefore stateless — the s == NULL
|
||||
* and NULL-destination forms all report "state-independent" — and no
|
||||
* mbstate_t variant is needed. mbtowc never fails in this locale (every
|
||||
* byte, including 0x80..0xFF, is a valid character); wctomb fails with
|
||||
* -1 + errno EILSEQ for any wchar_t outside 0..255.
|
||||
*/
|
||||
int
|
||||
mblen(const char *s, size_t n);
|
||||
|
||||
int
|
||||
mbtowc(wchar_t *restrict pwc, const char *restrict s, size_t n);
|
||||
|
||||
int
|
||||
wctomb(char *s, wchar_t wc);
|
||||
|
||||
size_t
|
||||
mbstowcs(wchar_t *restrict pwcs, const char *restrict s, size_t n);
|
||||
|
||||
size_t
|
||||
wcstombs(char *restrict s, const wchar_t *restrict pwcs, size_t n);
|
||||
|
||||
/*
|
||||
* Temporary files and directories. mkstemp replaces the trailing
|
||||
* "XXXXXX" of the template with random characters, creates the file
|
||||
* with mode 0600, and returns an open file descriptor (-1 with errno
|
||||
* EINVAL when the template has no "XXXXXX" suffix). mkdtemp does the
|
||||
* same replacement, creates a directory with mode 0700, and returns the
|
||||
* template (NULL on failure). The parameter is unnamed in these
|
||||
* declarations because POSIX's `template` is a C++ keyword and this
|
||||
* header is compiled by C++ consumers too.
|
||||
*/
|
||||
int
|
||||
mkstemp(char *);
|
||||
|
||||
char *
|
||||
mkdtemp(char *);
|
||||
|
||||
/* process control (todo 20) */
|
||||
|
||||
/*
|
||||
* Pass string to the command language interpreter: "sh -c string".
|
||||
* Returns the shell's wait status (e.g. 768 for "exit 3"), 1 when
|
||||
* string is NULL (a shell is always available), or -1 with errno set
|
||||
* when the child cannot be created or reaped. During the run, SIGCHLD
|
||||
* is blocked and SIGINT/SIGQUIT are ignored in the caller, as POSIX
|
||||
* requires.
|
||||
*/
|
||||
int
|
||||
system(const char *string);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): XSI and BSD environment extras. */
|
||||
|
||||
/*
|
||||
* Add string — a "name=value" pair in CALLER-OWNED storage — to the
|
||||
* environment: the caller's buffer becomes part of the environment and
|
||||
* must remain valid for the life of the process. An existing variable of
|
||||
* the same name is replaced (the old pointer is dropped); a string
|
||||
* without '=' removes the variable instead (glibc/musl behavior).
|
||||
* Returns 0, or -1 with errno ENOMEM.
|
||||
*/
|
||||
int
|
||||
putenv(char *string);
|
||||
|
||||
/*
|
||||
* Empty the environment, leaving environ as a fresh valid empty array
|
||||
* (never NULL) so later setenv calls keep working. The previous array
|
||||
* and its entries are deliberately not freed — entries may be putenv'd
|
||||
* caller storage or initial-stack strings. Returns 0, or -1 with errno
|
||||
* ENOMEM.
|
||||
*/
|
||||
int
|
||||
clearenv(void);
|
||||
|
||||
/*
|
||||
* Obsolescent mkstemp without the file: fills in the trailing "XXXXXX"
|
||||
* and returns the template, creating nothing. The name is not guaranteed
|
||||
* unique — nothing exists to race on. NULL with errno EINVAL when the
|
||||
* template has no "XXXXXX" suffix.
|
||||
*/
|
||||
char *
|
||||
mktemp(char *);
|
||||
|
||||
/*
|
||||
* XSI PTY helpers. posix_openpt opens the master side of a
|
||||
* pseudo-terminal via /dev/ptmx (flags, plus O_NOCTTY, are passed to
|
||||
* open) and returns the fd. grantpt returns 0 without doing anything:
|
||||
* modern kernels grant the slave when the master is opened. unlockpt
|
||||
* releases the slave-side lock. ptsname formats the slave path
|
||||
* "/dev/pts/N" into a shared static buffer — the result is valid until
|
||||
* the next ptsname call and is not thread-safe.
|
||||
*/
|
||||
int
|
||||
posix_openpt(int flags);
|
||||
|
||||
char *
|
||||
ptsname(int fd);
|
||||
|
||||
int
|
||||
grantpt(int fd);
|
||||
|
||||
int
|
||||
unlockpt(int fd);
|
||||
|
||||
/*
|
||||
* Parse one comma-separated suboption out of *optionp (POSIX XSI). On
|
||||
* success the index of the matching token is returned and *valuep points
|
||||
* to the "=value" part (or is NULL when the suboption has no '='); for
|
||||
* an unrecognized suboption -1 is returned and *valuep points to the
|
||||
* whole suboption string; at the end of the list -1 is returned and
|
||||
* *valuep is NULL. *optionp always advances past the comma, which is
|
||||
* overwritten with NUL (the string is modified in place).
|
||||
*/
|
||||
int
|
||||
getsubopt(char **optionp, char *const *tokens, char **valuep);
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* Resolve path to an absolute, NUL-terminated canonical pathname with no
|
||||
* symbolic links, "." or ".." components (todo 38). resolved_path may be
|
||||
* NULL, in which case the canonical path is returned in a malloc'd
|
||||
* buffer the caller must free; otherwise it must hold at least PATH_MAX
|
||||
* bytes. Returns resolved_path (or the malloc'd buffer), or NULL with
|
||||
* errno set. XSI.
|
||||
*/
|
||||
char *
|
||||
realpath(const char *restrict path, char *restrict resolved_path);
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
#ifndef VLIBC_SYS_MMAN_H
|
||||
#define VLIBC_SYS_MMAN_H
|
||||
|
||||
/*
|
||||
* vlibc — <sys/mman.h>.
|
||||
*
|
||||
* Memory mapping, protection, and synchronization (POSIX.1-2008). Every
|
||||
* function here is an unbuffered pass-through to the kernel: failures are
|
||||
* reported as -1 (or MAP_FAILED for mmap) with errno set by the syscall
|
||||
* layer.
|
||||
*
|
||||
* Level 1 (onlyposix): mmap, munmap, mprotect, msync, mlock, munlock,
|
||||
* mlockall, munlockall, posix_madvise.
|
||||
* Level 2 (muslmimic): madvise (XSI — posix_madvise is the POSIX base
|
||||
* form), shm_open, shm_unlink.
|
||||
*
|
||||
* All constant values are Linux x86_64 kernel-UAPI facts (asm-generic/
|
||||
* mman-common.h and mman.h), transcribed, not invented. PROT_*, MAP_*,
|
||||
* MAP_FAILED, MS_* and MCL_* exist at level 1. The POSIX_MADV_* advice
|
||||
* values match the Linux MADV_* values for the same advice (0-4), which is
|
||||
* what lets posix_madvise pass its advice straight to SYS_madvise; the
|
||||
* Linux MADV_* names themselves are gated at level 2 with madvise, the only
|
||||
* function that takes them. MAP_ANONYMOUS is provided as a source
|
||||
* compatibility alias of the primary spelling MAP_ANON (the BSD/POSIX
|
||||
* draft name glibc also accepts).
|
||||
*
|
||||
* POSIX shared memory (shm_open/shm_unlink, level 2) is backed by a real
|
||||
* named file under /dev/shm (tmpfs), NOT by memfd_create: the object is a
|
||||
* path in the shared-memory filesystem, visible there and re-openable by
|
||||
* name until shm_unlink removes it, which matches POSIX's named-object
|
||||
* semantics. vlibc renders name as "/dev/shm/" + name and requires that
|
||||
* name contain no '/' — the object is a single directory entry, so a slash
|
||||
* would escape the namespace (EINVAL). This differs from the POSIX wording
|
||||
* that a name begin with a slash: vlibc names carry no leading slash and
|
||||
* the prefix is added internally. Otherwise shm_open behaves as open(2) on
|
||||
* the rendered path (the oflag access modes plus O_CREAT/O_EXCL/O_TRUNC and
|
||||
* mode bits are honored; O_WRONLY alone is undefined by POSIX and passed
|
||||
* through to the kernel) and shm_unlink as unlink(2) on it.
|
||||
*
|
||||
* None of these declarations carry an intent attribute: every function
|
||||
* changes the caller's address space or performs I/O with side effects and
|
||||
* reports failures through errno, so const/pure would be unsound (the same
|
||||
* rationale unistd.h documents for its I/O family).
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ---- memory protection ---- */
|
||||
|
||||
#define PROT_NONE 0x0 /* page cannot be accessed */
|
||||
#define PROT_READ 0x1 /* page can be read */
|
||||
#define PROT_WRITE 0x2 /* page can be written */
|
||||
#define PROT_EXEC 0x4 /* page can be executed */
|
||||
|
||||
/* ---- mapping types and flags ---- */
|
||||
|
||||
#define MAP_SHARED 0x01 /* share changes with the file and other mappers */
|
||||
#define MAP_PRIVATE 0x02 /* private copy-on-write mapping */
|
||||
#define MAP_FIXED 0x10 /* interpret addr exactly; fail if it cannot be used */
|
||||
#define MAP_ANON 0x20 /* anonymous mapping, not file-backed (primary name) */
|
||||
#define MAP_ANONYMOUS MAP_ANON /* source-compatibility alias of MAP_ANON */
|
||||
|
||||
/* Value returned by mmap on failure (never a valid mapping address). */
|
||||
#define MAP_FAILED ((void *)-1)
|
||||
|
||||
/* ---- msync synchronization flags ---- */
|
||||
|
||||
#define MS_ASYNC 0x1 /* return before the writes take effect */
|
||||
#define MS_INVALIDATE 0x2 /* invalidate other mappings of the file */
|
||||
#define MS_SYNC 0x4 /* perform synchronous writes */
|
||||
|
||||
/* ---- mlockall flags ---- */
|
||||
|
||||
#define MCL_CURRENT 0x1 /* lock all currently mapped pages */
|
||||
#define MCL_FUTURE 0x2 /* lock all pages mapped in the future */
|
||||
|
||||
/* ---- posix_madvise advice (Linux MADV_* values, see the header note) ---- */
|
||||
|
||||
#define POSIX_MADV_NORMAL 0 /* no special treatment */
|
||||
#define POSIX_MADV_RANDOM 1 /* pages will be accessed randomly */
|
||||
#define POSIX_MADV_SEQUENTIAL 2 /* pages will be accessed sequentially */
|
||||
#define POSIX_MADV_WILLNEED 3 /* pages will be needed soon */
|
||||
#define POSIX_MADV_DONTNEED 4 /* pages are not needed soon */
|
||||
|
||||
/*
|
||||
* Map len bytes starting at offset off of the object open on fildes into
|
||||
* the process address space and return the mapping address, or MAP_FAILED
|
||||
* with errno set. addr is a hint for the placement (0 for "anywhere")
|
||||
* unless flags contains MAP_FIXED; prot is a combination of PROT_* (PROT_NONE
|
||||
* alone forbids all access). With MAP_ANON fildes is ignored and must be -1
|
||||
* and off 0; the mapping is zero-filled. The mapping is shared or private
|
||||
* per MAP_SHARED/MAP_PRIVATE. len is rounded up to whole pages.
|
||||
*/
|
||||
void *
|
||||
mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off);
|
||||
|
||||
/*
|
||||
* Remove the mapping at addr (a page boundary) covering len bytes; return
|
||||
* 0, or -1 with errno set. The address range becomes invalid.
|
||||
*/
|
||||
int
|
||||
munmap(void *addr, size_t len);
|
||||
|
||||
/*
|
||||
* Change the protection of the mapped pages at addr covering len bytes to
|
||||
* prot; return 0, or -1 with errno set. addr must be page-aligned.
|
||||
*/
|
||||
int
|
||||
mprotect(void *addr, size_t len, int prot);
|
||||
|
||||
/*
|
||||
* Flush the mapped pages at addr covering len bytes of a MAP_SHARED mapping
|
||||
* to (or invalidate them from) the underlying object per flags (MS_ASYNC,
|
||||
* MS_SYNC, MS_INVALIDATE); return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
msync(void *addr, size_t len, int flags);
|
||||
|
||||
/*
|
||||
* Lock the pages at addr covering len bytes into memory so they are never
|
||||
* paged out; return 0, or -1 with errno set. munlock unlocks them again.
|
||||
*/
|
||||
int
|
||||
mlock(const void *addr, size_t len);
|
||||
|
||||
int
|
||||
munlock(const void *addr, size_t len);
|
||||
|
||||
/*
|
||||
* Lock all pages mapped by the process into memory per flags (MCL_CURRENT,
|
||||
* MCL_FUTURE); return 0, or -1 with errno set. munlockall unlocks them.
|
||||
*/
|
||||
int
|
||||
mlockall(int flags);
|
||||
|
||||
int
|
||||
munlockall(void);
|
||||
|
||||
/*
|
||||
* Give the kernel advice (one of the POSIX_MADV_* values) about how the
|
||||
* pages at addr covering len bytes will be used; return 0, or -1 with errno
|
||||
* set. The advice may be ignored; the mapping is unchanged.
|
||||
*/
|
||||
int
|
||||
posix_madvise(void *addr, size_t len, int advice);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): XSI + POSIX shared memory. */
|
||||
|
||||
/* ---- madvise advice (Linux x86_64 kernel-UAPI values) ---- */
|
||||
|
||||
#define MADV_NORMAL 0 /* no special treatment */
|
||||
#define MADV_RANDOM 1 /* pages will be accessed randomly */
|
||||
#define MADV_SEQUENTIAL 2 /* pages will be accessed sequentially */
|
||||
#define MADV_WILLNEED 3 /* pages will be needed soon */
|
||||
#define MADV_DONTNEED 4 /* pages are not needed soon; free them */
|
||||
#define MADV_FREE 8 /* free pages; contents lost on later write */
|
||||
#define MADV_REMOVE 9 /* free the pages and punch a hole in the file */
|
||||
#define MADV_DONTFORK 10 /* do not inherit the pages across fork */
|
||||
#define MADV_DOFORK 11 /* revert MADV_DONTFORK */
|
||||
#define MADV_MERGEABLE 12 /* enable KSM merging of the pages */
|
||||
#define MADV_UNMERGEABLE 13 /* revert MADV_MERGEABLE */
|
||||
#define MADV_HUGEPAGE 14 /* prefer transparent huge pages */
|
||||
#define MADV_NOHUGEPAGE 15 /* revert MADV_HUGEPAGE */
|
||||
#define MADV_DONTDUMP 16 /* exclude the pages from core dumps */
|
||||
#define MADV_DODUMP 17 /* revert MADV_DONTDUMP */
|
||||
#define MADV_WIPEONFORK 18 /* zero the pages in the child after fork */
|
||||
#define MADV_KEEPONFORK 19 /* revert MADV_WIPEONFORK */
|
||||
#define MADV_COLD 20 /* pages will be accessed less soon */
|
||||
#define MADV_PAGEOUT 21 /* reclaim the pages immediately */
|
||||
#define MADV_POPULATE_READ 22 /* fault the pages in for reading */
|
||||
#define MADV_POPULATE_WRITE 23 /* fault the pages in for writing */
|
||||
#define MADV_DONTNEED_LOCKED 24 /* like MADV_DONTNEED on locked pages */
|
||||
#define MADV_COLLAPSE 25 /* collapse the range into a THP */
|
||||
|
||||
/*
|
||||
* Linux madvise: give the kernel advice (one of the MADV_* values above)
|
||||
* about the pages at addr covering len bytes; return 0, or -1 with errno
|
||||
* set. XSI — posix_madvise (level 1) is the POSIX base form; this is the
|
||||
* raw Linux interface exposing the full MADV_* set.
|
||||
*/
|
||||
int
|
||||
madvise(void *addr, size_t len, int advice);
|
||||
|
||||
/*
|
||||
* Open (creating with mode if oflag contains O_CREAT) the POSIX shared
|
||||
* memory object name and return a descriptor, or -1 with errno set. The
|
||||
* object is a file under /dev/shm; name must not contain '/', and the
|
||||
* object persists (re-openable by name) until shm_unlink removes it. See
|
||||
* the header note on the naming deviation. oflag behaves as for open(2).
|
||||
*/
|
||||
int
|
||||
shm_open(const char *name, int oflag, mode_t mode);
|
||||
|
||||
/*
|
||||
* Remove the shared memory object name from /dev/shm; return 0, or -1 with
|
||||
* errno set. Open descriptors of the object stay valid until closed. See
|
||||
* the header note on the naming deviation.
|
||||
*/
|
||||
int
|
||||
shm_unlink(const char *name);
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_SYS_MMAN_H */
|
||||
@@ -0,0 +1,203 @@
|
||||
#ifndef VLIBC_SYS_RESOURCE_H
|
||||
#define VLIBC_SYS_RESOURCE_H
|
||||
|
||||
/*
|
||||
* vlibc — <sys/resource.h>.
|
||||
*
|
||||
* Per-process resource limits and usage accounting. Nothing in this header
|
||||
* is POSIX.1-2008 base — getrlimit/setrlimit/getrusage/getpriority/
|
||||
* setpriority and nice are all XSI [CX] — so the entire surface is gated at
|
||||
* level 2:
|
||||
*
|
||||
* Level 2 (muslmimic): getrlimit, setrlimit, getrusage, getpriority,
|
||||
* setpriority, nice, getrlimit64, setrlimit64,
|
||||
* struct rlimit, struct rusage, rlim_t, RLIMIT_*,
|
||||
* RLIM_INFINITY, PRIO_*, RUSAGE_*.
|
||||
*
|
||||
* struct rusage matches the kernel layout on x86_64 verbatim: two struct
|
||||
* timevals (16 bytes each — the kernel record uses the same two-long
|
||||
* __kernel_old_timeval) followed by fourteen longs, and the kernel
|
||||
* getrusage copies it out without translation. struct rlimit is likewise
|
||||
* the kernel's struct rlimit64 (two 64-bit words), which is why
|
||||
* getrlimit/setrlimit pass the record straight to SYS_prlimit64 and why
|
||||
* getrlimit64/setrlimit64 are name-only aliases on this architecture.
|
||||
* Both shapes are pinned by the static asserts below.
|
||||
*
|
||||
* Every function here performs kernel I/O with side effects and reports
|
||||
* failures through errno, so no declaration carries an intent attribute
|
||||
* (const/pure would be unsound).
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
#include <stddef.h> /* offsetof */
|
||||
|
||||
#include <sys/time.h> /* struct timeval (shared VLIBC_TIMEVAL_DEFINED guard) */
|
||||
|
||||
#include <sys/types.h> /* id_t */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Resource limit values: the 64-bit width of the kernel's rlimit64 record
|
||||
* (unsigned long long on x86_64, where the classic long is also 64-bit but
|
||||
* the LFS spelling is authoritative).
|
||||
*/
|
||||
typedef unsigned long long rlim_t;
|
||||
|
||||
/*
|
||||
* A resource limit pair: rlim_cur is the soft limit (enforced), rlim_max
|
||||
* the hard limit (ceiling a soft limit may be raised to without
|
||||
* privilege). RLIM_INFINITY in either field means "unlimited".
|
||||
*/
|
||||
struct rlimit
|
||||
{
|
||||
rlim_t rlim_cur; /* soft limit (current) */
|
||||
rlim_t rlim_max; /* hard limit (ceiling) */
|
||||
};
|
||||
|
||||
/*
|
||||
* Per-process resource usage. The field order is the kernel's own
|
||||
* (getrusage copies the record out untouched): the two CPU-time timevals
|
||||
* first, then the fourteen accounting longs. ru_maxrss is in kilobytes.
|
||||
* The fields a kernel does not maintain stay zero.
|
||||
*/
|
||||
struct rusage
|
||||
{
|
||||
struct timeval ru_utime; /* user CPU time used */
|
||||
struct timeval ru_stime; /* system CPU time used */
|
||||
long ru_maxrss; /* maximum resident set size (KiB) */
|
||||
long ru_ixrss; /* integral shared memory size */
|
||||
long ru_idrss; /* integral unshared data size */
|
||||
long ru_isrss; /* integral unshared stack size */
|
||||
long ru_minflt; /* page reclaims (soft page faults) */
|
||||
long ru_majflt; /* page faults (hard page faults) */
|
||||
long ru_nswap; /* swaps */
|
||||
long ru_inblock; /* block input operations */
|
||||
long ru_oublock; /* block output operations */
|
||||
long ru_msgsnd; /* IPC messages sent */
|
||||
long ru_msgrcv; /* IPC messages received */
|
||||
long ru_nsignals; /* signals received */
|
||||
long ru_nvcsw; /* voluntary context switches */
|
||||
long ru_nivcsw; /* involuntary context switches */
|
||||
};
|
||||
|
||||
/* x86_64 kernel ABI: 2 x struct timeval (16 B) + 14 longs (8 B) = 144 B. */
|
||||
_Static_assert(sizeof(struct rusage) == 144, "struct rusage must match the x86_64 kernel layout");
|
||||
_Static_assert(offsetof(struct rusage, ru_maxrss) == 32, "ru_maxrss must follow the two timevals");
|
||||
_Static_assert(sizeof(struct rlimit) == 2 * sizeof(rlim_t),
|
||||
"struct rlimit is a pair of rlim_t words");
|
||||
|
||||
/* ---- Resource limit identifiers (kernel asm-generic values) ---- */
|
||||
|
||||
#define RLIMIT_CPU 0 /* CPU time in seconds */
|
||||
#define RLIMIT_FSIZE 1 /* maximum file size */
|
||||
#define RLIMIT_DATA 2 /* data segment size */
|
||||
#define RLIMIT_STACK 3 /* stack size */
|
||||
#define RLIMIT_CORE 4 /* core file size */
|
||||
#define RLIMIT_RSS 5 /* resident set size */
|
||||
#define RLIMIT_NPROC 6 /* number of processes */
|
||||
#define RLIMIT_NOFILE 7 /* number of open files */
|
||||
#define RLIMIT_MEMLOCK 8 /* locked-in-memory address space */
|
||||
#define RLIMIT_AS 9 /* address space size */
|
||||
#define RLIMIT_LOCKS 10 /* number of file locks held */
|
||||
#define RLIMIT_SIGPENDING 11 /* number of pending signals */
|
||||
#define RLIMIT_MSGQUEUE 12 /* bytes in POSIX message queues */
|
||||
#define RLIMIT_NICE 13 /* ceiling for the nice value */
|
||||
#define RLIMIT_RTPRIO 14 /* maximum realtime priority */
|
||||
#define RLIMIT_RTTIME 15 /* realtime CPU time (us) */
|
||||
#define RLIMIT_NLIMITS 16 /* number of resource kinds */
|
||||
|
||||
/* "No limit" value for either field of struct rlimit. */
|
||||
#define RLIM_INFINITY (~0UL)
|
||||
|
||||
/* Saved-limit markers (legacy; equal to RLIM_INFINITY on Linux). */
|
||||
#define RLIM_SAVED_CUR RLIM_INFINITY
|
||||
#define RLIM_SAVED_MAX RLIM_INFINITY
|
||||
|
||||
/* ---- getpriority/setpriority target selectors ---- */
|
||||
|
||||
#define PRIO_PROCESS 0 /* a single process */
|
||||
#define PRIO_PGRP 1 /* a process group */
|
||||
#define PRIO_USER 2 /* every process of a user */
|
||||
|
||||
/* The valid nice-value range (PRIO_MIN is the highest scheduling priority). */
|
||||
#define PRIO_MIN (-20)
|
||||
#define PRIO_MAX 19
|
||||
|
||||
/* ---- getrusage `who` selectors ---- */
|
||||
|
||||
#define RUSAGE_SELF 0 /* the calling process */
|
||||
#define RUSAGE_CHILDREN (-1) /* terminated and waited-for children */
|
||||
#define RUSAGE_THREAD 1 /* the calling thread */
|
||||
|
||||
/*
|
||||
* Return the current soft and hard limits of resource through rlim. The
|
||||
* call never fails for a valid resource. Return 0, or -1 with errno set
|
||||
* when resource is out of range or rlim points outside the address space.
|
||||
*/
|
||||
int
|
||||
getrlimit(int resource, struct rlimit *rlim);
|
||||
|
||||
/*
|
||||
* Set the soft and hard limits of resource from rlim. Raising the hard
|
||||
* limit (or the soft limit above the hard limit) requires privilege.
|
||||
* Return 0, or -1 with errno set for an out-of-range resource, a soft
|
||||
* limit above the hard limit, or a denied raise.
|
||||
*/
|
||||
int
|
||||
setrlimit(int resource, const struct rlimit *rlim);
|
||||
|
||||
/*
|
||||
* Fill usage with the resource usage of who (RUSAGE_SELF, RUSAGE_CHILDREN
|
||||
* or RUSAGE_THREAD). Return 0, or -1 with errno set for an unknown who or
|
||||
* when usage points outside the address space.
|
||||
*/
|
||||
int
|
||||
getrusage(int who, struct rusage *usage);
|
||||
|
||||
/*
|
||||
* Return the nice value of the target described by (which, who), in the
|
||||
* range PRIO_MIN..PRIO_MAX, or -1 with errno set on error. Because -1 is
|
||||
* also a valid priority, callers conventionally clear errno before the
|
||||
* call and treat a -1 return with a nonzero errno as an error.
|
||||
*/
|
||||
int
|
||||
getpriority(int which, id_t who);
|
||||
|
||||
/*
|
||||
* Set the nice value of every process named by (which, who) to prio,
|
||||
* clamping out-of-range values to PRIO_MIN..PRIO_MAX. Lowering the value
|
||||
* (raising priority) requires privilege. Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
setpriority(int which, id_t who, int prio);
|
||||
|
||||
/*
|
||||
* Add inc to the calling process's nice value and return the new value,
|
||||
* or -1 with errno set on error (see getpriority for the -1 convention).
|
||||
*/
|
||||
int
|
||||
nice(int inc);
|
||||
|
||||
/*
|
||||
* Large-file aliases. rlim_t is already 64-bit on x86_64, so these behave
|
||||
* exactly as getrlimit/setrlimit and exist as separate exported symbols for
|
||||
* LFS callers.
|
||||
*/
|
||||
int
|
||||
getrlimit64(int resource, struct rlimit *rlim);
|
||||
int
|
||||
setrlimit64(int resource, const struct rlimit *rlim);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#endif /* VLIBC_SYS_RESOURCE_H */
|
||||
@@ -0,0 +1,128 @@
|
||||
#ifndef VLIBC_SYS_SELECT_H
|
||||
#define VLIBC_SYS_SELECT_H
|
||||
|
||||
/*
|
||||
* vlibc — <sys/select.h>.
|
||||
*
|
||||
* Synchronous I/O multiplexing: wait for readiness on file-descriptor sets
|
||||
* with select() or pselect(). Both are thin wrappers over the SYS_pselect6
|
||||
* kernel ABI, (nfds, readfds, writefds, exceptfds, struct timespec *,
|
||||
* { sigset_t *, size_t } *): pselect passes its timespec and optional signal
|
||||
* mask straight through, select converts its struct timeval timeout to a
|
||||
* timespec and passes a NULL sigset pair.
|
||||
*
|
||||
* Level 1 (onlyposix): select, pselect, FD_* macros, fd_set, struct
|
||||
* timeval.
|
||||
*
|
||||
* fd_set is the kernel's bitmap layout on x86_64: an array of unsigned long
|
||||
* words, with descriptor d held in bit d % 64 of word d / 64. FD_SETSIZE
|
||||
* caps the tracked descriptors at 1024 (16 words), so select()/pselect()
|
||||
* must be called with nfds <= FD_SETSIZE and FD_SET() only ever receives
|
||||
* descriptors below FD_SETSIZE. select() and pselect() report the number of
|
||||
* ready descriptors, 0 on timeout, or -1 with errno set on error.
|
||||
*
|
||||
* The signal mask type is declared here under the shared guard; <signal.h>
|
||||
* is its canonical POSIX home and will own the full sig* API.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Maximum descriptors select()/pselect() track (fd_set's bit capacity). */
|
||||
#define FD_SETSIZE 1024
|
||||
|
||||
/*
|
||||
* A set of file descriptors, one bit per descriptor, stored in the kernel
|
||||
* bitmap layout: an array of unsigned long words, bit d in word
|
||||
* d / (8 * sizeof(unsigned long)). Descriptors 0..FD_SETSIZE-1 fit; no
|
||||
* operation may touch a descriptor at or above FD_SETSIZE.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
unsigned long fds_bits[FD_SETSIZE / (8 * sizeof(unsigned long))];
|
||||
} fd_set;
|
||||
|
||||
/* The word holding bit d of an fd_set, and that bit's mask within the word. */
|
||||
#define VLIBC_FDS_WORD(d) ((d) / (8 * sizeof(unsigned long)))
|
||||
#define VLIBC_FDS_MASK(d) (1UL << ((d) % (8 * sizeof(unsigned long))))
|
||||
|
||||
/* Clear every descriptor bit of set. */
|
||||
#define FD_ZERO(set) \
|
||||
do \
|
||||
{ \
|
||||
size_t fd_zero_i; \
|
||||
for (fd_zero_i = 0; fd_zero_i < sizeof(fd_set) / sizeof(unsigned long); fd_zero_i++) \
|
||||
{ \
|
||||
(set)->fds_bits[fd_zero_i] = 0UL; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* Add descriptor d to set. */
|
||||
#define FD_SET(d, set) ((set)->fds_bits[VLIBC_FDS_WORD(d)] |= VLIBC_FDS_MASK(d))
|
||||
|
||||
/* Remove descriptor d from set. */
|
||||
#define FD_CLR(d, set) ((set)->fds_bits[VLIBC_FDS_WORD(d)] &= ~VLIBC_FDS_MASK(d))
|
||||
|
||||
/* Nonzero when descriptor d is a member of set. */
|
||||
#define FD_ISSET(d, set) ((set)->fds_bits[VLIBC_FDS_WORD(d)] & VLIBC_FDS_MASK(d))
|
||||
|
||||
#ifndef VLIBC_SIGSET_T_DEFINED
|
||||
#define VLIBC_SIGSET_T_DEFINED
|
||||
/*
|
||||
* Signal mask type: a single 64-bit word — the x86_64 Linux sigset_t (see
|
||||
* setjmp.h). pselect() and ppoll() only ever forward a pointer to the
|
||||
* kernel, which reads the word directly. <signal.h> is the canonical POSIX
|
||||
* home for sigset_t and builds the sig* API on this same layout; the
|
||||
* typedef is repeated in <poll.h> under this guard so the two headers stay
|
||||
* consistent.
|
||||
*/
|
||||
typedef unsigned long sigset_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Elapsed time in seconds and microseconds (see select()'s timeout).
|
||||
* Shared guard with <sys/time.h>, the canonical POSIX home of struct
|
||||
* timeval: both headers define the same layout under
|
||||
* VLIBC_TIMEVAL_DEFINED, so either may be included first at any level.
|
||||
*/
|
||||
#ifndef VLIBC_TIMEVAL_DEFINED
|
||||
#define VLIBC_TIMEVAL_DEFINED
|
||||
struct timeval
|
||||
{
|
||||
time_t tv_sec;
|
||||
suseconds_t tv_usec;
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Wait for readiness on the descriptors marked in readfds, writefds and
|
||||
* exceptfds (each may be NULL), up to nfds descriptors (the highest
|
||||
* descriptor in any set plus one). timeout is an upper bound on the wait; a
|
||||
* NULL timeout blocks indefinitely, { 0, 0 } never blocks. On return each
|
||||
* non-NULL set holds only its ready descriptors. Return the number of ready
|
||||
* descriptors across the sets, 0 on timeout, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
|
||||
|
||||
/*
|
||||
* Like select(), but the timeout is a struct timespec and, when sigmask is
|
||||
* not NULL, the given signal mask is atomically installed for the duration
|
||||
* of the wait (the previous mask is restored before returning).
|
||||
*/
|
||||
int
|
||||
pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
|
||||
const struct timespec *timeout, const sigset_t *sigmask);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_SYS_SELECT_H */
|
||||
@@ -0,0 +1,294 @@
|
||||
#ifndef VLIBC_SYS_STAT_H
|
||||
#define VLIBC_SYS_STAT_H
|
||||
|
||||
/*
|
||||
* vlibc — <sys/stat.h>.
|
||||
*
|
||||
* File status: the x86_64 Linux struct stat layout and the S_* mode-bit
|
||||
* macros, plus the stat/mkdir/chmod/utimensat/chown wrapper families
|
||||
* (POSIX.1-2008 base). Every function is an unbuffered pass-through to the
|
||||
* kernel: failures are reported as -1 with errno set by the syscall layer.
|
||||
*
|
||||
* Level 1 (onlyposix): stat, fstat, lstat, fstatat, mkdir, mkdirat,
|
||||
* mkfifo, mkfifoat, chmod, fchmod, fchmodat, umask,
|
||||
* utimensat, futimens, chown, fchown, lchown,
|
||||
* fchownat (Issue 7 moved lchown from XSI to base).
|
||||
* Level 2 (muslmimic): mknod, mknodat (XSI).
|
||||
*
|
||||
* struct stat matches the x86_64 kernel layout exactly (144 bytes), pinned
|
||||
* by static assertions — the same layout src/stdio/stdio.c transcribed
|
||||
* privately as stdio_stat for its fstat-based isatty(). The
|
||||
* st_atime/st_mtime/st_ctime spellings are glibc-style macros onto
|
||||
* st_atim.tv_sec &c for POSIX source compatibility.
|
||||
*
|
||||
* The AT_* flag constants (AT_FDCWD, AT_SYMLINK_NOFOLLOW, ...) belong to
|
||||
* <fcntl.h> (todo 21) and are deliberately not defined here; the flag
|
||||
* arguments below are plain int and take their values from that header.
|
||||
*
|
||||
* None of these declarations carry an intent attribute: every function
|
||||
* performs I/O with side effects and reports failures through errno, so
|
||||
* const/pure would be unsound.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Provisional struct timespec definition. The canonical home of struct
|
||||
* timespec is <time.h> (todo 41); until it lands, sys/stat.h needs the
|
||||
* definition for the st_*tim members and utimensat/futimens. Todo 41 must
|
||||
* move (or reconcile) this definition — it currently exists nowhere else
|
||||
* (include/threads.h only forward-declares it).
|
||||
*/
|
||||
struct timespec
|
||||
{
|
||||
time_t tv_sec; /* seconds */
|
||||
long tv_nsec; /* nanoseconds (0..999999999) */
|
||||
};
|
||||
|
||||
/*
|
||||
* File status, x86_64 Linux kernel layout. Field widths are LP64: the
|
||||
* comment column gives the byte offset of each member. The order and the
|
||||
* two padding areas are kernel-ABI facts, not style choices.
|
||||
*/
|
||||
struct stat
|
||||
{
|
||||
dev_t st_dev; /* 0: device containing the file */
|
||||
ino_t st_ino; /* 8: inode number */
|
||||
nlink_t st_nlink; /* 16: hard link count */
|
||||
mode_t st_mode; /* 24: file type + permissions */
|
||||
uid_t st_uid; /* 28: owner user id */
|
||||
gid_t st_gid; /* 32: owner group id */
|
||||
unsigned int st_pad0; /* 36: kernel padding (int __pad0) */
|
||||
dev_t st_rdev; /* 40: device id (if device file) */
|
||||
off_t st_size; /* 48: size in bytes */
|
||||
blksize_t st_blksize; /* 56: preferred I/O block size */
|
||||
blkcnt_t st_blocks; /* 64: 512-byte blocks allocated */
|
||||
struct timespec st_atim; /* 72: last access time */
|
||||
struct timespec st_mtim; /* 88: last modification time */
|
||||
struct timespec st_ctim; /* 104: last status change time */
|
||||
long st_unused[3]; /* 120: kernel padding (__unused) */
|
||||
};
|
||||
|
||||
/* Pin the kernel layout: sizeof and the two offsets stdio consumes. */
|
||||
_Static_assert(sizeof(struct stat) == 144, "struct stat must match the x86_64 kernel layout");
|
||||
_Static_assert(offsetof(struct stat, st_mode) == 24, "st_mode must sit at offset 24");
|
||||
_Static_assert(offsetof(struct stat, st_size) == 48, "st_size must sit at offset 48");
|
||||
|
||||
/* File type bits (st_mode & S_IFMT). */
|
||||
#define S_IFMT 0170000 /* type-of-file mask */
|
||||
#define S_IFSOCK 0140000 /* socket */
|
||||
#define S_IFLNK 0120000 /* symbolic link */
|
||||
#define S_IFREG 0100000 /* regular file */
|
||||
#define S_IFBLK 0060000 /* block device */
|
||||
#define S_IFDIR 0040000 /* directory */
|
||||
#define S_IFCHR 0020000 /* character device */
|
||||
#define S_IFIFO 0010000 /* FIFO (named pipe) */
|
||||
|
||||
/* Special permission bits. */
|
||||
#define S_ISUID 04000 /* set-user-id on execution */
|
||||
#define S_ISGID 02000 /* set-group-id on execution */
|
||||
#define S_ISVTX 01000 /* sticky bit (restricted deletion on dirs) */
|
||||
|
||||
/* Owner permission bits. */
|
||||
#define S_IRWXU 0700 /* read, write, execute/search by owner */
|
||||
#define S_IRUSR 0400 /* read permission, owner */
|
||||
#define S_IWUSR 0200 /* write permission, owner */
|
||||
#define S_IXUSR 0100 /* execute/search permission, owner */
|
||||
|
||||
/* Group permission bits. */
|
||||
#define S_IRWXG 070 /* read, write, execute/search by group */
|
||||
#define S_IRGRP 040 /* read permission, group */
|
||||
#define S_IWGRP 020 /* write permission, group */
|
||||
#define S_IXGRP 010 /* execute/search permission, group */
|
||||
|
||||
/* Others permission bits. */
|
||||
#define S_IRWXO 07 /* read, write, execute/search by others */
|
||||
#define S_IROTH 04 /* read permission, others */
|
||||
#define S_IWOTH 02 /* write permission, others */
|
||||
#define S_IXOTH 01 /* execute/search permission, others */
|
||||
|
||||
/* File type predicates over the type bits. */
|
||||
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
|
||||
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
|
||||
#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
|
||||
#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
|
||||
#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
|
||||
#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
|
||||
#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
|
||||
|
||||
/* POSIX compatibility spellings of the struct timespec members. */
|
||||
#define st_atime st_atim.tv_sec
|
||||
#define st_mtime st_mtim.tv_sec
|
||||
#define st_ctime st_ctim.tv_sec
|
||||
|
||||
/* Level 1 (POSIX base). */
|
||||
|
||||
/*
|
||||
* Store the status of the file named by path into buf. Follows symbolic
|
||||
* links; return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
stat(const char *path, struct stat *buf);
|
||||
|
||||
/*
|
||||
* Store the status of the file descriptor fd into buf; return 0, or -1
|
||||
* with errno set.
|
||||
*/
|
||||
int
|
||||
fstat(int fd, struct stat *buf);
|
||||
|
||||
/*
|
||||
* Like stat(), but a symbolic link is reported itself, not its target;
|
||||
* return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
lstat(const char *path, struct stat *buf);
|
||||
|
||||
/*
|
||||
* Like stat(), but path is relative to the directory named by fd (use
|
||||
* AT_FDCWD from <fcntl.h> for the current working directory); flag may
|
||||
* hold AT_SYMLINK_NOFOLLOW to report the link itself. Return 0, or -1
|
||||
* with errno set.
|
||||
*/
|
||||
int
|
||||
fstatat(int fd, const char *path, struct stat *buf, int flag);
|
||||
|
||||
/*
|
||||
* Create the directory named by path with the access mode mode (masked by
|
||||
* the process umask); return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
mkdir(const char *path, mode_t mode);
|
||||
|
||||
/*
|
||||
* Like mkdir(), but path is relative to the directory named by fd (use
|
||||
* AT_FDCWD for the current working directory); return 0, or -1 with errno
|
||||
* set.
|
||||
*/
|
||||
int
|
||||
mkdirat(int fd, const char *path, mode_t mode);
|
||||
|
||||
/*
|
||||
* Create the FIFO (named pipe) named by path with mode; return 0, or -1
|
||||
* with errno set.
|
||||
*/
|
||||
int
|
||||
mkfifo(const char *path, mode_t mode);
|
||||
|
||||
/*
|
||||
* Like mkfifo(), but path is relative to the directory named by fd (use
|
||||
* AT_FDCWD for the current working directory); return 0, or -1 with errno
|
||||
* set.
|
||||
*/
|
||||
int
|
||||
mkfifoat(int fd, const char *path, mode_t mode);
|
||||
|
||||
/*
|
||||
* Change the access mode of the file named by path to mode; return 0, or
|
||||
* -1 with errno set.
|
||||
*/
|
||||
int
|
||||
chmod(const char *path, mode_t mode);
|
||||
|
||||
/*
|
||||
* Change the access mode of the file descriptor fd to mode; return 0, or
|
||||
* -1 with errno set.
|
||||
*/
|
||||
int
|
||||
fchmod(int fd, mode_t mode);
|
||||
|
||||
/*
|
||||
* Like chmod(), but path is relative to the directory named by fd (use
|
||||
* AT_FDCWD for the current working directory); flag may hold
|
||||
* AT_SYMLINK_NOFOLLOW. Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
fchmodat(int fd, const char *path, mode_t mode, int flag);
|
||||
|
||||
/*
|
||||
* Set the process file-mode creation mask to cmask and return the previous
|
||||
* mask. Never fails.
|
||||
*/
|
||||
mode_t
|
||||
umask(mode_t cmask);
|
||||
|
||||
/*
|
||||
* Set the access and modification times of the file named by path
|
||||
* (relative to fd, or AT_FDCWD) to times[0] (access) and times[1]
|
||||
* (modification). A NULL times sets both to the current time; a tv_nsec
|
||||
* of UTIME_NOW/UTIME_OMIT (from <time.h>) selects per-member behavior.
|
||||
* Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
utimensat(int fd, const char *path, const struct timespec times[2], int flag);
|
||||
|
||||
/*
|
||||
* Like utimensat() on the file descriptor fd (the path is implicit);
|
||||
* return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
futimens(int fd, const struct timespec times[2]);
|
||||
|
||||
/*
|
||||
* Change the owner and group of the file named by path to owner/group
|
||||
* (a value of (uid_t)-1 leaves the current one unchanged). Follows
|
||||
* symbolic links; return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
chown(const char *path, uid_t owner, gid_t group);
|
||||
|
||||
/*
|
||||
* Change the owner and group of the file descriptor fd; return 0, or -1
|
||||
* with errno set.
|
||||
*/
|
||||
int
|
||||
fchown(int fd, uid_t owner, gid_t group);
|
||||
|
||||
/*
|
||||
* Like chown(), but a symbolic link is changed itself, not its target;
|
||||
* return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
lchown(const char *path, uid_t owner, gid_t group);
|
||||
|
||||
/*
|
||||
* Like chown(), but path is relative to the directory named by fd (use
|
||||
* AT_FDCWD for the current working directory); flag may hold
|
||||
* AT_SYMLINK_NOFOLLOW. Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
fchownat(int fd, const char *path, uid_t owner, gid_t group, int flag);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): XSI. */
|
||||
|
||||
/*
|
||||
* Create a special file named by path with mode (a file-type bit like
|
||||
* S_IFIFO or S_IFCHR must be set) and device id dev; return 0, or -1 with
|
||||
* errno set. Creating device nodes requires privilege. XSI.
|
||||
*/
|
||||
int
|
||||
mknod(const char *path, mode_t mode, dev_t dev);
|
||||
|
||||
/*
|
||||
* Like mknod(), but path is relative to the directory named by fd (use
|
||||
* AT_FDCWD for the current working directory); return 0, or -1 with errno
|
||||
* set. XSI.
|
||||
*/
|
||||
int
|
||||
mknodat(int fd, const char *path, mode_t mode, dev_t dev);
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_SYS_STAT_H */
|
||||
@@ -0,0 +1,82 @@
|
||||
#ifndef VLIBC_SYS_STATVFS_H
|
||||
#define VLIBC_SYS_STATVFS_H
|
||||
|
||||
/*
|
||||
* vlibc — <sys/statvfs.h>.
|
||||
*
|
||||
* Filesystem statistics in an ABI-independent POSIX shape: block and file
|
||||
* counts as unsigned counts over a reported block size, plus the read-only
|
||||
* and setuid-disabling mount flags and the longest file name. This header
|
||||
* carries only the portable view; the Linux kernel's private <linux/statfs.h>
|
||||
* layout (signed longs, an int-pair f_fsid, a f_spare tail) never leaks
|
||||
* into it.
|
||||
*
|
||||
* Level 2 (muslmimic): the whole header (statvfs and fstatvfs are XSI
|
||||
* [CX] in POSIX.1-2008, not base; the base filesystem
|
||||
* queries are stat()/fstat() in <sys/stat.h>).
|
||||
*
|
||||
* f_flag holds ST_RDONLY and/or ST_NOSUID, and f_fsid is a best-effort
|
||||
* filesystem identifier (the kernel does not populate it for every
|
||||
* filesystem). The block counts are measured in f_frsize units (which is
|
||||
* f_bsize when the filesystem does not distinguish a transfer size).
|
||||
*
|
||||
* None of these declarations carries an intent attribute: both functions
|
||||
* perform I/O with side effects and report failures through errno, so
|
||||
* const/pure would be unsound.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <sys/types.h> /* fsblkcnt_t, fsfilcnt_t */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): XSI. */
|
||||
|
||||
/*
|
||||
* Filesystem statistics. f_flag is a bitwise OR of ST_RDONLY and
|
||||
* ST_NOSUID; f_frsize is the fundamental block size the count fields are
|
||||
* expressed in. f_fsid is 0 when the filesystem provides no identifier.
|
||||
*/
|
||||
struct statvfs
|
||||
{
|
||||
unsigned long f_bsize; /* preferred I/O block size, bytes */
|
||||
unsigned long f_frsize; /* fundamental block size, bytes */
|
||||
fsblkcnt_t f_blocks; /* total blocks, in f_frsize units */
|
||||
fsblkcnt_t f_bfree; /* free blocks */
|
||||
fsblkcnt_t f_bavail; /* free blocks available to an unprivileged process */
|
||||
fsfilcnt_t f_files; /* total file serial numbers (inodes) */
|
||||
fsfilcnt_t f_ffree; /* free file serial numbers */
|
||||
fsfilcnt_t f_favail; /* free serial numbers available to an unprivileged process */
|
||||
unsigned long f_fsid; /* filesystem ID (best effort) */
|
||||
unsigned long f_flag; /* mount flags: ST_RDONLY | ST_NOSUID */
|
||||
unsigned long f_namemax; /* maximum file name length */
|
||||
};
|
||||
|
||||
/* f_flag values (also the kernel's f_flags bits on Linux). */
|
||||
#define ST_RDONLY 1 /* read-only filesystem */
|
||||
#define ST_NOSUID 2 /* set-user-ID/set-group-ID bits ignored on exec */
|
||||
|
||||
/*
|
||||
* Store statistics for the filesystem holding path into buf; return 0, or
|
||||
* -1 with errno set. XSI.
|
||||
*/
|
||||
int
|
||||
statvfs(const char *restrict path, struct statvfs *restrict buf);
|
||||
|
||||
/*
|
||||
* Like statvfs(), for the filesystem holding the open descriptor fildes;
|
||||
* return 0, or -1 with errno set. XSI.
|
||||
*/
|
||||
int
|
||||
fstatvfs(int fildes, struct statvfs *buf);
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_SYS_STATVFS_H */
|
||||
@@ -0,0 +1,186 @@
|
||||
#ifndef VLIBC_SYS_TIME_H
|
||||
#define VLIBC_SYS_TIME_H
|
||||
|
||||
/*
|
||||
* vlibc — <sys/time.h>.
|
||||
*
|
||||
* Wall-clock access and per-process interval timers expressed in
|
||||
* microseconds. Everything here is XSI or BSD legacy — POSIX.1-2008 base
|
||||
* leaves only timerisset in this header — so the whole surface is gated at
|
||||
* level 2:
|
||||
*
|
||||
* Level 2 (muslmimic): gettimeofday, settimeofday, getitimer,
|
||||
* setitimer, utimes, futimes, lutimes,
|
||||
* struct timeval, struct itimerval,
|
||||
* struct timezone, the timer* macros.
|
||||
*
|
||||
* struct timeval is the same type <sys/select.h> uses for select()'s
|
||||
* timeout; that header defines it under the shared guard below so the two
|
||||
* headers agree and either may be included first. The layout is the kernel
|
||||
* ABI on x86_64 (two longs), which is why gettimeofday/setitimer pass it
|
||||
* through unmodified.
|
||||
*
|
||||
* ITIMER_REAL/ITIMER_VIRTUAL/ITIMER_PROF are kernel ABI values (the
|
||||
* getitimer/setitimer `which` argument). None of these declarations carries
|
||||
* an intent attribute: every function performs I/O with side effects and
|
||||
* reports failures through errno, so const/pure would be unsound.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <sys/types.h> /* time_t, suseconds_t */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): XSI/BSD date-and-time interfaces. */
|
||||
|
||||
/* Interval-timer kinds (kernel ABI; the `which` argument). */
|
||||
#define ITIMER_REAL 0 /* count down in real time */
|
||||
#define ITIMER_VIRTUAL 1 /* count down in process virtual time */
|
||||
#define ITIMER_PROF 2 /* count down in process virtual + system time */
|
||||
|
||||
/*
|
||||
* Elapsed time in seconds and microseconds. Shared guard with
|
||||
* <sys/select.h>, which needs struct timeval for select()'s timeout at
|
||||
* level 1 and defines it under the same macro; either header may therefore
|
||||
* be included first. tv_usec holds 0..999999.
|
||||
*/
|
||||
#ifndef VLIBC_TIMEVAL_DEFINED
|
||||
#define VLIBC_TIMEVAL_DEFINED
|
||||
struct timeval
|
||||
{
|
||||
time_t tv_sec; /* seconds */
|
||||
suseconds_t tv_usec; /* microseconds */
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* An interval timer setting: it_value is the time to the next expiry,
|
||||
* it_interval the period between expiries after the first (both zero for a
|
||||
* one-shot or a disarmed timer). Kernel ABI layout (x86_64): two timevals.
|
||||
*/
|
||||
struct itimerval
|
||||
{
|
||||
struct timeval it_interval; /* interval between periodic expiries */
|
||||
struct timeval it_value; /* time to the next expiry */
|
||||
};
|
||||
|
||||
/*
|
||||
* Historic timezone record for settimeofday(). POSIX.1-2008 removed struct
|
||||
* timezone; it is kept for the legacy gettimeofday/settimeofday signatures,
|
||||
* whose tz argument is ignored (see src/time/gettimeofday.c).
|
||||
*/
|
||||
struct timezone
|
||||
{
|
||||
int tz_minuteswest; /* minutes west of Greenwich */
|
||||
int tz_dsttime; /* type of daylight-saving correction */
|
||||
};
|
||||
|
||||
/* True when the timer described by tp is armed. */
|
||||
#define timerisset(tp) ((tp)->tv_sec != 0 || (tp)->tv_usec != 0)
|
||||
|
||||
/* Disarm the timer described by tp (zero both fields). */
|
||||
#define timerclear(tp) ((tp)->tv_sec = (tp)->tv_usec = 0)
|
||||
|
||||
/*
|
||||
* Set *result to *a plus *b, normalizing the carry into tv_sec. The
|
||||
* arguments may be evaluated more than once (classic BSD form).
|
||||
*/
|
||||
#define timeradd(a, b, result) \
|
||||
do \
|
||||
{ \
|
||||
(result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
|
||||
(result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
|
||||
if ((result)->tv_usec >= 1000000) \
|
||||
{ \
|
||||
(result)->tv_sec++; \
|
||||
(result)->tv_usec -= 1000000; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Set *result to *a minus *b, normalizing the borrow out of tv_sec. The
|
||||
* arguments may be evaluated more than once (classic BSD form).
|
||||
*/
|
||||
#define timersub(a, b, result) \
|
||||
do \
|
||||
{ \
|
||||
(result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
|
||||
(result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
|
||||
if ((result)->tv_usec < 0) \
|
||||
{ \
|
||||
(result)->tv_sec--; \
|
||||
(result)->tv_usec += 1000000; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* True when *a op *b, compared first by tv_sec and, on equality, by
|
||||
* tv_usec. op is a comparison operator (timercmp(x, y, <=)). The
|
||||
* arguments may be evaluated more than once (classic BSD form).
|
||||
*/
|
||||
/* clang-format off */
|
||||
#define timercmp(a, b, op) \
|
||||
(((a)->tv_sec == (b)->tv_sec) ? ((a)->tv_usec op (b)->tv_usec) : ((a)->tv_sec op (b)->tv_sec))
|
||||
/* clang-format on */
|
||||
|
||||
/*
|
||||
* Read the current wall-clock time into tv (seconds since the Epoch and
|
||||
* microseconds 0..999999). tz is ignored for compatibility with the
|
||||
* historic two-argument form and may be anything, including NULL; tv may
|
||||
* also be NULL, in which case nothing is written. Return 0, or -1 with
|
||||
* errno set when tv points outside the address space.
|
||||
*/
|
||||
int
|
||||
gettimeofday(struct timeval *restrict tv, void *restrict tz);
|
||||
|
||||
/*
|
||||
* Set the kernel's idea of the current time from tv. tz, when not NULL, is
|
||||
* honored only together with tv; passing tz alone is unsupported (ENOTSUP).
|
||||
* Requires privilege. Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
settimeofday(const struct timeval *tv, const struct timezone *tz);
|
||||
|
||||
/*
|
||||
* Store the current setting of interval timer which (ITIMER_REAL,
|
||||
* ITIMER_VIRTUAL or ITIMER_PROF) through value. Return 0, or -1 with errno
|
||||
* set for an unknown which or when value points outside the address space.
|
||||
*/
|
||||
int
|
||||
getitimer(int which, struct itimerval *value);
|
||||
|
||||
/*
|
||||
* Arm interval timer which from value (when value is not NULL) and store
|
||||
* the previous setting through ovalue (when not NULL). A value whose
|
||||
* it_value is zero disarms the timer. Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
setitimer(int which, const struct itimerval *restrict value, struct itimerval *restrict ovalue);
|
||||
|
||||
/*
|
||||
* Set the access (times[0]) and modification (times[1]) timestamps of path.
|
||||
* A NULL times array sets both to the current time. Return 0, or -1 with
|
||||
* errno set.
|
||||
*/
|
||||
int
|
||||
utimes(const char *path, const struct timeval times[2]);
|
||||
|
||||
/* utimes() on the open file fd. */
|
||||
int
|
||||
futimes(int fd, const struct timeval times[2]);
|
||||
|
||||
/* utimes() on the symbolic link path itself, never following it. */
|
||||
int
|
||||
lutimes(const char *path, const struct timeval times[2]);
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_SYS_TIME_H */
|
||||
@@ -0,0 +1,72 @@
|
||||
#ifndef VLIBC_SYS_TIMES_H
|
||||
#define VLIBC_SYS_TIMES_H
|
||||
|
||||
/*
|
||||
* vlibc — <sys/times.h>.
|
||||
*
|
||||
* Process CPU-time accounting in clock ticks. times() and struct tms are
|
||||
* XSI [CX] in POSIX.1-2008 (glibc gates them behind _XOPEN_SOURCE, not
|
||||
* _POSIX_C_SOURCE), so the whole surface sits at level 2 — nothing here is
|
||||
* POSIX.1-2008 base:
|
||||
*
|
||||
* Level 2 (muslmimic): times, struct tms.
|
||||
*
|
||||
* clock_t is the scalar tick type owned by <sys/types.h> (typedef long
|
||||
* clock_t; <time.h> pulls it from the same place for clock()); this header
|
||||
* includes that one rather than re-typedefing clock_t. The unit of every
|
||||
* times() figure is the tick: CLK_TCK per second, the fixed x86_64 USER_HZ
|
||||
* constant of 100 that todo 32's sysconf(_SC_CLK_TCK) reports (defined
|
||||
* once, in src/misc/sysconf.c).
|
||||
*
|
||||
* struct tms mirrors the x86_64 kernel's __kernel_tms exactly: four longs
|
||||
* in the same order (tms_utime, tms_stime, tms_cutime, tms_cstime), which
|
||||
* is why src/sys/times.c hands the struct straight to SYS_times. The kernel
|
||||
* writes all four words on every successful call, so no field is optional.
|
||||
*
|
||||
* The declaration carries no intent attribute: times() performs a syscall
|
||||
* with side effects and reports failures through errno, so const/pure
|
||||
* would be unsound (see the rationale in <sys/time.h>).
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <sys/types.h> /* clock_t */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): XSI process-CPU-time accounting. */
|
||||
|
||||
/*
|
||||
* CPU-time accounting for the calling process and its waited-for children,
|
||||
* measured in clock ticks (CLK_TCK per second). tms_utime and tms_stime
|
||||
* are the user and system CPU time of the calling process; tms_cutime and
|
||||
* tms_cstime are the user and system CPU time consumed by children that
|
||||
* have been waited for (all four zeroed before any child is waited on).
|
||||
*/
|
||||
struct tms
|
||||
{
|
||||
clock_t tms_utime; /* user CPU time of the calling process */
|
||||
clock_t tms_stime; /* system CPU time of the calling process */
|
||||
clock_t tms_cutime; /* user CPU time of waited-for children */
|
||||
clock_t tms_cstime; /* system CPU time of waited-for children */
|
||||
};
|
||||
|
||||
/*
|
||||
* Store the process CPU-time accounting through buf and return the number
|
||||
* of clock ticks since an arbitrary fixed point in the past (the Linux
|
||||
* kernel reports ticks since boot). Return (clock_t)-1 with errno set when
|
||||
* buf points outside the address space.
|
||||
*/
|
||||
clock_t
|
||||
times(struct tms *buf);
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_SYS_TIMES_H */
|
||||
@@ -0,0 +1,75 @@
|
||||
#ifndef VLIBC_SYS_UIO_H
|
||||
#define VLIBC_SYS_UIO_H
|
||||
|
||||
/*
|
||||
* vlibc — <sys/uio.h>.
|
||||
*
|
||||
* Scatter/gather I/O: readv() and writev() transfer data between a file
|
||||
* descriptor and a vector of memory buffers (POSIX.1-2008 base). Both are
|
||||
* unbuffered pass-throughs to the kernel: the iovec array is handed to
|
||||
* SYS_readv/SYS_writev as-is, and the kernel applies its own validation
|
||||
* (iovcnt in 0..IOV_MAX, iov NULL with iovcnt > 0 faults as EFAULT). Failures
|
||||
* are reported as -1 with errno set by the syscall layer.
|
||||
*
|
||||
* Level 1 (onlyposix): readv, writev.
|
||||
*
|
||||
* The Linux extensions that build on this ABI (preadv/pwritev,
|
||||
* process_vm_readv/process_vm_writev, ...) are deliberately absent — they are
|
||||
* not POSIX and belong to a later compatibility profile.
|
||||
*
|
||||
* struct iovec matches the kernel's struct iovec (x86_64: a pointer and a
|
||||
* size_t, no padding). IOV_MAX is 1024, the Linux UIO_MAXIOV; the kernel
|
||||
* rejects any iovcnt above it with EINVAL.
|
||||
*
|
||||
* None of these declarations carry an intent attribute: every function
|
||||
* performs I/O with side effects and reports failures through errno, so
|
||||
* const/pure would be unsound.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Maximum number of iovec entries a single readv()/writev() call accepts. */
|
||||
#define IOV_MAX 1024
|
||||
|
||||
/*
|
||||
* A scatter/gather segment: iov_base points at the buffer (or, for readv,
|
||||
* the buffer to fill) and iov_len is its size in bytes. Kernel layout, as
|
||||
* above.
|
||||
*/
|
||||
struct iovec
|
||||
{
|
||||
void *iov_base; /* starting address of the segment */
|
||||
size_t iov_len; /* number of bytes in the segment */
|
||||
};
|
||||
|
||||
/*
|
||||
* Read up to the sum of iov_len bytes from fd into the iovcnt buffers of
|
||||
* iov, in order; return the number of bytes read, 0 at end of file, or -1
|
||||
* with errno set on error. Buffers are filled completely before the next
|
||||
* one is touched.
|
||||
*/
|
||||
ssize_t
|
||||
readv(int fd, const struct iovec *iov, int iovcnt);
|
||||
|
||||
/*
|
||||
* Write up to the sum of iov_len bytes from the iovcnt buffers of iov to
|
||||
* fd, in order; return the number of bytes written, or -1 with errno set
|
||||
* on error. The kernel applies its own validation (iovcnt 0..IOV_MAX, iov
|
||||
* NULL with iovcnt > 0 faults as EFAULT).
|
||||
*/
|
||||
ssize_t
|
||||
writev(int fd, const struct iovec *iov, int iovcnt);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_SYS_UIO_H */
|
||||
@@ -0,0 +1,65 @@
|
||||
#ifndef VLIBC_SYS_UTSNAME_H
|
||||
#define VLIBC_SYS_UTSNAME_H
|
||||
|
||||
/*
|
||||
* vlibc — <sys/utsname.h>.
|
||||
*
|
||||
* System identification: uname() and struct utsname. uname is XSI [CX] in
|
||||
* POSIX.1-2008 (glibc gates it behind _XOPEN_SOURCE, not _POSIX_C_SOURCE),
|
||||
* so the whole surface sits at level 2 — nothing here is POSIX.1-2008 base.
|
||||
*
|
||||
* Level 2 (muslmimic): uname, struct utsname.
|
||||
*
|
||||
* struct utsname mirrors the x86_64 kernel's struct new_utsname exactly: six
|
||||
* NUL-terminated 65-byte character arrays (the kernel's _UTSNAME_LENGTH, 64
|
||||
* usable characters plus the terminator). The kernel's uname syscall copies
|
||||
* all 390 bytes, so the layout is an ABI fact, not a style choice.
|
||||
* domainname is a Linux extension (the kernel's uts namespace domain name)
|
||||
* and is not part of the POSIX struct; it is included for glibc
|
||||
* compatibility — POSIX only requires the first five fields.
|
||||
*
|
||||
* The declaration carries no intent attribute: uname performs I/O with side
|
||||
* effects and reports failures through errno, so const/pure would be
|
||||
* unsound (see the rationale in <unistd.h>).
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* The system identification strings. Each field is NUL-terminated; the
|
||||
* 65-byte width matches the kernel buffer (64 characters plus the NUL).
|
||||
* sysname is the operating system name ("Linux"), nodename the host name,
|
||||
* release the kernel release, version the kernel build, machine the
|
||||
* hardware identifier ("x86_64"), and domainname the NIS/UTS domain name.
|
||||
*/
|
||||
struct utsname
|
||||
{
|
||||
char sysname[65]; /* operating system name */
|
||||
char nodename[65]; /* name within the communications network */
|
||||
char release[65]; /* operating system release level */
|
||||
char version[65]; /* operating system version */
|
||||
char machine[65]; /* hardware type */
|
||||
char domainname[65]; /* NIS/UTS domain name (Linux extension) */
|
||||
};
|
||||
|
||||
/*
|
||||
* Fill the buffer with the current system identification strings; return
|
||||
* 0, or -1 with errno set when the buffer points outside the address
|
||||
* space. The strings are copies from the kernel's UTS namespace.
|
||||
*/
|
||||
int
|
||||
uname(struct utsname *buf);
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_SYS_UTSNAME_H */
|
||||
@@ -0,0 +1,207 @@
|
||||
#ifndef VLIBC_SYS_WAIT_H
|
||||
#define VLIBC_SYS_WAIT_H
|
||||
|
||||
/*
|
||||
* vlibc — <sys/wait.h>.
|
||||
*
|
||||
* Child-process status collection (POSIX.1-2008) and the wait status macros.
|
||||
* Everything here is a pass-through to the kernel: wait/waitpid/wait3/wait4
|
||||
* ride SYS_wait4, waitid rides SYS_waitid, and failures are reported as -1
|
||||
* (or the child pid on success) with errno set by the syscall layer.
|
||||
*
|
||||
* Level 1 (onlyposix): wait, waitpid, waitid + the W* status macros.
|
||||
* Level 2 (muslmimic): wait3, wait4 (XSI).
|
||||
*
|
||||
* The W* decode macros follow the kernel's wait status encoding: bits 0-6
|
||||
* hold the terminating signal (or 0x7f for a stop), bit 7 the core-dump
|
||||
* flag, bits 8-15 the exit status, and the word 0xffff marks a continued
|
||||
* child. WEXITED/WSTOPPED/WNOWAIT are the waitid-only option bits (XSI;
|
||||
* kernel-identical values) and WCOREDUMP is an XSI/Linux extension (bit
|
||||
* 0x80) kept alongside the POSIX set for source compatibility.
|
||||
*
|
||||
* None of the declarations carry an intent attribute: every function has
|
||||
* kernel-visible side effects and reports failures through errno.
|
||||
*
|
||||
* <signal.h> does not exist yet (a later todo owns it). The minimal
|
||||
* siginfo_t below and the CLD_* constants live here under guards so that
|
||||
* header can take them over without conflict; see the notes by each.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Selector type for waitid(). POSIX defines idtype_t as an integer type and
|
||||
* places it in <sys/types.h>, which does not define it yet; it is provided
|
||||
* here under a VLIBC_ guard so sys/types.h can take it over later without a
|
||||
* redefinition. int matches the kernel's `which` argument on x86_64.
|
||||
*/
|
||||
#ifndef VLIBC_DEFINED_IDTYPE_T
|
||||
#define VLIBC_DEFINED_IDTYPE_T
|
||||
typedef int idtype_t;
|
||||
#endif
|
||||
|
||||
/* waitid() idtype selectors (kernel-identical). */
|
||||
#define P_ALL 0 /* wait for any child */
|
||||
#define P_PID 1 /* wait for the specific child */
|
||||
#define P_PGID 2 /* wait for any child in the process group */
|
||||
|
||||
/* wait/waitpid option bits (kernel-identical). */
|
||||
#define WNOHANG 1 /* do not block; return 0 if no child has exited */
|
||||
#define WUNTRACED 2 /* also report stopped children */
|
||||
#define WCONTINUED 8 /* also report continued children */
|
||||
|
||||
/*
|
||||
* waitid-only option bits (kernel-identical; XSI). WSTOPPED shares its
|
||||
* value with WUNTRACED, so the two names are interchangeable where both
|
||||
* apply.
|
||||
*/
|
||||
#define WEXITED 4 /* wait for exited children */
|
||||
#define WSTOPPED 2 /* wait for stopped children */
|
||||
#define WNOWAIT 0x01000000 /* report but do not reap */
|
||||
|
||||
/* Status decode macros. The argument is the raw wait status word. */
|
||||
|
||||
/* True when the child terminated normally via exit() or _exit(). */
|
||||
#define WIFEXITED(s) (((s) & 0x7f) == 0)
|
||||
|
||||
/* Exit status of a normally terminated child (WIFEXITED true). */
|
||||
#define WEXITSTATUS(s) (((s) & 0xff00) >> 8)
|
||||
|
||||
/*
|
||||
* True when the child was killed by a signal. The 0x7f exclusion keeps the
|
||||
* stop code (0x7f) from being misread as a terminating signal.
|
||||
*/
|
||||
#define WIFSIGNALED(s) (((s) & 0x7f) != 0 && ((s) & 0x7f) != 0x7f)
|
||||
|
||||
/* Number of the signal that killed the child (WIFSIGNALED true). */
|
||||
#define WTERMSIG(s) ((s) & 0x7f)
|
||||
|
||||
/* True when the child is stopped by a signal (WUNTRACED). */
|
||||
#define WIFSTOPPED(s) (((s) & 0xff) == 0x7f)
|
||||
|
||||
/* Number of the signal that stopped the child (WIFSTOPPED true). */
|
||||
#define WSTOPSIG(s) WEXITSTATUS(s)
|
||||
|
||||
/* True when the child was resumed by SIGCONT (WCONTINUED). */
|
||||
#define WIFCONTINUED(s) ((s) == 0xffff)
|
||||
|
||||
/* True when the killed child dumped core (XSI/Linux extension, bit 0x80). */
|
||||
#define WCOREDUMP(s) (((s) & 0x80) != 0)
|
||||
|
||||
/*
|
||||
* si_code values reported by waitid() (kernel-identical). POSIX defines
|
||||
* these in <signal.h>, which does not exist yet; guarded per name so the
|
||||
* future signal.h can define them without a redefinition warning.
|
||||
*/
|
||||
#ifndef CLD_EXITED
|
||||
#define CLD_EXITED 1 /* child exited normally */
|
||||
#define CLD_KILLED 2 /* child killed by a signal */
|
||||
#define CLD_DUMPED 3 /* child killed by a signal and dumped core */
|
||||
#define CLD_TRAPPED 4 /* child stopped by a trace event */
|
||||
#define CLD_STOPPED 5 /* child stopped by a signal */
|
||||
#define CLD_CONTINUED 6 /* child resumed by SIGCONT */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Minimal siginfo_t, sized and laid out to match the kernel's x86_64
|
||||
* siginfo_t (128 bytes) for the fields waitid() fills: si_signo, si_errno,
|
||||
* si_code at offsets 0/4/8, then the wait-fields si_pid/si_uid/si_status at
|
||||
* offsets 16/20/24. The union is 8-aligned (si_utime/si_stime are 8-byte
|
||||
* clock_t in both the kernel and glibc layouts), so it starts at offset 16
|
||||
* with implicit padding after si_code. The kernel copies the full 128 bytes,
|
||||
* so the size must stay 128; the pad member guarantees it and the static
|
||||
* asserts pin the layout. The signal-handling fields (si_addr, si_value,
|
||||
* timers, ...) are deliberately absent — the future <signal.h> owns the
|
||||
* complete siginfo_t and must reconcile this guard.
|
||||
*/
|
||||
#ifndef VLIBC_INTERNAL_SIGINFO_DEFINED
|
||||
#define VLIBC_INTERNAL_SIGINFO_DEFINED
|
||||
typedef struct
|
||||
{
|
||||
int si_signo;
|
||||
int si_errno;
|
||||
int si_code;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
pid_t si_pid; /* 16 */
|
||||
uid_t si_uid; /* 20 */
|
||||
int si_status; /* 24 */
|
||||
long si_utime; /* 32 — 8-byte clock_t, matches the kernel/glibc ABI */
|
||||
long si_stime; /* 40 */
|
||||
};
|
||||
int vlibc_siginfo_pad[28]; /* union sized 112 so the struct stays 128 */
|
||||
};
|
||||
} siginfo_t;
|
||||
|
||||
_Static_assert(sizeof(siginfo_t) == 128, "siginfo_t must match the kernel size");
|
||||
_Static_assert(offsetof(siginfo_t, si_pid) == 16, "si_pid must sit at offset 16");
|
||||
#endif
|
||||
|
||||
/* Level 1 (POSIX base). */
|
||||
|
||||
/*
|
||||
* Wait for any child to terminate or stop and store its status in
|
||||
* *stat_loc (NULL skips the store); return the child pid, or -1 with
|
||||
* errno set. Equivalent to waitpid(-1, stat_loc, 0).
|
||||
*/
|
||||
pid_t
|
||||
wait(int *stat_loc);
|
||||
|
||||
/*
|
||||
* Wait for the child identified by pid (-1: any child, 0: any child in the
|
||||
* calling process group, < -1: any child in the process group -pid) and
|
||||
* store its status in *stat_loc (NULL skips the store). options are the
|
||||
* WNOHANG/WUNTRACED/WCONTINUED bits. Return the child pid, 0 when WNOHANG
|
||||
* found nothing, or -1 with errno set.
|
||||
*/
|
||||
pid_t
|
||||
waitpid(pid_t pid, int *stat_loc, int options);
|
||||
|
||||
/*
|
||||
* Wait for a child selected by idtype/id (P_ALL, P_PID, P_PGID) and fill
|
||||
* *infop with the siginfo details (si_pid, si_uid, si_status and a CLD_*
|
||||
* si_code); options are the WEXITED/WSTOPPED/WCONTINUED/WNOHANG/WNOWAIT
|
||||
* bits. Return 0, or -1 with errno set. infop must point to at least
|
||||
* 128 bytes (the kernel writes a full siginfo).
|
||||
*/
|
||||
int
|
||||
waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): XSI. */
|
||||
|
||||
/*
|
||||
* struct rusage is defined by <sys/resource.h>, which does not exist yet;
|
||||
* a forward declaration is enough to pass a pointer through to the kernel.
|
||||
*/
|
||||
struct rusage;
|
||||
|
||||
/*
|
||||
* Like waitpid(-1, stat_loc, options), and additionally store resource
|
||||
* usage in *rusage (NULL skips the store); return the child pid, or -1
|
||||
* with errno set. XSI.
|
||||
*/
|
||||
pid_t
|
||||
wait3(int *stat_loc, int options, struct rusage *rusage);
|
||||
|
||||
/*
|
||||
* Like waitpid(), and additionally store resource usage in *rusage (NULL
|
||||
* skips the store); return the child pid, or -1 with errno set. XSI.
|
||||
*/
|
||||
pid_t
|
||||
wait4(pid_t pid, int *stat_loc, int options, struct rusage *rusage);
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_SYS_WAIT_H */
|
||||
@@ -0,0 +1,172 @@
|
||||
#ifndef VLIBC_SYSLOG_H
|
||||
#define VLIBC_SYSLOG_H
|
||||
|
||||
/*
|
||||
* vlibc — <syslog.h>.
|
||||
*
|
||||
* The syslog(3) logging interface, writing BSD-syslog datagrams to the
|
||||
* /dev/log socket (see src/misc/syslog.c for the wire details). syslog is
|
||||
* NOT POSIX.1-2008 base — it is XSI [CX] (and historical BSD), so this
|
||||
* ENTIRE header is gated at `VLIBC_LEVEL >= 2`: at level 1 (onlyposix) it is
|
||||
* empty, exactly like <strings.h>.
|
||||
*
|
||||
* The interface has three parts:
|
||||
*
|
||||
* - facilities and severities, plus the option flags of openlog. The
|
||||
* facility constants carry the classic BSD values, which are the Linux
|
||||
* wire ABI for /dev/log: facility = code << 3, severity = 0..7, and the
|
||||
* datagram's numeric priority is facility | severity. The facility code
|
||||
* is passed on the wire, so these numbers must match what syslogd
|
||||
* expects and are therefore kernel-ABI facts, not invented values;
|
||||
*
|
||||
* - LOG_MASK / LOG_UPTO, the two macros that build the mask argument of
|
||||
* setlogmask (LOG_MASK is POSIX; LOG_UPTO is the classic convenience
|
||||
* form both musl and glibc provide);
|
||||
*
|
||||
* - openlog / syslog / closelog / setlogmask and the vsyslog entry point
|
||||
* the standard requires syslog to be implemented in terms of. None of
|
||||
* them carries an intent attribute: every one reads and writes global
|
||||
* logging state and performs I/O, so const/pure would be unsound.
|
||||
*
|
||||
* %m handling: inside the syslog format string, "%m" is replaced by the
|
||||
* strerror(errno) text (a syslog-specific feature, implemented in syslog.c —
|
||||
* NOT a printf conversion). All other conversions are the standard ones,
|
||||
* handled by the library's own vsnprintf.
|
||||
*
|
||||
* This header includes <vlibc/features.h> itself, so the gate below always
|
||||
* sees the configured VLIBC_LEVEL even when the caller included no vlibc
|
||||
* header first, and <stdarg.h> for va_list.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ---- openlog option flags (bit mask) ---- */
|
||||
|
||||
#define LOG_PID 0x01 /* include the calling process' PID in each message */
|
||||
#define LOG_CONS 0x02 /* on send failure, write the message to the console */
|
||||
#define LOG_ODELAY 0x04 /* delay opening the /dev/log socket until first use */
|
||||
#define LOG_NDELAY 0x08 /* open the /dev/log socket immediately in openlog */
|
||||
#define LOG_NOWAIT 0x10 /* do not wait for child processes (accepted, unused) */
|
||||
#define LOG_PERROR 0x20 /* also write each message to standard error */
|
||||
|
||||
/* ---- facility codes (code << 3; the wire value of the datagram) ---- */
|
||||
|
||||
#define LOG_KERN 0 /* kernel messages */
|
||||
#define LOG_USER (1 << 3) /* random user-level messages */
|
||||
#define LOG_MAIL (2 << 3) /* mail system */
|
||||
#define LOG_DAEMON (3 << 3) /* system daemons */
|
||||
#define LOG_AUTH (4 << 3) /* security/authorization messages */
|
||||
#define LOG_LPR (6 << 3) /* line printer subsystem */
|
||||
#define LOG_NEWS (7 << 3) /* network news subsystem */
|
||||
#define LOG_UUCP (8 << 3) /* UUCP subsystem */
|
||||
#define LOG_CRON (9 << 3) /* clock daemon */
|
||||
#define LOG_LOCAL0 (16 << 3) /* reserved for local use */
|
||||
#define LOG_LOCAL1 (17 << 3) /* reserved for local use */
|
||||
#define LOG_LOCAL2 (18 << 3) /* reserved for local use */
|
||||
#define LOG_LOCAL3 (19 << 3) /* reserved for local use */
|
||||
#define LOG_LOCAL4 (20 << 3) /* reserved for local use */
|
||||
#define LOG_LOCAL5 (21 << 3) /* reserved for local use */
|
||||
#define LOG_LOCAL6 (22 << 3) /* reserved for local use */
|
||||
#define LOG_LOCAL7 (23 << 3) /* reserved for local use */
|
||||
|
||||
/* ---- severities (the low three bits of the priority value) ---- */
|
||||
|
||||
#define LOG_EMERG 0 /* system is unusable */
|
||||
#define LOG_ALERT 1 /* action must be taken immediately */
|
||||
#define LOG_CRIT 2 /* critical conditions */
|
||||
#define LOG_ERR 3 /* error conditions */
|
||||
#define LOG_WARNING 4 /* warning conditions */
|
||||
#define LOG_NOTICE 5 /* normal but significant condition */
|
||||
#define LOG_INFO 6 /* informational */
|
||||
#define LOG_DEBUG 7 /* debug-level messages */
|
||||
|
||||
/* Bit field holding the facility part (bits 3..9) of a priority value. */
|
||||
#define LOG_FACMASK 0x03f8
|
||||
|
||||
/* Bit field holding the severity part (bits 0..2) of a priority value. */
|
||||
#define LOG_PRIMASK 0x07
|
||||
|
||||
/* A mask with exactly pri's bit set (pri is a LOG_* severity, 0..7). */
|
||||
#define LOG_MASK(pri) (1 << (pri))
|
||||
|
||||
/* A mask with every severity from LOG_EMERG through pri included. */
|
||||
#define LOG_UPTO(pri) ((1 << ((pri) + 1)) - 1)
|
||||
|
||||
/*
|
||||
* Open a connection to the system logger. ident, when non-NULL, is copied
|
||||
* (a caller-owned pointer is not retained) and prefixes every message; a
|
||||
* NULL ident logs messages with no tag. option ORs in the LOG_* option
|
||||
* flags; facility is one of the LOG_* facility constants and is used for
|
||||
* messages whose priority carries no facility of its own. With LOG_NDELAY
|
||||
* the /dev/log socket is opened here; otherwise (and by default) it is
|
||||
* opened lazily on the first syslog call, and a failure to open it is
|
||||
* silent. Prior state — the mask from setlogmask and the default facility
|
||||
* LOG_USER — persists.
|
||||
*
|
||||
* I/O and global state: no intent attribute.
|
||||
*/
|
||||
void
|
||||
openlog(const char *ident, int option, int facility);
|
||||
|
||||
/*
|
||||
* Log a message: build "<facility|severity>ident[pid]: message" and send it
|
||||
* as one datagram to /dev/log, subject to the setlogmask mask. severity is
|
||||
* the low three bits of priority; a facility encoded in priority overrides
|
||||
* the openlog facility, and an out-of-range (high-bit) facility is ignored,
|
||||
* falling back to the openlog facility. The format string is the printf
|
||||
* family's, with "%m" additionally meaning the strerror(errno) text. When
|
||||
* LOG_PERROR was passed to openlog the composed line also goes to standard
|
||||
* error; when the send fails and LOG_CONS was passed, it goes to the console
|
||||
* instead. A failed open or send never terminates the process.
|
||||
*
|
||||
* I/O and global state: no intent attribute.
|
||||
*/
|
||||
void
|
||||
syslog(int priority, const char *format, ...);
|
||||
|
||||
/*
|
||||
* Close the connection to the system logger: close the /dev/log socket if
|
||||
* one is open, and clear the ident and option state installed by openlog
|
||||
* (the facility and mask are retained). A later syslog reopens the socket
|
||||
* lazily.
|
||||
*
|
||||
* I/O and global state: no intent attribute.
|
||||
*/
|
||||
void
|
||||
closelog(void);
|
||||
|
||||
/*
|
||||
* The engine behind syslog: identical semantics, taking a va_list instead
|
||||
* of the variadic tail. Each message is formatted exactly once (through
|
||||
* vsnprintf into an internal buffer) before any send, so LOG_PERROR and the
|
||||
* datagram always carry the same text.
|
||||
*
|
||||
* I/O and global state: no intent attribute.
|
||||
*/
|
||||
void
|
||||
vsyslog(int priority, const char *format, va_list ap);
|
||||
|
||||
/*
|
||||
* Set the process' log priority mask to maskpri — a bit per severity where
|
||||
* bit LOG_MASK(severity) set means "log this severity" — and return the
|
||||
* previous mask. A mask of 0 suppresses every message. The mask applies to
|
||||
* the severity only: facility is never filtered.
|
||||
*/
|
||||
int
|
||||
setlogmask(int maskpri);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#endif /* VLIBC_SYSLOG_H */
|
||||
@@ -0,0 +1,366 @@
|
||||
#ifndef VLIBC_TERMIOS_H
|
||||
#define VLIBC_TERMIOS_H
|
||||
|
||||
/*
|
||||
* vlibc — <termios.h>.
|
||||
*
|
||||
* General terminal interface (POSIX.1-2008 base): the termios structure and
|
||||
* the tcgetattr/tcsetattr/tcsendbreak/tcdrain/tcflush/tcflow query-and-set
|
||||
* functions plus the cfgetispeed/cfgetospeed/cfsetispeed/cfsetospeed baud
|
||||
* helpers, over the kernel's TCGETS/TCSETS ioctls (SYS_ioctl).
|
||||
*
|
||||
* Level 1 (onlyposix): tcgetattr, tcsetattr, tcsendbreak, tcdrain,
|
||||
* tcflush, tcflow, cfgetispeed, cfgetospeed,
|
||||
* cfsetispeed, cfsetospeed (POSIX base).
|
||||
* Level 2 (muslmimic): cfmakeraw, cfsetspeed (BSD/glibc extensions),
|
||||
* tcgetsid (XSI [CX] — glibc declares it only under
|
||||
* __USE_XOPEN2K8, not base __USE_POSIX).
|
||||
*
|
||||
* struct termios is a byte-for-byte copy of the x86_64 kernel termios
|
||||
* (asm-generic/termbits.h), pinned by the static assertions below. The
|
||||
* kernel TCGETS/TCSETS ioctls copy exactly sizeof(struct termios) bytes of
|
||||
* this layout (verified empirically on this host: TCGETS writes 36 bytes);
|
||||
* any divergence would silently corrupt c_cc. Unlike glibc's 60-byte
|
||||
* userspace struct (NCCS 32 + trailing ispeed/ospeed members), the speed
|
||||
* is carried only in the c_cflag CBAUD/CIBAUD bit fields, exactly as the
|
||||
* kernel encodes it — so the ioctl copies the whole structure both ways
|
||||
* with nothing left stale.
|
||||
*
|
||||
* The baud-rate constants B0..B4000000 are the KERNEL CBAUD encodings
|
||||
* (B9600 is 0x0d, B38400 is 0x0f, B57600 is 0x1001, ...), NOT glibc's
|
||||
* real-numbers-in-speed_t scheme. cfgetospeed()/cfgetispeed() return these
|
||||
* constants and cfsetospeed()/cfsetispeed()/cfsetspeed() accept them, so
|
||||
* a speed round-trips through the c_cflag bits untouched. This is the same
|
||||
* choice musl makes.
|
||||
*
|
||||
* All flag, control-character, baud, and action constants below are Linux
|
||||
* x86_64 kernel-UAPI facts transcribed from asm-generic/termbits.h and
|
||||
* asm-generic/termbits-common.h. The ioctl request numbers (TCGETS,
|
||||
* TCSETS, TCSBRK, TCXONC, TCFLSH, TIOCGSID) are Linux-only and stay
|
||||
* private to src/termios/termios.c, matching the sys/ioctl.h-less layout
|
||||
* of the tree today (they belong to a future <sys/ioctl.h>).
|
||||
*
|
||||
* No declaration carries an intent attribute: tcgetattr/tcsetattr &c
|
||||
* perform I/O, and the cf* speed helpers read or write the caller's
|
||||
* termios through a pointer that may alias arbitrary memory — const/pure
|
||||
* would be unsound (same rationale as <fcntl.h>).
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ---- Basic types ---- */
|
||||
|
||||
typedef unsigned char cc_t; /* control-character value */
|
||||
typedef unsigned int speed_t; /* baud rate (a B* constant) */
|
||||
typedef unsigned int tcflag_t; /* mode-flag bit mask */
|
||||
|
||||
/* ---- struct termios (x86_64 kernel layout) ---- */
|
||||
|
||||
#define NCCS 19 /* control-character slots (kernel UAPI) */
|
||||
|
||||
struct termios
|
||||
{
|
||||
tcflag_t c_iflag; /* 0: input mode flags */
|
||||
tcflag_t c_oflag; /* 4: output mode flags */
|
||||
tcflag_t c_cflag; /* 8: control mode flags */
|
||||
tcflag_t c_lflag; /* 12: local mode flags */
|
||||
cc_t c_line; /* 16: line discipline */
|
||||
cc_t c_cc[NCCS]; /* 17: control characters */
|
||||
};
|
||||
|
||||
/* Pin the kernel layout (TCGETS/TCSETS copy this exact size and shape). */
|
||||
_Static_assert(sizeof(struct termios) == 36, "struct termios must match the x86_64 kernel layout");
|
||||
_Static_assert(offsetof(struct termios, c_lflag) == 12, "c_lflag must sit at offset 12");
|
||||
_Static_assert(offsetof(struct termios, c_line) == 16, "c_line must sit at offset 16");
|
||||
_Static_assert(offsetof(struct termios, c_cc) == 17, "c_cc must sit at offset 17");
|
||||
|
||||
/* ---- Control characters (indices into c_cc) ---- */
|
||||
|
||||
#define VINTR 0 /* interrupt character */
|
||||
#define VQUIT 1 /* quit character */
|
||||
#define VERASE 2 /* erase character */
|
||||
#define VKILL 3 /* kill character */
|
||||
#define VEOF 4 /* end-of-file character */
|
||||
#define VTIME 5 /* timeout for non-canonical read */
|
||||
#define VMIN 6 /* min characters for non-canonical read */
|
||||
#define VSWTC 7 /* switch character (System V) */
|
||||
#define VSTART 8 /* start character */
|
||||
#define VSTOP 9 /* stop character */
|
||||
#define VSUSP 10 /* suspend character */
|
||||
#define VEOL 11 /* alternate end-of-line character */
|
||||
#define VREPRINT 12 /* reprint character */
|
||||
#define VDISCARD 13 /* discard character */
|
||||
#define VWERASE 14 /* word-erase character */
|
||||
#define VLNEXT 15 /* literal-next character */
|
||||
#define VEOL2 16 /* alternate end-of-line character */
|
||||
|
||||
/* ---- c_iflag: input mode flags ---- */
|
||||
|
||||
#define IGNBRK 0x0001 /* ignore break condition */
|
||||
#define BRKINT 0x0002 /* signal interrupt on break */
|
||||
#define IGNPAR 0x0004 /* ignore characters with parity errors */
|
||||
#define PARMRK 0x0008 /* mark parity and framing errors */
|
||||
#define INPCK 0x0010 /* enable input parity check */
|
||||
#define ISTRIP 0x0020 /* strip 8th bit off characters */
|
||||
#define INLCR 0x0040 /* map NL to CR on input */
|
||||
#define IGNCR 0x0080 /* ignore CR */
|
||||
#define ICRNL 0x0100 /* map CR to NL on input */
|
||||
#define IUCLC 0x0200 /* map uppercase to lowercase on input */
|
||||
#define IXON 0x0400 /* enable start/stop output control */
|
||||
#define IXANY 0x0800 /* any character will restart after stop */
|
||||
#define IXOFF 0x1000 /* enable start/stop input control */
|
||||
#define IMAXBEL 0x2000 /* ring bell on input queue full */
|
||||
#define IUTF8 0x4000 /* input is UTF-8 */
|
||||
|
||||
/* ---- c_oflag: output mode flags ---- */
|
||||
|
||||
#define OPOST 0x0001 /* perform output processing */
|
||||
#define OLCUC 0x0002 /* map lowercase to uppercase on output */
|
||||
#define ONLCR 0x0004 /* map NL to CR-NL on output */
|
||||
#define OCRNL 0x0008 /* map CR to NL on output */
|
||||
#define ONOCR 0x0010 /* no CR output at column 0 */
|
||||
#define ONLRET 0x0020 /* NL performs CR function on output */
|
||||
#define OFILL 0x0040 /* use fill characters for delay */
|
||||
#define OFDEL 0x0080 /* fill is DEL, else NUL */
|
||||
#define NLDLY 0x0100 /* newline delay mask */
|
||||
#define NL0 0x0000
|
||||
#define NL1 0x0100
|
||||
#define CRDLY 0x0600 /* carriage-return delay mask */
|
||||
#define CR0 0x0000
|
||||
#define CR1 0x0200
|
||||
#define CR2 0x0400
|
||||
#define CR3 0x0600
|
||||
#define TABDLY 0x1800 /* horizontal-tab delay mask */
|
||||
#define TAB0 0x0000
|
||||
#define TAB1 0x0800
|
||||
#define TAB2 0x1000
|
||||
#define TAB3 0x1800
|
||||
#define XTABS 0x1800 /* TAB3: expand tabs to spaces */
|
||||
#define BSDLY 0x2000 /* backspace delay mask */
|
||||
#define BS0 0x0000
|
||||
#define BS1 0x2000
|
||||
#define VTDLY 0x4000 /* vertical-tab delay mask */
|
||||
#define VT0 0x0000
|
||||
#define VT1 0x4000
|
||||
#define FFDLY 0x8000 /* form-feed delay mask */
|
||||
#define FF0 0x0000
|
||||
#define FF1 0x8000
|
||||
|
||||
/* ---- c_cflag: control mode flags ---- */
|
||||
|
||||
#define CBAUD 0x100f /* baud-rate mask (includes CBAUDEX) */
|
||||
#define CSIZE 0x0030 /* character-size mask */
|
||||
#define CS5 0x0000 /* 5 bits */
|
||||
#define CS6 0x0010 /* 6 bits */
|
||||
#define CS7 0x0020 /* 7 bits */
|
||||
#define CS8 0x0030 /* 8 bits */
|
||||
#define CSTOPB 0x0040 /* two stop bits, else one */
|
||||
#define CREAD 0x0080 /* enable receiver */
|
||||
#define PARENB 0x0100 /* enable parity */
|
||||
#define PARODD 0x0200 /* odd parity, else even */
|
||||
#define HUPCL 0x0400 /* hang up on last close */
|
||||
#define CLOCAL 0x0800 /* ignore modem status lines */
|
||||
#define CBAUDEX 0x1000 /* extended baud-rate mask (above B38400) */
|
||||
#define CRTSCTS 0x80000000 /* enable RTS/CTS flow control */
|
||||
|
||||
/* ---- c_lflag: local mode flags ---- */
|
||||
|
||||
#define ISIG 0x0001 /* enable signals */
|
||||
#define ICANON 0x0002 /* canonical mode (line-buffered) */
|
||||
#define XCASE 0x0004 /* canonical uppercase/lowercase presentation */
|
||||
#define ECHO 0x0008 /* enable echo */
|
||||
#define ECHOE 0x0010 /* echo erase as backspace */
|
||||
#define ECHOK 0x0020 /* echo KILL */
|
||||
#define ECHONL 0x0040 /* echo NL */
|
||||
#define NOFLSH 0x0080 /* disable flush after interrupt/quit/suspend */
|
||||
#define TOSTOP 0x0100 /* send SIGTTOU on background output */
|
||||
#define ECHOCTL 0x0200 /* echo control characters as ^X */
|
||||
#define ECHOPRT 0x0400 /* echo erase as backspace-print-backspace */
|
||||
#define ECHOKE 0x0800 /* BSD: erase entire line on KILL */
|
||||
#define FLUSHO 0x1000 /* output is being flushed */
|
||||
#define PENDIN 0x4000 /* reprint pending input at next read */
|
||||
#define IEXTEN 0x8000 /* enable implementation-defined input processing */
|
||||
#define EXTPROC 0x10000 /* external processing */
|
||||
|
||||
/* ---- Baud rates (kernel CBAUD encodings — see the header comment) ---- */
|
||||
|
||||
#define B0 0x00000000 /* hang up */
|
||||
#define B50 0x00000001
|
||||
#define B75 0x00000002
|
||||
#define B110 0x00000003
|
||||
#define B134 0x00000004
|
||||
#define B150 0x00000005
|
||||
#define B200 0x00000006
|
||||
#define B300 0x00000007
|
||||
#define B600 0x00000008
|
||||
#define B1200 0x00000009
|
||||
#define B1800 0x0000000a
|
||||
#define B2400 0x0000000b
|
||||
#define B4800 0x0000000c
|
||||
#define B9600 0x0000000d
|
||||
#define B19200 0x0000000e
|
||||
#define B38400 0x0000000f
|
||||
#define B57600 0x00001001 /* CBAUDEX | 1 */
|
||||
#define B115200 0x00001002
|
||||
#define B230400 0x00001003
|
||||
#define B460800 0x00001004
|
||||
#define B500000 0x00001005
|
||||
#define B576000 0x00001006
|
||||
#define B921600 0x00001007
|
||||
#define B1000000 0x00001008
|
||||
#define B1152000 0x00001009
|
||||
#define B1500000 0x0000100a
|
||||
#define B2000000 0x0000100b
|
||||
#define B2500000 0x0000100c
|
||||
#define B3000000 0x0000100d
|
||||
#define B3500000 0x0000100e
|
||||
#define B4000000 0x0000100f
|
||||
|
||||
/* ---- tcsetattr optional_actions ---- */
|
||||
|
||||
#define TCSANOW 0 /* change immediately */
|
||||
#define TCSADRAIN 1 /* change after output drains */
|
||||
#define TCSAFLUSH 2 /* drain output and discard input */
|
||||
|
||||
/* ---- tcflush queue_selector ---- */
|
||||
|
||||
#define TCIFLUSH 0 /* discard data received but not yet read */
|
||||
#define TCOFLUSH 1 /* discard data written but not yet transmitted */
|
||||
#define TCIOFLUSH 2 /* discard both */
|
||||
|
||||
/* ---- tcflow action ---- */
|
||||
|
||||
#define TCOOFF 0 /* suspend output */
|
||||
#define TCOON 1 /* restart suspended output */
|
||||
#define TCIOFF 2 /* transmit a STOP character */
|
||||
#define TCION 3 /* transmit a START character */
|
||||
|
||||
/* ---- Level 1 (POSIX base) ---- */
|
||||
|
||||
/*
|
||||
* Store the terminal attributes of the terminal open on fildes into
|
||||
* *termios_p. Return 0 on success, or -1 with errno set (ENOTTY when
|
||||
* fildes is not a terminal, EBADF on a bad descriptor).
|
||||
*/
|
||||
int
|
||||
tcgetattr(int fildes, struct termios *termios_p);
|
||||
|
||||
/*
|
||||
* Set the terminal attributes of the terminal open on fildes from
|
||||
* *termios_p, applying optional_actions (TCSANOW apply immediately,
|
||||
* TCSADRAIN wait for output to drain, TCSAFLUSH drain and then discard
|
||||
* unread input). Return 0 on success, or -1 with errno set (EINVAL for an
|
||||
* invalid optional_actions, ENOTTY/EBADF as for tcgetattr).
|
||||
*/
|
||||
int
|
||||
tcsetattr(int fildes, int optional_actions, const struct termios *termios_p);
|
||||
|
||||
/*
|
||||
* Transmit a break condition on the terminal open on fildes. The duration
|
||||
* argument is ignored (POSIX leaves a nonzero duration
|
||||
* implementation-defined). Return 0 on success or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
tcsendbreak(int fildes, int duration);
|
||||
|
||||
/*
|
||||
* Wait until all output written to the terminal open on fildes has been
|
||||
* transmitted. Return 0 on success or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
tcdrain(int fildes);
|
||||
|
||||
/*
|
||||
* Discard data written but not yet transmitted (TCOFLUSH), data received
|
||||
* but not yet read (TCIFLUSH), or both (TCIOFLUSH) on the terminal open on
|
||||
* fildes. Return 0 on success or -1 with errno set (EINVAL for an invalid
|
||||
* queue_selector).
|
||||
*/
|
||||
int
|
||||
tcflush(int fildes, int queue_selector);
|
||||
|
||||
/*
|
||||
* Suspend or restart transmission (TCOOFF/TCOON) or transmit a STOP/START
|
||||
* character (TCIOFF/TCION) on the terminal open on fildes. Return 0 on
|
||||
* success or -1 with errno set (EINVAL for an invalid action).
|
||||
*/
|
||||
int
|
||||
tcflow(int fildes, int action);
|
||||
|
||||
/*
|
||||
* Return the input baud rate stored in *termios_p (a B* constant), as
|
||||
* encoded in the c_cflag CIBAUD field, or B0 when the input rate is not
|
||||
* separately stored (the kernel then treats it as equal to the output
|
||||
* rate, and cfsetispeed with B0 selects exactly that).
|
||||
*/
|
||||
speed_t
|
||||
cfgetispeed(const struct termios *termios_p);
|
||||
|
||||
/*
|
||||
* Return the output baud rate stored in *termios_p (a B* constant), as
|
||||
* encoded in the c_cflag CBAUD field.
|
||||
*/
|
||||
speed_t
|
||||
cfgetospeed(const struct termios *termios_p);
|
||||
|
||||
/*
|
||||
* Store speed as the input baud rate of *termios_p. Accept any value that
|
||||
* fits the CBAUD encoding (B0 means "input rate follows the output rate");
|
||||
* an out-of-range speed yields -1 with errno EINVAL.
|
||||
*/
|
||||
int
|
||||
cfsetispeed(struct termios *termios_p, speed_t speed);
|
||||
|
||||
/*
|
||||
* Store speed as the output baud rate of *termios_p. Accept any value that
|
||||
* fits the CBAUD encoding (see cfsetispeed); an out-of-range speed yields
|
||||
* -1 with errno EINVAL.
|
||||
*/
|
||||
int
|
||||
cfsetospeed(struct termios *termios_p, speed_t speed);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/* Level 2 (muslmimic): BSD/glibc extensions and XSI [CX] additions. */
|
||||
|
||||
/*
|
||||
* Set *termios_p to the "raw" mode of the old Version-7 terminal driver:
|
||||
* input available character by character, echoing and all special
|
||||
* processing of input and output characters disabled (clear IGNBRK BRKINT
|
||||
* PARMRK ISTRIP INLCR IGNCR ICRNL IXON, clear OPOST, clear ECHO ECHONL
|
||||
* ICANON ISIG IEXTEN, select CS8 and no parity). Does not report errors.
|
||||
*/
|
||||
void
|
||||
cfmakeraw(struct termios *termios_p);
|
||||
|
||||
/*
|
||||
* Store speed as both the input and the output baud rate of *termios_p
|
||||
* (cfsetospeed then cfsetispeed with the same value). Return 0 on success
|
||||
* or -1 with errno EINVAL for an out-of-range speed.
|
||||
*/
|
||||
int
|
||||
cfsetspeed(struct termios *termios_p, speed_t speed);
|
||||
|
||||
/*
|
||||
* Return the session id of the session that has the terminal open on
|
||||
* fildes as its controlling terminal, or -1 with errno set when fildes is
|
||||
* not a controlling terminal (ENOTTY).
|
||||
*/
|
||||
pid_t
|
||||
tcgetsid(int fildes);
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_TERMIOS_H */
|
||||
+64
-7
@@ -4,15 +4,72 @@
|
||||
/*
|
||||
* vlibc — <tgmath.h>.
|
||||
*
|
||||
* Type-generic math. STUB: the type-generic dispatch over <math.h> is
|
||||
* IMPLEMENTED by the first math todo (#39), not here. This stub exists so
|
||||
* that including <tgmath.h> alongside the other headers compiles cleanly
|
||||
* today; it deliberately defines no type-generic macros until #39 lands
|
||||
* the <math.h> function inventory it dispatches to.
|
||||
* Type-generic math (C23 7.25). Each macro inspects the type of its
|
||||
* controlling expression and dispatches to the matching precision of the
|
||||
* real <math.h> function: a float argument selects the `...f` variant, a
|
||||
* long double argument the `...l` variant, and everything else (double,
|
||||
* any integer type, and the _Complex types, whose imaginary part is
|
||||
* dropped by the usual argument conversion) the unsuffixed double
|
||||
* variant. The controlling expression is the first floating argument, so
|
||||
* the two-argument forms dispatch on x and require y to be convertible to
|
||||
* x's type.
|
||||
*
|
||||
* This header is ISO C core and is present in every profile.
|
||||
* This header is ISO C core and is present in every profile. The
|
||||
* classification macros (fpclassify/isnan/isinf/isfinite/isnormal/signbit)
|
||||
* need no dispatch of their own — <math.h> already defines them over
|
||||
* GCC's type-generic builtins, so including <math.h> below makes them
|
||||
* work unchanged for every real and complex type.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
#include <math.h>
|
||||
|
||||
/* fabs */
|
||||
#define fabs(x) _Generic((x), float: fabsf, long double: fabsl, default: fabs)(x)
|
||||
|
||||
/* copysign */
|
||||
#define copysign(x, y) \
|
||||
_Generic((x), float: copysignf, long double: copysignl, default: copysign)(x, y)
|
||||
|
||||
/* rounding toward zero / ±Inf / current mode (floor, ceil, trunc, round,
|
||||
* rint, nearbyint) */
|
||||
#define floor(x) _Generic((x), float: floorf, long double: floorl, default: floor)(x)
|
||||
#define ceil(x) _Generic((x), float: ceilf, long double: ceill, default: ceil)(x)
|
||||
#define trunc(x) _Generic((x), float: truncf, long double: truncl, default: trunc)(x)
|
||||
#define round(x) _Generic((x), float: roundf, long double: roundl, default: round)(x)
|
||||
#define rint(x) _Generic((x), float: rintf, long double: rintl, default: rint)(x)
|
||||
#define nearbyint(x) \
|
||||
_Generic((x), float: nearbyintf, long double: nearbyintl, default: nearbyint)(x)
|
||||
|
||||
/* integer-returning rounding (lrint, llrint, lround, llround) */
|
||||
#define lrint(x) _Generic((x), float: lrintf, long double: lrintl, default: lrint)(x)
|
||||
#define llrint(x) _Generic((x), float: llrintf, long double: llrintl, default: llrint)(x)
|
||||
#define lround(x) _Generic((x), float: lroundf, long double: lroundl, default: lround)(x)
|
||||
#define llround(x) _Generic((x), float: llroundf, long double: llroundl, default: llround)(x)
|
||||
|
||||
/* fraction/exponent splitting (frexp, modf) and exponent scaling (ldexp,
|
||||
* scalbn, scalbln): the second argument is an int/long or a pointer to
|
||||
* the first argument's type and follows x's precision automatically */
|
||||
#define frexp(x, e) _Generic((x), float: frexpf, long double: frexpl, default: frexp)(x, e)
|
||||
#define modf(x, i) _Generic((x), float: modff, long double: modfl, default: modf)(x, i)
|
||||
#define ldexp(x, n) _Generic((x), float: ldexpf, long double: ldexpl, default: ldexp)(x, n)
|
||||
#define scalbn(x, n) _Generic((x), float: scalbnf, long double: scalbnl, default: scalbn)(x, n)
|
||||
#define scalbln(x, n) _Generic((x), float: scalblnf, long double: scalblnl, default: scalbln)(x, n)
|
||||
|
||||
/* minimum/maximum/difference */
|
||||
#define fmin(x, y) _Generic((x), float: fminf, long double: fminl, default: fmin)(x, y)
|
||||
#define fmax(x, y) _Generic((x), float: fmaxf, long double: fmaxl, default: fmax)(x, y)
|
||||
#define fdim(x, y) _Generic((x), float: fdimf, long double: fdiml, default: fdim)(x, y)
|
||||
|
||||
/* remainder (fmod, remainder, remquo: the trailing int* follows x's
|
||||
* precision) */
|
||||
#define fmod(x, y) _Generic((x), float: fmodf, long double: fmodl, default: fmod)(x, y)
|
||||
#define remainder(x, y) \
|
||||
_Generic((x), float: remainderf, long double: remainderl, default: remainder)(x, y)
|
||||
#define remquo(x, y, q) \
|
||||
_Generic((x), float: remquof, long double: remquol, default: remquo)(x, y, q)
|
||||
|
||||
/* exponent reading (ilogb returns int, logb returns x's precision) */
|
||||
#define ilogb(x) _Generic((x), float: ilogbf, long double: ilogbl, default: ilogb)(x)
|
||||
#define logb(x) _Generic((x), float: logbf, long double: logbl, default: logb)(x)
|
||||
|
||||
#endif /* VLIBC_TGMATH_H */
|
||||
|
||||
+255
@@ -0,0 +1,255 @@
|
||||
#ifndef VLIBC_TIME_H
|
||||
#define VLIBC_TIME_H
|
||||
|
||||
/*
|
||||
* vlibc — <time.h>.
|
||||
*
|
||||
* Calendar time, clock ids, and interval timers. The ISO C functions
|
||||
* (time, clock, timespec_get, difftime, asctime/ctime/mktime/gmtime/
|
||||
* localtime, strftime) and the POSIX clock_* / timer_* / sleep family are
|
||||
* thin, level-gated wrappers over the Linux time syscalls.
|
||||
*
|
||||
* Level 1 (onlyposix): time, clock, timespec_get, difftime,
|
||||
* clock_gettime/settime/getres/getcpuclockid,
|
||||
* clock_nanosleep, nanosleep, sleep, and the
|
||||
* broken-down-time/formatting declarations
|
||||
* (mktime, gmtime/localtime and _r forms, asctime,
|
||||
* ctime, strftime, tzset).
|
||||
* Level 2 (muslmimic): POSIX interval timers (timer_create/delete/
|
||||
* settime/gettime/getoverrun) and the XSI
|
||||
* conveniences (timegm, strftime_l, usleep,
|
||||
* asctime_r, ctime_r, strptime, tzname/daylight/
|
||||
* timezone).
|
||||
*
|
||||
* The scalar time types (time_t, clock_t, clockid_t, pid_t, timer_t and
|
||||
* useconds_t) come from <sys/types.h>, which this header includes; struct
|
||||
* timespec and struct tm are defined here. timer_t is the <sys/types.h>
|
||||
* opaque pointer type (kernel timer ids are returned through it); struct
|
||||
* sigevent and struct itimerspec stay forward-declared here — their kernel
|
||||
* ABI mirrors are private to the timer implementation (see src/time/
|
||||
* timer.c) and to consumers that define matching structs.
|
||||
*
|
||||
* The clock ids and TIMER_ABSTIME mirror the kernel ABI (x86_64): the
|
||||
* CLOCK_* values are passed to SYS_clock_* and SYS_timer_* unchanged, and
|
||||
* TIMER_ABSTIME is the timer_settime/clock_nanosleep flag bit.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Base argument of timespec_get(); the only base defined by ISO C. */
|
||||
#define TIME_UTC 1
|
||||
|
||||
/* Ticks per second reported by clock() (vlibc always reports 1 MHz). */
|
||||
#define CLOCKS_PER_SEC 1000000
|
||||
|
||||
/* Clock ids (kernel ABI; SYS_clock_* argument). */
|
||||
#define CLOCK_REALTIME 0 /* wall clock since the Epoch */
|
||||
#define CLOCK_MONOTONIC 1 /* monotonic since boot */
|
||||
#define CLOCK_PROCESS_CPUTIME_ID 2 /* CPU time of this process */
|
||||
#define CLOCK_THREAD_CPUTIME_ID 3 /* CPU time of this thread */
|
||||
|
||||
/* Flag for absolute (vs relative) timer expiry. */
|
||||
#define TIMER_ABSTIME 1
|
||||
|
||||
/*
|
||||
* Elapsed time, seconds + nanoseconds. Kernel ABI layout (x86_64): the
|
||||
* wrappers pass this straight to the clock/nanosleep/timer syscalls.
|
||||
*/
|
||||
struct timespec
|
||||
{
|
||||
time_t tv_sec; /* seconds */
|
||||
long tv_nsec; /* nanoseconds (0..999999999) */
|
||||
};
|
||||
|
||||
/*
|
||||
* Broken-down civil time. tm_gmtoff and tm_zone are the XSI extensions
|
||||
* (seconds east of UTC / timezone name) carried for glibc compatibility.
|
||||
*/
|
||||
struct tm
|
||||
{
|
||||
int tm_sec; /* seconds after the minute (0..60) */
|
||||
int tm_min; /* minutes after the hour (0..59) */
|
||||
int tm_hour; /* hours since midnight (0..23) */
|
||||
int tm_mday; /* day of the month (1..31) */
|
||||
int tm_mon; /* months since January (0..11) */
|
||||
int tm_year; /* years since 1900 */
|
||||
int tm_wday; /* days since Sunday (0..6) */
|
||||
int tm_yday; /* days since January 1 (0..365) */
|
||||
int tm_isdst; /* daylight-saving flag */
|
||||
long tm_gmtoff; /* seconds east of UTC */
|
||||
const char *tm_zone; /* timezone abbreviation */
|
||||
};
|
||||
|
||||
/* Return the current wall-clock time in seconds; if t is not NULL store it
|
||||
* there too, and return (time_t)-1 with errno set on failure. */
|
||||
time_t
|
||||
time(time_t *t);
|
||||
|
||||
/* Approximate processor time in CLOCKS_PER_SEC units since an arbitrary
|
||||
* epoch, or (clock_t)-1 with errno set on failure. */
|
||||
clock_t
|
||||
clock(void);
|
||||
|
||||
/* Store the current time for base into ts and return base; return 0 if
|
||||
* base is not TIME_UTC or the clock cannot be read. */
|
||||
int
|
||||
timespec_get(struct timespec *ts, int base);
|
||||
|
||||
/* Read the given clock into tp. Return 0 or -1 with errno set. */
|
||||
int
|
||||
clock_gettime(clockid_t clockid, struct timespec *tp);
|
||||
|
||||
/* Set the given clock from tp. Return 0 or -1 with errno set. */
|
||||
int
|
||||
clock_settime(clockid_t clockid, const struct timespec *tp);
|
||||
|
||||
/* Resolution of the given clock; store it in res when res is not NULL.
|
||||
* Return 0 or -1 with errno set. */
|
||||
int
|
||||
clock_getres(clockid_t clockid, struct timespec *res);
|
||||
|
||||
/* Clock id measuring the CPU time of the given process (0 = calling
|
||||
* process). Return 0, or -1 with errno set when the process is unknown. */
|
||||
int
|
||||
clock_getcpuclockid(pid_t pid, clockid_t *clock_id);
|
||||
|
||||
/* Sleep on the given clock. Without TIMER_ABSTIME request is relative and
|
||||
* the sleep restarts from remain on signal interruption. */
|
||||
int
|
||||
clock_nanosleep(clockid_t clockid, int flags, const struct timespec *request,
|
||||
struct timespec *remain);
|
||||
|
||||
/* Sleep request seconds; when interrupted and remain is not NULL the sleep
|
||||
* restarts from the remaining time. Return 0 or -1 with errno set. */
|
||||
int
|
||||
nanosleep(const struct timespec *request, struct timespec *remain);
|
||||
|
||||
/* Sleep seconds (whole seconds; signals can shorten the sleep, which is
|
||||
* then resumed). Return the unslept seconds, normally 0. */
|
||||
unsigned int
|
||||
sleep(unsigned int seconds);
|
||||
|
||||
/* Convert broken-down civil time back to calendar seconds. */
|
||||
time_t
|
||||
mktime(struct tm *tm);
|
||||
|
||||
/* Broken-down local time; localtime uses an internal static buffer.
|
||||
* Return NULL on error. */
|
||||
struct tm *
|
||||
localtime(const time_t *timer);
|
||||
|
||||
/* Broken-down local time into buf. Return buf or NULL on error. */
|
||||
struct tm *
|
||||
localtime_r(const time_t *timer, struct tm *buf);
|
||||
|
||||
/* Broken-down UTC; gmtime uses an internal static buffer. */
|
||||
struct tm *
|
||||
gmtime(const time_t *timer);
|
||||
|
||||
/* Broken-down UTC into buf. Return buf or NULL on error. */
|
||||
struct tm *
|
||||
gmtime_r(const time_t *timer, struct tm *buf);
|
||||
|
||||
/* Fixed-format rendering of broken-down time ("Sun Sep 16 01:03:52 1973"). */
|
||||
char *
|
||||
asctime(const struct tm *tm);
|
||||
|
||||
/* asctime(localtime(timer)); fixed-format wall-clock rendering. */
|
||||
char *
|
||||
ctime(const time_t *timer);
|
||||
|
||||
/* Format broken-down time per format into s (at most maxsize bytes);
|
||||
* return the bytes written (0 when the buffer was too small). */
|
||||
size_t
|
||||
strftime(char *restrict s, size_t maxsize, const char *restrict format,
|
||||
const struct tm *restrict timeptr);
|
||||
|
||||
/* Seconds between two calendar times (b subtracted from a). */
|
||||
double
|
||||
difftime(time_t a, time_t b);
|
||||
|
||||
/* Establish the local timezone from TZ / the system default. */
|
||||
void
|
||||
tzset(void);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): POSIX interval timers and XSI conveniences. */
|
||||
|
||||
/* Opaque kernel-ABI structures; consumers define layout-compatible structs
|
||||
* (see src/time/timer.c). */
|
||||
struct sigevent;
|
||||
struct itimerspec;
|
||||
|
||||
/* Create a per-process interval timer on clockid; the timer id is stored
|
||||
* through timerid. Return 0 or -1 with errno set. */
|
||||
int
|
||||
timer_create(clockid_t clockid, const struct sigevent *restrict evp, timer_t *restrict timerid);
|
||||
|
||||
/* Destroy the given timer. Return 0 or -1 with errno set. */
|
||||
int
|
||||
timer_delete(timer_t timerid);
|
||||
|
||||
/* Arm/disarm the timer; when old_value is not NULL the previous setting is
|
||||
* stored there. Return 0 or -1 with errno set. */
|
||||
int
|
||||
timer_settime(timer_t timerid, int flags, const struct itimerspec *restrict new_value,
|
||||
struct itimerspec *restrict old_value);
|
||||
|
||||
/* Remaining time until the timer expires (and its interval). Return 0 or
|
||||
* -1 with errno set. */
|
||||
int
|
||||
timer_gettime(timer_t timerid, struct itimerspec *curr_value);
|
||||
|
||||
/* Number of timer expirations between the most recent signal delivery and
|
||||
* the one before it; -1 with errno set when there was no pending delivery. */
|
||||
int
|
||||
timer_getoverrun(timer_t timerid);
|
||||
|
||||
/* mktime in UTC rather than local time. */
|
||||
time_t
|
||||
timegm(struct tm *tm);
|
||||
|
||||
/* strftime with an explicit locale object (locale_t is not yet defined;
|
||||
* the locale argument is an opaque pointer). */
|
||||
size_t
|
||||
strftime_l(char *restrict s, size_t maxsize, const char *restrict format,
|
||||
const struct tm *restrict timeptr, void *loc);
|
||||
|
||||
/* Microsecond sleep (whole microseconds; signals resume the sleep).
|
||||
* Return 0 or the unslept microseconds with errno set. */
|
||||
useconds_t
|
||||
usleep(useconds_t usec);
|
||||
|
||||
/* asctime into a caller-provided buffer of at least 26 bytes. */
|
||||
char *
|
||||
asctime_r(const struct tm *tm, char *buf);
|
||||
|
||||
/* ctime into a caller-provided buffer of at least 26 bytes. */
|
||||
char *
|
||||
ctime_r(const time_t *timer, char *buf);
|
||||
|
||||
/* Parse format from s into broken-down time; return the first character
|
||||
* not consumed, or NULL when the format did not match. */
|
||||
char *
|
||||
strptime(const char *restrict s, const char *restrict format, struct tm *restrict tm);
|
||||
|
||||
/* Timezone state maintained by tzset(). */
|
||||
extern char *tzname[2];
|
||||
extern int daylight;
|
||||
extern long timezone;
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_TIME_H */
|
||||
@@ -0,0 +1,816 @@
|
||||
#ifndef VLIBC_UNISTD_H
|
||||
#define VLIBC_UNISTD_H
|
||||
|
||||
/*
|
||||
* vlibc — <unistd.h>.
|
||||
*
|
||||
* File descriptors, file I/O, and the access/whence symbolic constants
|
||||
* (POSIX.1-2008). Every function here is an unbuffered pass-through to the
|
||||
* kernel: failures are reported as -1 (or the fd/offset on success) with
|
||||
* errno set by the syscall layer.
|
||||
*
|
||||
* Level 1 (onlyposix): read, write, pread, pwrite, open, openat, close,
|
||||
* lseek, dup, dup2, pipe, fsync, fdatasync, ftruncate,
|
||||
* sync, access, faccessat.
|
||||
* Level 2 (muslmimic): dup3, pipe2 (Linux extensions), truncate (XSI),
|
||||
* lseek64 (glibc LFS alias of lseek on x86_64).
|
||||
*
|
||||
* Process control (todo 20) is declared further down:
|
||||
*
|
||||
* Level 1 (onlyposix): fork, exec family (execl/execlp/execle/execv/
|
||||
* execvp/execve/fexecve), getpid, getppid, getuid,
|
||||
* geteuid, getgid, getegid, setuid, seteuid, setgid,
|
||||
* setegid, getgroups, setpgid, getpgrp, setsid.
|
||||
* Level 2 (muslmimic): vfork, setpgrp (obsolescent), setgroups, getpgid,
|
||||
* getsid, tcgetpgrp, tcsetpgrp (XSI).
|
||||
*
|
||||
* System limits and options (todo 32) are declared after the process
|
||||
* block:
|
||||
*
|
||||
* Level 1 (onlyposix): sysconf, pathconf, fpathconf, the _SC_* and _PC_*
|
||||
* configuration keys.
|
||||
* Level 2 (muslmimic): gethostname, sethostname, gethostid (XSI/BSD
|
||||
* system identification — NOT POSIX.1-2008 base),
|
||||
* and the _CS_* keys for confstr().
|
||||
*
|
||||
* The current-working-directory, link/symlink/readlink, path-removal and
|
||||
* *at operations, the terminal-identity and login functions, getopt and
|
||||
* confstr (todo 38) come after the system-limits block:
|
||||
*
|
||||
* Level 1 (onlyposix): getcwd, chdir, fchdir, readlink, symlink, link,
|
||||
* unlink, rmdir, linkat, readlinkat, renameat,
|
||||
* symlinkat, unlinkat, isatty, ttyname, ttyname_r,
|
||||
* getlogin, getlogin_r, getopt plus the optarg/
|
||||
* optind/opterr/optopt globals, and confstr (its
|
||||
* _CS_* key constants are level 2, above).
|
||||
* Level 2 (muslmimic): getwd (obsolescent), chroot, swab (XSI).
|
||||
* basename/dirname live in <libgen.h> and realpath
|
||||
* in <stdlib.h> (their POSIX homes), also XSI.
|
||||
*
|
||||
* uname() and struct utsname live in <sys/utsname.h> (todo 32).
|
||||
*
|
||||
* The open-flag constants (O_RDONLY, O_CREAT, O_CLOEXEC, ...) belong to
|
||||
* <fcntl.h> and are deliberately not defined here; the oflag arguments below
|
||||
* are plain int and take their values from that header. The optional mode
|
||||
* argument of open/openat is a mode_t supplied only when oflag contains
|
||||
* O_CREAT or O_TMPFILE.
|
||||
*
|
||||
* None of these declarations carry an intent attribute: every function
|
||||
* performs I/O with side effects and reports failures through errno, so
|
||||
* const/pure would be unsound. The process functions additionally must not
|
||||
* be const/pure because their results are process state — marking getpid
|
||||
* const, for example, would let the compiler hoist it across fork() and
|
||||
* observe the parent's pid inside the child.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Level 1 (POSIX base). */
|
||||
|
||||
/* Access-check modes for access() and faccessat(). */
|
||||
#define F_OK 0 /* existence only */
|
||||
#define X_OK 1 /* execute (search for a directory) */
|
||||
#define W_OK 2 /* write */
|
||||
#define R_OK 4 /* read */
|
||||
|
||||
/* whence values for lseek() and lseek64() (also defined by <stdio.h>). */
|
||||
#define SEEK_SET 0 /* from the beginning of the file */
|
||||
#define SEEK_CUR 1 /* from the current position */
|
||||
#define SEEK_END 2 /* from the end of the file */
|
||||
|
||||
/*
|
||||
* Read up to nbyte bytes from fildes into buf and return the number of
|
||||
* bytes read, 0 at end of file, or -1 with errno set on error. Unbuffered.
|
||||
*/
|
||||
ssize_t
|
||||
read(int fildes, void *buf, size_t nbyte);
|
||||
|
||||
/*
|
||||
* Write up to nbyte bytes from buf to fildes and return the number of
|
||||
* bytes written, or -1 with errno set on error. Unbuffered.
|
||||
*/
|
||||
ssize_t
|
||||
write(int fildes, const void *buf, size_t nbyte);
|
||||
|
||||
/*
|
||||
* Read nbyte bytes from fildes starting at offset, without changing the
|
||||
* file position; return the number of bytes read, or -1 with errno set.
|
||||
*/
|
||||
ssize_t
|
||||
pread(int fildes, void *buf, size_t nbyte, off_t offset);
|
||||
|
||||
/*
|
||||
* Write nbyte bytes from buf to fildes starting at offset, without
|
||||
* changing the file position; return the number of bytes written, or -1
|
||||
* with errno set.
|
||||
*/
|
||||
ssize_t
|
||||
pwrite(int fildes, const void *buf, size_t nbyte, off_t offset);
|
||||
|
||||
/*
|
||||
* Open path with the oflag access mode (from <fcntl.h>) and return a file
|
||||
* descriptor, or -1 with errno set. A mode argument is required — and read
|
||||
* from the varargs — only when oflag contains O_CREAT or O_TMPFILE.
|
||||
*/
|
||||
int
|
||||
open(const char *path, int oflag, ...);
|
||||
|
||||
/*
|
||||
* Like open(), but path is relative to the directory named by fd (use
|
||||
* AT_FDCWD from <fcntl.h> for the current working directory). The mode
|
||||
* varargs rule is the same as open().
|
||||
*/
|
||||
int
|
||||
openat(int fd, const char *path, int oflag, ...);
|
||||
|
||||
/*
|
||||
* Close the file descriptor fildes; return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
close(int fildes);
|
||||
|
||||
/*
|
||||
* Reposition the file offset of fildes to offset per whence (SEEK_SET,
|
||||
* SEEK_CUR, SEEK_END) and return the resulting offset, or (off_t)-1 with
|
||||
* errno set. The full 64-bit offset is returned; errno is untouched on
|
||||
* success.
|
||||
*/
|
||||
off_t
|
||||
lseek(int fildes, off_t offset, int whence);
|
||||
|
||||
/*
|
||||
* Duplicate fildes to the lowest-numbered free descriptor; return it, or
|
||||
* -1 with errno set. The copy shares the file description (position,
|
||||
* flags, locks) with the original.
|
||||
*/
|
||||
int
|
||||
dup(int fildes);
|
||||
|
||||
/*
|
||||
* Duplicate fildes onto fildes2, closing fildes2 first if it was open;
|
||||
* return fildes2, or -1 with errno set. dup2(f, f) returns f without
|
||||
* doing anything (POSIX).
|
||||
*/
|
||||
int
|
||||
dup2(int fildes, int fildes2);
|
||||
|
||||
/*
|
||||
* Create a pipe: fildes[0] becomes the read end, fildes[1] the write end.
|
||||
* Return 0, or -1 with errno set. No descriptor flags are set (unlike
|
||||
* pipe2, this is plain POSIX).
|
||||
*/
|
||||
int
|
||||
pipe(int fildes[2]);
|
||||
|
||||
/*
|
||||
* Flush all buffered modifications of fildes and its metadata to stable
|
||||
* storage; return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
fsync(int fildes);
|
||||
|
||||
/*
|
||||
* Like fsync(), but may skip the metadata work needed only to preserve
|
||||
* file contents; return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
fdatasync(int fildes);
|
||||
|
||||
/*
|
||||
* Truncate fildes to length bytes; return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
ftruncate(int fildes, off_t length);
|
||||
|
||||
/*
|
||||
* Flush all filesystem caches to stable storage. Returns nothing.
|
||||
*/
|
||||
void
|
||||
sync(void);
|
||||
|
||||
/*
|
||||
* Check accessibility of path under amode (R_OK, W_OK, X_OK, F_OK); return
|
||||
* 0, or -1 with errno set. Uses the real IDs of the calling process.
|
||||
*/
|
||||
int
|
||||
access(const char *path, int amode);
|
||||
|
||||
/*
|
||||
* Like access(), but path is relative to the directory named by fd (use
|
||||
* AT_FDCWD for the current working directory) and flag may hold
|
||||
* AT_EACCESS; return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
faccessat(int fd, const char *path, int amode, int flag);
|
||||
|
||||
/* Level 1 (POSIX base): process control. */
|
||||
|
||||
/*
|
||||
* Create a child process that is a copy of the caller: fork() returns 0
|
||||
* in the child, the child's pid in the parent, and -1 with errno set on
|
||||
* failure.
|
||||
*
|
||||
* Around the fork, the atfork hook table runs: the prepare handlers in
|
||||
* reverse registration order before the fork, then the child handlers in
|
||||
* the child and the parent handlers in the parent, both in registration
|
||||
* order (POSIX pthread_atfork protocol). The table is registered by
|
||||
* pthread_atfork() (todo 45) and is empty — a no-op — in a process
|
||||
* without threads.
|
||||
*/
|
||||
pid_t
|
||||
fork(void);
|
||||
|
||||
/*
|
||||
* Replace the calling process image. Success never returns; -1 with
|
||||
* errno set otherwise. execl/execle/execlp take the arguments as a
|
||||
* varargs list terminated by (char *)NULL (execle is followed by one
|
||||
* final char *const envp[] argument); execv/execvp take an argv array.
|
||||
* execvp and execlp search the PATH environment variable (default
|
||||
* "/bin:/usr/bin") when file contains no '/'. execv/execvp/execl/execlp
|
||||
* use the caller's environment; execve/execle/fexecve take envp.
|
||||
*/
|
||||
// NOLINTBEGIN(bugprone-easily-swappable-parameters)
|
||||
int
|
||||
execl(const char *path, const char *arg0, ...);
|
||||
|
||||
int
|
||||
execle(const char *path, const char *arg0, ...);
|
||||
|
||||
int
|
||||
execlp(const char *file, const char *arg0, ...);
|
||||
// NOLINTEND(bugprone-easily-swappable-parameters)
|
||||
|
||||
int
|
||||
execv(const char *path, char *const argv[]);
|
||||
|
||||
int
|
||||
execvp(const char *file, char *const argv[]);
|
||||
|
||||
int
|
||||
execve(const char *path, char *const argv[], char *const envp[]);
|
||||
|
||||
/*
|
||||
* Like execve, but the image is the open descriptor fd.
|
||||
*/
|
||||
int
|
||||
fexecve(int fd, char *const argv[], char *const envp[]);
|
||||
|
||||
/*
|
||||
* Process ids. getpid/getppid/getuid/geteuid/getgid/getegid read the
|
||||
* kernel's per-process ids; setuid/seteuid/setgid/setegid change them.
|
||||
* seteuid/setegid change only the effective id (setresuid/setresgid
|
||||
* underneath: Linux has no seteuid/setegid syscall).
|
||||
*/
|
||||
pid_t
|
||||
getpid(void);
|
||||
|
||||
pid_t
|
||||
getppid(void);
|
||||
|
||||
uid_t
|
||||
getuid(void);
|
||||
|
||||
uid_t
|
||||
geteuid(void);
|
||||
|
||||
gid_t
|
||||
getgid(void);
|
||||
|
||||
gid_t
|
||||
getegid(void);
|
||||
|
||||
int
|
||||
setuid(uid_t uid);
|
||||
|
||||
int
|
||||
seteuid(uid_t euid);
|
||||
|
||||
int
|
||||
setgid(gid_t gid);
|
||||
|
||||
int
|
||||
setegid(gid_t egid);
|
||||
|
||||
/*
|
||||
* Supplementary groups: getgroups fills grouplist with up to gidsetsize
|
||||
* group ids and returns the total count (0 can be passed to count only);
|
||||
* setgroups (level 2, XSI) installs the list.
|
||||
*/
|
||||
int
|
||||
getgroups(int gidsetsize, gid_t grouplist[]);
|
||||
|
||||
/*
|
||||
* Session control: setpgid moves pid into process group pgid (0 means
|
||||
* the caller / the caller's pid); getpgrp returns the caller's process
|
||||
* group; setsid creates a new session with the caller as leader and
|
||||
* returns the new session id.
|
||||
*/
|
||||
int
|
||||
setpgid(pid_t pid, pid_t pgid);
|
||||
|
||||
pid_t
|
||||
getpgrp(void);
|
||||
|
||||
pid_t
|
||||
setsid(void);
|
||||
|
||||
/* Level 1 (POSIX base): system limits and options. */
|
||||
|
||||
/*
|
||||
* The configuration keys for sysconf(), pathconf() and fpathconf(), and
|
||||
* (level 2) the _CS_* keys for confstr() (todo 38).
|
||||
*
|
||||
* POSIX defines the _SC_* and _PC_* names and the SEMANTICS of the values
|
||||
* the configuration functions return for them, but NOT their numeric
|
||||
* values: those are private to each C library (glibc numbers them
|
||||
* arbitrarily in <bits/confname.h>, musl in a private table). The values
|
||||
* below are vlibc's OWN stable ABI, assigned in declaration order and never
|
||||
* reused — program source must pass the symbolic constants, never literal
|
||||
* integers. They are deliberately NOT glibc's numbers. Any value outside
|
||||
* these enums is unknown to the configuration functions and yields -1
|
||||
* without errno (the unsupported-key convention, see below).
|
||||
*/
|
||||
|
||||
/* NOLINTBEGIN(bugprone-reserved-identifier) -- POSIX-mandated _SC_/_PC_ names */
|
||||
enum
|
||||
{
|
||||
/* Process and file limits (sysconf). */
|
||||
_SC_ARG_MAX = 0, /* max bytes of arguments + environment for exec */
|
||||
_SC_CHILD_MAX, /* max simultaneous processes per real user id */
|
||||
_SC_CLK_TCK, /* clock ticks per second, the times() unit (100) */
|
||||
_SC_NGROUPS_MAX, /* max supplementary group ids */
|
||||
_SC_OPEN_MAX, /* max open file descriptors per process */
|
||||
_SC_PAGESIZE, /* memory page size in bytes (from AT_PAGESZ) */
|
||||
_SC_PAGE_SIZE, /* POSIX synonym of _SC_PAGESIZE */
|
||||
_SC_NPROCESSORS_CONF, /* number of processors configured */
|
||||
_SC_NPROCESSORS_ONLN, /* number of processors online */
|
||||
_SC_PHYS_PAGES, /* number of physical memory pages */
|
||||
_SC_AVPHYS_PAGES, /* number of currently available memory pages */
|
||||
_SC_SYMLOOP_MAX, /* max symlink traversals while resolving a path */
|
||||
_SC_STREAM_MAX, /* max open standard I/O streams (FOPEN_MAX) */
|
||||
_SC_TZNAME_MAX, /* max bytes in a timezone name */
|
||||
_SC_VERSION, /* POSIX.1 version, 200809L */
|
||||
_SC_2_VERSION, /* POSIX.2 version, 200809L */
|
||||
_SC_JOB_CONTROL, /* 1 if job control is supported */
|
||||
_SC_SAVED_IDS, /* 1 if saved set-user/group-id is supported */
|
||||
_SC_HOST_NAME_MAX, /* max bytes in a host name (excluding NUL) */
|
||||
_SC_LOGIN_NAME_MAX, /* max bytes in a login name */
|
||||
_SC_GETPW_R_SIZE_MAX, /* recommended buffer size for getpw*_r */
|
||||
_SC_IOV_MAX, /* max iovec entries in readv/writev */
|
||||
_SC_LINE_MAX, /* max bytes in an input line (POSIX.2) */
|
||||
_SC_RE_DUP_MAX, /* max duplicate regexp interval counts */
|
||||
_SC_COLL_WEIGHTS_MAX, /* max weights for collating elements */
|
||||
_SC_EXPR_NEST_MAX, /* max expression nesting in expr(1) */
|
||||
|
||||
/* POSIX option indicators (sysconf). Values follow the _POSIX_*
|
||||
* convention: a version value means supported, -1 unsupported. */
|
||||
_SC_THREADS, /* _POSIX_THREADS */
|
||||
_SC_SEMAPHORES, /* _POSIX_SEMAPHORES */
|
||||
_SC_SHARED_MEMORY_OBJECTS, /* _POSIX_SHARED_MEMORY_OBJECTS */
|
||||
_SC_MESSAGE_PASSING, /* _POSIX_MESSAGE_PASSING */
|
||||
_SC_MONOTONIC_CLOCK, /* _POSIX_MONOTONIC_CLOCK */
|
||||
_SC_TIMERS, /* _POSIX_TIMERS */
|
||||
_SC_SYNCHRONIZED_IO, /* _POSIX_SYNCHRONIZED_IO */
|
||||
_SC_THREAD_SAFE_FUNCTIONS, /* _POSIX_THREAD_SAFE_FUNCTIONS */
|
||||
_SC_THREAD_ATTR_STACKADDR, /* _POSIX_THREAD_ATTR_STACKADDR */
|
||||
_SC_THREAD_ATTR_STACKSIZE, /* _POSIX_THREAD_ATTR_STACKSIZE */
|
||||
_SC_THREAD_PRIORITY_SCHEDULING, /* _POSIX_THREAD_PRIORITY_SCHEDULING */
|
||||
_SC_THREAD_PRIO_INHERIT, /* _POSIX_THREAD_PRIO_INHERIT */
|
||||
_SC_THREAD_PRIO_PROTECT, /* _POSIX_THREAD_PRIO_PROTECT */
|
||||
_SC_THREAD_PROCESS_SHARED, /* _POSIX_THREAD_PROCESS_SHARED */
|
||||
_SC_REALTIME_SIGNALS, /* _POSIX_REALTIME_SIGNALS */
|
||||
|
||||
/* POSIX.1-2008 option indicators that use plain 1 for supported. */
|
||||
_SC_REGEXP, /* 1 if the RE functions are supported */
|
||||
_SC_SHELL, /* 1 if the shell is supported */
|
||||
_SC_XOPEN_VERSION, /* XSI version, 700 */
|
||||
_SC_XOPEN_UNIX, /* 1 if XSI interfaces are supported */
|
||||
_SC_XOPEN_CRYPT, /* 1 if the crypt function is available */
|
||||
_SC_XOPEN_ENH_I18N, /* 1 if enhanced internationalization is present */
|
||||
_SC_XOPEN_SHM, /* 1 if XSI shared memory is present */
|
||||
_SC_2_C_BIND, /* POSIX.2 C-language binding */
|
||||
_SC_2_C_DEV, /* POSIX.2 C-language development utilities */
|
||||
_SC_2_FORT_DEV, /* POSIX.2 FORTRAN development utilities */
|
||||
_SC_2_FORT_RUN, /* POSIX.2 FORTRAN runtime utilities */
|
||||
_SC_2_LOCALEDEF, /* POSIX.2 locale creation utilities */
|
||||
_SC_2_SW_DEV, /* POSIX.2 software development utilities */
|
||||
_SC_2_UPE /* POSIX.2 user-portability utilities */
|
||||
};
|
||||
|
||||
/*
|
||||
* The _PC_* keys for pathconf() and fpathconf(). Each names a limit or an
|
||||
* option of the FILESYSTEM holding the queried path or descriptor; see the
|
||||
* pathconf() documentation below for the supported keys and their values.
|
||||
*/
|
||||
enum
|
||||
{
|
||||
_PC_LINK_MAX = 0, /* max link count of a file */
|
||||
_PC_MAX_CANON, /* max bytes in a terminal canonical input line */
|
||||
_PC_MAX_INPUT, /* max bytes available on a terminal input queue */
|
||||
_PC_NAME_MAX, /* max bytes in a file name (excluding NUL) */
|
||||
_PC_PATH_MAX, /* max bytes in a pathname (excluding NUL) */
|
||||
_PC_PIPE_BUF, /* max bytes atomically writable to a pipe */
|
||||
_PC_CHOWN_RESTRICTED, /* 1 if chown is restricted to the owner */
|
||||
_PC_NO_TRUNC, /* 1 if overlong file names are an error */
|
||||
_PC_VDISABLE, /* the terminal disable character (0 = none) */
|
||||
_PC_ASYNC_IO, /* 200809L if asynchronous I/O is supported */
|
||||
_PC_SYNC_IO, /* 200809L if synchronized I/O is supported */
|
||||
_PC_PRIO_IO, /* 200809L if prioritized I/O is supported */
|
||||
_PC_SOCK_MAXBUF, /* max bytes storable in a socket buffer */
|
||||
_PC_FILESIZEBITS, /* bits in a file size field */
|
||||
_PC_REC_INCR_XFER_SIZE, /* recommended increment for transfer sizes */
|
||||
_PC_REC_MAX_XFER_SIZE, /* recommended maximum transfer size */
|
||||
_PC_REC_MIN_XFER_SIZE, /* recommended minimum transfer size */
|
||||
_PC_REC_XFER_ALIGN, /* recommended transfer alignment */
|
||||
_PC_ALLOC_SIZE_MIN, /* minimum bytes of a file allocation */
|
||||
_PC_SYMLINK_MAX, /* max bytes in a symbolic link target */
|
||||
_PC_2_SYMLINKS /* 1 if symlinks support the ".." resolution rule */
|
||||
};
|
||||
/* NOLINTEND(bugprone-reserved-identifier) */
|
||||
|
||||
/*
|
||||
* Query a system configuration value for key name (one of the _SC_* above)
|
||||
* and return it as a long. A negative value means the option is not
|
||||
* supported or the key is unknown; for unsupported options and for any key
|
||||
* outside the _SC_* enum, -1 is returned WITHOUT setting errno (callers
|
||||
* must not rely on errno after -1).
|
||||
*
|
||||
* The supported keys and their vlibc values: _SC_ARG_MAX (2097152),
|
||||
* _SC_CHILD_MAX (65535), _SC_CLK_TCK (100, the fixed x86_64 clock-tick
|
||||
* rate — see the note in src/misc/sysconf.c, todo 35's times() consumes
|
||||
* that same constant), _SC_NGROUPS_MAX (65536), _SC_OPEN_MAX (the current
|
||||
* RLIMIT_NOFILE soft limit, 1024 if it cannot be read), _SC_PAGESIZE and
|
||||
* _SC_PAGE_SIZE (the memory page size from the AT_PAGESZ auxiliary vector
|
||||
* entry, 4096 on x86_64), _SC_NPROCESSORS_CONF and _SC_NPROCESSORS_ONLN
|
||||
* (the processor count), _SC_PHYS_PAGES and _SC_AVPHYS_PAGES (physical and
|
||||
* available memory page counts), _SC_SYMLOOP_MAX (40), _SC_STREAM_MAX
|
||||
* (16), _SC_TZNAME_MAX (6), _SC_VERSION and _SC_2_VERSION (200809L),
|
||||
* _SC_JOB_CONTROL, _SC_SAVED_IDS, _SC_REGEXP, _SC_SHELL (1 each),
|
||||
* _SC_HOST_NAME_MAX (64), _SC_LOGIN_NAME_MAX (256), _SC_GETPW_R_SIZE_MAX
|
||||
* (1024), _SC_IOV_MAX (1024), _SC_LINE_MAX (2048), _SC_RE_DUP_MAX (32767),
|
||||
* _SC_COLL_WEIGHTS_MAX (255), _SC_EXPR_NEST_MAX (32), _SC_XOPEN_VERSION
|
||||
* (700), _SC_XOPEN_UNIX, _SC_XOPEN_CRYPT, _SC_XOPEN_ENH_I18N, _SC_XOPEN_SHM
|
||||
* (1 each), and the thread/option version keys (_SC_THREADS ... _SC_2_UPE,
|
||||
* 200809L each).
|
||||
*/
|
||||
long
|
||||
sysconf(int name);
|
||||
|
||||
/*
|
||||
* Query a configurable limit or option of the filesystem holding path (the
|
||||
* _PC_* keys above) and return it as a long; -1 with errno set when path
|
||||
* cannot be examined. Fixed values: _PC_LINK_MAX (32000), _PC_MAX_CANON
|
||||
* and _PC_MAX_INPUT (255), _PC_PATH_MAX (4096), _PC_PIPE_BUF (4096),
|
||||
* _PC_CHOWN_RESTRICTED, _PC_NO_TRUNC and _PC_2_SYMLINKS (1), _PC_VDISABLE
|
||||
* (0), _PC_FILESIZEBITS (64), and _PC_REC_MIN_XFER_SIZE,
|
||||
* _PC_REC_XFER_ALIGN and _PC_ALLOC_SIZE_MIN (the filesystem block size).
|
||||
* _PC_NAME_MAX is the filesystem's f_namelen (255 on ext4/xfs). For keys
|
||||
* whose option the filesystem does not provide (_PC_ASYNC_IO, _PC_SYNC_IO,
|
||||
* _PC_PRIO_IO, _PC_SOCK_MAXBUF, _PC_REC_INCR_XFER_SIZE,
|
||||
* _PC_REC_MAX_XFER_SIZE, _PC_SYMLINK_MAX) and for any key outside the
|
||||
* _PC_* enum, -1 is returned WITHOUT setting errno.
|
||||
*/
|
||||
long
|
||||
pathconf(const char *path, int name);
|
||||
|
||||
/*
|
||||
* Like pathconf(), but the filesystem is the one holding the open
|
||||
* descriptor fildes.
|
||||
*/
|
||||
long
|
||||
fpathconf(int fildes, int name);
|
||||
|
||||
/* Level 1 (POSIX base): current directory, links, and path operations. */
|
||||
|
||||
/*
|
||||
* Store the absolute pathname of the current working directory into buf.
|
||||
* When buf is NULL, the path is returned in a malloc'd buffer instead
|
||||
* (size is then only a hint: 0 grows from a default of 128 bytes, and
|
||||
* the buffer is enlarged as the kernel reports ERANGE); the caller must
|
||||
* free it. Return buf (or the malloc'd buffer), or NULL with errno set
|
||||
* (EINVAL for a non-NULL buf with size 0).
|
||||
*/
|
||||
char *
|
||||
getcwd(char *buf, size_t size);
|
||||
|
||||
/*
|
||||
* Change the current working directory to path (chdir) or to the
|
||||
* directory referred to by the open descriptor fildes (fchdir). Return
|
||||
* 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
chdir(const char *path);
|
||||
|
||||
int
|
||||
fchdir(int fildes);
|
||||
|
||||
/*
|
||||
* Create a symbolic link: path2 is created as a link whose contents are
|
||||
* the string path1 (which need not exist). Return 0, or -1 with errno
|
||||
* set.
|
||||
*/
|
||||
int
|
||||
symlink(const char *path1, const char *path2);
|
||||
|
||||
/*
|
||||
* Like symlink(), but the link is created in the directory named by fd
|
||||
* when path2 is relative (use AT_FDCWD for the current working
|
||||
* directory). Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
symlinkat(const char *path1, int fd, const char *path2);
|
||||
|
||||
/*
|
||||
* Read the contents of the symbolic link path into buf (bufsize bytes,
|
||||
* no NUL is appended) and return the number of bytes placed in buf, 0
|
||||
* for an empty target, or -1 with errno set. The buffer is not
|
||||
* terminated: callers sized for bufsize + 1 append the NUL themselves.
|
||||
*/
|
||||
ssize_t
|
||||
readlink(const char *restrict path, char *restrict buf, size_t bufsize);
|
||||
|
||||
/*
|
||||
* Like readlink(), but path is resolved in the directory named by fd
|
||||
* when relative. Return the number of bytes placed in buf, or -1 with
|
||||
* errno set.
|
||||
*/
|
||||
ssize_t
|
||||
readlinkat(int fd, const char *restrict path, char *restrict buf, size_t bufsize);
|
||||
|
||||
/*
|
||||
* Create a hard link: new is created as a second name for the file old
|
||||
* (which must not be a directory and must be on the same filesystem).
|
||||
* Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
link(const char *old, const char *new);
|
||||
|
||||
/*
|
||||
* Like link(), but old and new are resolved against the directories
|
||||
* named by fd1 and fd2 when relative (AT_FDCWD selects the current
|
||||
* working directory) and flag may hold AT_SYMLINK_FOLLOW from <fcntl.h>.
|
||||
* Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
linkat(int fd1, const char *old, int fd2, const char *new, int flag);
|
||||
|
||||
/*
|
||||
* Remove the name path (a hard link; symbolic links are removed, not
|
||||
* followed). Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
unlink(const char *path);
|
||||
|
||||
/*
|
||||
* Like unlink(), but path is resolved in the directory named by fd when
|
||||
* relative and flag may hold AT_REMOVEDIR from <fcntl.h> (making it
|
||||
* remove an empty directory instead). Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
unlinkat(int fd, const char *path, int flag);
|
||||
|
||||
/*
|
||||
* Remove the empty directory path. Return 0, or -1 with errno set
|
||||
* (ENOTEMPTY/EEXIST when the directory is not empty).
|
||||
*/
|
||||
int
|
||||
rmdir(const char *path);
|
||||
|
||||
/*
|
||||
* Rename old to new within the filesystem: old is resolved against fd1
|
||||
* and new against fd2 (AT_FDCWD for the current working directory). Both
|
||||
* names may be directories. Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
renameat(int fd1, const char *old, int fd2, const char *new);
|
||||
|
||||
/* Level 1 (POSIX base): terminal identity, login name, getopt, confstr. */
|
||||
|
||||
/*
|
||||
* Test whether fildes refers to a terminal. Return 1 when it does, and
|
||||
* 0 otherwise (errno may be set — e.g. EBADF — but the caller must not
|
||||
* rely on it after a 0 return).
|
||||
*/
|
||||
int
|
||||
isatty(int fildes);
|
||||
|
||||
/*
|
||||
* Return the pathname of the terminal open on fildes in a shared static
|
||||
* buffer (overwritten by the next call), or NULL with errno set when
|
||||
* fildes is not a terminal. ttyname_r stores the path in the caller's
|
||||
* name buffer instead and returns 0, or an error NUMBER (ENOTTY when
|
||||
* fildes is not a terminal, ERANGE when the name does not fit in
|
||||
* namesize).
|
||||
*/
|
||||
char *
|
||||
ttyname(int fildes);
|
||||
|
||||
int
|
||||
ttyname_r(int fildes, char *name, size_t namesize);
|
||||
|
||||
/*
|
||||
* Return the login name of the user associated with the calling process
|
||||
* (the owner of its controlling terminal) in a shared static buffer, or
|
||||
* NULL with errno set when the name cannot be determined. getlogin_r
|
||||
* stores the name in the caller's buffer and returns 0, or an error
|
||||
* number (ERANGE when the name does not fit in namesize).
|
||||
*/
|
||||
char *
|
||||
getlogin(void);
|
||||
|
||||
int
|
||||
getlogin_r(char *name, size_t namesize);
|
||||
|
||||
/*
|
||||
* Command-line option parsing (POSIX.1-2008). getopt returns the next
|
||||
* option character from argv per the option characters in optstring; a
|
||||
* character followed by ':' in optstring takes an argument (delivered in
|
||||
* optarg). A leading ':' in optstring makes missing-argument errors
|
||||
* return ':' silently instead of '?' with a diagnostic to stderr.
|
||||
* Return -1 when all options have been consumed: optind then points at
|
||||
* the first non-option argument, and "--" terminates the scan.
|
||||
*/
|
||||
int
|
||||
getopt(int argc, char *const argv[], const char *optstring);
|
||||
|
||||
/* The getopt option state (POSIX.1-2008 base). */
|
||||
extern char *optarg; /* the argument of the option just returned */
|
||||
extern int optind; /* index of the next argument to scan (init 1) */
|
||||
extern int opterr; /* nonzero: write diagnostics to stderr (init 1) */
|
||||
extern int optopt; /* the option character that caused an error */
|
||||
|
||||
/*
|
||||
* Query the implementation-defined string value of configuration name
|
||||
* (one of the _CS_* keys above). When buf is not NULL and len is large
|
||||
* enough, the value (NUL-terminated) is copied into buf; the return
|
||||
* value is the length of the value including the terminating NUL, so a
|
||||
* return greater than len means the buffer was too small and nothing was
|
||||
* copied. For an unknown name, 0 is returned and errno is set to EINVAL.
|
||||
*/
|
||||
size_t
|
||||
confstr(int name, char *buf, size_t len);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): Linux extensions + XSI + obsolescent. */
|
||||
|
||||
/*
|
||||
* Store the current working directory into pathname, which must hold at
|
||||
* least PATH_MAX bytes. Return pathname, or NULL with errno set.
|
||||
* Obsolescent: prefer getcwd().
|
||||
*/
|
||||
char *
|
||||
getwd(char *pathname);
|
||||
|
||||
/*
|
||||
* Change the process root directory to path (used with chdir by
|
||||
* chroot-style confinement). Requires privilege. Return 0, or -1 with
|
||||
* errno set. XSI.
|
||||
*/
|
||||
int
|
||||
chroot(const char *path);
|
||||
|
||||
/*
|
||||
* Copy nbytes bytes from from to to, swapping each adjacent pair of
|
||||
* bytes (the odd byte of an odd count is not copied). Used for historic
|
||||
* byte-order conversion; returns nothing. XSI.
|
||||
*/
|
||||
void
|
||||
swab(const void *restrict from, void *restrict to, ssize_t nbytes);
|
||||
|
||||
/*
|
||||
* Like dup2(), but with descriptor flags (O_CLOEXEC from <fcntl.h>) applied
|
||||
* atomically; return fildes2, or -1 with errno set. Linux-specific.
|
||||
*/
|
||||
int
|
||||
dup3(int fildes, int fildes2, int flags);
|
||||
|
||||
/*
|
||||
* Like pipe(), but with descriptor flags (e.g. O_CLOEXEC) applied
|
||||
* atomically; return 0, or -1 with errno set. Linux-specific.
|
||||
*/
|
||||
int
|
||||
pipe2(int fildes[2], int flags);
|
||||
|
||||
/*
|
||||
* Truncate the file named by path to length bytes; return 0, or -1 with
|
||||
* errno set. XSI.
|
||||
*/
|
||||
int
|
||||
truncate(const char *path, off_t length);
|
||||
|
||||
/*
|
||||
* glibc LFS alias of lseek(): on x86_64 the LFS and non-LFS off_t are
|
||||
* identical (both 64-bit), so this simply calls lseek(). Provided for
|
||||
* source compatibility only.
|
||||
*/
|
||||
off_t
|
||||
lseek64(int fildes, off_t offset, int whence);
|
||||
|
||||
/*
|
||||
* vfork (obsolescent): like fork(), but the child borrows the parent's
|
||||
* address space until it execs or exits.
|
||||
*/
|
||||
pid_t
|
||||
vfork(void);
|
||||
|
||||
/*
|
||||
* setpgrp (obsolescent): setpgid(0, 0) — the caller becomes the leader
|
||||
* of its own process group.
|
||||
*/
|
||||
int
|
||||
setpgrp(void);
|
||||
|
||||
/*
|
||||
* setgroups (XSI): install the supplementary group list of gidsetsize
|
||||
* entries. Requires privilege.
|
||||
*/
|
||||
int
|
||||
setgroups(size_t gidsetsize, const gid_t *grouplist);
|
||||
|
||||
/*
|
||||
* getpgid/getsid (XSI): the process group / session of pid (0 = the
|
||||
* caller).
|
||||
*/
|
||||
pid_t
|
||||
getpgid(pid_t pid);
|
||||
|
||||
pid_t
|
||||
getsid(pid_t pid);
|
||||
|
||||
/*
|
||||
* Controlling-terminal foreground process group (XSI): tcgetpgrp reads
|
||||
* the foreground group of the terminal on fildes; tcsetpgrp makes pgid
|
||||
* the foreground group.
|
||||
*/
|
||||
pid_t
|
||||
tcgetpgrp(int fildes);
|
||||
|
||||
int
|
||||
tcsetpgrp(int fildes, pid_t pgid);
|
||||
|
||||
/* Level 2 (muslmimic): system identification (XSI / BSD, not POSIX base). */
|
||||
|
||||
/*
|
||||
* The _CS_* keys for confstr() (todo 38, which owns the function and its
|
||||
* string results). Values are vlibc-local ABI like the _SC_ and _PC_ keys
|
||||
* above; they are defined here now so todo 38 only implements confstr()
|
||||
* itself.
|
||||
*/
|
||||
/* NOLINTBEGIN(bugprone-reserved-identifier) -- POSIX-mandated _CS_ names */
|
||||
enum
|
||||
{
|
||||
_CS_PATH = 0, /* value of PATH (always a usable default) */
|
||||
_CS_POSIX_V6_WIDTH_RESTRICTED_ENVS, /* 1 if the V6 width-restricted envs exist */
|
||||
_CS_POSIX_V7_WIDTH_RESTRICTED_ENVS, /* 1 if the V7 width-restricted envs exist */
|
||||
_CS_LFS_CFLAGS, /* cpp/cflags for large-file support */
|
||||
_CS_LFS_LDFLAGS, /* ld flags for large-file support */
|
||||
_CS_LFS_LIBS, /* libraries for large-file support */
|
||||
_CS_LFS_LINTFLAGS, /* lint flags for large-file support */
|
||||
_CS_LFS64_CFLAGS, /* cpp/cflags for large-file64 support */
|
||||
_CS_LFS64_LDFLAGS, /* ld flags for large-file64 support */
|
||||
_CS_LFS64_LIBS, /* libraries for large-file64 support */
|
||||
_CS_LFS64_LINTFLAGS, /* lint flags for large-file64 support */
|
||||
_CS_GNU_LIBC_VERSION, /* "glibc X.Y" (vlibc extension) */
|
||||
_CS_GNU_LIBPTHREAD_VERSION /* "NPTL X.Y" (vlibc extension) */
|
||||
};
|
||||
/* NOLINTEND(bugprone-reserved-identifier) */
|
||||
|
||||
/*
|
||||
* Store the NUL-terminated host name of the current machine into name,
|
||||
* sized len bytes. Return 0 on success; if the name (including its NUL
|
||||
* terminator) does not fit in len bytes, nothing is written beyond the
|
||||
* fit and -1 is returned with errno set to ENAMETOOLONG. XSI.
|
||||
*/
|
||||
int
|
||||
gethostname(char *name, size_t len);
|
||||
|
||||
/*
|
||||
* Set the kernel's host name from name (len bytes, no NUL required).
|
||||
* Requires privilege. Return 0, or -1 with errno set. BSD, not POSIX.
|
||||
*/
|
||||
int
|
||||
sethostname(const char *name, size_t len);
|
||||
|
||||
/*
|
||||
* Return the 32-bit host identifier of the current machine. vlibc returns
|
||||
* 0 (musl's behavior): the historic value was derived from the host's
|
||||
* primary IP address via gethostbyname, which the modern library does not
|
||||
* perform. [OB] — obsolescent in POSIX.
|
||||
*/
|
||||
long
|
||||
gethostid(void);
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_UNISTD_H */
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* alphasort (todo 24, XSI): the scandir comparator that orders entries by
|
||||
* strcmp on their names. Receives two pointers to the array's struct
|
||||
* dirent pointers.
|
||||
*/
|
||||
int
|
||||
alphasort(const struct dirent **a, const struct dirent **b)
|
||||
{
|
||||
return strcmp((*a)->d_name, (*b)->d_name);
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include "../internal/malloc.h"
|
||||
#include "../internal/syscall.h"
|
||||
#include "dirent_impl.h"
|
||||
|
||||
/*
|
||||
* closedir (todo 24): release the descriptor and the stream storage. The
|
||||
* descriptor is closed first so a close failure still reports -1, but the
|
||||
* stream is freed either way (its errno is preserved across the free).
|
||||
*/
|
||||
int
|
||||
closedir(DIR *dir)
|
||||
{
|
||||
struct vlibc_DIR *d = dir;
|
||||
int fd = d->fd;
|
||||
int r = syscall_ret(__syscall1(SYS_close, fd));
|
||||
|
||||
__libc_free(d);
|
||||
return r;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#ifndef VLIBC_DIRENT_INTERNAL_H
|
||||
#define VLIBC_DIRENT_INTERNAL_H
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/*
|
||||
* vlibc — internal directory-stream state (todo 24).
|
||||
*
|
||||
* struct vlibc_DIR is the completion of the opaque DIR handle from
|
||||
* <dirent.h>. readdir parses raw getdents64 records out of buf; the kernel
|
||||
* fills buf with whole records and advances its own per-fd directory
|
||||
* offset, so sequential iteration needs no lseek. seekdir/telldir hand the
|
||||
* kernel's per-record d_off cookie (the offset of the NEXT entry) back and
|
||||
* forth via lseek: d->de.d_off therefore always holds the resume point
|
||||
* after the entry most recently returned by readdir.
|
||||
*/
|
||||
#define VLIBC_DIRENT_BUFSZ 2048
|
||||
|
||||
struct vlibc_DIR
|
||||
{
|
||||
int fd; /* directory descriptor, owned by the stream */
|
||||
size_t buf_pos; /* next unparsed byte within buf */
|
||||
size_t buf_end; /* first unused byte of buf */
|
||||
struct dirent de; /* storage for the entry readdir returns */
|
||||
char buf[VLIBC_DIRENT_BUFSZ];
|
||||
};
|
||||
|
||||
/*
|
||||
* The kernel-side record produced by SYS_getdents64, transcribed as the
|
||||
* x86_64 linux_dirent64 layout: ino at 0, off at 8, reclen at 16, type at
|
||||
* 18, name at 19. The 256-byte name array makes the struct exactly the
|
||||
* size of a maximal record: 19 + (255-name + NUL) = 275, padded by struct
|
||||
* alignment to 280, which is also the largest d_reclen the kernel emits
|
||||
* (every record length is rounded up to a multiple of 8). readdir memcpy's
|
||||
* a whole record into this struct before reading the fields, so no
|
||||
* unaligned or aliasing access ever happens.
|
||||
*/
|
||||
struct vlibc_linux_dirent64
|
||||
{
|
||||
ino_t d_ino;
|
||||
off_t d_off;
|
||||
unsigned short d_reclen;
|
||||
unsigned char d_type;
|
||||
char d_name[256];
|
||||
};
|
||||
|
||||
_Static_assert(sizeof(struct vlibc_linux_dirent64) == 280,
|
||||
"linux_dirent64 with a 256-byte name must be 280 bytes");
|
||||
_Static_assert(offsetof(struct vlibc_linux_dirent64, d_name) == 19,
|
||||
"linux_dirent64 name must sit at offset 19");
|
||||
_Static_assert(offsetof(struct dirent, d_name) ==
|
||||
offsetof(struct vlibc_linux_dirent64, d_name),
|
||||
"dirent and linux_dirent64 must share the name offset");
|
||||
|
||||
#endif /* VLIBC_DIRENT_INTERNAL_H */
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include "dirent_impl.h"
|
||||
|
||||
/*
|
||||
* dirfd (todo 24): return the descriptor the stream reads through. The
|
||||
* descriptor stays owned by the stream; closedir closes it.
|
||||
*/
|
||||
int
|
||||
dirfd(DIR *dir)
|
||||
{
|
||||
struct vlibc_DIR *d = dir;
|
||||
|
||||
return d->fd;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "../internal/malloc.h"
|
||||
#include "../internal/syscall.h"
|
||||
#include "dirent_impl.h"
|
||||
|
||||
/*
|
||||
* fdopendir (todo 24): validate the descriptor with SYS_fstat (POSIX
|
||||
* requires the fd to name a directory), then allocate the stream state.
|
||||
* On failure the descriptor is NOT closed — it stays owned by the caller,
|
||||
* exactly as passed in.
|
||||
*/
|
||||
DIR *
|
||||
fdopendir(int fd)
|
||||
{
|
||||
struct stat st;
|
||||
DIR *d;
|
||||
|
||||
if (syscall_ret(__syscall2(SYS_fstat, fd, (long)&st)) < 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
if (!S_ISDIR(st.st_mode))
|
||||
{
|
||||
errno = ENOTDIR;
|
||||
return NULL;
|
||||
}
|
||||
d = __libc_malloc(sizeof *d);
|
||||
if (d == NULL)
|
||||
{
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
d->fd = fd;
|
||||
d->buf_pos = 0;
|
||||
d->buf_end = 0;
|
||||
d->de.d_off = 0;
|
||||
return d;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
#include "dirent_impl.h"
|
||||
|
||||
/*
|
||||
* opendir (todo 24): open the directory via SYS_openat with O_DIRECTORY so
|
||||
* the kernel rejects non-directories with ENOTDIR before any stream state
|
||||
* exists, then hand the descriptor to fdopendir (which re-validates with
|
||||
* fstat). On fdopendir failure the descriptor is closed again: opendir
|
||||
* never leaks.
|
||||
*/
|
||||
DIR *
|
||||
opendir(const char *path)
|
||||
{
|
||||
int fd = syscall_ret(
|
||||
__syscall3(SYS_openat, AT_FDCWD, (long)path, O_RDONLY | O_DIRECTORY));
|
||||
DIR *d;
|
||||
|
||||
if (fd < 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
d = fdopendir(fd);
|
||||
if (d == NULL)
|
||||
{
|
||||
syscall_ret(__syscall1(SYS_close, fd));
|
||||
return NULL;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
#include "dirent_impl.h"
|
||||
|
||||
/*
|
||||
* readdir (todo 24): expose the entries the kernel wrote into the stream
|
||||
* buffer by SYS_getdents64, one per call. When the buffer is exhausted a
|
||||
* fresh getdents64 continues at the kernel's per-fd directory offset (no
|
||||
* lseek needed for sequential iteration). A zero-length result is end of
|
||||
* directory: NULL is returned with errno untouched. Each record is copied
|
||||
* into the stream's own struct dirent, so the returned pointer stays valid
|
||||
* until the next call on the same stream.
|
||||
*/
|
||||
struct dirent *
|
||||
readdir(DIR *dir)
|
||||
{
|
||||
struct vlibc_DIR *d = dir;
|
||||
struct vlibc_linux_dirent64 k;
|
||||
unsigned short reclen;
|
||||
size_t namelen;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (d->buf_pos >= d->buf_end)
|
||||
{
|
||||
long r =
|
||||
__syscall3(SYS_getdents64, d->fd, (long)d->buf,
|
||||
(long)sizeof(d->buf));
|
||||
|
||||
if (r <= 0)
|
||||
{
|
||||
syscall_ret(r);
|
||||
return NULL;
|
||||
}
|
||||
d->buf_pos = 0;
|
||||
d->buf_end = (size_t)r;
|
||||
}
|
||||
|
||||
if (d->buf_end - d->buf_pos < offsetof(struct vlibc_linux_dirent64, d_name))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
memcpy(&reclen,
|
||||
d->buf + d->buf_pos + offsetof(struct vlibc_linux_dirent64, d_reclen),
|
||||
sizeof reclen);
|
||||
if (reclen < offsetof(struct vlibc_linux_dirent64, d_name) + 2 ||
|
||||
reclen > sizeof k)
|
||||
{
|
||||
/* The kernel always emits whole well-formed records; a broken
|
||||
* length would otherwise walk off the buffer. Stop the scan. */
|
||||
return NULL;
|
||||
}
|
||||
memcpy(&k, d->buf + d->buf_pos, reclen);
|
||||
d->buf_pos += reclen;
|
||||
|
||||
d->de.d_ino = k.d_ino;
|
||||
d->de.d_off = k.d_off;
|
||||
d->de.d_reclen = reclen;
|
||||
d->de.d_type = k.d_type;
|
||||
namelen = (size_t)reclen - offsetof(struct vlibc_linux_dirent64, d_name);
|
||||
if (namelen >= sizeof(d->de.d_name))
|
||||
{
|
||||
namelen = sizeof(d->de.d_name) - 1;
|
||||
}
|
||||
memcpy(d->de.d_name, k.d_name, namelen);
|
||||
d->de.d_name[namelen] = '\0';
|
||||
return &d->de;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* readdir_r (todo 24, obsolescent): reentrant readdir. The result is
|
||||
* copied into the caller's buf and *result points at it; at end of
|
||||
* directory *result is NULL. Errors are reported as the return value
|
||||
* (never through errno), and errno is preserved across successful calls.
|
||||
* readdir_r is distinguished from end-of-directory by checking whether
|
||||
* readdir moved errno.
|
||||
*/
|
||||
int
|
||||
readdir_r(DIR *restrict dir, struct dirent *restrict buf,
|
||||
struct dirent **restrict result)
|
||||
{
|
||||
struct dirent *de;
|
||||
int saved = errno;
|
||||
|
||||
errno = 0;
|
||||
de = readdir(dir);
|
||||
if (de != NULL)
|
||||
{
|
||||
memcpy(buf, de, sizeof *buf);
|
||||
*result = buf;
|
||||
errno = saved;
|
||||
return 0;
|
||||
}
|
||||
if (errno != 0)
|
||||
{
|
||||
return errno;
|
||||
}
|
||||
errno = saved;
|
||||
*result = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
#include "dirent_impl.h"
|
||||
|
||||
/*
|
||||
* rewinddir (todo 24): return the stream to the start of the directory.
|
||||
* SYS_lseek back to offset 0 resets the kernel's per-fd directory
|
||||
* position; the cached records (if any) are dropped and the stored seek
|
||||
* cookie is reset, so the next readdir refills from the first entry.
|
||||
* rewinddir has no error return; a failed lseek leaves the stream
|
||||
* positioned wherever the kernel is.
|
||||
*/
|
||||
void
|
||||
rewinddir(DIR *dir)
|
||||
{
|
||||
struct vlibc_DIR *d = dir;
|
||||
|
||||
syscall_ret(__syscall3(SYS_lseek, d->fd, 0, SEEK_SET));
|
||||
d->buf_pos = 0;
|
||||
d->buf_end = 0;
|
||||
d->de.d_off = 0;
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "../internal/malloc.h"
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* scandir (todo 24, XSI): walk the whole directory and hand back a sorted,
|
||||
* NULL-terminated array of heap copies. Entries are copied into
|
||||
* minimal-size blocks (header fields plus the name), all released with the
|
||||
* public free(); the array itself is a malloc'd growable buffer resized
|
||||
* with realloc. "." and ".." are reported like any other entry.
|
||||
*/
|
||||
|
||||
/* Copy one entry into minimal heap storage sized for its name. */
|
||||
static struct dirent *
|
||||
dirent_dup(const struct dirent *de)
|
||||
{
|
||||
size_t nlen = strlen(de->d_name) + 1;
|
||||
struct dirent *nd =
|
||||
__libc_malloc(offsetof(struct dirent, d_name) + nlen);
|
||||
|
||||
if (nd == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
memcpy(nd, de, offsetof(struct dirent, d_name));
|
||||
memcpy(nd->d_name, de->d_name, nlen);
|
||||
return nd;
|
||||
}
|
||||
|
||||
int
|
||||
scandir(const char *path, struct dirent ***res,
|
||||
int (*sel)(const struct dirent *),
|
||||
int (*cmp)(const struct dirent **, const struct dirent **))
|
||||
{
|
||||
DIR *d;
|
||||
struct dirent *de;
|
||||
struct dirent **names = NULL;
|
||||
size_t cnt = 0;
|
||||
size_t cap = 0;
|
||||
|
||||
d = opendir(path);
|
||||
if (d == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
while ((de = readdir(d)) != NULL)
|
||||
{
|
||||
if (sel != NULL && !sel(de))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (cnt == cap)
|
||||
{
|
||||
size_t ncap = cap == 0 ? 8 : cap * 2;
|
||||
|
||||
if (ncap <= cap || ncap > (size_t)-1 / sizeof *names)
|
||||
{
|
||||
errno = ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
names = realloc(names, ncap * sizeof *names);
|
||||
if (names == NULL)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
cap = ncap;
|
||||
}
|
||||
names[cnt] = dirent_dup(de);
|
||||
if (names[cnt] == NULL)
|
||||
{
|
||||
errno = ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
cnt++;
|
||||
}
|
||||
closedir(d);
|
||||
|
||||
if (cmp != NULL)
|
||||
{
|
||||
qsort(names, cnt, sizeof *names,
|
||||
(int (*)(const void *, const void *))cmp);
|
||||
}
|
||||
names = realloc(names, (cnt + 1) * sizeof *names);
|
||||
if (names == NULL)
|
||||
{
|
||||
goto fail_nofree;
|
||||
}
|
||||
names[cnt] = NULL;
|
||||
*res = names;
|
||||
return (int)cnt;
|
||||
|
||||
fail:
|
||||
while (cnt > 0)
|
||||
{
|
||||
__libc_free(names[--cnt]);
|
||||
}
|
||||
__libc_free(names);
|
||||
closedir(d);
|
||||
return -1;
|
||||
|
||||
fail_nofree:
|
||||
while (cnt > 0)
|
||||
{
|
||||
__libc_free(names[--cnt]);
|
||||
}
|
||||
__libc_free(names);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
#include "dirent_impl.h"
|
||||
|
||||
/*
|
||||
* seekdir (todo 24): reposition the stream to the directory location loc
|
||||
* (a d_off cookie previously returned by telldir). SYS_lseek to that
|
||||
* cookie makes the next SYS_getdents64 resume at the entry the cookie
|
||||
* denotes; any records cached ahead of the seek point are dropped. The
|
||||
* kernel accepts exactly the cookies getdents64 hands out, which is all
|
||||
* telldir ever returns. seekdir has no error return; on a failed lseek
|
||||
* the buffered records are kept so reading continues where it left off.
|
||||
*/
|
||||
void
|
||||
seekdir(DIR *dir, long loc)
|
||||
{
|
||||
struct vlibc_DIR *d = dir;
|
||||
|
||||
if (syscall_ret(__syscall3(SYS_lseek, d->fd, (long)loc, SEEK_SET)) >= 0)
|
||||
{
|
||||
d->buf_pos = 0;
|
||||
d->buf_end = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include "dirent_impl.h"
|
||||
|
||||
/*
|
||||
* telldir (todo 24): return the stream's current directory location.
|
||||
* readdir stores the kernel's d_off cookie of every entry it hands out —
|
||||
* the offset at which iteration resumes AFTER that entry — so the cookie
|
||||
* of the most recently returned entry is exactly the location a matching
|
||||
* seekdir must restore. A fresh or rewound stream reports 0, the start.
|
||||
*/
|
||||
long
|
||||
telldir(DIR *dir)
|
||||
{
|
||||
struct vlibc_DIR *d = dir;
|
||||
|
||||
return (long)d->de.d_off;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* creat ≡ open(path, O_WRONLY | O_CREAT | O_TRUNC, mode) (POSIX). Done as
|
||||
* a direct SYS_openat rather than a call to open(): the flag set is fixed,
|
||||
* the mode is always supplied, and this keeps src/fcntl self-contained
|
||||
* with no inter-object dependency on src/unistd. The kernel applies the
|
||||
* process umask to mode.
|
||||
*/
|
||||
int
|
||||
creat(const char *path, mode_t mode)
|
||||
{
|
||||
return syscall_ret(
|
||||
__syscall4(SYS_openat, AT_FDCWD, (long)path, O_WRONLY | O_CREAT | O_TRUNC, mode));
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* fcntl over SYS_fcntl (72). The third argument is read from the varargs
|
||||
* only for the commands POSIX defines one for (F_DUPFD, F_DUPFD_CLOEXEC,
|
||||
* F_SETFD, F_SETFL, the lock commands, and the owner/signal commands);
|
||||
* every other command passes 0, which the kernel ignores. Pointers (the
|
||||
* struct flock * of F_GETLK/F_SETLK/F_SETLKW/F_OFD_*) ride the varargs
|
||||
* slot as a long — on x86_64 long and void * share one GPR slot, so the
|
||||
* read is ABI-exact.
|
||||
*
|
||||
* F_DUPFD_CLOEXEC passes straight through: the x86_64 kernel has supported
|
||||
* it as a single native operation since 2.6.24, so no F_DUPFD + F_SETFD
|
||||
* fallback is needed. F_SETFL needs no O_LARGEFILE massaging on x86_64
|
||||
* (that is a 32-bit compat concern only).
|
||||
*/
|
||||
int
|
||||
fcntl(int fildes, int cmd, ...)
|
||||
{
|
||||
long arg = 0;
|
||||
va_list ap;
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case F_DUPFD:
|
||||
case F_DUPFD_CLOEXEC:
|
||||
case F_SETFD:
|
||||
case F_SETFL:
|
||||
case F_GETLK:
|
||||
case F_SETLK:
|
||||
case F_SETLKW:
|
||||
case F_SETOWN:
|
||||
case F_SETSIG:
|
||||
case F_GETOWN_EX:
|
||||
case F_SETOWN_EX:
|
||||
case F_OFD_GETLK:
|
||||
case F_OFD_SETLK:
|
||||
case F_OFD_SETLKW:
|
||||
va_start(ap, cmd);
|
||||
arg = va_arg(ap, long);
|
||||
va_end(ap);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return syscall_ret(__syscall3(SYS_fcntl, fildes, cmd, arg));
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* lockf (XSI) over the fcntl record-lock commands: the kernel has no lockf
|
||||
* syscall, so every cmd maps to a struct flock operation on the range
|
||||
* [current offset, current offset + len) — the POSIX definition. len 0
|
||||
* means through EOF, which the kernel's l_len 0 convention already
|
||||
* expresses.
|
||||
*
|
||||
* The region start uses l_whence = SEEK_CUR with l_start = 0: the kernel
|
||||
* resolves the current file offset at syscall time, atomically with the
|
||||
* lock operation, so no lseek round-trip (and no TOCTOU window) is needed.
|
||||
*
|
||||
* F_TEST probes with F_GETLK using a read lock (the probe type that
|
||||
* conflicts with any exclusive lock): if the kernel reports no lock, or
|
||||
* the reported holder is this process itself (a different descriptor of
|
||||
* ours may hold it — POSIX says our own process never conflicts with
|
||||
* itself), the region is lockable. The pid is read via raw SYS_getpid;
|
||||
* the process wrappers are todo 20's and not a dependency of this file.
|
||||
*/
|
||||
int
|
||||
lockf(int fd, int cmd, off_t len)
|
||||
{
|
||||
struct flock lk;
|
||||
|
||||
lk.l_type = F_WRLCK;
|
||||
lk.l_whence = SEEK_CUR;
|
||||
lk.l_start = 0;
|
||||
lk.l_len = len;
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case F_TEST:
|
||||
lk.l_type = F_RDLCK;
|
||||
if (fcntl(fd, F_GETLK, &lk) == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (lk.l_type == F_UNLCK || lk.l_pid == (pid_t)__syscall0(SYS_getpid))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
errno = EACCES;
|
||||
return -1;
|
||||
case F_ULOCK:
|
||||
lk.l_type = F_UNLCK;
|
||||
return fcntl(fd, F_SETLK, &lk);
|
||||
case F_LOCK:
|
||||
return fcntl(fd, F_SETLKW, &lk);
|
||||
case F_TLOCK:
|
||||
return fcntl(fd, F_SETLK, &lk);
|
||||
default:
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* posix_fadvise over SYS_fadvise64 (221). POSIX return convention, unlike
|
||||
* the rest of the family: an error NUMBER directly — 0 on success, else
|
||||
* the positive errno value (EBADF, ESPIPE, EINVAL, ...) — and errno is
|
||||
* left untouched. The raw syscall result is therefore mapped without the
|
||||
* syscall_ret() translation (which would write errno): a negative return
|
||||
* is negated, 0 stays 0. On x86_64 the syscall takes the offset as one
|
||||
* 64-bit value (fd, offset, len, advice) — no lo/hi split.
|
||||
*/
|
||||
int
|
||||
posix_fadvise(int fd, off_t offset, off_t len, int advice)
|
||||
{
|
||||
long r = __syscall4(SYS_fadvise64, fd, offset, len, advice);
|
||||
|
||||
return r < 0 ? (int)-r : 0;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* posix_fallocate over SYS_fallocate (285). The kernel signature is
|
||||
* (fd, mode, offset, len); mode is 0 — POSIX exposes no FALLOC_FL_* flags,
|
||||
* so the plain allocate-and-grow operation is all there is (the kernel
|
||||
* rejects any other mode with EOPNOTSUPP/EINVAL, which is exactly what
|
||||
* POSIX wants). Same error-number return convention as posix_fadvise:
|
||||
* 0 on success, the positive errno value on failure, errno untouched —
|
||||
* so the raw result is negated directly, no syscall_ret().
|
||||
*/
|
||||
int
|
||||
posix_fallocate(int fd, off_t offset, off_t len)
|
||||
{
|
||||
long r = __syscall4(SYS_fallocate, fd, 0, offset, len);
|
||||
|
||||
return r < 0 ? (int)-r : 0;
|
||||
}
|
||||
@@ -0,0 +1,565 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <grp.h>
|
||||
|
||||
/*
|
||||
* vlibc — group database (todo 37).
|
||||
*
|
||||
* getgrnam/getgrgid scan /etc/group with a fresh fopen per call (no
|
||||
* persistent cursor); getgrent walks one FILE kept open between calls,
|
||||
* which setgrent rewinds and endgrent closes. Every line is read with
|
||||
* getline, one at a time — the whole file is never cached. fgetgrent is
|
||||
* the XSI variant that parses the next record from an arbitrary caller-
|
||||
* supplied stream; it is gated at level 2 (whole-level-gate rule: XSI is
|
||||
* not POSIX.1-2008 base), everything else here is level 1.
|
||||
*
|
||||
* Storage model: two static buffers back every non-reentrant result. A
|
||||
* successful call returns a struct whose string members and gr_mem
|
||||
* entries point into a private result copy; the getline scan that found
|
||||
* the match ran in a separate buffer, so a caller may feed the returned
|
||||
* record's own name back into a later lookup (the
|
||||
* getgrnam(getgrgid(x)->gr_name) round-trip) without the scan clobbering
|
||||
* its key. The result copy is replaced by the next call to any of
|
||||
* getgrnam/getgrgid/getgrent/fgetgrent (or a realloc of the copy),
|
||||
* matching POSIX result-lifetime rules. The _r forms never use this
|
||||
* static storage for their result: the caller's struct and buffer
|
||||
* receive the record, and the caller's *result is how the outcome is
|
||||
* reported.
|
||||
*
|
||||
* Record syntax: name:passwd:gid:member,member,... — the first three
|
||||
* fields are ':'-separated and the member list is the comma-separated
|
||||
* tail (empty when the group has no members). Blank or comment-style
|
||||
* lines and records whose gid is not a decimal number are skipped like
|
||||
* malformed input.
|
||||
*/
|
||||
|
||||
/* The database path; not overridable (no NSS, no shadow, POSIX only). */
|
||||
#define GRP_PATH "/etc/group"
|
||||
|
||||
/* Non-reentrant result: struct, the private copy it points into, and the
|
||||
* member pointer array whose entries point into that same copy. */
|
||||
static struct group gr_result;
|
||||
static char *gr_out;
|
||||
static size_t gr_out_cap;
|
||||
static char **gr_mem;
|
||||
static size_t gr_mem_cap;
|
||||
|
||||
/* The getline scan buffer shared by every lookup and stream walk. */
|
||||
static char *gr_line;
|
||||
static size_t gr_cap;
|
||||
|
||||
/* The persistent getgrent stream; NULL when closed. */
|
||||
static FILE *gr_stream;
|
||||
|
||||
/*
|
||||
* Parse the leading digit string of s as an unsigned id. Accepts only
|
||||
* plain decimal digits and rejects empty fields and overflow; returns 0
|
||||
* on success.
|
||||
*/
|
||||
static int
|
||||
gr_parse_id(const char *s, unsigned *out)
|
||||
{
|
||||
unsigned long v = 0;
|
||||
const char *p;
|
||||
|
||||
if (*s == '\0')
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
for (p = s; *p != '\0'; p++)
|
||||
{
|
||||
if (*p < '0' || *p > '9')
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
v = v * 10 + (unsigned long)(*p - '0');
|
||||
if (v > 0xffffffffUL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
*out = (unsigned)v;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Grow the static member pointer array to hold cnt entries plus the
|
||||
* terminating NULL; return 0 on success, -1 when realloc fails. */
|
||||
static int
|
||||
gr_reserve_mem(size_t cnt)
|
||||
{
|
||||
char **newmem;
|
||||
size_t ncap;
|
||||
|
||||
if (gr_mem_cap >= cnt + 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
ncap = gr_mem_cap == 0 ? 8 : gr_mem_cap * 2;
|
||||
while (ncap < cnt + 1)
|
||||
{
|
||||
ncap *= 2;
|
||||
}
|
||||
newmem = (char **)realloc((void *)gr_mem, ncap * sizeof(*newmem));
|
||||
if (newmem == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
gr_mem = newmem;
|
||||
gr_mem_cap = ncap;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Split the comma-separated member tail in place: every name becomes a
|
||||
* NUL-terminated substring of line, member[i] points at the i-th one and
|
||||
* member[cnt] is NULL. An empty tail yields a single NULL entry. The
|
||||
* parsed group's gr_mem is set to the shared member array.
|
||||
*/
|
||||
static int
|
||||
gr_split_members(char *tail, struct group *gr)
|
||||
{
|
||||
size_t cnt = 0;
|
||||
char *p;
|
||||
|
||||
if (*tail != '\0')
|
||||
{
|
||||
cnt = 1;
|
||||
for (p = tail; *p != '\0'; p++)
|
||||
{
|
||||
if (*p == ',')
|
||||
{
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (gr_reserve_mem(cnt) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
p = tail;
|
||||
for (size_t i = 0; i < cnt; i++)
|
||||
{
|
||||
char *comma = strchr(p, ',');
|
||||
|
||||
if (comma != NULL)
|
||||
{
|
||||
*comma = '\0';
|
||||
}
|
||||
gr_mem[i] = p;
|
||||
p = comma == NULL ? p + strlen(p) : comma + 1;
|
||||
}
|
||||
gr_mem[cnt] = NULL;
|
||||
gr->gr_mem = gr_mem;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Split the line (newline already stripped) into its fields in place,
|
||||
* replacing the first three ':' separators with NULs. On success the
|
||||
* struct fields point into line and the member list has been split in
|
||||
* place, and 0 is returned; a record with fewer than three separators or
|
||||
* a non-numeric gid yields -1.
|
||||
*/
|
||||
static int
|
||||
gr_split(struct group *gr, char *line)
|
||||
{
|
||||
char *fields[4];
|
||||
char *p;
|
||||
unsigned gid;
|
||||
int i;
|
||||
|
||||
p = line;
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
fields[i] = p;
|
||||
p = strchr(p, ':');
|
||||
if (p == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
*p = '\0';
|
||||
p++;
|
||||
}
|
||||
fields[3] = p;
|
||||
if (gr_parse_id(fields[2], &gid) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (gr_split_members(fields[3], gr) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
gr->gr_name = fields[0];
|
||||
gr->gr_passwd = fields[1];
|
||||
gr->gr_gid = (gid_t)gid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Strip a trailing newline (and CR) from the line just read by getline. */
|
||||
static void
|
||||
gr_chomp(char *line, ssize_t len)
|
||||
{
|
||||
while (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r'))
|
||||
{
|
||||
line[--len] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Make line — a chomped, already-split record in the scan buffer — the
|
||||
* non-reentrant result: copy it into the private result buffer and
|
||||
* re-split it there, so the returned struct's strings and member
|
||||
* pointers reference storage the scanner never touches again. Returns
|
||||
* the result struct, or NULL if the result copy could not be grown.
|
||||
*/
|
||||
static struct group *
|
||||
gr_publish(char *line)
|
||||
{
|
||||
size_t len = strlen(line) + 1;
|
||||
|
||||
if (len > gr_out_cap)
|
||||
{
|
||||
char *nb = (char *)realloc(gr_out, len);
|
||||
|
||||
if (nb == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
gr_out = nb;
|
||||
gr_out_cap = len;
|
||||
}
|
||||
strcpy(gr_out, line);
|
||||
if (gr_split(&gr_result, gr_out) != 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return &gr_result;
|
||||
}
|
||||
|
||||
/* Match predicate over a parsed record, keyed by name or gid. */
|
||||
typedef int (*gr_matcher)(const struct group *gr, const void *key);
|
||||
|
||||
static int
|
||||
gr_match_name(const struct group *gr, const void *key)
|
||||
{
|
||||
return strcmp(gr->gr_name, (const char *)key) == 0;
|
||||
}
|
||||
|
||||
static int
|
||||
gr_match_gid(const struct group *gr, const void *key)
|
||||
{
|
||||
return gr->gr_gid == *(const gid_t *)key;
|
||||
}
|
||||
|
||||
/*
|
||||
* Non-reentrant lookup core: open the database, scan with getline until
|
||||
* the predicate matches, and hand back a pointer into the shared static
|
||||
* line buffer. NULL means "no such entry" (or an unopenable database).
|
||||
*/
|
||||
static struct group *
|
||||
gr_find(gr_matcher match, const void *key)
|
||||
{
|
||||
FILE *f = fopen(GRP_PATH, "r");
|
||||
struct group cur;
|
||||
ssize_t got;
|
||||
|
||||
if (f == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
got = getline(&gr_line, &gr_cap, f);
|
||||
if (got < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
gr_chomp(gr_line, got);
|
||||
if (gr_split(&cur, gr_line) == 0 && match(&cur, key))
|
||||
{
|
||||
(void)fclose(f);
|
||||
return gr_publish(gr_line);
|
||||
}
|
||||
}
|
||||
(void)fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reentrant serializer: copy the parsed record into the caller's struct
|
||||
* and buffer. The string bytes go first; the NULL-terminated member
|
||||
* pointer array follows on a pointer-aligned boundary. Returns 0 on
|
||||
* success, or ERANGE (as the POSIX _r functions return, never touching
|
||||
* errno) when the record needs more than bufsize bytes.
|
||||
*/
|
||||
static int
|
||||
gr_store(const struct group *in, struct group *gr, char *buf, size_t bufsize)
|
||||
{
|
||||
size_t nmem = 0;
|
||||
size_t strneed;
|
||||
size_t ptoff;
|
||||
char *s;
|
||||
char **mem;
|
||||
size_t i;
|
||||
|
||||
while (in->gr_mem != NULL && in->gr_mem[nmem] != NULL)
|
||||
{
|
||||
nmem++;
|
||||
}
|
||||
strneed = strlen(in->gr_name) + 1;
|
||||
strneed += strlen(in->gr_passwd) + 1;
|
||||
for (i = 0; i < nmem; i++)
|
||||
{
|
||||
strneed += strlen(in->gr_mem[i]) + 1;
|
||||
}
|
||||
ptoff = (strneed + sizeof(char *) - 1) & ~(sizeof(char *) - 1);
|
||||
if (bufsize < ptoff + (nmem + 1) * sizeof(char *))
|
||||
{
|
||||
return ERANGE;
|
||||
}
|
||||
gr->gr_gid = in->gr_gid;
|
||||
mem = (char **)(void *)(buf + ptoff);
|
||||
gr->gr_mem = mem;
|
||||
s = buf;
|
||||
gr->gr_name = s;
|
||||
s += strlen(in->gr_name) + 1;
|
||||
gr->gr_passwd = s;
|
||||
s += strlen(in->gr_passwd) + 1;
|
||||
for (i = 0; i < nmem; i++)
|
||||
{
|
||||
size_t len = strlen(in->gr_mem[i]) + 1;
|
||||
|
||||
gr->gr_mem[i] = s;
|
||||
s += len;
|
||||
}
|
||||
gr->gr_mem[nmem] = NULL;
|
||||
strcpy(gr->gr_name, in->gr_name);
|
||||
strcpy(gr->gr_passwd, in->gr_passwd);
|
||||
for (i = 0; i < nmem; i++)
|
||||
{
|
||||
size_t len = strlen(in->gr_mem[i]) + 1;
|
||||
|
||||
__builtin_memcpy(gr->gr_mem[i], in->gr_mem[i], len);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct group *
|
||||
getgrnam(const char *name)
|
||||
{
|
||||
if (name == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return gr_find(gr_match_name, name);
|
||||
}
|
||||
|
||||
struct group *
|
||||
getgrgid(gid_t gid)
|
||||
{
|
||||
return gr_find(gr_match_gid, &gid);
|
||||
}
|
||||
|
||||
struct group *
|
||||
getgrent(void)
|
||||
{
|
||||
struct group cur;
|
||||
ssize_t got;
|
||||
|
||||
if (gr_stream == NULL)
|
||||
{
|
||||
gr_stream = fopen(GRP_PATH, "r");
|
||||
if (gr_stream == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
got = getline(&gr_line, &gr_cap, gr_stream);
|
||||
if (got < 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
gr_chomp(gr_line, got);
|
||||
if (gr_split(&cur, gr_line) == 0)
|
||||
{
|
||||
return gr_publish(gr_line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
setgrent(void)
|
||||
{
|
||||
if (gr_stream != NULL)
|
||||
{
|
||||
rewind(gr_stream);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
endgrent(void)
|
||||
{
|
||||
if (gr_stream != NULL)
|
||||
{
|
||||
(void)fclose(gr_stream);
|
||||
gr_stream = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Reentrant lookup core: scan for the first matching record and serialize
|
||||
* it into the caller's storage. Returns 0 with *result pointing at the
|
||||
* filled struct on success, 0 with *result NULL when no record matches,
|
||||
* ERANGE when the record does not fit bufsize, and EINVAL for a NULL
|
||||
* result pointer.
|
||||
*/
|
||||
static int
|
||||
gr_find_r(gr_matcher match, const void *key, struct group *gr, char *buf, size_t bufsize,
|
||||
struct group **result)
|
||||
{
|
||||
FILE *f;
|
||||
struct group cur;
|
||||
ssize_t got;
|
||||
int rc;
|
||||
|
||||
if (result == NULL || gr == NULL)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
if (buf == NULL && bufsize != 0)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
*result = NULL;
|
||||
f = fopen(GRP_PATH, "r");
|
||||
if (f == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
got = getline(&gr_line, &gr_cap, f);
|
||||
if (got < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
gr_chomp(gr_line, got);
|
||||
if (gr_split(&cur, gr_line) != 0 || !match(&cur, key))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
rc = gr_store(&cur, gr, buf, bufsize);
|
||||
(void)fclose(f);
|
||||
if (rc == 0)
|
||||
{
|
||||
*result = gr;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
(void)fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
getgrnam_r(const char *name, struct group *gr, char *buf, size_t bufsize, struct group **result)
|
||||
{
|
||||
if (name == NULL)
|
||||
{
|
||||
if (result != NULL)
|
||||
{
|
||||
*result = NULL;
|
||||
}
|
||||
return EINVAL;
|
||||
}
|
||||
return gr_find_r(gr_match_name, name, gr, buf, bufsize, result);
|
||||
}
|
||||
|
||||
int
|
||||
getgrgid_r(gid_t gid, struct group *gr, char *buf, size_t bufsize, struct group **result)
|
||||
{
|
||||
return gr_find_r(gr_match_gid, &gid, gr, buf, bufsize, result);
|
||||
}
|
||||
|
||||
int
|
||||
getgrent_r(struct group *gr, char *buf, size_t bufsize, struct group **result)
|
||||
{
|
||||
struct group cur;
|
||||
ssize_t got;
|
||||
int rc;
|
||||
|
||||
if (result == NULL || gr == NULL)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
if (buf == NULL && bufsize != 0)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
*result = NULL;
|
||||
if (gr_stream == NULL)
|
||||
{
|
||||
gr_stream = fopen(GRP_PATH, "r");
|
||||
if (gr_stream == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
got = getline(&gr_line, &gr_cap, gr_stream);
|
||||
if (got < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
gr_chomp(gr_line, got);
|
||||
if (gr_split(&cur, gr_line) != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
rc = gr_store(&cur, gr, buf, bufsize);
|
||||
if (rc == 0)
|
||||
{
|
||||
*result = gr;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
struct group *
|
||||
fgetgrent(FILE *stream)
|
||||
{
|
||||
struct group cur;
|
||||
ssize_t got;
|
||||
|
||||
if (stream == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
got = getline(&gr_line, &gr_cap, stream);
|
||||
if (got < 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
gr_chomp(gr_line, got);
|
||||
if (gr_split(&cur, gr_line) == 0)
|
||||
{
|
||||
return gr_publish(gr_line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -12,6 +12,14 @@
|
||||
* All wrappers are sequentially consistent by default: a libc-internal
|
||||
* operation is almost never hot enough to justify weakening, and a single
|
||||
* memory order everywhere is the least error-prone choice.
|
||||
*
|
||||
* The fetch_or / fetch_and / fetch_sub family and the add_fetch / sub_fetch
|
||||
* result-returning forms were added by todo 44 for the futex lock words of
|
||||
* the pthread layer (#46-#48): futex wait words are built with or/and
|
||||
* (musl's a_or/a_and) and waiter counts with add/sub that return the NEW
|
||||
* value (musl's a_inc/a_dec pair). The fetch_* forms return the OLD value,
|
||||
* matching the C11/GCC __atomic_fetch_* convention; the *_fetch forms
|
||||
* return the NEW one.
|
||||
*/
|
||||
|
||||
static inline int
|
||||
@@ -32,6 +40,36 @@ atomic_fetch_add(volatile int *p, int v) // NOLINT(readability-non-const-paramet
|
||||
return __atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline int
|
||||
atomic_add_fetch(volatile int *p, int v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
return __atomic_add_fetch(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline int
|
||||
atomic_fetch_sub(volatile int *p, int v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
return __atomic_fetch_sub(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline int
|
||||
atomic_sub_fetch(volatile int *p, int v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
return __atomic_sub_fetch(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline int
|
||||
atomic_fetch_or(volatile int *p, int v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
return __atomic_fetch_or(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline int
|
||||
atomic_fetch_and(volatile int *p, int v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
return __atomic_fetch_and(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline int
|
||||
atomic_exchange(volatile int *p, int v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
@@ -71,6 +109,36 @@ atomic_fetch_add_l(volatile long *p, long v) // NOLINT(readability-non-const-par
|
||||
return __atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline long
|
||||
atomic_add_fetch_l(volatile long *p, long v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
return __atomic_add_fetch(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline long
|
||||
atomic_fetch_sub_l(volatile long *p, long v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
return __atomic_fetch_sub(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline long
|
||||
atomic_sub_fetch_l(volatile long *p, long v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
return __atomic_sub_fetch(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline long
|
||||
atomic_fetch_or_l(volatile long *p, long v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
return __atomic_fetch_or(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline long
|
||||
atomic_fetch_and_l(volatile long *p, long v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
return __atomic_fetch_and(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline long
|
||||
atomic_exchange_l(volatile long *p, long v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
#ifndef VLIBC_INTERNAL_FUTEX_H
|
||||
#define VLIBC_INTERNAL_FUTEX_H
|
||||
|
||||
/*
|
||||
* vlibc — internal futex substrate (todo 44).
|
||||
*
|
||||
* Linux futex operations over SYS_futex, the kernel's process-shared sleep
|
||||
* primitive that every lock, condvar, barrier, and rwlock in the library
|
||||
* rests on. Two calling conventions are deliberately provided:
|
||||
*
|
||||
* - __futex() is the RAW seam: it returns exactly what the kernel put in
|
||||
* rax (0 or a positive value on success, a NEGATIVE errno on failure)
|
||||
* and never touches errno. Consumers that need custom error handling
|
||||
* (retry-on-EINTR, -ENOSYS fallbacks, or errno-code returns) use this.
|
||||
*
|
||||
* - __futex_wait() / __futex_timedwait() / __futex_wake() are the mapped
|
||||
* wrappers in the house syscall_ret() convention: 0 (or the number of
|
||||
* waiters woken) on success, -1 with errno set on failure. These are
|
||||
* what the pthread layer (#45-#48) calls.
|
||||
*
|
||||
* Op codes and flags are kernel UAPI constants (linux/futex.h); the values
|
||||
* are pinned below so this header stays self-contained. FUTEX_PRIVATE adds
|
||||
* no extra atomics: the wait word is shared only between threads of one
|
||||
* process. The ancient-kernel ENOSYS fallback path musl carries (retry
|
||||
* without FUTEX_PRIVATE) is deliberately NOT implemented — every kernel
|
||||
* since 2.6.22 handles the private flag, and the plan targets modern
|
||||
* x86_64 only.
|
||||
*
|
||||
* Timeout semantics: __futex_timedwait() takes an ABSOLUTE deadline and
|
||||
* uses FUTEX_WAIT_BITSET (kernel-compared, no userspace clock read):
|
||||
* CLOCK_MONOTONIC by default, CLOCK_REALTIME when the FUTEX_CLOCK_REALTIME
|
||||
* flag is added. A deadline already in the past returns -1/ETIMEDOUT from
|
||||
* the kernel immediately. musl's relative-timeout __timedwait shape is not
|
||||
* copied: it exists there only to keep pre-WAIT_BITSET kernels working.
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include "../internal/libc.h"
|
||||
|
||||
/* futex op codes (linux/futex.h). */
|
||||
#define FUTEX_WAIT 0
|
||||
#define FUTEX_WAKE 1
|
||||
#define FUTEX_FD 2
|
||||
#define FUTEX_REQUEUE 3
|
||||
#define FUTEX_CMP_REQUEUE 4
|
||||
#define FUTEX_WAKE_OP 5
|
||||
#define FUTEX_LOCK_PI 6
|
||||
#define FUTEX_UNLOCK_PI 7
|
||||
#define FUTEX_TRYLOCK_PI 8
|
||||
#define FUTEX_WAIT_BITSET 9
|
||||
|
||||
/* op flags (linux/futex.h). */
|
||||
#define FUTEX_PRIVATE 128
|
||||
#define FUTEX_CLOCK_REALTIME 256
|
||||
|
||||
/* bitset for "any bits match" (linux/futex.h). */
|
||||
#define FUTEX_BITSET_MATCH_ANY 0xffffffffu
|
||||
|
||||
/*
|
||||
* The __futex family lives in the implementation-reserved namespace, same
|
||||
* as the __syscall<n> seam — intentional, waived here for the whole block.
|
||||
*/
|
||||
// NOLINTBEGIN(bugprone-reserved-identifier)
|
||||
|
||||
/*
|
||||
* Raw futex syscall: return the kernel's raw result — 0 or a positive value
|
||||
* on success, a negative errno on failure. errno is never touched. op may
|
||||
* be any FUTEX_* op code; ts is the timeout pointer for FUTEX_WAIT /
|
||||
* FUTEX_WAIT_BITSET, NULL for the others.
|
||||
*/
|
||||
hidden long
|
||||
__futex(volatile int *addr, int op, int val, void *ts);
|
||||
|
||||
/*
|
||||
* Mapped wait: block until *addr != val or a wakeup arrives. Returns 0 on
|
||||
* wakeup, -1 with errno set on failure. priv != 0 selects FUTEX_PRIVATE.
|
||||
*/
|
||||
hidden int
|
||||
__futex_wait(volatile int *addr, int val, int priv);
|
||||
|
||||
/*
|
||||
* Mapped timed wait: like __futex_wait, but with an ABSOLUTE deadline on
|
||||
* clock clk (CLOCK_MONOTONIC or CLOCK_REALTIME). Returns 0 on wakeup, -1
|
||||
* with errno set (ETIMEDOUT when the deadline passes, EINTR when a signal
|
||||
* interrupts the wait) on failure.
|
||||
*/
|
||||
hidden int
|
||||
__futex_timedwait(volatile int *addr, int val, int priv, int clk, const struct timespec *at);
|
||||
|
||||
/*
|
||||
* Mapped wake: wake up to cnt waiters on *addr (cnt < 0 = wake all, the
|
||||
* INT_MAX kernel convention musl uses). Returns the number of waiters
|
||||
* actually woken, or -1 with errno set on failure.
|
||||
*/
|
||||
hidden int
|
||||
__futex_wake(volatile int *addr, int cnt, int priv);
|
||||
|
||||
// NOLINTEND(bugprone-reserved-identifier)
|
||||
|
||||
#endif /* VLIBC_INTERNAL_FUTEX_H */
|
||||
@@ -0,0 +1,73 @@
|
||||
#ifndef VLIBC_INTERNAL_STRTOX_H
|
||||
#define VLIBC_INTERNAL_STRTOX_H
|
||||
|
||||
/*
|
||||
* vlibc — internal numeric-scan engine (todo 11).
|
||||
*
|
||||
* This header declares the shared core of the strtol/strtoul family. The
|
||||
* engine performs C23 7.24.1.4 steps 1-7 — whitespace skip, optional sign,
|
||||
* base resolution (0/2..36 with the 0x/0 prefix rules), and saturating
|
||||
* digit accumulation — and reports the subject sequence's magnitude, sign,
|
||||
* and overflow status. The public wrappers in src/stdlib/strtox_impl.c own
|
||||
* the per-type clamping and errno policy (signed clamps at LONG_MIN/LONG_MAX
|
||||
* with ERANGE; unsigned negates in the return type modulo 2^width, with
|
||||
* ERANGE only when the magnitude itself overflows the type, C23 7.24.1.4p8).
|
||||
*
|
||||
* Todo 14 (strtoimax/strtoumax, which on x86_64 are long/unsigned long)
|
||||
* consumes this header and reuses the engine unchanged; the wcsto* family
|
||||
* only needs a byte-level shim before it.
|
||||
*
|
||||
* The helpers below are static inline so the engine and every wrapper share
|
||||
* one C-locale whitespace/digit definition without a call boundary.
|
||||
*/
|
||||
|
||||
#include "libc.h"
|
||||
|
||||
/* Scan results. */
|
||||
#define STRTOX_OK 0 /* subject sequence present (digits > 0) */
|
||||
#define STRTOX_EMPTY 1 /* no subject sequence; *endptr = nptr */
|
||||
#define STRTOX_BADBASE 2 /* base not in {0} union [2, 36]; *endptr = nptr */
|
||||
|
||||
/*
|
||||
* Scan the subject sequence at nptr (C23 7.24.1.4 steps 1-7).
|
||||
*
|
||||
* On STRTOX_OK: *mag holds the magnitude (exact unless *over), *neg the
|
||||
* sign, *endptr the position past the last digit, *over whether the
|
||||
* magnitude exceeded unsigned long long (callers must clamp + ERANGE).
|
||||
* On STRTOX_EMPTY / STRTOX_BADBASE: *endptr is set to nptr, *mag and *neg
|
||||
* are zeroed; the caller sets errno = EINVAL for STRTOX_BADBASE and
|
||||
* returns 0.
|
||||
*/
|
||||
// NOLINTBEGIN(bugprone-reserved-identifier)
|
||||
hidden int
|
||||
__strtox_scan(const char *nptr, char **endptr, int base, int *neg, unsigned long long *mag,
|
||||
int *over);
|
||||
// NOLINTEND(bugprone-reserved-identifier)
|
||||
|
||||
/* C-locale whitespace: exactly ' ' and '\t'..'\r'. */
|
||||
static inline int
|
||||
strtox_isspace(int c)
|
||||
{
|
||||
return c == ' ' || (unsigned int)c - '\t' < 5U;
|
||||
}
|
||||
|
||||
/*
|
||||
* Value of c as a base-36 digit, or -1 when c is not an ASCII letter/digit.
|
||||
* c | 0x20 folds 'A'..'F' to 'a'..'f'; the unsigned cast keeps negative
|
||||
* values out of the range checks (same arithmetic-range idiom as ctype.c).
|
||||
*/
|
||||
static inline int
|
||||
strtox_digit(int c)
|
||||
{
|
||||
if ((unsigned int)c - '0' < 10U)
|
||||
{
|
||||
return c - '0';
|
||||
}
|
||||
if ((unsigned int)(c | 0x20) - 'a' < 26U)
|
||||
{
|
||||
return (c | 0x20) - 'a' + 10;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif /* VLIBC_INTERNAL_STRTOX_H */
|
||||
+236
@@ -0,0 +1,236 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
#include <math.h>
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#define SET_ERANGE() (errno = ERANGE)
|
||||
#define SET_EDOM() (errno = EDOM)
|
||||
#else
|
||||
#define SET_ERANGE() \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
#define SET_EDOM() \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
/* cbrt(x) = cube root of x, after musl src/math/cbrt.c (FreeBSD msun
|
||||
* s_cbrt.c, Bruce D. Evans): a 5-bit bit-hack estimate, a degree-4
|
||||
* polynomial refinement to 23 bits, and one Newton step to 53 bits. */
|
||||
static const unsigned int B1 = 715094163; /* B1 = (1023-1023/3-0.03306235651)*2**20 */
|
||||
static const unsigned int B2 = 696219795; /* B2 = (1023-1023/3-54/3-0.03306235651)*2**20 */
|
||||
|
||||
/* |1/cbrt(x) - p(x)| < 2**-23.5 (~[-7.93e-8, 7.929e-8]). */
|
||||
static const double P0 = 1.87595182427177009643; /* 0x3ffe03e60f61e692 */
|
||||
static const double P1 = -1.88497979543377169875; /* 0xbffe28e092f02420 */
|
||||
static const double P2 = 1.621429720105354466140; /* 0x3ff9f1604a49d6c2 */
|
||||
static const double P3 = -0.758397934778766047437; /* 0xbfe844cbbee751d9 */
|
||||
static const double P4 = 0.145996192886612446982; /* 0x3fc2b000d4e4edd7 */
|
||||
|
||||
double
|
||||
cbrt(double x)
|
||||
{
|
||||
union
|
||||
{
|
||||
double f;
|
||||
unsigned long long i;
|
||||
} u;
|
||||
double r;
|
||||
double s;
|
||||
double t;
|
||||
double w;
|
||||
unsigned int hx;
|
||||
|
||||
u.f = x;
|
||||
hx = (unsigned int)(u.i >> 32) & 0x7fffffffU;
|
||||
if (hx >= 0x7ff00000U)
|
||||
return x + x; /* cbrt(NaN,Inf) is itself */
|
||||
|
||||
/* rough cbrt to 5 bits */
|
||||
if (hx < 0x00100000U)
|
||||
{ /* zero or subnormal? */
|
||||
u.f = x * 0x1p54;
|
||||
hx = (unsigned int)(u.i >> 32) & 0x7fffffffU;
|
||||
if (hx == 0)
|
||||
return x; /* cbrt(0) is itself */
|
||||
hx = hx / 3 + B2;
|
||||
}
|
||||
else
|
||||
hx = hx / 3 + B1;
|
||||
u.i &= 1ULL << 63;
|
||||
u.i |= (unsigned long long)hx << 32;
|
||||
t = u.f;
|
||||
|
||||
/* new cbrt to 23 bits */
|
||||
r = (t * t) * (t / x);
|
||||
t = t * ((P0 + r * (P1 + r * P2)) + ((r * r) * r) * (P3 + r * P4));
|
||||
|
||||
/* round t away from zero to 23 bits, larger in magnitude than cbrt(x) */
|
||||
u.f = t;
|
||||
u.i = (u.i + 0x80000000ULL) & 0xffffffffc0000000ULL;
|
||||
t = u.f;
|
||||
|
||||
/* one Newton iteration to 53 bits with error < 0.667 ulps */
|
||||
s = t * t; /* t*t is exact */
|
||||
r = x / s; /* error <= 0.5 ulps; |r| < |t| */
|
||||
w = t + t; /* t+t is exact */
|
||||
r = (r - t) / (w + r);
|
||||
t = t + t * r;
|
||||
return t;
|
||||
}
|
||||
|
||||
/* cbrtf(x) = cube root of x, after musl src/math/cbrtf.c: the 5-bit
|
||||
* bit-hack plus two Newton steps evaluated in double precision. */
|
||||
static const unsigned int B1f = 709958130; /* B1f = (127-127.0/3-0.03306235651)*2**23 */
|
||||
static const unsigned int B2f = 642849266; /* B2f = (127-127.0/3-24/3-0.03306235651)*2**23 */
|
||||
|
||||
float
|
||||
cbrtf(float x)
|
||||
{
|
||||
union
|
||||
{
|
||||
float f;
|
||||
unsigned int i;
|
||||
} u;
|
||||
double r;
|
||||
double T;
|
||||
unsigned int hx;
|
||||
|
||||
u.f = x;
|
||||
hx = u.i & 0x7fffffffU;
|
||||
if (hx >= 0x7f800000U)
|
||||
return x + x; /* cbrt(NaN,Inf) is itself */
|
||||
|
||||
/* rough cbrt to 5 bits */
|
||||
if (hx < 0x00800000U)
|
||||
{ /* zero or subnormal? */
|
||||
if (hx == 0)
|
||||
return x; /* cbrt(+-0) is itself */
|
||||
u.f = x * 0x1p24f;
|
||||
hx = u.i & 0x7fffffffU;
|
||||
hx = hx / 3 + B2f;
|
||||
}
|
||||
else
|
||||
hx = hx / 3 + B1f;
|
||||
u.i &= 0x80000000U;
|
||||
u.i |= hx;
|
||||
|
||||
/* first Newton step (solving t*t - x/t == 0) to 16 bits, in double */
|
||||
T = u.f;
|
||||
r = T * T * T;
|
||||
T = T * ((double)x + x + r) / (x + r + r);
|
||||
|
||||
/* second Newton step to 47 bits; rounding to 24 bits is exact in
|
||||
* round-to-nearest mode */
|
||||
r = T * T * T;
|
||||
T = T * ((double)x + x + r) / (x + r + r);
|
||||
return (float)T;
|
||||
}
|
||||
|
||||
/* cbrtl(x) = cube root of x, after musl src/math/cbrtl.c (ld80 branch,
|
||||
* S. Kargl / B. Evans / D. Schultz): reduce x to [1,8) via e%3 and run the
|
||||
* cbrtf-style estimates in double, then one long-double Newton step. */
|
||||
static const unsigned int B1l = 709958130; /* B1l = (127-127.0/3-0.03306235651)*2**23 */
|
||||
|
||||
long double
|
||||
cbrtl(long double x)
|
||||
{
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} u;
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} v;
|
||||
union
|
||||
{
|
||||
float f;
|
||||
unsigned int i;
|
||||
} uft;
|
||||
long double r;
|
||||
long double s;
|
||||
long double t;
|
||||
long double w;
|
||||
double dr;
|
||||
double dt;
|
||||
double dx;
|
||||
float ft;
|
||||
int e;
|
||||
int sign;
|
||||
|
||||
u.f = x;
|
||||
e = u.p.se & 0x7fff;
|
||||
sign = u.p.se & 0x8000;
|
||||
if (e == 0x7fff)
|
||||
return x + x; /* cbrt(+-Inf) = +-Inf, cbrt(NaN) = NaN */
|
||||
if (e == 0)
|
||||
{ /* adjust subnormal numbers */
|
||||
u.f *= 0x1p120L;
|
||||
e = u.p.se & 0x7fff;
|
||||
if (e == 0)
|
||||
return x; /* cbrt(+-0) = +-0 */
|
||||
e -= 120;
|
||||
}
|
||||
e -= 0x3fff;
|
||||
u.p.se = 0x3fff;
|
||||
x = u.f;
|
||||
switch (e % 3)
|
||||
{
|
||||
case 1:
|
||||
case -2:
|
||||
x *= 2;
|
||||
e--;
|
||||
break;
|
||||
case 2:
|
||||
case -1:
|
||||
x *= 4;
|
||||
e -= 2;
|
||||
break;
|
||||
}
|
||||
v.f = 1.0L;
|
||||
v.p.se = (unsigned short)(sign | (0x3fff + e / 3));
|
||||
|
||||
/* ~5-bit estimate */
|
||||
uft.f = x;
|
||||
uft.i = (uft.i & 0x7fffffffU) / 3 + B1l;
|
||||
ft = uft.f;
|
||||
|
||||
/* ~16-bit estimate */
|
||||
dx = x;
|
||||
dt = ft;
|
||||
dr = dt * dt * dt;
|
||||
dt = dt * (dx + dx + dr) / (dx + dr + dr);
|
||||
|
||||
/* ~47-bit estimate */
|
||||
dr = dt * dt * dt;
|
||||
dt = dt * (dx + dx + dr) / (dx + dr + dr);
|
||||
|
||||
/* round dt away from zero to 32 bits so t*t is exact */
|
||||
t = dt + (0x1.0p32L + 0x1.0p-31L) - 0x1.0p32;
|
||||
|
||||
/* final Newton iteration to 64 bits with error < 0.667 ulps */
|
||||
s = t * t; /* t*t is exact */
|
||||
r = x / s; /* error <= 0.5 ulps; |r| < |t| */
|
||||
w = t + t; /* t+t is exact */
|
||||
r = (r - t) / (w + r);
|
||||
t = t + t * r;
|
||||
t *= v.f;
|
||||
return t;
|
||||
}
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* Smallest integral value not less than x (C23 7.12.9.1), all three
|
||||
* precisions.
|
||||
*
|
||||
* As with floor (see floor.c), GCC does not fold the __builtin_ceil forms
|
||||
* on this target, so each function works on the IEEE 754 bit pattern.
|
||||
* Clearing the fraction bits rounds toward zero, which is already the
|
||||
* correct direction for a negative argument; a positive argument with a
|
||||
* nonzero fraction must then step up by one (an exact integer + 1.0).
|
||||
* ceil(-0.3) is a real -0.0, and ±0/±Inf/NaN pass through unchanged.
|
||||
*/
|
||||
|
||||
static double
|
||||
ceil_d(double x)
|
||||
{
|
||||
const unsigned long long sign_mask = 1ULL << 63;
|
||||
unsigned long long bits;
|
||||
unsigned long long frac;
|
||||
int e;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
e = (int)((bits >> 52) & 0x7ff);
|
||||
|
||||
if (e >= 1075)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e < 1023)
|
||||
{
|
||||
if ((bits & ~sign_mask) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
return (bits & sign_mask) != 0 ? -0.0 : 1.0;
|
||||
}
|
||||
|
||||
shift = 1075 - e;
|
||||
frac = (1ULL << shift) - 1;
|
||||
if ((bits & sign_mask) != 0)
|
||||
{
|
||||
bits &= ~frac;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
if ((bits & frac) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
bits &= ~frac;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x + 1.0;
|
||||
}
|
||||
|
||||
static float
|
||||
ceil_f(float x)
|
||||
{
|
||||
const unsigned int sign_mask = 1U << 31;
|
||||
unsigned int bits;
|
||||
unsigned int frac;
|
||||
int e;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
e = (int)((bits >> 23) & 0xff);
|
||||
|
||||
if (e >= 150)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e < 127)
|
||||
{
|
||||
if ((bits & ~sign_mask) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
return (bits & sign_mask) != 0 ? -0.0f : 1.0f;
|
||||
}
|
||||
|
||||
shift = 150 - e;
|
||||
frac = (1U << shift) - 1U;
|
||||
if ((bits & sign_mask) != 0)
|
||||
{
|
||||
bits &= ~frac;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
if ((bits & frac) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
bits &= ~frac;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x + 1.0f;
|
||||
}
|
||||
|
||||
static long double
|
||||
ceil_ld(long double x)
|
||||
{
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
unsigned long long frac;
|
||||
int e;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&p, &x, sizeof p);
|
||||
e = p.se & 0x7fff;
|
||||
|
||||
if (e >= 16446)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e < 16383)
|
||||
{
|
||||
if (p.m == 0 && (p.se & 0x7fff) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
return (p.se & 0x8000) != 0 ? -0.0L : 1.0L;
|
||||
}
|
||||
|
||||
shift = 63 - (e - 16383);
|
||||
frac = (1ULL << shift) - 1ULL;
|
||||
if ((p.se & 0x8000) != 0)
|
||||
{
|
||||
p.m &= ~frac;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
if ((p.m & frac) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
p.m &= ~frac;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x + 1.0L;
|
||||
}
|
||||
|
||||
/*
|
||||
* As ceil, for a float argument.
|
||||
*/
|
||||
float
|
||||
ceilf(float x)
|
||||
{
|
||||
return ceil_f(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* As ceil, for a double argument.
|
||||
*/
|
||||
double
|
||||
ceil(double x)
|
||||
{
|
||||
return ceil_d(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* As ceil, for a long double argument.
|
||||
*/
|
||||
long double
|
||||
ceill(long double x)
|
||||
{
|
||||
return ceil_ld(x);
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* Copy the sign of y onto the magnitude of x, all three precisions (C23
|
||||
* 7.12.7.3). Each function below splices the sign bit of y into the IEEE
|
||||
* 754 bit pattern of x: the magnitude of x is untouched (so a NaN keeps
|
||||
* its payload) and copysign(±0, y) carries y's sign. GCC's
|
||||
* __builtin_copysign forms do fold to andp/orp pairs on this target, but
|
||||
* the compiler diagnoses the __builtin_ call inside the identically named
|
||||
* function as infinite recursion, so the splice is written out directly;
|
||||
* it compiles to the same two instructions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* A float with the magnitude of x and the sign of y.
|
||||
*/
|
||||
float
|
||||
copysignf(float x, float y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
unsigned int xb;
|
||||
unsigned int yb;
|
||||
|
||||
__builtin_memcpy(&xb, &x, sizeof xb);
|
||||
__builtin_memcpy(&yb, &y, sizeof yb);
|
||||
xb = (xb & 0x7fffffffU) | (yb & 0x80000000U);
|
||||
__builtin_memcpy(&x, &xb, sizeof xb);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* A double with the magnitude of x and the sign of y.
|
||||
*/
|
||||
double
|
||||
copysign(double x, double y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
unsigned long long xb;
|
||||
unsigned long long yb;
|
||||
|
||||
__builtin_memcpy(&xb, &x, sizeof xb);
|
||||
__builtin_memcpy(&yb, &y, sizeof yb);
|
||||
xb = (xb & 0x7fffffffffffffffULL) | (yb & 0x8000000000000000ULL);
|
||||
__builtin_memcpy(&x, &xb, sizeof xb);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* A long double with the magnitude of x and the sign of y. The x86 80-bit
|
||||
* extended format keeps the sign in bit 15 of the sign/exponent word at
|
||||
* bytes 8..9, so only that bit is spliced.
|
||||
*/
|
||||
long double
|
||||
copysignl(long double x, long double y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} xp;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} yp;
|
||||
|
||||
__builtin_memcpy(&xp, &x, sizeof xp);
|
||||
__builtin_memcpy(&yp, &y, sizeof yp);
|
||||
xp.se = (unsigned short)((xp.se & 0x7fff) | (yp.se & 0x8000));
|
||||
__builtin_memcpy(&x, &xp, sizeof xp);
|
||||
return x;
|
||||
}
|
||||
+661
@@ -0,0 +1,661 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
#include <math.h>
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#define SET_ERANGE() (errno = ERANGE)
|
||||
#else
|
||||
#define SET_ERANGE() \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
static const double ln2hi = 6.93147180369123816490e-01; /* 0x3fe62e42fee00000 */
|
||||
static const double ln2lo = 1.90821492927058770002e-10; /* 0x3dea39ef35793c76 */
|
||||
static const double invln2 = 1.44269504088896338700e+00; /* 0x3ff71547652b82fe */
|
||||
static const double P1 = 1.66666666666666019037e-01; /* 0x3FC555555555553E */
|
||||
static const double P2 = -2.77777777770155933842e-03; /* 0xBF66C16C16BEBD93 */
|
||||
static const double P3 = 6.61375632143793436117e-05; /* 0x3F11566AAF25DE2C */
|
||||
static const double P4 = -1.65339022054652515390e-06; /* 0xBEBBBD41C5D26BF1 */
|
||||
static const double P5 = 4.13813679705723846039e-08; /* 0x3E66376972BEA4D0 */
|
||||
static const double o_threshold = 709.782712893384;
|
||||
static const double u_threshold = -745.133219101941;
|
||||
static const double twom1000 = 9.33263618503218878990e-302;
|
||||
static const double tiny = 1.0e-300;
|
||||
|
||||
static double
|
||||
vlibc_exp_core(double x)
|
||||
{
|
||||
union
|
||||
{
|
||||
double f;
|
||||
unsigned long long i;
|
||||
} u;
|
||||
|
||||
u.f = x;
|
||||
unsigned long long sign = u.i >> 63;
|
||||
unsigned int hx = (unsigned int)((u.i >> 32) & 0x7fffffffULL);
|
||||
|
||||
/* NaN or +/-Inf: exp(-inf)=+0, exp(+inf)=+inf, exp(NaN)=NaN */
|
||||
if ((u.i & 0x7fffffffffffffffULL) >= 0x7ff0000000000000ULL)
|
||||
{
|
||||
if ((u.i & 0x000fffffffffffffULL) != 0ULL)
|
||||
return x + x; /* NaN */
|
||||
return sign != 0ULL ? 0.0 : __builtin_huge_val();
|
||||
}
|
||||
|
||||
if (x > o_threshold)
|
||||
return __builtin_huge_val(); /* caller marks ERANGE */
|
||||
if (x < u_threshold)
|
||||
return 0.0; /* caller marks ERANGE */
|
||||
|
||||
double hi = 0.0;
|
||||
double lo = 0.0;
|
||||
int k = 0;
|
||||
|
||||
if (hx > 0x3fd62e42U)
|
||||
{ /* |x| > 0.5 ln2 */
|
||||
if (hx < 0x3ff0a2b2U)
|
||||
{ /* |x| < 1.5 ln2 */
|
||||
if (sign == 0ULL)
|
||||
{
|
||||
hi = x - ln2hi;
|
||||
lo = ln2lo;
|
||||
k = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
hi = x + ln2hi;
|
||||
lo = -ln2lo;
|
||||
k = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
k = (int)(invln2 * x + (sign != 0ULL ? -0.5 : 0.5));
|
||||
double t = (double)k;
|
||||
hi = x - t * ln2hi;
|
||||
lo = t * ln2lo;
|
||||
}
|
||||
x = hi - lo;
|
||||
}
|
||||
else if (hx < 0x3e300000U)
|
||||
{ /* |x| < 2^-28: exp(x) ~ 1+x */
|
||||
(void)tiny;
|
||||
return 1.0 + x;
|
||||
}
|
||||
else
|
||||
{
|
||||
k = 0;
|
||||
}
|
||||
|
||||
double t = x * x;
|
||||
double c = x - t * (P1 + t * (P2 + t * (P3 + t * (P4 + t * P5))));
|
||||
|
||||
if (k == 0)
|
||||
return 1.0 - ((x * c) / (c - 2.0) - x);
|
||||
|
||||
double y = 1.0 - ((lo - (x * c) / (2.0 - c)) - hi);
|
||||
|
||||
/* scale y by 2^k by adding k to the exponent field */
|
||||
if (k >= -1021 && k <= 1023)
|
||||
{
|
||||
union
|
||||
{
|
||||
double f;
|
||||
unsigned long long i;
|
||||
} b;
|
||||
b.f = y;
|
||||
b.i += (unsigned long long)k << 52;
|
||||
return b.f;
|
||||
}
|
||||
if (k == 1024)
|
||||
return y * 2.0 * 0x1p1023;
|
||||
/* k <= -1022: scale up into the normal range, then multiply down */
|
||||
union
|
||||
{
|
||||
double f;
|
||||
unsigned long long i;
|
||||
} b;
|
||||
b.f = y;
|
||||
b.i += (unsigned long long)(k + 1000) << 52;
|
||||
return b.f * twom1000;
|
||||
}
|
||||
|
||||
double
|
||||
exp(double x)
|
||||
{
|
||||
if (x > o_threshold)
|
||||
SET_ERANGE();
|
||||
else if (x < u_threshold)
|
||||
SET_ERANGE();
|
||||
return vlibc_exp_core(x);
|
||||
}
|
||||
|
||||
#ifndef exp
|
||||
double
|
||||
exp(double);
|
||||
#endif
|
||||
|
||||
double
|
||||
exp2(double x)
|
||||
{
|
||||
union
|
||||
{
|
||||
double f;
|
||||
unsigned long long i;
|
||||
} u;
|
||||
|
||||
u.f = x;
|
||||
|
||||
/* NaN or +/-Inf: 2^(-inf)=+0, 2^(+inf)=+inf, 2^(NaN)=NaN */
|
||||
if ((u.i & 0x7fffffffffffffffULL) >= 0x7ff0000000000000ULL)
|
||||
{
|
||||
if ((u.i & 0x000fffffffffffffULL) != 0ULL)
|
||||
return x; /* NaN */
|
||||
return (u.i >> 63) != 0ULL ? 0.0 : __builtin_huge_val();
|
||||
}
|
||||
|
||||
if (x >= 1024.0)
|
||||
{
|
||||
SET_ERANGE();
|
||||
return __builtin_huge_val();
|
||||
}
|
||||
if (x <= -1075.0)
|
||||
{
|
||||
SET_ERANGE();
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
/* round to nearest integer; |x| < 1024 so the conversion is safe */
|
||||
long long k = (long long)(x + (x > 0.0 ? 0.5 : -0.5));
|
||||
/* exact by Sterbenz: k is within 2^52 of x */
|
||||
double r = x - (double)k;
|
||||
/* e^(r ln2) = 2^r */
|
||||
double y = vlibc_exp_core(r * 0.6931471805599453);
|
||||
|
||||
/* scale y by 2^k by adding k to the exponent field */
|
||||
if (k >= -1021 && k <= 1023)
|
||||
{
|
||||
union
|
||||
{
|
||||
double f;
|
||||
unsigned long long i;
|
||||
} b;
|
||||
b.f = y;
|
||||
b.i += (unsigned long long)k << 52;
|
||||
return b.f;
|
||||
}
|
||||
if (k == 1024)
|
||||
return y * 2.0 * 0x1p1023;
|
||||
/* k <= -1022: scale up into the normal range, then multiply down */
|
||||
union
|
||||
{
|
||||
double f;
|
||||
unsigned long long i;
|
||||
} b;
|
||||
b.f = y;
|
||||
b.i += (unsigned long long)(k + 1000) << 52;
|
||||
return b.f * twom1000;
|
||||
}
|
||||
|
||||
float
|
||||
exp2f(float x)
|
||||
{
|
||||
if (x >= 128.0f)
|
||||
return __builtin_huge_valf();
|
||||
if (x <= -150.0f)
|
||||
return 0.0f;
|
||||
return (float)exp2((double)x);
|
||||
}
|
||||
|
||||
long double
|
||||
exp2l(long double x)
|
||||
{
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} u;
|
||||
|
||||
u.f = x;
|
||||
if ((u.p.se & 0x7fffU) == 0x7fffU)
|
||||
{
|
||||
if (u.p.m != 0x8000000000000000ULL)
|
||||
return x; /* NaN */
|
||||
return (u.p.se & 0x8000U) != 0 ? 0.0L : x; /* +/-Inf */
|
||||
}
|
||||
|
||||
if (x >= 16384.0L)
|
||||
{
|
||||
SET_ERANGE();
|
||||
return __builtin_huge_vall();
|
||||
}
|
||||
if (x <= -16445.0L)
|
||||
{
|
||||
SET_ERANGE();
|
||||
return 0.0L;
|
||||
}
|
||||
|
||||
/* round to nearest integer; |x| < 16384 so the conversion is safe */
|
||||
long long k = (long long)(x + (x > 0.0L ? 0.5L : -0.5L));
|
||||
/* exact by Sterbenz: k is within 2^63 of x */
|
||||
long double r = x - (long double)k;
|
||||
long double y = r * 0.693147180559945309417232121458176568L;
|
||||
|
||||
/* e^y, |y| <= ln2/2: Taylor series with compensated summation */
|
||||
long double s;
|
||||
if (y > -1e-8L && y < 1e-8L)
|
||||
s = 1.0L + y;
|
||||
else
|
||||
{
|
||||
long double term = y;
|
||||
long double comp = 0.0L;
|
||||
s = 1.0L + y;
|
||||
for (int i = 2; i <= 60; i++)
|
||||
{
|
||||
term *= y / (long double)i;
|
||||
long double yk = term - comp;
|
||||
long double t = s + yk;
|
||||
comp = (t - s) - yk;
|
||||
s = t;
|
||||
if (term > -1e-30L && term < 1e-30L)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (k < -16445)
|
||||
return x > 0.0L ? 0.0L : -0.0L; /* unreachable; defensive */
|
||||
|
||||
/* scale s by 2^k: construct 2^k in long double and multiply */
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} two;
|
||||
|
||||
if (k >= -16382 && k <= 16383)
|
||||
{
|
||||
two.p.m = 0x8000000000000000ULL;
|
||||
two.p.se = (unsigned short)(16383 + (int)k);
|
||||
return s * two.f;
|
||||
}
|
||||
if (k == 16384)
|
||||
{
|
||||
/* x in [16383.5, 16384): s = 2^r with r in [-0.5, 0) < 1, so
|
||||
* s * 2^16383 * 2 stays finite */
|
||||
two.p.m = 0x8000000000000000ULL;
|
||||
two.p.se = (unsigned short)(16383 + 16383);
|
||||
return s * two.f * 2.0L;
|
||||
}
|
||||
/* k in [-16445, -16383]: 2^k is a pseudo-denormal, int bit clear */
|
||||
two.p.m = 1ULL << (unsigned int)(k + 16445);
|
||||
two.p.se = 0;
|
||||
return s * two.f;
|
||||
}
|
||||
|
||||
static const double Q1 = -3.33333333333331316428e-02; /* 0xbfa1111111110f54 */
|
||||
static const double Q2 = 1.58730158725481460165e-03; /* 0x3f5a01a019fe5585 */
|
||||
static const double Q3 = -7.93650757867487942473e-05; /* 0xbf14ce199eaadbb7 */
|
||||
static const double Q4 = 4.00821782732936260352e-06; /* 0x3ed0cfca86e65239 */
|
||||
static const double Q5 = -2.01099218183624371326e-07; /* 0xbe8afdb76e09c32d */
|
||||
|
||||
/* expm1(x) = e^x - 1, accurate for small x (fdlibm s_expm1). */
|
||||
double
|
||||
expm1(double x)
|
||||
{
|
||||
union
|
||||
{
|
||||
double f;
|
||||
unsigned long long i;
|
||||
} u;
|
||||
double hi = 0.0;
|
||||
double lo = 0.0;
|
||||
double c = 0.0;
|
||||
unsigned int sign;
|
||||
unsigned int hx;
|
||||
int k = 0;
|
||||
|
||||
u.f = x;
|
||||
sign = (unsigned int)(u.i >> 63);
|
||||
hx = (unsigned int)((u.i >> 32) & 0x7fffffffULL);
|
||||
|
||||
/* filter out huge and non-finite argument */
|
||||
if (hx >= 0x7ff00000U)
|
||||
{
|
||||
if ((u.i & 0xfffffffffffffULL) != 0ULL)
|
||||
return x; /* NaN */
|
||||
return sign != 0U ? -1.0 : x; /* expm1(+-inf) = {-1, +inf} */
|
||||
}
|
||||
if (x == 0.0)
|
||||
return x; /* preserve -0.0 */
|
||||
|
||||
if (x > o_threshold)
|
||||
{
|
||||
SET_ERANGE();
|
||||
return __builtin_huge_val(); /* overflow */
|
||||
}
|
||||
if (sign != 0U && hx >= 0x4043687AU)
|
||||
return -1.0; /* x < -56*ln2, exp(x) < 2^-56 -> -1.0 */
|
||||
|
||||
/* argument reduction */
|
||||
if (hx > 0x3fd62e42U)
|
||||
{
|
||||
if (hx < 0x3ff0a2b2U)
|
||||
{
|
||||
if (sign == 0U)
|
||||
{
|
||||
hi = x - ln2hi;
|
||||
lo = ln2lo;
|
||||
k = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
hi = x + ln2hi;
|
||||
lo = -ln2lo;
|
||||
k = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
k = (int)(invln2 * x + (sign != 0U ? -0.5 : 0.5));
|
||||
{
|
||||
double t = (double)k;
|
||||
hi = x - t * ln2hi; /* t*ln2hi is exact */
|
||||
lo = t * ln2lo;
|
||||
}
|
||||
}
|
||||
x = hi - lo;
|
||||
c = (hi - x) - lo;
|
||||
}
|
||||
else if (hx < 0x3c900000U)
|
||||
{
|
||||
return x; /* |x| < 2^-54: expm1(x) == x */
|
||||
}
|
||||
else
|
||||
{
|
||||
k = 0;
|
||||
}
|
||||
|
||||
/* x is now in primary range */
|
||||
{
|
||||
double hfx = 0.5 * x;
|
||||
double hxs = x * hfx;
|
||||
double r1 = 1.0 + hxs * (Q1 + hxs * (Q2 + hxs * (Q3 + hxs * (Q4 + hxs * Q5))));
|
||||
double t = 3.0 - r1 * hfx;
|
||||
double e = hxs * ((r1 - t) / (6.0 - x * t));
|
||||
|
||||
if (k == 0)
|
||||
return x - (x * e - hxs); /* c is 0 */
|
||||
|
||||
e = (x * (e - c) - c) - hxs;
|
||||
if (k == -1)
|
||||
return 0.5 * (x - e) - 0.5;
|
||||
if (k == 1)
|
||||
{
|
||||
if (x < -0.25)
|
||||
return -2.0 * (e - (x + 0.5));
|
||||
return 1.0 + 2.0 * (x - e);
|
||||
}
|
||||
/* general: expm1(x) = 2^k * (1 - (e - x)) - 1 */
|
||||
{
|
||||
double y = 1.0 - (e - x);
|
||||
union
|
||||
{
|
||||
double f;
|
||||
unsigned long long i;
|
||||
} b;
|
||||
if (k == 1024)
|
||||
return y * 2.0 * 0x1p1023 - 1.0;
|
||||
b.f = y;
|
||||
if (k >= -1021)
|
||||
b.i += (unsigned long long)k << 52;
|
||||
else
|
||||
{
|
||||
b.i += (unsigned long long)(k + 1000) << 52;
|
||||
return b.f * twom1000 - 1.0;
|
||||
}
|
||||
return b.f - 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float
|
||||
expm1f(float x)
|
||||
{
|
||||
if (x >= 88.72283905206835f && !isinf(x))
|
||||
{
|
||||
SET_ERANGE();
|
||||
return __builtin_huge_valf(); /* overflow */
|
||||
}
|
||||
return (float)expm1((double)x);
|
||||
}
|
||||
|
||||
/* 80-bit x87 expm1l */
|
||||
long double
|
||||
expm1l(long double x)
|
||||
{
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} u;
|
||||
long double r;
|
||||
long double sum;
|
||||
long long k;
|
||||
|
||||
u.f = x;
|
||||
if ((u.p.se & 0x7fffU) == 0x7fffU)
|
||||
{
|
||||
if (u.p.m != 0x8000000000000000ULL)
|
||||
return x; /* NaN */
|
||||
return (u.p.se & 0x8000U) != 0U ? -1.0L : x; /* expm1(+-inf) = {-1, +inf} */
|
||||
}
|
||||
if (x == 0.0L)
|
||||
return x;
|
||||
|
||||
if (x > 11356.523406294143L)
|
||||
{
|
||||
SET_ERANGE();
|
||||
return __builtin_huge_vall();
|
||||
}
|
||||
if (x < -16447.0L)
|
||||
return -1.0L;
|
||||
|
||||
/* 2^x = 2^k * e^r with r in [-ln2/2, ln2/2]; ln2 split hi/lo so that
|
||||
* k*ln2hi is exact and r keeps full 64-bit precision */
|
||||
{
|
||||
const long double ln2hi_l = 6.93147180369123816490e-01L;
|
||||
const long double ln2lo_l = 1.90821492927058770002e-10L;
|
||||
const long double invln2l = 1.442695040888963407359924681001892137L;
|
||||
k = (long long)(x * invln2l + (x > 0.0L ? 0.5L : -0.5L));
|
||||
r = x - (long double)k * ln2hi_l - (long double)k * ln2lo_l;
|
||||
}
|
||||
|
||||
/* expm1(r) = e^r - 1 via Kahan-compensated Taylor sum */
|
||||
{
|
||||
long double term = r;
|
||||
long double comp = 0.0L;
|
||||
int i;
|
||||
sum = r;
|
||||
for (i = 2; i <= 60; i++)
|
||||
{
|
||||
long double yt;
|
||||
long double t;
|
||||
term *= r / (long double)i;
|
||||
if (term == 0.0L)
|
||||
break;
|
||||
yt = term - comp;
|
||||
t = sum + yt;
|
||||
comp = (t - sum) - yt;
|
||||
sum = t;
|
||||
}
|
||||
}
|
||||
|
||||
if (k == 0)
|
||||
return sum;
|
||||
|
||||
/* scale (sum + 1) by 2^k and subtract 1 */
|
||||
{
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} b;
|
||||
long double z = sum + 1.0L;
|
||||
|
||||
if (k >= 16384)
|
||||
return z * 2.0L * 0x1p16383L - 1.0L;
|
||||
if (k >= -16382)
|
||||
{
|
||||
b.p.m = 0x8000000000000000ULL;
|
||||
b.p.se = (unsigned short)(16383 + k);
|
||||
return z * b.f - 1.0L;
|
||||
}
|
||||
/* k in [-16445, -16383]: e^x < 2^-16382, so e^x - 1 rounds to -1 */
|
||||
return -1.0L;
|
||||
}
|
||||
}
|
||||
|
||||
float
|
||||
expf(float x)
|
||||
{
|
||||
if (isinf(x))
|
||||
return x > 0.0f ? x : 0.0f; /* exp(+inf) = +inf, exp(-inf) = +0 */
|
||||
if (x >= 88.72283905206835f)
|
||||
{
|
||||
SET_ERANGE();
|
||||
return __builtin_huge_valf(); /* overflow */
|
||||
}
|
||||
if (x < -103.97207708219999f)
|
||||
{
|
||||
SET_ERANGE();
|
||||
return 0.0f; /* underflow to zero */
|
||||
}
|
||||
{
|
||||
double y = exp((double)x);
|
||||
if (y < 0x1p-149) /* glibc flags ERANGE below FLT_TRUE_MIN */
|
||||
SET_ERANGE();
|
||||
return (float)y;
|
||||
}
|
||||
}
|
||||
|
||||
/* 80-bit x87 expl: exp(x) = 2^k * e^r, r in [-ln2/2, ln2/2] */
|
||||
long double
|
||||
expl(long double x)
|
||||
{
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} u;
|
||||
long double r;
|
||||
long double sum;
|
||||
long long k;
|
||||
|
||||
u.f = x;
|
||||
if ((u.p.se & 0x7fffU) == 0x7fffU)
|
||||
{
|
||||
if (u.p.m != 0x8000000000000000ULL)
|
||||
return x; /* NaN */
|
||||
return (u.p.se & 0x8000U) != 0U ? 0.0L : x; /* exp(+-inf) = {+0, inf} */
|
||||
}
|
||||
if (x == 0.0L)
|
||||
return 1.0L;
|
||||
|
||||
if (x >= 0xb.17217f7d1cf79acp+10L) /* first 80-bit x where e^x rounds to +inf */
|
||||
{
|
||||
SET_ERANGE();
|
||||
return __builtin_huge_vall();
|
||||
}
|
||||
if (x <= -11399.8484L) /* exp(x) < 2^-16446: rounds to +0 */
|
||||
{
|
||||
SET_ERANGE();
|
||||
return 0.0L;
|
||||
}
|
||||
|
||||
/* ln2 split so k*ln2hi is exact (15-bit hi) and r keeps full
|
||||
* 64-bit precision; k fits long long since |x| < 11356.6 */
|
||||
{
|
||||
const long double ln2hi_l = 6.9314575195312500000000e-1L;
|
||||
const long double ln2lo_l = 1.4286068203094172321214581765680755e-6L;
|
||||
const long double invln2l = 1.442695040888963407359924681001892137L;
|
||||
k = (long long)(x * invln2l + (x > 0.0L ? 0.5L : -0.5L));
|
||||
r = x - (long double)k * ln2hi_l;
|
||||
r -= (long double)k * ln2lo_l;
|
||||
}
|
||||
|
||||
/* e^r via Kahan-compensated Taylor */
|
||||
{
|
||||
long double term = r;
|
||||
long double comp = 0.0L;
|
||||
int i;
|
||||
sum = 1.0L + r;
|
||||
for (i = 2; i <= 60; i++)
|
||||
{
|
||||
long double yt;
|
||||
long double t;
|
||||
term *= r / (long double)i;
|
||||
if (term == 0.0L)
|
||||
break;
|
||||
yt = term - comp;
|
||||
t = sum + yt;
|
||||
comp = (t - sum) - yt;
|
||||
sum = t;
|
||||
}
|
||||
}
|
||||
|
||||
/* scale sum by 2^k */
|
||||
{
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} b;
|
||||
|
||||
if (k == 0)
|
||||
return sum;
|
||||
if (k >= 16384)
|
||||
return sum * 2.0L * 0x1p16383L; /* overflow path */
|
||||
if (k <= -16382)
|
||||
{
|
||||
/* result is subnormal: scale up by 2^(k+16382), then by 2^-16382 */
|
||||
b.p.m = 0x8000000000000000ULL;
|
||||
b.p.se = (unsigned short)(16383 + k + 16382);
|
||||
sum *= b.f;
|
||||
return sum * 0x1p-16382L;
|
||||
}
|
||||
b.p.m = 0x8000000000000000ULL;
|
||||
b.p.se = (unsigned short)(16383 + k);
|
||||
return sum * b.f;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* Absolute value, all three precisions (C23 7.12.7.2). GCC folds
|
||||
* __builtin_fabs/__builtin_fabsf/__builtin_fabsl into a single
|
||||
* sign-clearing SSE/x87 instruction at every optimization level (verified
|
||||
* at -O0 and -O2), so the call never recurses and no errno path exists.
|
||||
* The sign-bit clear is exact: fabs(-0.0) is +0.0, fabs(±Inf) is +Inf,
|
||||
* and a NaN keeps its payload.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Absolute value of x as a float.
|
||||
*/
|
||||
float
|
||||
fabsf(float x)
|
||||
{
|
||||
return __builtin_fabsf(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* Absolute value of x as a double.
|
||||
*/
|
||||
double
|
||||
fabs(double x)
|
||||
{
|
||||
return __builtin_fabs(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* Absolute value of x as a long double.
|
||||
*/
|
||||
long double
|
||||
fabsl(long double x)
|
||||
{
|
||||
return __builtin_fabsl(x);
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* The positive difference x - y when x > y and +0.0 otherwise (C23
|
||||
* 7.12.12.2), all three precisions.
|
||||
*
|
||||
* C23 keeps fdim(x, NaN) and fdim(NaN, x) NaN, so the two arguments are
|
||||
* screened before the ordering test; a NaN result is the quiet NaN that
|
||||
* the x + y addition below produces (matching the host glibc). With both
|
||||
* operands numeric, x > y is an ordinary comparison (no exceptions) and
|
||||
* the single subtraction x - y is the whole computation: an exact
|
||||
* representable difference stays exact, and a difference too large for the
|
||||
* format overflows through the hardware into +Inf exactly as glibc's does,
|
||||
* with the overflow flag raised and no extra help needed. x <= y -- the
|
||||
* signed-zero and equal cases included -- returns a plain +0.0.
|
||||
*/
|
||||
|
||||
/*
|
||||
* fdim of two floats.
|
||||
*/
|
||||
float
|
||||
fdimf(float x, float y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
if (isnan(x) || isnan(y))
|
||||
{
|
||||
return x + y;
|
||||
}
|
||||
if (x > y)
|
||||
{
|
||||
return x - y;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
/*
|
||||
* fdim of two doubles.
|
||||
*/
|
||||
double
|
||||
fdim(double x, double y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
if (isnan(x) || isnan(y))
|
||||
{
|
||||
return x + y;
|
||||
}
|
||||
if (x > y)
|
||||
{
|
||||
return x - y;
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
/*
|
||||
* fdim of two long doubles.
|
||||
*/
|
||||
long double
|
||||
fdiml(long double x, long double y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
if (isnan(x) || isnan(y))
|
||||
{
|
||||
return x + y;
|
||||
}
|
||||
if (x > y)
|
||||
{
|
||||
return x - y;
|
||||
}
|
||||
return 0.0L;
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* Largest integral value not greater than x (C23 7.12.9.2), all three
|
||||
* precisions.
|
||||
*
|
||||
* GCC emits an external floor/floorf/floorl call for the __builtin_ forms
|
||||
* at every optimization level on this target (no SSE4.1 in the default
|
||||
* -march, so the roundsd expansion is unavailable and the builtin is not
|
||||
* folded), so each function below is implemented directly on the IEEE 754
|
||||
* bit pattern instead: clearing the fraction bits rounds toward zero, and
|
||||
* a negative argument with a nonzero fraction must then step down by one.
|
||||
* The clearing and the exact integer - 1.0 are both exact, so no rounding
|
||||
* mode and no floating-point exception is involved; ±0/±Inf/NaN pass
|
||||
* through and signed zero is preserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The double format: bit 63 is the sign, bits 62..52 the exponent biased
|
||||
* by 1023, bits 51..0 the fraction. A finite value with exponent e has
|
||||
* |x| in [2^(e-1023), 2^(e-1022)) and a fraction only when e - 1023 < 52,
|
||||
* i.e. e < 1075; for e in [1023, 1074] exactly the low (1075 - e) bits of
|
||||
* the fraction word are the fractional part.
|
||||
*/
|
||||
static double
|
||||
floor_d(double x)
|
||||
{
|
||||
const unsigned long long sign_mask = 1ULL << 63;
|
||||
unsigned long long bits;
|
||||
unsigned long long frac;
|
||||
int e;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
e = (int)((bits >> 52) & 0x7ff);
|
||||
|
||||
/* |x| >= 2^52 is already integral; Inf (e == 0x7ff) and NaN must pass
|
||||
* through unchanged as well. */
|
||||
if (e >= 1075)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
/* |x| < 1: floor is +0 for a nonnegative x and -1 for a negative one,
|
||||
* except that ±0 is its own floor. */
|
||||
if (e < 1023)
|
||||
{
|
||||
if ((bits & ~sign_mask) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
return (bits & sign_mask) != 0 ? -1.0 : 0.0;
|
||||
}
|
||||
|
||||
shift = 1075 - e;
|
||||
frac = (1ULL << shift) - 1;
|
||||
if ((bits & sign_mask) == 0)
|
||||
{
|
||||
bits &= ~frac;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
/* Negative: round toward zero, then take one more step down when a
|
||||
* fraction was dropped. The truncation leaves an integer-valued
|
||||
* double, so subtracting 1.0 is exact. */
|
||||
if ((bits & frac) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
bits &= ~frac;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x - 1.0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The float format: bit 31 is the sign, bits 30..23 the exponent biased
|
||||
* by 127, bits 22..0 the fraction. Fraction bits exist exactly when the
|
||||
* exponent e is in [127, 149]; the low (150 - e) bits are fractional.
|
||||
*/
|
||||
static float
|
||||
floor_f(float x)
|
||||
{
|
||||
const unsigned int sign_mask = 1U << 31;
|
||||
unsigned int bits;
|
||||
unsigned int frac;
|
||||
int e;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
e = (int)((bits >> 23) & 0xff);
|
||||
|
||||
if (e >= 150)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e < 127)
|
||||
{
|
||||
if ((bits & ~sign_mask) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
return (bits & sign_mask) != 0 ? -1.0f : 0.0f;
|
||||
}
|
||||
|
||||
shift = 150 - e;
|
||||
frac = (1U << shift) - 1U;
|
||||
if ((bits & sign_mask) == 0)
|
||||
{
|
||||
bits &= ~frac;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
if ((bits & frac) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
bits &= ~frac;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x - 1.0f;
|
||||
}
|
||||
|
||||
/*
|
||||
* The x86 80-bit extended format: 64 significand bits m (the integer bit
|
||||
* is explicit) in bytes 0..7 and a sign/exponent word se in bytes 8..9,
|
||||
* with the sign in bit 15 and the exponent (biased by 16383) in bits
|
||||
* 14..0. A value m * 2^(e - 16446) has fractional bits only when
|
||||
* e - 16383 < 63, i.e. e < 16446; for e in [16383, 16445] exactly the low
|
||||
* (63 - (e - 16383)) bits of m are fractional.
|
||||
*/
|
||||
static long double
|
||||
floor_ld(long double x)
|
||||
{
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
unsigned long long frac;
|
||||
int e;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&p, &x, sizeof p);
|
||||
e = p.se & 0x7fff;
|
||||
|
||||
/* |x| >= 2^63 is already integral; Inf (e == 0x7fff) and NaN pass
|
||||
* through unchanged as well. */
|
||||
if (e >= 16446)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e < 16383)
|
||||
{
|
||||
if (p.m == 0 && (p.se & 0x7fff) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
return (p.se & 0x8000) != 0 ? -1.0L : 0.0L;
|
||||
}
|
||||
|
||||
shift = 63 - (e - 16383);
|
||||
frac = (1ULL << shift) - 1ULL;
|
||||
if ((p.se & 0x8000) == 0)
|
||||
{
|
||||
p.m &= ~frac;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
if ((p.m & frac) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
p.m &= ~frac;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x - 1.0L;
|
||||
}
|
||||
|
||||
/*
|
||||
* As floor, for a float argument.
|
||||
*/
|
||||
float
|
||||
floorf(float x)
|
||||
{
|
||||
return floor_f(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* As floor, for a double argument.
|
||||
*/
|
||||
double
|
||||
floor(double x)
|
||||
{
|
||||
return floor_d(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* As floor, for a long double argument.
|
||||
*/
|
||||
long double
|
||||
floorl(long double x)
|
||||
{
|
||||
return floor_ld(x);
|
||||
}
|
||||
+261
@@ -0,0 +1,261 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* The larger of x and y (C23 7.12.12.3), all three precisions.
|
||||
*
|
||||
* The mirror image of fmin (see fmin.c for the full reasoning): a quiet
|
||||
* NaN argument is ignored in favor of the numeric one, a signaling NaN
|
||||
* argument makes the result that signaling NaN quieted, and a +0.0/-0.0
|
||||
* pair returns +0.0. Only the equal-argument tie rule (and, with it, the
|
||||
* direction of the zero preference) differs from fmin; the numeric
|
||||
* comparisons, the NaN screens and the word-level quiet splice are
|
||||
* otherwise identical, so the implementation shares the shape of fmin.c
|
||||
* and is likewise pure.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The double format: bit 63 is the sign, bits 62..52 the exponent biased
|
||||
* by 1023, bits 51..0 the fraction; exponent 0x7ff with a nonzero fraction
|
||||
* is a NaN, quiet bit 0x0008000000000000.
|
||||
*/
|
||||
static int
|
||||
fmax_nan_d(unsigned long long b)
|
||||
{
|
||||
return ((b >> 52) & 0x7ff) == 0x7ff && (b & 0xFFFFFFFFFFFFFULL) != 0;
|
||||
}
|
||||
|
||||
static int
|
||||
fmax_snan_d(unsigned long long b)
|
||||
{
|
||||
return fmax_nan_d(b) && (b & 0x0008000000000000ULL) == 0;
|
||||
}
|
||||
|
||||
static double
|
||||
fmax_mm_d(double x, double y)
|
||||
{
|
||||
unsigned long long xb;
|
||||
unsigned long long yb;
|
||||
int xneg;
|
||||
int yneg;
|
||||
|
||||
__builtin_memcpy(&xb, &x, sizeof xb);
|
||||
__builtin_memcpy(&yb, &y, sizeof yb);
|
||||
|
||||
if (fmax_snan_d(xb))
|
||||
{
|
||||
xb |= 0x0008000000000000ULL;
|
||||
__builtin_memcpy(&x, &xb, sizeof xb);
|
||||
return x;
|
||||
}
|
||||
if (fmax_snan_d(yb))
|
||||
{
|
||||
yb |= 0x0008000000000000ULL;
|
||||
__builtin_memcpy(&y, &yb, sizeof yb);
|
||||
return y;
|
||||
}
|
||||
if (fmax_nan_d(xb))
|
||||
{
|
||||
return fmax_nan_d(yb) ? x : y;
|
||||
}
|
||||
if (fmax_nan_d(yb))
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
if (x < y)
|
||||
{
|
||||
return y;
|
||||
}
|
||||
if (y < x)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
/* Equal, so possibly a +0/-0 pair: fmax prefers +0. */
|
||||
if (x == 0.0)
|
||||
{
|
||||
xneg = (int)(xb >> 63);
|
||||
yneg = (int)(yb >> 63);
|
||||
if (xneg != yneg)
|
||||
{
|
||||
return xneg ? y : x;
|
||||
}
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* The float format: bit 31 the sign, bits 30..23 the exponent biased by
|
||||
* 127, bits 22..0 the fraction; quiet bit 0x00400000.
|
||||
*/
|
||||
static int
|
||||
fmax_nan_f(unsigned int b)
|
||||
{
|
||||
return ((b >> 23) & 0xff) == 0xff && (b & 0x7FFFFFU) != 0;
|
||||
}
|
||||
|
||||
static int
|
||||
fmax_snan_f(unsigned int b)
|
||||
{
|
||||
return fmax_nan_f(b) && (b & 0x00400000U) == 0;
|
||||
}
|
||||
|
||||
static float
|
||||
fmax_mm_f(float x, float y)
|
||||
{
|
||||
unsigned int xb;
|
||||
unsigned int yb;
|
||||
int xneg;
|
||||
int yneg;
|
||||
|
||||
__builtin_memcpy(&xb, &x, sizeof xb);
|
||||
__builtin_memcpy(&yb, &y, sizeof yb);
|
||||
|
||||
if (fmax_snan_f(xb))
|
||||
{
|
||||
xb |= 0x00400000U;
|
||||
__builtin_memcpy(&x, &xb, sizeof xb);
|
||||
return x;
|
||||
}
|
||||
if (fmax_snan_f(yb))
|
||||
{
|
||||
yb |= 0x00400000U;
|
||||
__builtin_memcpy(&y, &yb, sizeof yb);
|
||||
return y;
|
||||
}
|
||||
if (fmax_nan_f(xb))
|
||||
{
|
||||
return fmax_nan_f(yb) ? x : y;
|
||||
}
|
||||
if (fmax_nan_f(yb))
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
if (x < y)
|
||||
{
|
||||
return y;
|
||||
}
|
||||
if (y < x)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (x == 0.0f)
|
||||
{
|
||||
xneg = (int)(xb >> 31);
|
||||
yneg = (int)(yb >> 31);
|
||||
if (xneg != yneg)
|
||||
{
|
||||
return xneg ? y : x;
|
||||
}
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* The x86 80-bit extended format: 64-bit significand m (explicit integer
|
||||
* bit) in bytes 0..7, sign/exponent word se in bytes 8..9 with the sign in
|
||||
* bit 15 and the exponent biased by 16383 in bits 14..0. NaN has
|
||||
* (se & 0x7fff) == 0x7fff with a nonzero fraction; the quiet bit is bit 62
|
||||
* of m.
|
||||
*/
|
||||
struct fmax_ld_word
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
};
|
||||
|
||||
static int
|
||||
fmax_nan_l(struct fmax_ld_word p)
|
||||
{
|
||||
return (p.se & 0x7fff) == 0x7fff && (p.m & 0x7FFFFFFFFFFFFFFFULL) != 0;
|
||||
}
|
||||
|
||||
static int
|
||||
fmax_snan_l(struct fmax_ld_word p)
|
||||
{
|
||||
return fmax_nan_l(p) && (p.m & (1ULL << 62)) == 0;
|
||||
}
|
||||
|
||||
static long double
|
||||
fmax_mm_l(long double x, long double y)
|
||||
{
|
||||
struct fmax_ld_word p;
|
||||
struct fmax_ld_word q;
|
||||
int xneg;
|
||||
int yneg;
|
||||
|
||||
__builtin_memcpy(&p, &x, sizeof p);
|
||||
__builtin_memcpy(&q, &y, sizeof q);
|
||||
|
||||
if (fmax_snan_l(p))
|
||||
{
|
||||
p.m |= 1ULL << 62;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
if (fmax_snan_l(q))
|
||||
{
|
||||
q.m |= 1ULL << 62;
|
||||
__builtin_memcpy(&y, &q, sizeof q);
|
||||
return y;
|
||||
}
|
||||
if (fmax_nan_l(p))
|
||||
{
|
||||
return fmax_nan_l(q) ? x : y;
|
||||
}
|
||||
if (fmax_nan_l(q))
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
if (x < y)
|
||||
{
|
||||
return y;
|
||||
}
|
||||
if (y < x)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (x == 0.0L)
|
||||
{
|
||||
xneg = (int)(p.se >> 15);
|
||||
yneg = (int)(q.se >> 15);
|
||||
if (xneg != yneg)
|
||||
{
|
||||
return xneg ? y : x;
|
||||
}
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* The larger of two floats.
|
||||
*/
|
||||
float
|
||||
fmaxf(float x, float y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return fmax_mm_f(x, y);
|
||||
}
|
||||
|
||||
/*
|
||||
* The larger of two doubles.
|
||||
*/
|
||||
double
|
||||
fmax(double x, double y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return fmax_mm_d(x, y);
|
||||
}
|
||||
|
||||
/*
|
||||
* The larger of two long doubles.
|
||||
*/
|
||||
long double
|
||||
fmaxl(long double x, long double y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return fmax_mm_l(x, y);
|
||||
}
|
||||
+267
@@ -0,0 +1,267 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* The smaller of x and y (C23 7.12.12.4), all three precisions.
|
||||
*
|
||||
* A quiet NaN argument is ignored in favor of the numeric one; a signaling
|
||||
* NaN argument makes the result a quiet NaN (the host glibc returns the
|
||||
* signaling argument quieted, payload preserved). When the arguments are
|
||||
* +0.0 and -0.0 the result is -0.0, and a pair of equal nonzero values
|
||||
* returns either. Numeric ordering is an ordinary comparison on the
|
||||
* already-NaN-screened operands, so no exception is raised and the chosen
|
||||
* operand is returned with its bits untouched. The NaN detection and the
|
||||
* quiet splice are done on the raw IEEE 754 word (the __builtin_isnan
|
||||
* classification would be equally exact but the word form also exposes the
|
||||
* signaling bit); no rounding mode and no arithmetic instruction is
|
||||
* involved, so the function is pure and fold-free at every level.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The double format: bit 63 is the sign, bits 62..52 the exponent biased
|
||||
* by 1023, bits 51..0 the fraction; exponent 0x7ff with a nonzero fraction
|
||||
* is a NaN, and the quiet bit is 0x0008000000000000 (bit 51).
|
||||
*/
|
||||
static int
|
||||
fmin_nan_d(unsigned long long b)
|
||||
{
|
||||
return ((b >> 52) & 0x7ff) == 0x7ff && (b & 0xFFFFFFFFFFFFFULL) != 0;
|
||||
}
|
||||
|
||||
static int
|
||||
fmin_snan_d(unsigned long long b)
|
||||
{
|
||||
return fmin_nan_d(b) && (b & 0x0008000000000000ULL) == 0;
|
||||
}
|
||||
|
||||
static double
|
||||
fmin_mm_d(double x, double y, int want_max)
|
||||
{
|
||||
unsigned long long xb;
|
||||
unsigned long long yb;
|
||||
int xneg;
|
||||
int yneg;
|
||||
|
||||
__builtin_memcpy(&xb, &x, sizeof xb);
|
||||
__builtin_memcpy(&yb, &y, sizeof yb);
|
||||
|
||||
/* A signaling NaN in either argument wins, quieted with its payload. */
|
||||
if (fmin_snan_d(xb))
|
||||
{
|
||||
xb |= 0x0008000000000000ULL;
|
||||
__builtin_memcpy(&x, &xb, sizeof xb);
|
||||
return x;
|
||||
}
|
||||
if (fmin_snan_d(yb))
|
||||
{
|
||||
yb |= 0x0008000000000000ULL;
|
||||
__builtin_memcpy(&y, &yb, sizeof yb);
|
||||
return y;
|
||||
}
|
||||
|
||||
/* Quiet NaNs are ignored: the numeric argument (or x, for two) wins. */
|
||||
if (fmin_nan_d(xb))
|
||||
{
|
||||
return fmin_nan_d(yb) ? x : y;
|
||||
}
|
||||
if (fmin_nan_d(yb))
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
if (x < y)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (y < x)
|
||||
{
|
||||
return y;
|
||||
}
|
||||
|
||||
/* Equal, so possibly a +0/-0 pair: fmin prefers -0, fmax prefers +0. */
|
||||
if (x == 0.0)
|
||||
{
|
||||
xneg = (int)(xb >> 63);
|
||||
yneg = (int)(yb >> 63);
|
||||
if (xneg != yneg)
|
||||
{
|
||||
return want_max ? (xneg ? y : x) : (xneg ? x : y);
|
||||
}
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* The float format: bit 31 the sign, bits 30..23 the exponent biased by
|
||||
* 127, bits 22..0 the fraction; quiet bit 0x00400000 (bit 22).
|
||||
*/
|
||||
static int
|
||||
fmin_nan_f(unsigned int b)
|
||||
{
|
||||
return ((b >> 23) & 0xff) == 0xff && (b & 0x7FFFFFU) != 0;
|
||||
}
|
||||
|
||||
static int
|
||||
fmin_snan_f(unsigned int b)
|
||||
{
|
||||
return fmin_nan_f(b) && (b & 0x00400000U) == 0;
|
||||
}
|
||||
|
||||
static float
|
||||
fmin_mm_f(float x, float y, int want_max)
|
||||
{
|
||||
unsigned int xb;
|
||||
unsigned int yb;
|
||||
int xneg;
|
||||
int yneg;
|
||||
|
||||
__builtin_memcpy(&xb, &x, sizeof xb);
|
||||
__builtin_memcpy(&yb, &y, sizeof yb);
|
||||
|
||||
if (fmin_snan_f(xb))
|
||||
{
|
||||
xb |= 0x00400000U;
|
||||
__builtin_memcpy(&x, &xb, sizeof xb);
|
||||
return x;
|
||||
}
|
||||
if (fmin_snan_f(yb))
|
||||
{
|
||||
yb |= 0x00400000U;
|
||||
__builtin_memcpy(&y, &yb, sizeof yb);
|
||||
return y;
|
||||
}
|
||||
if (fmin_nan_f(xb))
|
||||
{
|
||||
return fmin_nan_f(yb) ? x : y;
|
||||
}
|
||||
if (fmin_nan_f(yb))
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
if (x < y)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (y < x)
|
||||
{
|
||||
return y;
|
||||
}
|
||||
if (x == 0.0f)
|
||||
{
|
||||
xneg = (int)(xb >> 31);
|
||||
yneg = (int)(yb >> 31);
|
||||
if (xneg != yneg)
|
||||
{
|
||||
return want_max ? (xneg ? y : x) : (xneg ? x : y);
|
||||
}
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* The x86 80-bit extended format: 64-bit significand m in bytes 0..7 (the
|
||||
* integer bit is explicit) and a sign/exponent word se in bytes 8..9, sign
|
||||
* in bit 15 and exponent biased by 16383 in bits 14..0. A NaN has
|
||||
* (se & 0x7fff) == 0x7fff with a nonzero fraction; the quiet bit is bit 62
|
||||
* of m.
|
||||
*/
|
||||
struct fmin_ld_word
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
};
|
||||
|
||||
static int
|
||||
fmin_nan_l(struct fmin_ld_word p)
|
||||
{
|
||||
return (p.se & 0x7fff) == 0x7fff && (p.m & 0x7FFFFFFFFFFFFFFFULL) != 0;
|
||||
}
|
||||
|
||||
static int
|
||||
fmin_snan_l(struct fmin_ld_word p)
|
||||
{
|
||||
return fmin_nan_l(p) && (p.m & (1ULL << 62)) == 0;
|
||||
}
|
||||
|
||||
static long double
|
||||
fmin_mm_l(long double x, long double y, int want_max)
|
||||
{
|
||||
struct fmin_ld_word p;
|
||||
struct fmin_ld_word q;
|
||||
int xneg;
|
||||
int yneg;
|
||||
|
||||
__builtin_memcpy(&p, &x, sizeof p);
|
||||
__builtin_memcpy(&q, &y, sizeof q);
|
||||
|
||||
if (fmin_snan_l(p))
|
||||
{
|
||||
p.m |= 1ULL << 62;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
if (fmin_snan_l(q))
|
||||
{
|
||||
q.m |= 1ULL << 62;
|
||||
__builtin_memcpy(&y, &q, sizeof q);
|
||||
return y;
|
||||
}
|
||||
if (fmin_nan_l(p))
|
||||
{
|
||||
return fmin_nan_l(q) ? x : y;
|
||||
}
|
||||
if (fmin_nan_l(q))
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
if (x < y)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (y < x)
|
||||
{
|
||||
return y;
|
||||
}
|
||||
if (x == 0.0L)
|
||||
{
|
||||
xneg = (int)(p.se >> 15);
|
||||
yneg = (int)(q.se >> 15);
|
||||
if (xneg != yneg)
|
||||
{
|
||||
return want_max ? (xneg ? y : x) : (xneg ? x : y);
|
||||
}
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* The smaller of two floats.
|
||||
*/
|
||||
float
|
||||
fminf(float x, float y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return fmin_mm_f(x, y, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* The smaller of two doubles.
|
||||
*/
|
||||
double
|
||||
fmin(double x, double y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return fmin_mm_d(x, y, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* The smaller of two long doubles.
|
||||
*/
|
||||
long double
|
||||
fminl(long double x, long double y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return fmin_mm_l(x, y, 0);
|
||||
}
|
||||
+423
@@ -0,0 +1,423 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The floating-point remainder x - n*y with n = x/y truncated toward zero
|
||||
* (C23 7.12.10.1), all three precisions. The remainder is always exact
|
||||
* and carries x's sign (a zero result is a signed zero with x's sign), so
|
||||
* every implementation must agree bit for bit; the result is computed with
|
||||
* a restoring long division on the significands, in the style of the musl
|
||||
* fmod family but derived here from first principles.
|
||||
*
|
||||
* Each operand is normalized into
|
||||
*
|
||||
* value = m * 2^(ex - W)
|
||||
*
|
||||
* where m is the significand with its msb pinned to bit P (P = 52/23/63
|
||||
* and W = 1075/150/16446 for double/float/80-bit) and ex is the exponent
|
||||
* in the same units as the biased field (so ex is the stored exponent for
|
||||
* normals and goes negative for subnormals). Aligned this way, one binary
|
||||
* long-division step per exponent difference decides whether a multiple of
|
||||
* the divisor fits: the current remainder significand is compared against
|
||||
* the divisor significand, the divisor is subtracted once when it fits,
|
||||
* and the remainder is doubled for the next, half-weight, digit. The
|
||||
* comparison invariant keeps the remainder below twice the divisor at
|
||||
* every step, so one subtraction per digit is always enough and every
|
||||
* subtraction (and the exact zero test) is a plain integer operation.
|
||||
*
|
||||
* Domain errors mirror the host glibc: fmod(+-0, +-0), fmod(+-Inf, y) and
|
||||
* any NaN argument return a NaN (the classic indefinite pattern, which is
|
||||
* what glibc's x87 path produces) and, in the library build only, set
|
||||
* errno to EDOM. fmod(x, +-Inf) is x for a finite x, and |x| <= |y| hands
|
||||
* x back unchanged, which keeps the signed-zero and exact cases exact.
|
||||
*/
|
||||
|
||||
/* The double format: bit 63 the sign, bits 62..52 the exponent biased by
|
||||
* 1023, bits 51..0 the fraction. Value = m * 2^(ex - 1075) with the msb
|
||||
* of m at bit 52. */
|
||||
static double
|
||||
fmod_d(double x, double y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
unsigned long long xb;
|
||||
unsigned long long yb;
|
||||
unsigned long long sx;
|
||||
unsigned long long mx;
|
||||
unsigned long long my;
|
||||
unsigned long long s;
|
||||
unsigned long long i;
|
||||
int ex;
|
||||
int ey;
|
||||
|
||||
__builtin_memcpy(&xb, &x, sizeof xb);
|
||||
__builtin_memcpy(&yb, &y, sizeof yb);
|
||||
sx = xb & (1ULL << 63);
|
||||
ex = (int)((xb >> 52) & 0x7ff);
|
||||
ey = (int)((yb >> 52) & 0x7ff);
|
||||
|
||||
/* x not finite, y zero, or y a NaN: domain error. */
|
||||
if (ex == 0x7ff || (yb << 1) == 0 || (ey == 0x7ff && (yb & 0xFFFFFFFFFFFFFULL) != 0))
|
||||
{
|
||||
#ifdef HAVE_CONFIG_H
|
||||
errno = EDOM;
|
||||
#endif
|
||||
return __builtin_nan(""); /* indefinite NaN, sign negative */
|
||||
}
|
||||
/* x zero or a finite x below an infinite y: x unchanged. (x a NaN or
|
||||
* +-Inf fell into the domain branch above, which returns a NaN for
|
||||
* both.) */
|
||||
if ((xb << 1) == 0 || ey == 0x7ff)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
/* Compare magnitudes directly on the (biased exponent, fraction)
|
||||
* words: finite IEEE magnitudes order lexicographically, so a plain
|
||||
* integer compare of the sign-stripped words decides |x| vs |y|. */
|
||||
if ((xb & 0x7FFFFFFFFFFFFFFFULL) <= (yb & 0x7FFFFFFFFFFFFFFFULL))
|
||||
{
|
||||
if ((xb & 0x7FFFFFFFFFFFFFFFULL) == (yb & 0x7FFFFFFFFFFFFFFFULL))
|
||||
{
|
||||
return x * 0.0; /* exact: result is +-0 with x's sign */
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
/* Normalize x: subnormals (ex == 0) get their msb shifted up to bit
|
||||
* 52 and ex counts the shift below the smallest normal. */
|
||||
mx = xb & 0xFFFFFFFFFFFFFULL;
|
||||
if (ex == 0)
|
||||
{
|
||||
s = 63 - (unsigned long long)__builtin_clzll(mx);
|
||||
mx <<= (52 - s);
|
||||
ex = (int)s - 51;
|
||||
}
|
||||
else
|
||||
{
|
||||
mx |= 1ULL << 52;
|
||||
}
|
||||
my = yb & 0xFFFFFFFFFFFFFULL;
|
||||
if (ey == 0)
|
||||
{
|
||||
s = 63 - (unsigned long long)__builtin_clzll(my);
|
||||
my <<= (52 - s);
|
||||
ey = (int)s - 51;
|
||||
}
|
||||
else
|
||||
{
|
||||
my |= 1ULL << 52;
|
||||
}
|
||||
|
||||
/* Long division: subtract the aligned divisor significand once per
|
||||
* bit of quotient, doubling the remainder between bits. */
|
||||
for (; ex > ey; ex--)
|
||||
{
|
||||
if (mx >= my)
|
||||
{
|
||||
i = mx - my;
|
||||
if (i == 0)
|
||||
{
|
||||
return x * 0.0;
|
||||
}
|
||||
mx = i;
|
||||
}
|
||||
mx <<= 1;
|
||||
}
|
||||
i = mx - my;
|
||||
if (mx >= my)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
return x * 0.0;
|
||||
}
|
||||
mx = i;
|
||||
}
|
||||
|
||||
/* Bring the msb of the remainder back to bit 52. */
|
||||
for (; (mx >> 52) == 0; mx <<= 1, ex--)
|
||||
{
|
||||
}
|
||||
|
||||
if (ex > 0)
|
||||
{
|
||||
xb = sx | ((unsigned long long)ex << 52) | (mx - (1ULL << 52));
|
||||
}
|
||||
else
|
||||
{
|
||||
xb = sx | (mx >> (1 - ex));
|
||||
}
|
||||
__builtin_memcpy(&x, &xb, sizeof xb);
|
||||
return x;
|
||||
}
|
||||
|
||||
/* The float format: bit 31 the sign, bits 30..23 the exponent biased by
|
||||
* 127, bits 22..0 the fraction. Value = m * 2^(ex - 150) with the msb of
|
||||
* m at bit 23. */
|
||||
static float
|
||||
fmod_f(float x, float y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
unsigned int xb;
|
||||
unsigned int yb;
|
||||
unsigned int sx;
|
||||
unsigned int mx;
|
||||
unsigned int my;
|
||||
unsigned int s;
|
||||
unsigned int i;
|
||||
int ex;
|
||||
int ey;
|
||||
|
||||
__builtin_memcpy(&xb, &x, sizeof xb);
|
||||
__builtin_memcpy(&yb, &y, sizeof yb);
|
||||
sx = xb & (1U << 31);
|
||||
ex = (int)((xb >> 23) & 0xff);
|
||||
ey = (int)((yb >> 23) & 0xff);
|
||||
|
||||
if (ex == 0xff || (yb << 1) == 0 || (ey == 0xff && (yb & 0x7FFFFFU) != 0))
|
||||
{
|
||||
#ifdef HAVE_CONFIG_H
|
||||
errno = EDOM;
|
||||
#endif
|
||||
return __builtin_nanf("");
|
||||
}
|
||||
if ((xb << 1) == 0 || ey == 0xff)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
if ((xb & 0x7FFFFFFFU) <= (yb & 0x7FFFFFFFU))
|
||||
{
|
||||
if ((xb & 0x7FFFFFFFU) == (yb & 0x7FFFFFFFU))
|
||||
{
|
||||
return x * 0.0f;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
mx = xb & 0x7FFFFFU;
|
||||
if (ex == 0)
|
||||
{
|
||||
s = 31 - (unsigned int)__builtin_clz(mx);
|
||||
mx <<= (23 - s);
|
||||
ex = (int)s - 22;
|
||||
}
|
||||
else
|
||||
{
|
||||
mx |= 1U << 23;
|
||||
}
|
||||
my = yb & 0x7FFFFFU;
|
||||
if (ey == 0)
|
||||
{
|
||||
s = 31 - (unsigned int)__builtin_clz(my);
|
||||
my <<= (23 - s);
|
||||
ey = (int)s - 22;
|
||||
}
|
||||
else
|
||||
{
|
||||
my |= 1U << 23;
|
||||
}
|
||||
|
||||
for (; ex > ey; ex--)
|
||||
{
|
||||
if (mx >= my)
|
||||
{
|
||||
i = mx - my;
|
||||
if (i == 0)
|
||||
{
|
||||
return x * 0.0f;
|
||||
}
|
||||
mx = i;
|
||||
}
|
||||
mx <<= 1;
|
||||
}
|
||||
i = mx - my;
|
||||
if (mx >= my)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
return x * 0.0f;
|
||||
}
|
||||
mx = i;
|
||||
}
|
||||
|
||||
for (; (mx >> 23) == 0; mx <<= 1, ex--)
|
||||
{
|
||||
}
|
||||
|
||||
if (ex > 0)
|
||||
{
|
||||
xb = sx | ((unsigned int)ex << 23) | (mx - (1U << 23));
|
||||
}
|
||||
else
|
||||
{
|
||||
xb = sx | (mx >> (1 - ex));
|
||||
}
|
||||
__builtin_memcpy(&x, &xb, sizeof xb);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* The x86 80-bit extended format: 64-bit significand m with an explicit
|
||||
* integer bit in bytes 0..7, sign/exponent word se in bytes 8..9. Value =
|
||||
* m * 2^(ex - 16446) with the msb of m at bit 63; a canonical normal has
|
||||
* ex = se & 0x7fff, and a subnormal (se field 0) or a defensive unnormal
|
||||
* (nonzero field with m < 2^63) is normalized by shifting m's msb up to
|
||||
* bit 63. The division reuses the same digit loop as the narrower
|
||||
* formats; only the doubling step differs, because a full-width
|
||||
* significand has no free bit above its msb (see the three-way branch in
|
||||
* the loop below).
|
||||
*/
|
||||
struct fmod_ld_word
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
};
|
||||
|
||||
static long double
|
||||
fmod_l(long double x, long double y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
struct fmod_ld_word p;
|
||||
struct fmod_ld_word q;
|
||||
unsigned long long s;
|
||||
unsigned long long mx;
|
||||
unsigned long long my;
|
||||
unsigned long long i;
|
||||
int ex;
|
||||
int ey;
|
||||
|
||||
__builtin_memcpy(&p, &x, sizeof p);
|
||||
__builtin_memcpy(&q, &y, sizeof q);
|
||||
ex = p.se & 0x7fff;
|
||||
ey = q.se & 0x7fff;
|
||||
|
||||
/* x not finite, y zero, or y a NaN: domain error. */
|
||||
if (ex == 0x7fff || (q.m == 0 && ey == 0) ||
|
||||
(ey == 0x7fff && (q.m & 0x7FFFFFFFFFFFFFFFULL) != 0))
|
||||
{
|
||||
#ifdef HAVE_CONFIG_H
|
||||
errno = EDOM;
|
||||
#endif
|
||||
p.m = 0xC000000000000000ULL;
|
||||
p.se = 0xFFFF;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
/* x zero or a finite x below an infinite y: x unchanged. (x a NaN or
|
||||
* +-Inf fell into the domain branch above.) */
|
||||
if ((p.m == 0 && ex == 0) || ey == 0x7fff)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
/* Normalize both significands to the [2^63, 2^64) frame; a subnormal
|
||||
* (field 0) or defensive unnormal (field nonzero, msb below 63) has
|
||||
* its msb shifted up to bit 63 with the exponent adjusted, so every
|
||||
* value below obeys value = m * 2^(ex - 16446). */
|
||||
if (p.m < 0x8000000000000000ULL)
|
||||
{
|
||||
s = 63 - (unsigned long long)__builtin_clzll(p.m);
|
||||
p.m <<= (63 - s);
|
||||
ex = (ex == 0 ? 1 : ex) + (int)s - 63;
|
||||
}
|
||||
if (q.m < 0x8000000000000000ULL)
|
||||
{
|
||||
s = 63 - (unsigned long long)__builtin_clzll(q.m);
|
||||
q.m <<= (63 - s);
|
||||
ey = (ey == 0 ? 1 : ey) + (int)s - 63;
|
||||
}
|
||||
mx = p.m;
|
||||
my = q.m;
|
||||
|
||||
if (ex < ey || (ex == ey && mx <= my))
|
||||
{
|
||||
if (ex == ey && mx == my)
|
||||
{
|
||||
return x * 0.0L;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
/* Long division with a full-width significand: a 64-bit divisor
|
||||
* leaves no headroom above its own msb for the per-bit doubling, so
|
||||
* the doubled remainder that overflows is exactly one divisor at the
|
||||
* next, half, weight and is absorbed by a subtraction there. */
|
||||
for (; ex > ey; ex--)
|
||||
{
|
||||
i = mx - my;
|
||||
if (mx >= my)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
return x * 0.0L;
|
||||
}
|
||||
mx = 2 * i;
|
||||
}
|
||||
else if (2 * mx < mx)
|
||||
{
|
||||
mx = 2 * mx - my;
|
||||
}
|
||||
else
|
||||
{
|
||||
mx = 2 * mx;
|
||||
}
|
||||
}
|
||||
i = mx - my;
|
||||
if (mx >= my)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
return x * 0.0L;
|
||||
}
|
||||
mx = i;
|
||||
}
|
||||
|
||||
/* Bring the msb of the remainder back to bit 63. */
|
||||
for (; mx < 0x8000000000000000ULL; mx <<= 1, ex--)
|
||||
{
|
||||
}
|
||||
|
||||
if (ex > 0)
|
||||
{
|
||||
p.se = (unsigned short)((p.se & 0x8000) | (unsigned short)ex);
|
||||
p.m = mx;
|
||||
}
|
||||
else
|
||||
{
|
||||
p.se = (unsigned short)(p.se & 0x8000);
|
||||
p.m = mx >> (1 - ex);
|
||||
}
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* The float remainder, as fmod_d.
|
||||
*/
|
||||
float
|
||||
fmodf(float x, float y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return fmod_f(x, y);
|
||||
}
|
||||
|
||||
/*
|
||||
* The double remainder, as fmod_d.
|
||||
*/
|
||||
double
|
||||
fmod(double x, double y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return fmod_d(x, y);
|
||||
}
|
||||
|
||||
/*
|
||||
* The long double remainder, as fmod_d.
|
||||
*/
|
||||
long double
|
||||
fmodl(long double x, long double y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return fmod_l(x, y);
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* Split x into a fraction f in [1/2, 1) (or 0) and an integer exponent
|
||||
* *exp with x == f * 2^(*exp), all three precisions (C23 7.12.6.4).
|
||||
* frexp(±0) returns x with *exp 0; frexp(±Inf) and frexp(NaN) return x
|
||||
* with *exp 0 (the glibc behavior the tests pin down; the C standard
|
||||
* leaves the Inf/NaN exponent unspecified). The fraction keeps x's sign,
|
||||
* so frexp(-6.0) is -0.75 with *exp 3 and frexp(0x1p-1074) is 0.5 with
|
||||
* *exp -1073.
|
||||
*
|
||||
* Each precision works from the same unified view used across the
|
||||
* src/math/ slices (see math_impl.h): a finite value is m * 2^p with m the
|
||||
* integer significand carrying the explicit integer bit. The fraction is
|
||||
* m * 2^-w (w = 53/24/64 significand bits), which sits in [1/2, 1), so
|
||||
* *exp is p + w and the result keeps the significand's low bits — for a
|
||||
* normal input the fraction field is simply unchanged and only the
|
||||
* exponent field is rewritten.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The double format: bit 63 the sign, bits 62..52 the exponent biased by
|
||||
* 1023, bits 51..0 the fraction, 53-bit significand. A normal input has
|
||||
* p = ef - 1075, so *exp = ef - 1022 and the fraction field is the input's
|
||||
* own 52-bit fraction (the implicit bit stays put as the leading 1 of the
|
||||
* fraction's [1/2, 1) significand). A subnormal input with fraction msb
|
||||
* at position s (0..51) is value frac * 2^-1074; shifting frac up to the
|
||||
* implicit-bit position makes m = frac << (52 - s), hence *exp = s - 1073
|
||||
* and fraction field m - 2^52.
|
||||
*/
|
||||
double
|
||||
frexp(double x, int *exp)
|
||||
{
|
||||
unsigned long long bits;
|
||||
unsigned long long sign;
|
||||
unsigned long long ef;
|
||||
unsigned long long frac;
|
||||
unsigned long long m;
|
||||
int s;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
sign = bits & (1ULL << 63);
|
||||
ef = (bits >> 52) & 0x7ff;
|
||||
frac = bits & 0xFFFFFFFFFFFFFULL;
|
||||
|
||||
if (ef == 0x7ff)
|
||||
{
|
||||
*exp = 0;
|
||||
return x; /* ±Inf and NaN */
|
||||
}
|
||||
if (ef == 0)
|
||||
{
|
||||
if (frac == 0)
|
||||
{
|
||||
*exp = 0;
|
||||
return x; /* ±0 */
|
||||
}
|
||||
s = 63 - __builtin_clzll(frac);
|
||||
m = frac << (52 - s);
|
||||
*exp = s - 1073;
|
||||
bits = sign | (0x3feULL << 52) | (m - (1ULL << 52));
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
*exp = (int)ef - 1022;
|
||||
bits = sign | (0x3feULL << 52) | frac;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* The float format: bit 31 the sign, bits 30..23 the exponent biased by
|
||||
* 127, bits 22..0 the fraction, 24-bit significand. Normal *exp =
|
||||
* ef - 126; a subnormal input with fraction msb at s (0..22) normalizes to
|
||||
* m = frac << (23 - s) and has *exp = s - 148.
|
||||
*/
|
||||
float
|
||||
frexpf(float x, int *exp)
|
||||
{
|
||||
unsigned int bits;
|
||||
unsigned int sign;
|
||||
unsigned int ef;
|
||||
unsigned int frac;
|
||||
unsigned int m;
|
||||
int s;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
sign = bits & (1U << 31);
|
||||
ef = (bits >> 23) & 0xff;
|
||||
frac = bits & 0x7FFFFFU;
|
||||
|
||||
if (ef == 0xff)
|
||||
{
|
||||
*exp = 0;
|
||||
return x;
|
||||
}
|
||||
if (ef == 0)
|
||||
{
|
||||
if (frac == 0)
|
||||
{
|
||||
*exp = 0;
|
||||
return x;
|
||||
}
|
||||
s = 31 - __builtin_clz(frac);
|
||||
m = frac << (23 - s);
|
||||
*exp = s - 148;
|
||||
bits = sign | (0x7eU << 23) | (m - (1U << 23));
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
*exp = (int)ef - 126;
|
||||
bits = sign | (0x7eU << 23) | frac;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* The x86 80-bit extended format: 64 significand bits m with an explicit
|
||||
* integer bit, plus a sign/exponent word se biased by 16383. A canonical
|
||||
* normal has m in [2^63, 2^64) and p = ef - 16446, so *exp = ef - 16382
|
||||
* and the fraction is m * 2^-64 with se rewritten to 16382. A subnormal
|
||||
* (ef == 0) is value m * 2^-16445; shifting m's msb (position s, 0..62)
|
||||
* up to bit 63 gives *exp = s - 16444. Unnormal inputs (ef > 0 with
|
||||
* m < 2^63) normalize the same way with *exp = ef + s - 16445.
|
||||
*/
|
||||
long double
|
||||
frexpl(long double x, int *exp)
|
||||
{
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
unsigned long long m;
|
||||
int ef;
|
||||
int s;
|
||||
|
||||
__builtin_memcpy(&p, &x, sizeof p);
|
||||
m = p.m;
|
||||
ef = p.se & 0x7fff;
|
||||
|
||||
if (ef == 0x7fff)
|
||||
{
|
||||
*exp = 0;
|
||||
return x;
|
||||
}
|
||||
if (m == 0)
|
||||
{
|
||||
*exp = 0;
|
||||
return x; /* ±0 and empty degenerate encodings */
|
||||
}
|
||||
if (m < 0x8000000000000000ULL)
|
||||
{
|
||||
s = 63 - __builtin_clzll(m);
|
||||
m <<= (63 - s);
|
||||
*exp = (ef == 0 ? 1 : ef) + s - 16445;
|
||||
p.se = (unsigned short)((p.se & 0x8000) | 0x3ffe);
|
||||
p.m = m;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
|
||||
*exp = ef - 16382;
|
||||
p.se = (unsigned short)((p.se & 0x8000) | 0x3ffe);
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
#include <math.h>
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#define SET_ERANGE() (errno = ERANGE)
|
||||
#define SET_EDOM() (errno = EDOM)
|
||||
#else
|
||||
#define SET_ERANGE() \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
#define SET_EDOM() \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
/* hypot(x,y) = sqrt(x*x + y*y) without spurious overflow/underflow, after
|
||||
* musl src/math/hypot.c: |x| >= |y| via union sort, inf/NaN specials, a
|
||||
* power-of-two scaling window, and an exact double-double square so the
|
||||
* final sqrt argument is precise. No errno: the scaling avoids overflow. */
|
||||
static const double split = 0x1p27 + 1.0; /* FLT_EVAL_METHOD <= 1 on x86-64 */
|
||||
|
||||
static double
|
||||
vlibc_hypot_core(double x, double y)
|
||||
{
|
||||
union
|
||||
{
|
||||
double f;
|
||||
unsigned long long i;
|
||||
} ux, uy, ut;
|
||||
int ex;
|
||||
int ey;
|
||||
double hx;
|
||||
double lx;
|
||||
double hy;
|
||||
double ly;
|
||||
double z;
|
||||
double xc;
|
||||
double xh;
|
||||
double xl;
|
||||
|
||||
/* arrange |x| >= |y| */
|
||||
ux.f = x;
|
||||
uy.f = y;
|
||||
ux.i &= 0x7fffffffffffffffULL;
|
||||
uy.i &= 0x7fffffffffffffffULL;
|
||||
if (ux.i < uy.i)
|
||||
{
|
||||
ut = ux;
|
||||
ux = uy;
|
||||
uy = ut;
|
||||
}
|
||||
ex = (int)(ux.i >> 52);
|
||||
ey = (int)(uy.i >> 52);
|
||||
x = ux.f;
|
||||
y = uy.f;
|
||||
/* note: hypot(inf,nan) == inf */
|
||||
if (ey == 0x7ff)
|
||||
return y;
|
||||
if (ex == 0x7ff || uy.i == 0)
|
||||
return x;
|
||||
/* note: hypot(x,y) ~= x + y*y/x/2 with inexact for small y/x */
|
||||
if (ex - ey > 64)
|
||||
return x + y;
|
||||
|
||||
/* precise sqrt argument in nearest rounding mode without overflow:
|
||||
* xh*xh must not overflow and xl*xl must not underflow */
|
||||
z = 1.0;
|
||||
if (ex > 0x3ff + 510)
|
||||
{
|
||||
z = 0x1p700;
|
||||
x *= 0x1p-700;
|
||||
y *= 0x1p-700;
|
||||
}
|
||||
else if (ey < 0x3ff - 450)
|
||||
{
|
||||
z = 0x1p-700;
|
||||
x *= 0x1p700;
|
||||
y *= 0x1p700;
|
||||
}
|
||||
/* hx+lx == x*x exactly (double-double square) */
|
||||
xc = x * split;
|
||||
xh = x - xc + xc;
|
||||
xl = x - xh;
|
||||
hx = x * x;
|
||||
lx = xh * xh - hx + 2.0 * xh * xl + xl * xl;
|
||||
xc = y * split;
|
||||
xh = y - xc + xc;
|
||||
xl = y - xh;
|
||||
hy = y * y;
|
||||
ly = xh * xh - hy + 2.0 * xh * xl + xl * xl;
|
||||
x = ly + lx + hy + hx;
|
||||
__asm__("sqrtsd %1, %0" : "=x"(y) : "x"(x));
|
||||
return z * y;
|
||||
}
|
||||
|
||||
/* hypotf after musl src/math/hypotf.c: same shape, scaled into a safe
|
||||
* window and evaluated in double for the final square and sqrt. */
|
||||
static float
|
||||
vlibc_hypotf_core(float x, float y)
|
||||
{
|
||||
union
|
||||
{
|
||||
float f;
|
||||
unsigned int i;
|
||||
} ux, uy, ut;
|
||||
float z;
|
||||
|
||||
ux.f = x;
|
||||
uy.f = y;
|
||||
ux.i &= 0x7fffffffU;
|
||||
uy.i &= 0x7fffffffU;
|
||||
if (ux.i < uy.i)
|
||||
{
|
||||
ut = ux;
|
||||
ux = uy;
|
||||
uy = ut;
|
||||
}
|
||||
x = ux.f;
|
||||
y = uy.f;
|
||||
if (uy.i == 0xffU << 23)
|
||||
return y; /* hypot(inf,nan) == inf */
|
||||
if (ux.i >= 0xffU << 23 || uy.i == 0 || ux.i - uy.i >= 25U << 23)
|
||||
return x + y;
|
||||
z = 1.0f;
|
||||
if (ux.i >= (0x7fU + 60) << 23)
|
||||
{
|
||||
z = 0x1p90f;
|
||||
x *= 0x1p-90f;
|
||||
y *= 0x1p-90f;
|
||||
}
|
||||
else if (uy.i < (0x7fU - 60) << 23)
|
||||
{
|
||||
z = 0x1p-90f;
|
||||
x *= 0x1p90f;
|
||||
y *= 0x1p90f;
|
||||
}
|
||||
x = (float)((double)x * x + (double)y * y);
|
||||
__asm__("sqrtss %1, %0" : "=x"(y) : "x"(x));
|
||||
return z * y;
|
||||
}
|
||||
|
||||
/* hypotl after musl src/math/hypotl.c (ld80 branch): the full 64-bit
|
||||
* mantissa path with a 2^10000 scaling window and an exact double-double
|
||||
* square. */
|
||||
static long double
|
||||
vlibc_hypotl_core(long double x, long double y)
|
||||
{
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} ux;
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} uy;
|
||||
int ex;
|
||||
int ey;
|
||||
long double hx;
|
||||
long double lx;
|
||||
long double hy;
|
||||
long double ly;
|
||||
long double z;
|
||||
long double xc;
|
||||
long double xh;
|
||||
long double xl;
|
||||
|
||||
ux.f = x;
|
||||
uy.f = y;
|
||||
ux.p.se &= 0x7fff;
|
||||
uy.p.se &= 0x7fff;
|
||||
if (ux.p.se < uy.p.se)
|
||||
{
|
||||
ex = uy.p.se;
|
||||
ey = ux.p.se;
|
||||
x = uy.f;
|
||||
y = ux.f;
|
||||
}
|
||||
else
|
||||
{
|
||||
ex = ux.p.se;
|
||||
ey = uy.p.se;
|
||||
x = ux.f;
|
||||
y = uy.f;
|
||||
}
|
||||
if (ex == 0x7fff && __builtin_isinf(y))
|
||||
return y; /* hypot(inf,nan) == inf */
|
||||
if (ex == 0x7fff || y == 0.0L)
|
||||
return x;
|
||||
if (ex - ey > 64)
|
||||
return x + y;
|
||||
z = 1.0L;
|
||||
if (ex > 0x3fff + 8000)
|
||||
{
|
||||
z = 0x1p10000L;
|
||||
x *= 0x1p-10000L;
|
||||
y *= 0x1p-10000L;
|
||||
}
|
||||
else if (ey < 0x3fff - 8000)
|
||||
{
|
||||
z = 0x1p-10000L;
|
||||
x *= 0x1p10000L;
|
||||
y *= 0x1p10000L;
|
||||
}
|
||||
/* hx+lx == x*x exactly (double-double square) */
|
||||
xc = x * (0x1p32L + 1.0L);
|
||||
xh = x - xc + xc;
|
||||
xl = x - xh;
|
||||
hx = x * x;
|
||||
lx = xh * xh - hx + 2.0L * xh * xl + xl * xl;
|
||||
xc = y * (0x1p32L + 1.0L);
|
||||
xh = y - xc + xc;
|
||||
xl = y - xh;
|
||||
hy = y * y;
|
||||
ly = xh * xh - hy + 2.0L * xh * xl + xl * xl;
|
||||
x = ly + lx + hy + hx;
|
||||
__asm__("fsqrt" : "+t"(x));
|
||||
return z * x;
|
||||
}
|
||||
|
||||
double
|
||||
hypot(double x, double y)
|
||||
{
|
||||
double z = vlibc_hypot_core(x, y);
|
||||
|
||||
if (z == __builtin_huge_val() && __builtin_isfinite(x) && __builtin_isfinite(y))
|
||||
SET_ERANGE(); /* genuine overflow: glibc parity */
|
||||
return z;
|
||||
}
|
||||
|
||||
float
|
||||
hypotf(float x, float y)
|
||||
{
|
||||
float z = vlibc_hypotf_core(x, y);
|
||||
|
||||
if (z == __builtin_huge_valf() && __builtin_isfinite(x) && __builtin_isfinite(y))
|
||||
SET_ERANGE();
|
||||
return z;
|
||||
}
|
||||
|
||||
long double
|
||||
hypotl(long double x, long double y)
|
||||
{
|
||||
long double z = vlibc_hypotl_core(x, y);
|
||||
|
||||
if (z == __builtin_huge_vall() && __builtin_isfinite(x) && __builtin_isfinite(y))
|
||||
SET_ERANGE();
|
||||
return z;
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* The signed exponent of x as an int (C23 7.12.6.5p1), all three
|
||||
* precisions: ilogb(x) is floor(log2 |x|) for a nonzero finite x. The
|
||||
* sentinel returns are FP_ILOGB0 (INT_MIN) for ±0 and FP_ILOGBNAN
|
||||
* (INT_MAX) for ±Inf and NaN, without touching errno, so the functions
|
||||
* carry the const attribute and never trap. Subnormal arguments are
|
||||
* normalized by scanning the fraction for its highest set bit.
|
||||
*
|
||||
* ilogb(1.0) == 0, ilogb(8.0) == 3, ilogb(0.5) == -1, and the smallest
|
||||
* subnormal double, 2^-1074, yields -1074.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The double format: bits 62..52 hold the exponent biased by 1023 and
|
||||
* bits 51..0 the fraction; a subnormal with fraction F (no implicit
|
||||
* bit) is F * 2^-1074, so its exponent is the fraction's highest set
|
||||
* bit position minus 1074.
|
||||
*/
|
||||
int
|
||||
ilogb(double x)
|
||||
{
|
||||
unsigned long long bits;
|
||||
unsigned long long frac;
|
||||
int e;
|
||||
int msb;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
frac = bits & 0xFFFFFFFFFFFFFULL;
|
||||
e = (int)((bits >> 52) & 0x7ff);
|
||||
|
||||
if (e == 0x7ff)
|
||||
{
|
||||
/* ±Inf and NaN both map to FP_ILOGBNAN. */
|
||||
return FP_ILOGBNAN;
|
||||
}
|
||||
if (e == 0)
|
||||
{
|
||||
if (frac == 0)
|
||||
{
|
||||
return FP_ILOGB0;
|
||||
}
|
||||
msb = 63 - __builtin_clzll(frac);
|
||||
return msb - 1074;
|
||||
}
|
||||
return e - 1023;
|
||||
}
|
||||
|
||||
/*
|
||||
* The float format: bits 30..23 hold the exponent biased by 127 and
|
||||
* bits 22..0 the fraction; a subnormal with fraction F is F * 2^-149.
|
||||
*/
|
||||
int
|
||||
ilogbf(float x)
|
||||
{
|
||||
unsigned int bits;
|
||||
unsigned int frac;
|
||||
int e;
|
||||
int msb;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
frac = bits & 0x7FFFFFU;
|
||||
e = (int)((bits >> 23) & 0xff);
|
||||
|
||||
if (e == 0xff)
|
||||
{
|
||||
return FP_ILOGBNAN;
|
||||
}
|
||||
if (e == 0)
|
||||
{
|
||||
if (frac == 0)
|
||||
{
|
||||
return FP_ILOGB0;
|
||||
}
|
||||
msb = 31 - __builtin_clz(frac);
|
||||
return msb - 149;
|
||||
}
|
||||
return e - 127;
|
||||
}
|
||||
|
||||
/*
|
||||
* The x86 80-bit extended format: 64 significand bits m (explicit
|
||||
* integer bit) with a sign/exponent word se biased by 16383. A normal m
|
||||
* in [2^63, 2^64) sits in [2^(e-16383), 2^(e-16382)). A subnormal with
|
||||
* e == 0 is interpreted as if e were 1, i.e. m * 2^-16445; an unnormal
|
||||
* (e > 0 with m < 2^63) is m * 2^(e - 16446).
|
||||
*/
|
||||
int
|
||||
ilogbl(long double x)
|
||||
{
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
int e;
|
||||
int msb;
|
||||
|
||||
__builtin_memcpy(&p, &x, sizeof p);
|
||||
e = p.se & 0x7fff;
|
||||
|
||||
if (e == 0x7fff)
|
||||
{
|
||||
return FP_ILOGBNAN;
|
||||
}
|
||||
if (p.m == 0)
|
||||
{
|
||||
return FP_ILOGB0;
|
||||
}
|
||||
if (e == 0)
|
||||
{
|
||||
msb = 63 - __builtin_clzll(p.m);
|
||||
return msb - 16445;
|
||||
}
|
||||
if (p.m < 0x8000000000000000ULL)
|
||||
{
|
||||
msb = 63 - __builtin_clzll(p.m);
|
||||
return msb + e - 16446;
|
||||
}
|
||||
return e - 16383;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "math_impl.h"
|
||||
|
||||
/*
|
||||
* x times 2^n (C23 7.12.6.3), all three precisions: the inverse of frexp.
|
||||
* Scaling a finite value by a power of two is exact whenever the result is
|
||||
* a normal number; only a result that lands in the subnormal range can
|
||||
* need rounding (the vl_rne_u64 round-to-nearest-even shift in the shared
|
||||
* scale cores, see math_impl.h). Overflow returns +-Inf and, in the
|
||||
* library build, sets errno to ERANGE as POSIX requires.
|
||||
*
|
||||
* The errno write is a TCB dereference that only exists when this file is
|
||||
* compiled as part of the real library (config.h present). The host-
|
||||
* linked standalone test binaries compile these sources without
|
||||
* HAVE_CONFIG_H, so they never reference __errno_location and never touch
|
||||
* the host's errno thread slot; the tests therefore check values only.
|
||||
* ldexp(±0) is ±0 for any n, and ±Inf/NaN pass through unchanged.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Scale a double: the shared core reports overflow through the flag and
|
||||
* this wrapper raises ERANGE when the library build demands it.
|
||||
*/
|
||||
double
|
||||
ldexp(double x, int n)
|
||||
{
|
||||
int overflowed = 0;
|
||||
double r = vl_scale2_d(x, n, &overflowed);
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
if (overflowed)
|
||||
{
|
||||
errno = ERANGE;
|
||||
}
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
float
|
||||
ldexpf(float x, int n)
|
||||
{
|
||||
int overflowed = 0;
|
||||
float r = vl_scale2_f(x, n, &overflowed);
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
if (overflowed)
|
||||
{
|
||||
errno = ERANGE;
|
||||
}
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
long double
|
||||
ldexpl(long double x, int n)
|
||||
{
|
||||
int overflowed = 0;
|
||||
long double r = vl_scale2_ld(x, n, &overflowed);
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
if (overflowed)
|
||||
{
|
||||
errno = ERANGE;
|
||||
}
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* The nearest integral value to x in the current rounding direction,
|
||||
* returned as long long (C23 7.12.9.8), all three precisions. A result
|
||||
* outside the range of long long is a range error whose return value is
|
||||
* unspecified, so the header declares no const attribute; the test
|
||||
* corpus keeps |x| < 2^62 where every result is exact.
|
||||
*
|
||||
* As for lrint (see lrint.c): the __builtin_llrint* forms are never
|
||||
* folded on this target, so llrint is built as rint-then-convert, with
|
||||
* __builtin_rint* folding to the in-line round-to-nearest-even sequence
|
||||
* and the cast to long long being exact on the integral result.
|
||||
*/
|
||||
|
||||
/*
|
||||
* As llrint, for a float argument.
|
||||
*/
|
||||
long long
|
||||
llrintf(float x)
|
||||
{
|
||||
return (long long)__builtin_rintf(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* As llrint, for a double argument.
|
||||
*/
|
||||
long long
|
||||
llrint(double x)
|
||||
{
|
||||
return (long long)__builtin_rint(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* As llrint, for a long double argument.
|
||||
*/
|
||||
long long
|
||||
llrintl(long double x)
|
||||
{
|
||||
return (long long)__builtin_rintl(x);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* The nearest integral value to x, halfway cases rounded away from zero,
|
||||
* returned as long long (C23 7.12.9.10), all three precisions.
|
||||
*
|
||||
* Rounding is delegated to the round/roundf/roundl implementations (see
|
||||
* round.c): on this target every __builtin_round* is an external call even
|
||||
* at -O2 -- no SSE4.1 in the default -march -- so round() here reaches the
|
||||
* library's own half-away-from-zero rounding, returning an exactly
|
||||
* integral value of the same floating type. Narrowing that value to long
|
||||
* long is then a plain, exact conversion whenever it is in range.
|
||||
*
|
||||
* The unrepresentable cases mirror the host glibc on x86-64, whose lround
|
||||
* family is the hardware cvttsd2si sequence: a NaN, an Inf, and any finite
|
||||
* result of magnitude >= 2^63 (the smallest such rounded value is exactly
|
||||
* 2^63) all collapse to LLONG_MIN with errno untouched -- measured against
|
||||
* glibc 2.44, which returns LLONG_MIN for 1e300, -1e300, +-Inf and NaN and
|
||||
* never sets errno. The range checks happen in the argument's own
|
||||
* precision (0x1p63 is exact in float, double and the x87 extended
|
||||
* format), and -2^63 -- exactly representable and equal to LLONG_MIN -- is
|
||||
* deliberately allowed through the strict lower-bound test.
|
||||
*/
|
||||
|
||||
/*
|
||||
* As llround, for a float argument.
|
||||
*/
|
||||
long long
|
||||
llroundf(float x)
|
||||
{
|
||||
float r = roundf(x);
|
||||
|
||||
if (isnan(r) || r >= 0x1p63f || r < -0x1p63f)
|
||||
{
|
||||
return LLONG_MIN;
|
||||
}
|
||||
return (long long)r;
|
||||
}
|
||||
|
||||
/*
|
||||
* As llround, for a double argument.
|
||||
*/
|
||||
long long
|
||||
llround(double x)
|
||||
{
|
||||
double r = round(x);
|
||||
|
||||
if (isnan(r) || r >= 0x1p63 || r < -0x1p63)
|
||||
{
|
||||
return LLONG_MIN;
|
||||
}
|
||||
return (long long)r;
|
||||
}
|
||||
|
||||
/*
|
||||
* As llround, for a long double argument.
|
||||
*/
|
||||
long long
|
||||
llroundl(long double x)
|
||||
{
|
||||
long double r = roundl(x);
|
||||
|
||||
if (isnan(r) || r >= 0x1p63L || r < -0x1p63L)
|
||||
{
|
||||
return LLONG_MIN;
|
||||
}
|
||||
return (long long)r;
|
||||
}
|
||||
+457
@@ -0,0 +1,457 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
#include <math.h>
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#define SET_ERANGE() (errno = ERANGE)
|
||||
#define SET_EDOM() (errno = EDOM)
|
||||
#else
|
||||
#define SET_ERANGE() \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
#define SET_EDOM() \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
static const double two54 = 1.80143985094819840000e+16; /* 0x4350000000000000 */
|
||||
static const double ln2_hi = 6.93147180369123816490e-01; /* 0x3fe62e42fee00000 */
|
||||
static const double ln2_lo = 1.90821492927058770002e-10; /* 0x3dea39ef35793c76 */
|
||||
static const double Lg1 = 6.666666666666735130e-01; /* 0x3FE5555555555593 */
|
||||
static const double Lg2 = 3.999999999940941908e-01; /* 0x3FD999999997FA04 */
|
||||
static const double Lg3 = 2.857142874366239149e-01; /* 0x3FD2492494229359 */
|
||||
static const double Lg4 = 2.222219843214978396e-01; /* 0x3FCC71C51D8E78AF */
|
||||
static const double Lg5 = 1.818357216161805012e-01; /* 0x3FC7466496CB03DE */
|
||||
static const double Lg6 = 1.531383769920937332e-01; /* 0x3FC39A09D078C69F */
|
||||
static const double Lg7 = 1.479819860511658591e-01; /* 0x3FC2F112DF3E5244 */
|
||||
|
||||
/* 80-bit ld80 ln2 split: ln2hi_l is a 15-bit hi word (0xb.172p-4L) so that
|
||||
* k * ln2hi_l is exact for |k| < 2^15, and ln2hi_l + ln2lo_l == ln2. */
|
||||
static const long double ln2hi_l = 6.9314575195312500000000e-1L; /* 0xb.172p-4L */
|
||||
static const long double ln2lo_l = 1.4286068203094172321214581765680755e-6L;
|
||||
static const long double ivln2_l = 0x1.71547652b82fe178p+0L; /* 1/ln2, 64-bit */
|
||||
static const long double ivln10_l = 0x1.bcb7b1526e50e32ap-2L; /* 1/ln10, 64-bit */
|
||||
|
||||
/* log(x) = k * ln2 + log(1+f), fdlibm e_log with the double split into
|
||||
* ln2_hi/ln2_lo so k*ln2_hi is exact for |k| <= 1024. */
|
||||
static double
|
||||
vlibc_log_core(double x)
|
||||
{
|
||||
union
|
||||
{
|
||||
double f;
|
||||
unsigned long long i;
|
||||
} u;
|
||||
unsigned int hx;
|
||||
unsigned int lx;
|
||||
int k;
|
||||
int i;
|
||||
int j;
|
||||
double f;
|
||||
double s;
|
||||
double z;
|
||||
double R;
|
||||
double w;
|
||||
double t1;
|
||||
double t2;
|
||||
double hfsq;
|
||||
double dk;
|
||||
|
||||
u.f = x;
|
||||
hx = (unsigned int)(u.i >> 32);
|
||||
lx = (unsigned int)u.i;
|
||||
k = 0;
|
||||
|
||||
if (hx >= 0x80000000U)
|
||||
{ /* sign bit set: -0 or negative */
|
||||
if (((hx & 0x7fffffffU) | lx) == 0U)
|
||||
return -__builtin_huge_val(); /* log(-0) = -inf */
|
||||
return __builtin_nan(""); /* log(negative) = NaN */
|
||||
}
|
||||
if ((hx | lx) == 0U)
|
||||
return -__builtin_huge_val(); /* log(+0) = -inf */
|
||||
if (hx >= 0x7ff00000U)
|
||||
return x + x; /* +inf -> +inf, NaN -> NaN */
|
||||
|
||||
if (hx < 0x00100000U)
|
||||
{ /* subnormal: scale into the normal range */
|
||||
k -= 54;
|
||||
x *= two54;
|
||||
u.f = x;
|
||||
hx = (unsigned int)(u.i >> 32);
|
||||
lx = (unsigned int)u.i;
|
||||
}
|
||||
k += (int)(hx >> 20) - 1023;
|
||||
hx &= 0x000fffffU;
|
||||
i = (int)((hx + 0x95f64U) & 0x100000U);
|
||||
u.i = ((unsigned long long)(hx | (i ^ 0x3ff00000U)) << 32) | (unsigned long long)lx;
|
||||
x = u.f;
|
||||
k += i >> 20;
|
||||
f = x - 1.0;
|
||||
|
||||
if ((0x000fffffU & (2U + hx)) < 3U)
|
||||
{ /* |f| < 2^-20 */
|
||||
if (f == 0.0)
|
||||
{
|
||||
if (k == 0)
|
||||
return 0.0;
|
||||
dk = (double)k;
|
||||
return dk * ln2_hi + dk * ln2_lo;
|
||||
}
|
||||
R = f * f * (0.5 - 0.33333333333333333333 * f);
|
||||
if (k == 0)
|
||||
return f - R;
|
||||
dk = (double)k;
|
||||
return dk * ln2_hi - ((R - dk * ln2_lo) - f);
|
||||
}
|
||||
s = f / (2.0 + f);
|
||||
dk = (double)k;
|
||||
z = s * s;
|
||||
i = (int)hx - 0x6147a;
|
||||
w = z * z;
|
||||
j = 0x6b851 - (int)hx;
|
||||
t1 = w * (Lg2 + w * (Lg4 + w * Lg6));
|
||||
t2 = z * (Lg1 + w * (Lg3 + w * (Lg5 + w * Lg7)));
|
||||
i |= j;
|
||||
R = t2 + t1;
|
||||
if (i > 0)
|
||||
{
|
||||
hfsq = 0.5 * f * f;
|
||||
if (k == 0)
|
||||
return f - (hfsq - s * (hfsq + R));
|
||||
return dk * ln2_hi - ((hfsq - (s * (hfsq + R) + dk * ln2_lo)) - f);
|
||||
}
|
||||
if (k == 0)
|
||||
return f - s * (f - R);
|
||||
return dk * ln2_hi - ((s * (f - R) - dk * ln2_lo) - f);
|
||||
}
|
||||
|
||||
double
|
||||
log(double x)
|
||||
{
|
||||
if (x < 0.0)
|
||||
SET_EDOM();
|
||||
else if (x == 0.0)
|
||||
SET_ERANGE();
|
||||
return vlibc_log_core(x);
|
||||
}
|
||||
|
||||
float
|
||||
logf(float x)
|
||||
{
|
||||
return (float)log((double)x);
|
||||
}
|
||||
|
||||
/* z = A/(2^64 + A) for 0 <= A <= 2^62, correctly rounded to long double,
|
||||
* via integer long division of A^2/(2^64 + A) */
|
||||
static long double
|
||||
vlibc_logl_ratio(unsigned long long A)
|
||||
{
|
||||
unsigned long long a0;
|
||||
unsigned long long a1;
|
||||
unsigned long long p0;
|
||||
unsigned long long p1;
|
||||
unsigned long long p2;
|
||||
unsigned long long lo;
|
||||
unsigned long long hi;
|
||||
unsigned long long q;
|
||||
unsigned long long rhi;
|
||||
unsigned long long rlo;
|
||||
unsigned long long zq;
|
||||
int msb;
|
||||
int e;
|
||||
int j;
|
||||
unsigned long long dj;
|
||||
unsigned long long shi;
|
||||
unsigned long long slo;
|
||||
unsigned long long M;
|
||||
unsigned long long c2;
|
||||
unsigned long long l2;
|
||||
int i;
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long mm;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} two;
|
||||
|
||||
a0 = A & 0xffffffffULL;
|
||||
a1 = A >> 32;
|
||||
p0 = a0 * a0;
|
||||
p1 = (a1 * a0) << 1;
|
||||
p2 = a1 * a1;
|
||||
lo = p0 + (p1 << 32);
|
||||
hi = p2 + (p1 >> 32) + (lo < p0);
|
||||
|
||||
/* q = floor(A^2 / (2^64 + A)); the top dividend bits shift in with no
|
||||
* quotient bits (q < 2^60), so start with R = hi and shift in lo */
|
||||
q = 0;
|
||||
rhi = 0;
|
||||
rlo = hi;
|
||||
for (i = 63; i >= 0; i--)
|
||||
{
|
||||
unsigned long long nhi = (rhi << 1) | (rlo >> 63);
|
||||
unsigned long long nlo = (rlo << 1) | ((lo >> i) & 1ULL);
|
||||
if (nhi > 1 || (nhi == 1 && nlo >= A))
|
||||
{
|
||||
unsigned long long nnlo = nlo - A;
|
||||
unsigned long long borrow = nnlo > nlo;
|
||||
nlo = nnlo;
|
||||
nhi -= 1 + borrow;
|
||||
if (i < 60)
|
||||
q |= 1ULL << i;
|
||||
}
|
||||
rhi = nhi;
|
||||
rlo = nlo;
|
||||
}
|
||||
|
||||
/* z*2^64 = zq - R/D with R = (rhi,rlo) in [0, D) */
|
||||
zq = A - q;
|
||||
if (zq == 0)
|
||||
return 0.0L;
|
||||
if (A == 1)
|
||||
{ /* z = (2^64 - 1) * 2^-128: below the general path's reach */
|
||||
two.p.mm = 0xffffffffffffffffULL;
|
||||
two.p.se = (unsigned short)(16383 - 65);
|
||||
return two.f;
|
||||
}
|
||||
/* e = floor(log2 z): z*2^64 is in (zq-1, zq]; significand is
|
||||
* M = round(z * 2^(63-e)) = round(z*2^64 * 2^j) with j = -1-e */
|
||||
msb = 63 - __builtin_clzll((rhi != 0 || rlo != 0) ? zq - 1 : zq);
|
||||
e = msb - 64;
|
||||
j = -1 - e;
|
||||
/* floor(R * 2^j / D) via j more long-division steps */
|
||||
dj = 0;
|
||||
shi = rhi;
|
||||
slo = rlo;
|
||||
for (i = j - 1; i >= 0; i--)
|
||||
{
|
||||
unsigned long long nhi = (shi << 1) | (slo >> 63);
|
||||
unsigned long long nlo = slo << 1;
|
||||
if (nhi > 1 || (nhi == 1 && nlo >= A))
|
||||
{
|
||||
unsigned long long nnlo = nlo - A;
|
||||
unsigned long long borrow = nnlo > nlo;
|
||||
nlo = nnlo;
|
||||
nhi -= 1 + borrow;
|
||||
dj |= 1ULL << i;
|
||||
}
|
||||
shi = nhi;
|
||||
slo = nlo;
|
||||
}
|
||||
/* M = zq*2^j - dj - r_j/D; round to nearest even */
|
||||
M = (zq << j) - dj;
|
||||
c2 = (shi << 1) + (slo >> 63);
|
||||
l2 = slo << 1;
|
||||
if (c2 > 1 || (c2 == 1 && l2 > A) || (c2 == 1 && l2 == A && (M & 1ULL)))
|
||||
M--;
|
||||
two.p.mm = 0x8000000000000000ULL;
|
||||
two.p.se = (unsigned short)(16383 + e - 63);
|
||||
return (long double)M * two.f;
|
||||
}
|
||||
|
||||
/* 80-bit x87 log: normalize m to [2^63, 2^64), then
|
||||
* log(x) = k * ln2 + 2*atanh(f/(2+f)). The k*ln2 part is exact and the
|
||||
* atanh sum is accumulated as a double-double pair so the result keeps
|
||||
* full 64-bit precision even when k*ln2 and ln(1+f) cancel. The caller
|
||||
* scales by c to get log, log2, or log10. */
|
||||
static long double
|
||||
vlibc_logl_scaled(long double x, long double c)
|
||||
{
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} u;
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
int k;
|
||||
int s;
|
||||
int n;
|
||||
long double f;
|
||||
long double z;
|
||||
long double w;
|
||||
long double term;
|
||||
long double sh;
|
||||
long double sl;
|
||||
long double t;
|
||||
long double e;
|
||||
long double s2;
|
||||
long double zz;
|
||||
long double a1;
|
||||
long double a2;
|
||||
long double b1;
|
||||
long double b2;
|
||||
long double hi;
|
||||
long double lo;
|
||||
|
||||
u.f = x;
|
||||
se = u.p.se;
|
||||
m = u.p.m;
|
||||
if (m == 0)
|
||||
return -__builtin_huge_vall(); /* +-0 -> -inf */
|
||||
if (se != 0U && m < 0x8000000000000000ULL)
|
||||
return __builtin_nanl(""); /* unnormal: invalid operand, glibc NaN */
|
||||
if ((se & 0x8000U) != 0U)
|
||||
return __builtin_nanl(""); /* negative incl -inf -> NaN */
|
||||
if ((se & 0x7fffU) == 0x7fffU)
|
||||
return x; /* +inf, NaN */
|
||||
|
||||
if (m < 0x8000000000000000ULL)
|
||||
{ /* subnormal or pseudo-denormal: normalize */
|
||||
s = __builtin_clzll(m);
|
||||
m <<= s;
|
||||
}
|
||||
else
|
||||
s = 0;
|
||||
k = (se == 0U ? -16382 : (int)(se & 0x7fffU) - 16383) - s;
|
||||
|
||||
f = (long double)m * 0x1p-63L - 1.0L; /* exact, f in [0,1) */
|
||||
if (f >= 0.5L)
|
||||
{ /* fold [0.5,1) into [-0.25,0) so k*ln2 + ln(1+f) does not cancel:
|
||||
* z = -v/(1-v) = -(v + v^2 + v^3 + ...) with v = (2^64 - m)*2^-65 in
|
||||
* (0,1/8], accumulated with TwoSum so the fast convergence keeps z
|
||||
* accurate (a plain division would round its 65-bit denominator) */
|
||||
unsigned long long C = 0ULL - m;
|
||||
long double v = (long double)C * 0x1p-65L;
|
||||
long double term;
|
||||
long double sh;
|
||||
long double sl;
|
||||
long double t;
|
||||
long double e;
|
||||
long double s2;
|
||||
long double zz;
|
||||
int n;
|
||||
|
||||
k += 1;
|
||||
sh = -v;
|
||||
sl = 0.0L;
|
||||
term = v;
|
||||
for (n = 2; n <= 43; n++)
|
||||
{
|
||||
term *= v;
|
||||
if (term == 0.0L)
|
||||
break;
|
||||
t = -term;
|
||||
s2 = sh + t;
|
||||
zz = s2 - sh;
|
||||
e = (sh - (s2 - zz)) + (t - zz);
|
||||
sh = s2;
|
||||
sl += e;
|
||||
}
|
||||
z = sh + sl;
|
||||
}
|
||||
else
|
||||
z = vlibc_logl_ratio(m - 0x8000000000000000ULL);
|
||||
w = z * z;
|
||||
term = z;
|
||||
sh = z;
|
||||
sl = 0.0L;
|
||||
for (n = 3; n <= 43; n += 2)
|
||||
{ /* TwoSum accumulation: (sh,sl) == sum of terms to ~2^-126 */
|
||||
term *= w;
|
||||
if (term == 0.0L)
|
||||
break;
|
||||
t = term / (long double)n;
|
||||
s2 = sh + t;
|
||||
zz = s2 - sh;
|
||||
e = (sh - (s2 - zz)) + (t - zz);
|
||||
sh = s2;
|
||||
sl += e;
|
||||
}
|
||||
a1 = (long double)k * ln2hi_l; /* exact: |k| < 2^15, ln2hi_l is 15 bits */
|
||||
a2 = (long double)k * ln2lo_l;
|
||||
b1 = 2.0L * sh;
|
||||
b2 = 2.0L * sl;
|
||||
/* double-double add (a1,a2) + (b1,b2), then renormalize */
|
||||
s2 = a1 + b1;
|
||||
zz = s2 - a1;
|
||||
e = (a1 - (s2 - zz)) + (b1 - zz);
|
||||
hi = s2;
|
||||
lo = e + a2 + b2;
|
||||
s2 = hi + lo;
|
||||
lo -= s2 - hi;
|
||||
hi = s2;
|
||||
return hi * c + lo * c;
|
||||
}
|
||||
|
||||
static long double
|
||||
vlibc_logl_core(long double x)
|
||||
{
|
||||
return vlibc_logl_scaled(x, 1.0L);
|
||||
}
|
||||
|
||||
long double
|
||||
logl(long double x)
|
||||
{
|
||||
if (x < 0.0L)
|
||||
SET_EDOM();
|
||||
else if (x == 0.0L)
|
||||
SET_ERANGE();
|
||||
return vlibc_logl_core(x);
|
||||
}
|
||||
|
||||
double
|
||||
log2(double x)
|
||||
{
|
||||
if (x < 0.0)
|
||||
SET_EDOM();
|
||||
else if (x == 0.0)
|
||||
SET_ERANGE();
|
||||
return (double)vlibc_logl_scaled((long double)x, ivln2_l);
|
||||
}
|
||||
|
||||
float
|
||||
log2f(float x)
|
||||
{
|
||||
return (float)log2((double)x);
|
||||
}
|
||||
|
||||
long double
|
||||
log2l(long double x)
|
||||
{
|
||||
if (x < 0.0L)
|
||||
SET_EDOM();
|
||||
else if (x == 0.0L)
|
||||
SET_ERANGE();
|
||||
return vlibc_logl_scaled(x, ivln2_l);
|
||||
}
|
||||
|
||||
double
|
||||
log10(double x)
|
||||
{
|
||||
if (x < 0.0)
|
||||
SET_EDOM();
|
||||
else if (x == 0.0)
|
||||
SET_ERANGE();
|
||||
return (double)vlibc_logl_scaled((long double)x, ivln10_l);
|
||||
}
|
||||
|
||||
float
|
||||
log10f(float x)
|
||||
{
|
||||
return (float)log10((double)x);
|
||||
}
|
||||
|
||||
long double
|
||||
log10l(long double x)
|
||||
{
|
||||
if (x < 0.0L)
|
||||
SET_EDOM();
|
||||
else if (x == 0.0L)
|
||||
SET_ERANGE();
|
||||
return vlibc_logl_scaled(x, ivln10_l);
|
||||
}
|
||||
@@ -0,0 +1,427 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
#include <math.h>
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#define SET_ERANGE() (errno = ERANGE)
|
||||
#define SET_EDOM() (errno = EDOM)
|
||||
#else
|
||||
#define SET_ERANGE() \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
#define SET_EDOM() \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
static const double ln2_hi = 6.93147180369123816490e-01; /* 0x3fe62e42fee00000 */
|
||||
static const double ln2_lo = 1.90821492927058770002e-10; /* 0x3dea39ef35793c76 */
|
||||
static const double Lg1 = 6.666666666666735130e-01; /* 0x3FE5555555555593 */
|
||||
static const double Lg2 = 3.999999999940941908e-01; /* 0x3FD999999997FA04 */
|
||||
static const double Lg3 = 2.857142874366239149e-01; /* 0x3FD2492494229359 */
|
||||
static const double Lg4 = 2.222219843214978396e-01; /* 0x3FCC71C51D8E78AF */
|
||||
static const double Lg5 = 1.818357216161805012e-01; /* 0x3FC7466496CB03DE */
|
||||
static const double Lg6 = 1.531383769920937332e-01; /* 0x3FC39A09D078C69F */
|
||||
static const double Lg7 = 1.479819860511658591e-01; /* 0x3FC2F112DF3E5244 */
|
||||
|
||||
/* 80-bit ld80 ln2 split: ln2hi_l is a 15-bit hi word (0xb.172p-4L) so that
|
||||
* k * ln2hi_l is exact for |k| < 2^15, and ln2hi_l + ln2lo_l == ln2. */
|
||||
static const long double ln2hi_l = 6.9314575195312500000000e-1L; /* 0xb.172p-4L */
|
||||
static const long double ln2lo_l = 1.4286068203094172321214581765680755e-6L;
|
||||
|
||||
/* log1p(x) = log(1+x) for the double case: fdlibm e_log1p with the argument
|
||||
* reduced into [sqrt(2)/2-1, sqrt(2)-1] so no precision is lost for small x. */
|
||||
static double
|
||||
vlibc_log1p_core(double x)
|
||||
{
|
||||
union
|
||||
{
|
||||
double f;
|
||||
unsigned long long i;
|
||||
} u;
|
||||
unsigned int hx;
|
||||
unsigned int hu;
|
||||
int k;
|
||||
double hfsq;
|
||||
double f;
|
||||
double c;
|
||||
double s;
|
||||
double z;
|
||||
double R;
|
||||
double w;
|
||||
double t1;
|
||||
double t2;
|
||||
double dk;
|
||||
|
||||
u.f = x;
|
||||
hx = (unsigned int)(u.i >> 32);
|
||||
k = 1;
|
||||
if (hx < 0x3fda827aU || (hx >> 31) != 0U)
|
||||
{ /* 1+x < sqrt(2)+, or x < 0 */
|
||||
if (hx >= 0xbff00000U)
|
||||
{ /* x <= -1.0 */
|
||||
if (x == -1.0)
|
||||
return -__builtin_huge_val(); /* log1p(-1) = -inf */
|
||||
return __builtin_nan(""); /* log1p(x<-1) = NaN */
|
||||
}
|
||||
if (hx << 1 < 0x3ca00000U << 1)
|
||||
return x; /* |x| < 2^-53: log1p(x) == x */
|
||||
if (hx <= 0xbfd2bec4U)
|
||||
{ /* x in [-sqrt(2)/2+1, sqrt(2)-1]: no reduction needed */
|
||||
k = 0;
|
||||
c = 0.0;
|
||||
f = x;
|
||||
}
|
||||
}
|
||||
else if (hx >= 0x7ff00000U)
|
||||
return x; /* +inf -> +inf, NaN -> NaN */
|
||||
|
||||
if (k != 0)
|
||||
{ /* reduce 1+x into [sqrt(2)/2, sqrt(2)] */
|
||||
u.f = 1.0 + x;
|
||||
hu = (unsigned int)(u.i >> 32);
|
||||
hu += 0x3ff00000U - 0x3fe6a09eU;
|
||||
k = (int)(hu >> 20) - 0x3ff;
|
||||
if (k < 54)
|
||||
{
|
||||
c = k >= 2 ? 1.0 - (u.f - x) : x - (u.f - 1.0);
|
||||
c /= u.f;
|
||||
}
|
||||
else
|
||||
c = 0.0;
|
||||
hu = (hu & 0x000fffffU) + 0x3fe6a09eU;
|
||||
u.i = ((unsigned long long)hu << 32) | (u.i & 0xffffffffULL);
|
||||
f = u.f - 1.0;
|
||||
}
|
||||
hfsq = 0.5 * f * f;
|
||||
s = f / (2.0 + f);
|
||||
z = s * s;
|
||||
w = z * z;
|
||||
t1 = w * (Lg2 + w * (Lg4 + w * Lg6));
|
||||
t2 = z * (Lg1 + w * (Lg3 + w * (Lg5 + w * Lg7)));
|
||||
R = t2 + t1;
|
||||
dk = (double)k;
|
||||
return s * (hfsq + R) + (dk * ln2_lo + c) - hfsq + f + dk * ln2_hi;
|
||||
}
|
||||
|
||||
double
|
||||
log1p(double x)
|
||||
{
|
||||
if (x < -1.0)
|
||||
SET_EDOM();
|
||||
else if (x == -1.0)
|
||||
SET_ERANGE();
|
||||
return vlibc_log1p_core(x);
|
||||
}
|
||||
|
||||
float
|
||||
log1pf(float x)
|
||||
{
|
||||
return (float)log1p((double)x);
|
||||
}
|
||||
|
||||
/* z = A/(2^64 + A) for 0 <= A <= 2^62, correctly rounded to long double,
|
||||
* via integer long division of A^2/(2^64 + A) */
|
||||
static long double
|
||||
vlibc_logl_ratio(unsigned long long A)
|
||||
{
|
||||
unsigned long long a0;
|
||||
unsigned long long a1;
|
||||
unsigned long long p0;
|
||||
unsigned long long p1;
|
||||
unsigned long long p2;
|
||||
unsigned long long lo;
|
||||
unsigned long long hi;
|
||||
unsigned long long q;
|
||||
unsigned long long rhi;
|
||||
unsigned long long rlo;
|
||||
unsigned long long zq;
|
||||
int msb;
|
||||
int e;
|
||||
int j;
|
||||
unsigned long long dj;
|
||||
unsigned long long shi;
|
||||
unsigned long long slo;
|
||||
unsigned long long M;
|
||||
unsigned long long c2;
|
||||
unsigned long long l2;
|
||||
int i;
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long mm;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} two;
|
||||
|
||||
a0 = A & 0xffffffffULL;
|
||||
a1 = A >> 32;
|
||||
p0 = a0 * a0;
|
||||
p1 = (a1 * a0) << 1;
|
||||
p2 = a1 * a1;
|
||||
lo = p0 + (p1 << 32);
|
||||
hi = p2 + (p1 >> 32) + (lo < p0);
|
||||
|
||||
/* q = floor(A^2 / (2^64 + A)); the top dividend bits shift in with no
|
||||
* quotient bits (q < 2^60), so start with R = hi and shift in lo */
|
||||
q = 0;
|
||||
rhi = 0;
|
||||
rlo = hi;
|
||||
for (i = 63; i >= 0; i--)
|
||||
{
|
||||
unsigned long long nhi = (rhi << 1) | (rlo >> 63);
|
||||
unsigned long long nlo = (rlo << 1) | ((lo >> i) & 1ULL);
|
||||
if (nhi > 1 || (nhi == 1 && nlo >= A))
|
||||
{
|
||||
unsigned long long nnlo = nlo - A;
|
||||
unsigned long long borrow = nnlo > nlo;
|
||||
nlo = nnlo;
|
||||
nhi -= 1 + borrow;
|
||||
if (i < 60)
|
||||
q |= 1ULL << i;
|
||||
}
|
||||
rhi = nhi;
|
||||
rlo = nlo;
|
||||
}
|
||||
|
||||
/* z*2^64 = zq - R/D with R = (rhi,rlo) in [0, D) */
|
||||
zq = A - q;
|
||||
if (zq == 0)
|
||||
return 0.0L;
|
||||
if (A == 1)
|
||||
{ /* z = (2^64 - 1) * 2^-128: below the general path's reach */
|
||||
two.p.mm = 0xffffffffffffffffULL;
|
||||
two.p.se = (unsigned short)(16383 - 65);
|
||||
return two.f;
|
||||
}
|
||||
/* e = floor(log2 z): z*2^64 is in (zq-1, zq]; significand is
|
||||
* M = round(z * 2^(63-e)) = round(z*2^64 * 2^j) with j = -1-e */
|
||||
msb = 63 - __builtin_clzll((rhi != 0 || rlo != 0) ? zq - 1 : zq);
|
||||
e = msb - 64;
|
||||
j = -1 - e;
|
||||
/* floor(R * 2^j / D) via j more long-division steps */
|
||||
dj = 0;
|
||||
shi = rhi;
|
||||
slo = rlo;
|
||||
for (i = j - 1; i >= 0; i--)
|
||||
{
|
||||
unsigned long long nhi = (shi << 1) | (slo >> 63);
|
||||
unsigned long long nlo = slo << 1;
|
||||
if (nhi > 1 || (nhi == 1 && nlo >= A))
|
||||
{
|
||||
unsigned long long nnlo = nlo - A;
|
||||
unsigned long long borrow = nnlo > nlo;
|
||||
nlo = nnlo;
|
||||
nhi -= 1 + borrow;
|
||||
dj |= 1ULL << i;
|
||||
}
|
||||
shi = nhi;
|
||||
slo = nlo;
|
||||
}
|
||||
/* M = zq*2^j - dj - r_j/D; round to nearest even */
|
||||
M = (zq << j) - dj;
|
||||
c2 = (shi << 1) + (slo >> 63);
|
||||
l2 = slo << 1;
|
||||
if (c2 > 1 || (c2 == 1 && l2 > A) || (c2 == 1 && l2 == A && (M & 1ULL)))
|
||||
M--;
|
||||
two.p.mm = 0x8000000000000000ULL;
|
||||
two.p.se = (unsigned short)(16383 + e - 63);
|
||||
return (long double)M * two.f;
|
||||
}
|
||||
|
||||
/* 80-bit x87 log: normalize m to [2^63, 2^64), then
|
||||
* log(x) = k * ln2 + 2*atanh(f/(2+f)). The k*ln2 part is exact and the
|
||||
* atanh sum is accumulated as a double-double pair so the result keeps
|
||||
* full 64-bit precision even when k*ln2 and ln(1+f) cancel. */
|
||||
static long double
|
||||
vlibc_logl_core(long double x)
|
||||
{
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} u;
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
int k;
|
||||
int s;
|
||||
int n;
|
||||
long double f;
|
||||
long double z;
|
||||
long double w;
|
||||
long double term;
|
||||
long double sh;
|
||||
long double sl;
|
||||
long double t;
|
||||
long double e;
|
||||
long double s2;
|
||||
long double zz;
|
||||
long double a1;
|
||||
long double a2;
|
||||
long double b1;
|
||||
long double b2;
|
||||
long double hi;
|
||||
long double lo;
|
||||
|
||||
u.f = x;
|
||||
se = u.p.se;
|
||||
m = u.p.m;
|
||||
if (m == 0)
|
||||
return -__builtin_huge_vall(); /* +-0 -> -inf */
|
||||
if (se != 0U && m < 0x8000000000000000ULL)
|
||||
return __builtin_nanl(""); /* unnormal: invalid operand, glibc NaN */
|
||||
if ((se & 0x8000U) != 0U)
|
||||
return __builtin_nanl(""); /* negative incl -inf -> NaN */
|
||||
if ((se & 0x7fffU) == 0x7fffU)
|
||||
return x; /* +inf, NaN */
|
||||
|
||||
if (m < 0x8000000000000000ULL)
|
||||
{ /* subnormal or pseudo-denormal: normalize */
|
||||
s = __builtin_clzll(m);
|
||||
m <<= s;
|
||||
}
|
||||
else
|
||||
s = 0;
|
||||
k = (se == 0U ? -16382 : (int)(se & 0x7fffU) - 16383) - s;
|
||||
|
||||
f = (long double)m * 0x1p-63L - 1.0L; /* exact, f in [0,1) */
|
||||
if (f >= 0.5L)
|
||||
{ /* fold [0.5,1) into [-0.25,0) so k*ln2 + ln(1+f) does not cancel:
|
||||
* z = -v/(1-v) = -(v + v^2 + v^3 + ...) with v = (2^64 - m)*2^-65 in
|
||||
* (0,1/8], accumulated with TwoSum so the fast convergence keeps z
|
||||
* accurate (a plain division would round its 65-bit denominator) */
|
||||
unsigned long long C = 0ULL - m;
|
||||
long double v = (long double)C * 0x1p-65L;
|
||||
long double term;
|
||||
long double sh;
|
||||
long double sl;
|
||||
long double t;
|
||||
long double e;
|
||||
long double s2;
|
||||
long double zz;
|
||||
int n;
|
||||
|
||||
k += 1;
|
||||
sh = -v;
|
||||
sl = 0.0L;
|
||||
term = v;
|
||||
for (n = 2; n <= 43; n++)
|
||||
{
|
||||
term *= v;
|
||||
if (term == 0.0L)
|
||||
break;
|
||||
t = -term;
|
||||
s2 = sh + t;
|
||||
zz = s2 - sh;
|
||||
e = (sh - (s2 - zz)) + (t - zz);
|
||||
sh = s2;
|
||||
sl += e;
|
||||
}
|
||||
z = sh + sl;
|
||||
}
|
||||
else
|
||||
z = vlibc_logl_ratio(m - 0x8000000000000000ULL);
|
||||
w = z * z;
|
||||
term = z;
|
||||
sh = z;
|
||||
sl = 0.0L;
|
||||
for (n = 3; n <= 43; n += 2)
|
||||
{ /* TwoSum accumulation: (sh,sl) == sum of terms to ~2^-126 */
|
||||
term *= w;
|
||||
if (term == 0.0L)
|
||||
break;
|
||||
t = term / (long double)n;
|
||||
s2 = sh + t;
|
||||
zz = s2 - sh;
|
||||
e = (sh - (s2 - zz)) + (t - zz);
|
||||
sh = s2;
|
||||
sl += e;
|
||||
}
|
||||
a1 = (long double)k * ln2hi_l; /* exact: |k| < 2^15, ln2hi_l is 15 bits */
|
||||
a2 = (long double)k * ln2lo_l;
|
||||
b1 = 2.0L * sh;
|
||||
b2 = 2.0L * sl;
|
||||
/* double-double add (a1,a2) + (b1,b2), then renormalize */
|
||||
s2 = a1 + b1;
|
||||
zz = s2 - a1;
|
||||
e = (a1 - (s2 - zz)) + (b1 - zz);
|
||||
hi = s2;
|
||||
lo = e + a2 + b2;
|
||||
s2 = hi + lo;
|
||||
lo -= s2 - hi;
|
||||
hi = s2;
|
||||
return hi + lo;
|
||||
}
|
||||
|
||||
/* 80-bit x87 log1p: y = 1+x as an exact double-double pair (yh,yl), then
|
||||
* log1p(x) = log(yh) + log(1 + yl/yh) = log(yh) + t with |t| <= 2^-64, so
|
||||
* 1+x never rounds away the small argument. */
|
||||
static long double
|
||||
vlibc_log1pl_core(long double x)
|
||||
{
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} u;
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
long double yh;
|
||||
long double yl;
|
||||
long double t;
|
||||
|
||||
u.f = x;
|
||||
se = u.p.se;
|
||||
m = u.p.m;
|
||||
if (m == 0)
|
||||
return x; /* +-0 -> +-0 */
|
||||
if (se != 0U && m < 0x8000000000000000ULL)
|
||||
return __builtin_nanl(""); /* unnormal: invalid operand, glibc NaN */
|
||||
if ((se & 0x7fffU) == 0x7fffU)
|
||||
{
|
||||
if (m == 0x8000000000000000ULL)
|
||||
return (se & 0x8000U) != 0U ? __builtin_nanl("") : x; /* -inf, +inf */
|
||||
return x; /* NaN */
|
||||
}
|
||||
if (x <= -1.0L)
|
||||
return x == -1.0L ? -__builtin_huge_vall() : __builtin_nanl("");
|
||||
if (x > -0x1p-65L && x < 0x1p-65L)
|
||||
{
|
||||
if (se == 0U && m >= 0x8000000000000000ULL)
|
||||
{ /* pseudo-denormal: glibc returns the (m, 1) encoding */
|
||||
u.p.se = 1;
|
||||
return u.f;
|
||||
}
|
||||
return x; /* |x| < 2^-65: log1p(x) == x */
|
||||
}
|
||||
yh = 1.0L + x;
|
||||
yl = x - (yh - 1.0L); /* exact residual of the 1+x rounding for |x| <= 1 */
|
||||
t = yl / yh;
|
||||
return vlibc_logl_core(yh) + t;
|
||||
}
|
||||
|
||||
long double
|
||||
log1pl(long double x)
|
||||
{
|
||||
if (x < -1.0L)
|
||||
SET_EDOM();
|
||||
else if (x == -1.0L)
|
||||
SET_ERANGE();
|
||||
return vlibc_log1pl_core(x);
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* The signed exponent of x as a floating-point value (C23 7.12.6.6),
|
||||
* all three precisions: logb(x) is floor(log2 |x|) as a float/double/
|
||||
* long double. logb(±0) is -Inf and logb(±Inf) is +Inf, each raising
|
||||
* the division-by-zero/invalid exception through the hardware but
|
||||
* without an errno path in the representable domain; logb(NaN) returns
|
||||
* the NaN. Pure, so the header marks the functions const; the exponent
|
||||
* extraction is exactly the ilogb one (see ilogb.c) with the int result
|
||||
* converted back to the argument's precision.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The double format: bits 62..52 hold the exponent biased by 1023 and
|
||||
* bits 51..0 the fraction. A subnormal with fraction F is F * 2^-1074.
|
||||
*/
|
||||
double
|
||||
logb(double x)
|
||||
{
|
||||
unsigned long long bits;
|
||||
unsigned long long frac;
|
||||
int e;
|
||||
int msb;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
frac = bits & 0xFFFFFFFFFFFFFULL;
|
||||
e = (int)((bits >> 52) & 0x7ff);
|
||||
|
||||
if (e == 0x7ff)
|
||||
{
|
||||
/* Inf maps to +Inf (both signs); NaN passes through. */
|
||||
return frac == 0 ? HUGE_VAL : x;
|
||||
}
|
||||
if (e == 0)
|
||||
{
|
||||
if (frac == 0)
|
||||
{
|
||||
return -HUGE_VAL;
|
||||
}
|
||||
msb = 63 - __builtin_clzll(frac);
|
||||
return (double)(msb - 1074);
|
||||
}
|
||||
return (double)(e - 1023);
|
||||
}
|
||||
|
||||
/*
|
||||
* The float format: bits 30..23 hold the exponent biased by 127 and
|
||||
* bits 22..0 the fraction; a subnormal with fraction F is F * 2^-149.
|
||||
*/
|
||||
float
|
||||
logbf(float x)
|
||||
{
|
||||
unsigned int bits;
|
||||
unsigned int frac;
|
||||
int e;
|
||||
int msb;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
frac = bits & 0x7FFFFFU;
|
||||
e = (int)((bits >> 23) & 0xff);
|
||||
|
||||
if (e == 0xff)
|
||||
{
|
||||
return frac == 0 ? HUGE_VALF : x;
|
||||
}
|
||||
if (e == 0)
|
||||
{
|
||||
if (frac == 0)
|
||||
{
|
||||
return -HUGE_VALF;
|
||||
}
|
||||
msb = 31 - __builtin_clz(frac);
|
||||
return (float)(msb - 149);
|
||||
}
|
||||
return (float)(e - 127);
|
||||
}
|
||||
|
||||
/*
|
||||
* The x86 80-bit extended format: 64 significand bits m (explicit
|
||||
* integer bit) with a sign/exponent word se biased by 16383. A normal m
|
||||
* in [2^63, 2^64) sits in [2^(e-16383), 2^(e-16382)). A subnormal with
|
||||
* e == 0 is interpreted as if e were 1, i.e. m * 2^-16445; an unnormal
|
||||
* (e > 0 with m < 2^63) is m * 2^(e - 16446).
|
||||
*/
|
||||
long double
|
||||
logbl(long double x)
|
||||
{
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
int e;
|
||||
int msb;
|
||||
|
||||
__builtin_memcpy(&p, &x, sizeof p);
|
||||
e = p.se & 0x7fff;
|
||||
|
||||
if (e == 0x7fff)
|
||||
{
|
||||
return (p.m & 0x7FFFFFFFFFFFFFFFULL) == 0 ? HUGE_VALL : x;
|
||||
}
|
||||
if (p.m == 0)
|
||||
{
|
||||
return -HUGE_VALL;
|
||||
}
|
||||
if (e == 0)
|
||||
{
|
||||
msb = 63 - __builtin_clzll(p.m);
|
||||
return (long double)(msb - 16445);
|
||||
}
|
||||
if (p.m < 0x8000000000000000ULL)
|
||||
{
|
||||
msb = 63 - __builtin_clzll(p.m);
|
||||
return (long double)(msb + e - 16446);
|
||||
}
|
||||
return (long double)(e - 16383);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* The nearest integral value to x in the current rounding direction,
|
||||
* returned as long (C23 7.12.9.7), all three precisions. A result
|
||||
* outside the range of long is a range error whose return value is
|
||||
* unspecified, so the header declares no const attribute; the test
|
||||
* corpus keeps |x| < 2^62 where every result is exact.
|
||||
*
|
||||
* GCC never folds the __builtin_lrint* forms on this target (external
|
||||
* lrint@PLT calls at every optimization level), so lrint is built as
|
||||
* rint-then-convert: __builtin_rint* folds to the in-line round-to-
|
||||
* nearest-even sequence (see rint.c), producing an exact integral value,
|
||||
* and the cast to long is then exact no matter which conversion
|
||||
* instruction GCC emits. The rint step honors the MXCSR/x87 rounding
|
||||
* mode, the only reachable one being the default round-to-nearest-even
|
||||
* (no <fenv.h> exists in vlibc yet).
|
||||
*/
|
||||
|
||||
/*
|
||||
* As lrint, for a float argument.
|
||||
*/
|
||||
long
|
||||
lrintf(float x)
|
||||
{
|
||||
return (long)__builtin_rintf(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* As lrint, for a double argument.
|
||||
*/
|
||||
long
|
||||
lrint(double x)
|
||||
{
|
||||
return (long)__builtin_rint(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* As lrint, for a long double argument.
|
||||
*/
|
||||
long
|
||||
lrintl(long double x)
|
||||
{
|
||||
return (long)__builtin_rintl(x);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* The nearest integral value to x, halfway cases rounded away from zero,
|
||||
* returned as long (C23 7.12.9.9), all three precisions.
|
||||
*
|
||||
* On this LP64 target long is 64 bits wide, so lround is llround in every
|
||||
* observable way: the value is rounded half away from zero by round.c and
|
||||
* narrowed, and a NaN, an Inf, or any rounded magnitude >= 2^63 collapses
|
||||
* to LONG_MIN, exactly what the host glibc's hardware-conversion lround
|
||||
* returns for such arguments (measured on glibc 2.44; errno untouched).
|
||||
* The reasoning, the range test shape and the -2^63 == LONG_MIN boundary
|
||||
* subtlety are those documented in llround.c.
|
||||
*/
|
||||
|
||||
/*
|
||||
* As lround, for a float argument.
|
||||
*/
|
||||
long
|
||||
lroundf(float x)
|
||||
{
|
||||
float r = roundf(x);
|
||||
|
||||
if (isnan(r) || r >= 0x1p63f || r < -0x1p63f)
|
||||
{
|
||||
return LONG_MIN;
|
||||
}
|
||||
return (long)r;
|
||||
}
|
||||
|
||||
/*
|
||||
* As lround, for a double argument.
|
||||
*/
|
||||
long
|
||||
lround(double x)
|
||||
{
|
||||
double r = round(x);
|
||||
|
||||
if (isnan(r) || r >= 0x1p63 || r < -0x1p63)
|
||||
{
|
||||
return LONG_MIN;
|
||||
}
|
||||
return (long)r;
|
||||
}
|
||||
|
||||
/*
|
||||
* As lround, for a long double argument.
|
||||
*/
|
||||
long
|
||||
lroundl(long double x)
|
||||
{
|
||||
long double r = roundl(x);
|
||||
|
||||
if (isnan(r) || r >= 0x1p63L || r < -0x1p63L)
|
||||
{
|
||||
return LONG_MIN;
|
||||
}
|
||||
return (long)r;
|
||||
}
|
||||
@@ -0,0 +1,341 @@
|
||||
#ifndef VLIBC_MATH_IMPL_H
|
||||
#define VLIBC_MATH_IMPL_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/*
|
||||
* vlibc — private helpers shared by the src/math/ implementation files.
|
||||
*
|
||||
* This header is internal to the todo-39 arithmetic slices (frexp, ldexp,
|
||||
* modf, scalbn, scalbln, and later slices that need to scale a value by a
|
||||
* power of two); it is never installed and is not public API.
|
||||
*
|
||||
* Every real function below is spelled for all three precisions around one
|
||||
* unified representation of a finite value:
|
||||
*
|
||||
* value = m * 2^p
|
||||
*
|
||||
* where m is an integer significand that carries the explicit integer bit
|
||||
* (m in [2^52, 2^53) for double, [2^23, 2^24) for float, [2^63, 2^64) for
|
||||
* the x86 80-bit extended format) and p is the exact unbiased power of
|
||||
* two. Normalizing a subnormal input (and an 80-bit unnormal) into this
|
||||
* shape is an exact left shift, so the scaling logic that follows never
|
||||
* has to special-case the input class again.
|
||||
*
|
||||
* Scaling by 2^n only moves p: value = m * 2^(p + n). The result class
|
||||
* is read straight off the target exponent k = p + n, and a right shift
|
||||
* with round-to-nearest-even (vl_rne_u64) is needed only when the scaled
|
||||
* value lands in the subnormal range and some low significand bits must be
|
||||
* dropped.
|
||||
*
|
||||
* The format constants (mask widths, exponent biases) are the ones the
|
||||
* rounding-family files (round.c, rint.c, ilogb.c, ...) already document;
|
||||
* only the value interpretation changes here.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Round m >> r to nearest, ties to even, returning the kept integer. r is
|
||||
* in [1, 64]; for r == 64 the entire significand is dropped and the only
|
||||
* values that can survive are those above the tie point (1 is returned),
|
||||
* while r > 64 can never round up because m < 2^64 is below the half-way
|
||||
* threshold. Callers guarantee m < 2^64 and r >= 1.
|
||||
*/
|
||||
static inline unsigned long long
|
||||
vl_rne_u64(unsigned long long m, int r)
|
||||
{
|
||||
unsigned long long kept;
|
||||
unsigned long long dropped;
|
||||
unsigned long long half;
|
||||
|
||||
if (r >= 64)
|
||||
{
|
||||
return (r > 64 || m <= (1ULL << 63)) ? 0 : 1;
|
||||
}
|
||||
kept = m >> r;
|
||||
dropped = m & ((1ULL << r) - 1ULL);
|
||||
half = 1ULL << (r - 1);
|
||||
if (dropped > half || (dropped == half && (kept & 1ULL) != 0))
|
||||
{
|
||||
kept++;
|
||||
}
|
||||
return kept;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scale a double by 2^n. Bits 62..52 are the exponent biased by 1023,
|
||||
* bits 51..0 the fraction; the significand is 53 bits wide, so a normal
|
||||
* input has m = 2^52 | frac and p = ef - 1075. A subnormal (ef == 0,
|
||||
* frac != 0) with msb at position s (0..51) normalizes exactly to
|
||||
* m = frac << (52 - s) with p = s - 1126.
|
||||
*
|
||||
* Target k = p + n: k >= 972 overflows (the exact result exceeds DBL_MAX;
|
||||
* the flag is raised and +-Inf returned), k in [-1074, 971] is an exact
|
||||
* normal result, and k <= -1075 is subnormal-or-zero with r = -1074 - k
|
||||
* dropped bits; r >= 54 always rounds to zero, and a rounded-up field of
|
||||
* exactly 2^52 is the smallest normal (ef 1, frac 0), reached when the
|
||||
* exact value is the tie just above the largest subnormal.
|
||||
*/
|
||||
static inline double
|
||||
vl_scale2_d(double x, int n, int *overflowed) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
unsigned long long bits;
|
||||
unsigned long long sign;
|
||||
unsigned long long ef;
|
||||
unsigned long long frac;
|
||||
unsigned long long m;
|
||||
unsigned long long f;
|
||||
int s;
|
||||
int p;
|
||||
int k;
|
||||
int r;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
sign = bits & (1ULL << 63);
|
||||
ef = (bits >> 52) & 0x7ff;
|
||||
frac = bits & 0xFFFFFFFFFFFFFULL;
|
||||
|
||||
if (ef == 0x7ff)
|
||||
{
|
||||
return x; /* +-Inf and NaN pass through unchanged */
|
||||
}
|
||||
if (ef == 0)
|
||||
{
|
||||
if (frac == 0)
|
||||
{
|
||||
return x; /* +-0 stays +-0 for any n */
|
||||
}
|
||||
s = 63 - __builtin_clzll(frac);
|
||||
m = frac << (52 - s);
|
||||
p = s - 1126;
|
||||
}
|
||||
else
|
||||
{
|
||||
m = frac | (1ULL << 52);
|
||||
p = (int)ef - 1075;
|
||||
}
|
||||
|
||||
k = p + n;
|
||||
if (k >= 972)
|
||||
{
|
||||
if (overflowed != NULL)
|
||||
{
|
||||
*overflowed = 1;
|
||||
}
|
||||
bits = sign | (0x7ffULL << 52);
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
if (k >= -1074)
|
||||
{
|
||||
bits = sign | ((unsigned long long)(k + 1075) << 52) | (m - (1ULL << 52));
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
r = -1074 - k;
|
||||
if (r >= 54)
|
||||
{
|
||||
bits = sign;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
f = vl_rne_u64(m, r);
|
||||
if (f == 0)
|
||||
{
|
||||
bits = sign;
|
||||
}
|
||||
else if (f == (1ULL << 52))
|
||||
{
|
||||
bits = sign | (1ULL << 52); /* smallest normal, reached by rounding up */
|
||||
}
|
||||
else
|
||||
{
|
||||
bits = sign | f;
|
||||
}
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scale a float by 2^n. Bits 30..23 are the exponent biased by 127, bits
|
||||
* 22..0 the fraction; a normal input has m = 2^23 | frac and p = ef - 150.
|
||||
* A subnormal (ef == 0, frac != 0) with msb at s (0..22) normalizes to
|
||||
* m = frac << (23 - s) with p = s - 172.
|
||||
*
|
||||
* k >= 105 overflows, k in [-149, 104] is an exact normal result, and
|
||||
* k <= -150 is subnormal-or-zero with r = -149 - k; r >= 25 always rounds
|
||||
* to zero and a rounded-up field of exactly 2^23 is the smallest normal.
|
||||
*/
|
||||
static inline float
|
||||
vl_scale2_f(float x, int n, int *overflowed) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
unsigned int bits;
|
||||
unsigned int sign;
|
||||
unsigned int ef;
|
||||
unsigned int frac;
|
||||
unsigned int m;
|
||||
unsigned int f;
|
||||
int s;
|
||||
int p;
|
||||
int k;
|
||||
int r;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
sign = bits & (1U << 31);
|
||||
ef = (bits >> 23) & 0xff;
|
||||
frac = bits & 0x7FFFFFU;
|
||||
|
||||
if (ef == 0xff)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (ef == 0)
|
||||
{
|
||||
if (frac == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
s = 31 - __builtin_clz(frac);
|
||||
m = frac << (23 - s);
|
||||
p = s - 172;
|
||||
}
|
||||
else
|
||||
{
|
||||
m = frac | (1U << 23);
|
||||
p = (int)ef - 150;
|
||||
}
|
||||
|
||||
k = p + n;
|
||||
if (k >= 105)
|
||||
{
|
||||
if (overflowed != NULL)
|
||||
{
|
||||
*overflowed = 1;
|
||||
}
|
||||
bits = sign | (0xffU << 23);
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
if (k >= -149)
|
||||
{
|
||||
bits = sign | ((unsigned int)(k + 150) << 23) | (m - (1U << 23));
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
r = -149 - k;
|
||||
if (r >= 25)
|
||||
{
|
||||
bits = sign;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
f = (unsigned int)vl_rne_u64((unsigned long long)m, r);
|
||||
if (f == 0)
|
||||
{
|
||||
bits = sign;
|
||||
}
|
||||
else if (f == (1U << 23))
|
||||
{
|
||||
bits = sign | (1U << 23);
|
||||
}
|
||||
else
|
||||
{
|
||||
bits = sign | f;
|
||||
}
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scale an x86 80-bit extended value by 2^n. The significand m is 64 bits
|
||||
* wide with an explicit integer bit; se holds the sign (bit 15) and the
|
||||
* exponent biased by 16383 (bits 14..0). A canonical normal has m in
|
||||
* [2^63, 2^64) and p = ef - 16446 (value = m * 2^(ef - 16383 - 63)). A
|
||||
* subnormal (ef == 0, m != 0) — and, defensively, an unnormal (ef > 0 with
|
||||
* m < 2^63) — is interpreted as if the exponent were max(ef, 1) and
|
||||
* normalizes exactly by shifting m left until its msb sits at bit 63.
|
||||
*
|
||||
* k >= 16321 overflows, k in [-16445, 16320] is an exact normal result
|
||||
* (for the subnormal minimum, m = 2^63 with ef' = 1), and k <= -16446 is
|
||||
* subnormal-or-zero with r = -16445 - k; r >= 65 always rounds to zero.
|
||||
* The 64-bit significand makes r == 64 the deepest meaningful shift: only
|
||||
* m above 2^63 then rounds up (to the smallest subnormal, 2^-16445).
|
||||
*/
|
||||
static inline long double
|
||||
vl_scale2_ld(long double x, int n, int *overflowed) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
unsigned long long m;
|
||||
unsigned long long f;
|
||||
unsigned short sign;
|
||||
int ef;
|
||||
int s;
|
||||
int pwr;
|
||||
int k;
|
||||
int r;
|
||||
|
||||
__builtin_memcpy(&p, &x, sizeof p);
|
||||
m = p.m;
|
||||
sign = (unsigned short)(p.se & 0x8000);
|
||||
ef = p.se & 0x7fff;
|
||||
|
||||
if (ef == 0x7fff)
|
||||
{
|
||||
return x; /* +-Inf and NaN pass through unchanged */
|
||||
}
|
||||
if (m == 0)
|
||||
{
|
||||
return x; /* +-0 (and degenerate empty encodings) stay put */
|
||||
}
|
||||
if (m < 0x8000000000000000ULL)
|
||||
{
|
||||
s = 63 - __builtin_clzll(m);
|
||||
m <<= (63 - s);
|
||||
pwr = (ef == 0 ? 1 : ef) + s - 16509;
|
||||
}
|
||||
else
|
||||
{
|
||||
pwr = ef - 16446;
|
||||
}
|
||||
|
||||
k = pwr + n;
|
||||
if (k >= 16321)
|
||||
{
|
||||
if (overflowed != NULL)
|
||||
{
|
||||
*overflowed = 1;
|
||||
}
|
||||
p.se = (unsigned short)(sign | 0x7fff);
|
||||
p.m = 0x8000000000000000ULL;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
if (k >= -16445)
|
||||
{
|
||||
p.se = (unsigned short)(sign | (unsigned short)(k + 16446));
|
||||
p.m = m;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
|
||||
r = -16445 - k;
|
||||
if (r >= 65)
|
||||
{
|
||||
p.se = sign;
|
||||
p.m = 0;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
f = vl_rne_u64(m, r);
|
||||
p.se = sign;
|
||||
p.m = f;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
|
||||
#endif /* VLIBC_MATH_IMPL_H */
|
||||
+215
@@ -0,0 +1,215 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* Split x into an integral part stored in *iptr and a fractional part
|
||||
* returned, both carrying x's sign (C23 7.12.6.5): modf(-1.5, &i) puts
|
||||
* -1.0 in i and returns -0.5. The integral part is x truncated toward
|
||||
* zero. modf(±0, &i) stores ±0 and returns ±0; modf(±Inf, &i) stores
|
||||
* ±Inf and returns ±0 with x's sign; modf(NaN, &i) stores the NaN and
|
||||
* returns it; a subnormal |x| < 1 stores ±0 (x's sign) and returns x.
|
||||
*
|
||||
* The implementation is a bit-level truncation (the same "clear the
|
||||
* fractional mantissa bits" shape as trunc.c): a value with |x| < 1 has
|
||||
* integral part ±0; a value too large to carry a fraction (|x| >= 2^52,
|
||||
* >= 2^23, >= 2^63 for the three formats) is its own integral part; the
|
||||
* values in between have their low (fraction) bits masked off. The
|
||||
* fractional part is then x - i, an exact subtraction — x and i share the
|
||||
* significand's leading bits, so the difference is a representable
|
||||
* multiple of the common unit in the last place. When the subtraction is
|
||||
* an exact zero the fraction is ±0 with x's sign, which the hardware
|
||||
* would otherwise always report as +0.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The double format: bit 63 the sign, bits 62..52 the exponent biased by
|
||||
* 1023, bits 51..0 the fraction. A normal with exponent field e in
|
||||
* [1023, 1074] has its low (1075 - e) fraction bits below the binary
|
||||
* point; e >= 1075 means |x| >= 2^52, an integer already.
|
||||
*/
|
||||
double
|
||||
modf(double x, double *iptr)
|
||||
{
|
||||
unsigned long long bits;
|
||||
unsigned long long sign;
|
||||
unsigned long long ef;
|
||||
unsigned long long frac;
|
||||
unsigned long long ibits;
|
||||
double i;
|
||||
double f;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
sign = bits & (1ULL << 63);
|
||||
ef = (bits >> 52) & 0x7ff;
|
||||
frac = bits & 0xFFFFFFFFFFFFFULL;
|
||||
|
||||
if (ef == 0x7ff)
|
||||
{
|
||||
*iptr = x;
|
||||
if (frac != 0)
|
||||
{
|
||||
return x; /* NaN: both parts are the NaN */
|
||||
}
|
||||
return sign != 0 ? -0.0 : 0.0; /* ±Inf: fraction ±0 of x's sign */
|
||||
}
|
||||
if (ef == 0)
|
||||
{
|
||||
if (frac == 0)
|
||||
{
|
||||
*iptr = x; /* ±0 */
|
||||
return x;
|
||||
}
|
||||
*iptr = sign != 0 ? -0.0 : 0.0; /* subnormal: |x| < 1 */
|
||||
return x;
|
||||
}
|
||||
if (ef >= 1075)
|
||||
{
|
||||
*iptr = x; /* |x| >= 2^52: already integral */
|
||||
return sign != 0 ? -0.0 : 0.0;
|
||||
}
|
||||
if (ef < 1023)
|
||||
{
|
||||
*iptr = sign != 0 ? -0.0 : 0.0; /* 0 < |x| < 1 */
|
||||
return x;
|
||||
}
|
||||
|
||||
shift = 1075 - (int)ef;
|
||||
ibits = bits & ~((1ULL << shift) - 1ULL);
|
||||
__builtin_memcpy(&i, &ibits, sizeof i);
|
||||
*iptr = i;
|
||||
f = x - i;
|
||||
if (f == 0.0)
|
||||
{
|
||||
return sign != 0 ? -0.0 : 0.0;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
/*
|
||||
* The float format: bit 31 the sign, bits 30..23 the exponent biased by
|
||||
* 127, bits 22..0 the fraction. For e in [127, 149] the low (150 - e)
|
||||
* fraction bits are fractional; e >= 150 means |x| >= 2^23, integral.
|
||||
*/
|
||||
float
|
||||
modff(float x, float *iptr)
|
||||
{
|
||||
unsigned int bits;
|
||||
unsigned int sign;
|
||||
unsigned int ef;
|
||||
unsigned int frac;
|
||||
unsigned int ibits;
|
||||
float i;
|
||||
float f;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
sign = bits & (1U << 31);
|
||||
ef = (bits >> 23) & 0xff;
|
||||
frac = bits & 0x7FFFFFU;
|
||||
|
||||
if (ef == 0xff)
|
||||
{
|
||||
*iptr = x;
|
||||
if (frac != 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
return sign != 0 ? -0.0f : 0.0f;
|
||||
}
|
||||
if (ef == 0)
|
||||
{
|
||||
if (frac == 0)
|
||||
{
|
||||
*iptr = x;
|
||||
return x;
|
||||
}
|
||||
*iptr = sign != 0 ? -0.0f : 0.0f;
|
||||
return x;
|
||||
}
|
||||
if (ef >= 150)
|
||||
{
|
||||
*iptr = x;
|
||||
return sign != 0 ? -0.0f : 0.0f;
|
||||
}
|
||||
if (ef < 127)
|
||||
{
|
||||
*iptr = sign != 0 ? -0.0f : 0.0f;
|
||||
return x;
|
||||
}
|
||||
|
||||
shift = 150 - (int)ef;
|
||||
ibits = bits & ~((1U << shift) - 1U);
|
||||
__builtin_memcpy(&i, &ibits, sizeof i);
|
||||
*iptr = i;
|
||||
f = x - i;
|
||||
if (f == 0.0f)
|
||||
{
|
||||
return sign != 0 ? -0.0f : 0.0f;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
/*
|
||||
* The x86 80-bit extended format: 64 significand bits m with an explicit
|
||||
* integer bit and a sign/exponent word se biased by 16383. For e in
|
||||
* [16383, 16445] the low (16446 - e) bits of m are fractional; e >= 16446
|
||||
* means |x| >= 2^63, integral.
|
||||
*/
|
||||
long double
|
||||
modfl(long double x, long double *iptr)
|
||||
{
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
unsigned long long ibits;
|
||||
long double i;
|
||||
long double f;
|
||||
int ef;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&p, &x, sizeof p);
|
||||
ef = p.se & 0x7fff;
|
||||
|
||||
if (ef == 0x7fff)
|
||||
{
|
||||
*iptr = x;
|
||||
if (p.m != 0x8000000000000000ULL)
|
||||
{
|
||||
return x; /* NaN */
|
||||
}
|
||||
return (p.se & 0x8000) != 0 ? -0.0L : 0.0L; /* ±Inf */
|
||||
}
|
||||
if (p.m == 0)
|
||||
{
|
||||
*iptr = x; /* ±0 */
|
||||
return x;
|
||||
}
|
||||
if (ef >= 16446)
|
||||
{
|
||||
*iptr = x;
|
||||
return (p.se & 0x8000) != 0 ? -0.0L : 0.0L;
|
||||
}
|
||||
if (ef < 16383)
|
||||
{
|
||||
*iptr = (p.se & 0x8000) != 0 ? -0.0L : 0.0L; /* subnormal: |x| < 1 */
|
||||
return x;
|
||||
}
|
||||
|
||||
shift = 16446 - ef;
|
||||
ibits = p.m & ~((1ULL << shift) - 1ULL);
|
||||
p.m = ibits;
|
||||
__builtin_memcpy(&i, &p, sizeof i);
|
||||
*iptr = i;
|
||||
f = x - i;
|
||||
if (f == 0.0L)
|
||||
{
|
||||
return (p.se & 0x8000) != 0 ? -0.0L : 0.0L;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* Integral value nearest to x in the current rounding direction (C23
|
||||
* 7.12.9.5), all three precisions, guaranteed never to raise the inexact
|
||||
* exception. No <fenv.h> exists in vlibc yet, so the only reachable
|
||||
* rounding mode is the hardware default, round-to-nearest-even; the
|
||||
* functions must still fold to correct results once <fenv.h> lands, and
|
||||
* the const attribute promises no exception and no errno path.
|
||||
*
|
||||
* GCC never folds the __builtin_nearbyint forms on this target (the
|
||||
* roundsd expansion needs SSE4.1, absent from the default -march, so
|
||||
* every __builtin_nearbyint call becomes an external nearbyint@PLT call
|
||||
* at every optimization level), so each function below rounds directly
|
||||
* on the IEEE 754 bit pattern with round-to-nearest-even: drop the low
|
||||
* (frac-bits) of the significand; a dropped part above half an ulp steps
|
||||
* the kept significand up, a dropped part of exactly half an ulp steps
|
||||
* up only when the kept LSB is odd (ties to even). The step is a plain
|
||||
* integer add that carries into the exponent field when the kept
|
||||
* fraction is all ones, which renormalizes exactly; nothing here touches
|
||||
* a rounding mode or raises an exception. nearbyint(±0) is ±0,
|
||||
* nearbyint(-0.5) is -0.0 (tie to even zero), and ±Inf/NaN pass through.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The double format: bit 63 is the sign, bits 62..52 the exponent biased
|
||||
* by 1023, bits 51..0 the fraction. A finite value with exponent e has
|
||||
* fractional bits only when e < 1075; for e in [1023, 1074] exactly the
|
||||
* low (1075 - e) bits of the fraction word are the fractional part. The
|
||||
* significand is 53 bits wide (implicit 1 plus the 52-bit fraction), so
|
||||
* in the e == 1023 binade the tie-even test looks at the implicit bit:
|
||||
* the only half-way value there is 1.5, which rounds up to 2.
|
||||
*/
|
||||
static double
|
||||
nearbyint_d(double x)
|
||||
{
|
||||
unsigned long long bits;
|
||||
unsigned long long kept;
|
||||
unsigned long long frac;
|
||||
unsigned long long half;
|
||||
int e;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
e = (int)((bits >> 52) & 0x7ff);
|
||||
|
||||
if (e >= 1075)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e < 1023)
|
||||
{
|
||||
if ((bits & ~(1ULL << 63)) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e == 1022)
|
||||
{
|
||||
/* [1/2, 1): 0.5 itself is a tie toward even zero; anything
|
||||
* above it rounds to ±1. */
|
||||
if ((bits & 0xFFFFFFFFFFFFFULL) == 0)
|
||||
{
|
||||
bits &= 1ULL << 63;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
bits = (bits & (1ULL << 63)) | 0x3FF0000000000000ULL;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
bits &= 1ULL << 63;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
shift = 1075 - e;
|
||||
half = 1ULL << (shift - 1);
|
||||
frac = bits & ((1ULL << shift) - 1ULL);
|
||||
kept = bits & ~((1ULL << shift) - 1ULL);
|
||||
if (frac > half || (frac == half && (shift == 52 || ((kept >> shift) & 1ULL) != 0)))
|
||||
{
|
||||
kept += 1ULL << shift;
|
||||
}
|
||||
__builtin_memcpy(&x, &kept, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* The float format: bit 31 is the sign, bits 30..23 the exponent biased
|
||||
* by 127, bits 22..0 the fraction; the significand is 24 bits wide. For
|
||||
* e in [127, 149] the low (150 - e) fraction bits are fractional, and in
|
||||
* the e == 127 binade the tie-even test looks at the implicit bit.
|
||||
*/
|
||||
static float
|
||||
nearbyint_f(float x)
|
||||
{
|
||||
unsigned int bits;
|
||||
unsigned int kept;
|
||||
unsigned int frac;
|
||||
unsigned int half;
|
||||
int e;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
e = (int)((bits >> 23) & 0xff);
|
||||
|
||||
if (e >= 150)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e < 127)
|
||||
{
|
||||
if ((bits & ~(1U << 31)) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e == 126)
|
||||
{
|
||||
if ((bits & 0x7FFFFFU) == 0)
|
||||
{
|
||||
bits &= 1U << 31;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
bits = (bits & (1U << 31)) | 0x3F800000U;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
bits &= 1U << 31;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
shift = 150 - e;
|
||||
half = 1U << (shift - 1);
|
||||
frac = bits & ((1U << shift) - 1U);
|
||||
kept = bits & ~((1U << shift) - 1U);
|
||||
if (frac > half || (frac == half && (shift == 23 || ((kept >> shift) & 1U) != 0)))
|
||||
{
|
||||
kept += 1U << shift;
|
||||
}
|
||||
__builtin_memcpy(&x, &kept, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* The x86 80-bit extended format: 64 significand bits m (the integer bit
|
||||
* is explicit, so the tie-even test is always m's kept LSB) and a
|
||||
* sign/exponent word se. A value m * 2^(e - 16446) has fractional bits
|
||||
* only when e < 16446; for e in [16383, 16445] the low (16446 - e) bits
|
||||
* of m are fractional. The step may overflow m when the kept significand
|
||||
* is all ones; the carry then moves the value to the next binade.
|
||||
*/
|
||||
static long double
|
||||
nearbyint_ld(long double x)
|
||||
{
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
unsigned long long frac;
|
||||
unsigned long long half;
|
||||
int e;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&p, &x, sizeof p);
|
||||
e = p.se & 0x7fff;
|
||||
|
||||
if (e >= 16446)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e < 16383)
|
||||
{
|
||||
if (p.m == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e == 16382)
|
||||
{
|
||||
if (p.m == 0x8000000000000000ULL)
|
||||
{
|
||||
/* Exactly 0.5: tie toward even zero. */
|
||||
p.m = 0;
|
||||
p.se &= 0x8000;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
p.m = 0x8000000000000000ULL;
|
||||
p.se = (p.se & 0x8000) | 16383;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
p.m = 0;
|
||||
p.se &= 0x8000;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
|
||||
shift = 16446 - e;
|
||||
half = 1ULL << (shift - 1);
|
||||
frac = p.m & ((1ULL << shift) - 1ULL);
|
||||
p.m &= ~((1ULL << shift) - 1ULL);
|
||||
if (frac > half || (frac == half && ((p.m >> shift) & 1ULL) != 0))
|
||||
{
|
||||
p.m += 1ULL << shift;
|
||||
if (p.m == 0)
|
||||
{
|
||||
/* Kept significand was all ones: carry to the next binade. */
|
||||
p.m = 0x8000000000000000ULL;
|
||||
p.se = (p.se & 0x8000) | (unsigned short)(e + 1);
|
||||
}
|
||||
}
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* As nearbyint, for a float argument.
|
||||
*/
|
||||
float
|
||||
nearbyintf(float x)
|
||||
{
|
||||
return nearbyint_f(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* As nearbyint, for a double argument.
|
||||
*/
|
||||
double
|
||||
nearbyint(double x)
|
||||
{
|
||||
return nearbyint_d(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* As nearbyint, for a long double argument.
|
||||
*/
|
||||
long double
|
||||
nearbyintl(long double x)
|
||||
{
|
||||
return nearbyint_ld(x);
|
||||
}
|
||||
+1347
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,284 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The IEEE remainder x - n*y with n = x/y rounded to the nearest integer,
|
||||
* ties to even (C23 7.12.10.2), all three precisions.
|
||||
*
|
||||
* The implementation mirrors the host glibc 2.44 remainder, which is a
|
||||
* two-stage reduction: the exact fmod(x, y + y) first clears every whole
|
||||
* multiple of 2y, leaving a residual below 2y (the doubling is finite by
|
||||
* construction of the branch below), and the residual is then rounded to
|
||||
* the nearest multiple of y -- at most one subtraction can still be wrong,
|
||||
* so comparing the doubled residual against y decides the final n exactly,
|
||||
* and each subtraction is exact (Sterbenz), making the result exact. The
|
||||
* two stages run in x's magnitude and the sign of x is applied at the
|
||||
* end, matching glibc.
|
||||
*
|
||||
* For |y| so large that y + y would overflow (|y| >= 2^1023 for double,
|
||||
* >= 2^127 for float, >= 2^16383 for the 80-bit format) the fmod stage is
|
||||
* skipped and the same rounding logic runs against y/2 -- with x below
|
||||
* 2y at those magnitudes, at most two y subtractions can be needed, which
|
||||
* the doubled-compare decision still performs exactly. Domain errors
|
||||
* return a NaN (the classic indefinite pattern) and set errno to EDOM in
|
||||
* the library build, matching the measured host behavior.
|
||||
*/
|
||||
|
||||
/* The double format: bit 63 the sign, bits 62..52 the exponent biased by
|
||||
* 1023, bits 51..0 the fraction. */
|
||||
static double
|
||||
remainder_d(double x, double y)
|
||||
{
|
||||
const unsigned long long dbl_inf = 0x7FF0000000000000ULL;
|
||||
unsigned long long xw;
|
||||
unsigned long long yw;
|
||||
unsigned long long hx;
|
||||
unsigned long long hy;
|
||||
unsigned long long sx;
|
||||
double v;
|
||||
double yh;
|
||||
|
||||
__builtin_memcpy(&xw, &x, sizeof xw);
|
||||
__builtin_memcpy(&yw, &y, sizeof yw);
|
||||
sx = xw >> 63;
|
||||
hx = xw & 0x7FFFFFFFFFFFFFFFULL;
|
||||
hy = yw & 0x7FFFFFFFFFFFFFFFULL;
|
||||
|
||||
v = fabs(x);
|
||||
y = fabs(y);
|
||||
if (hy < 0x7FE0000000000000ULL)
|
||||
{
|
||||
v = fmod(v, y + y); /* now v < 2y */
|
||||
if (v + v > y)
|
||||
{
|
||||
v -= y;
|
||||
if (v + v >= y)
|
||||
{
|
||||
v -= y;
|
||||
}
|
||||
else if (v == 0.0)
|
||||
{
|
||||
v = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* x not finite or y a NaN */
|
||||
if (hx >= dbl_inf || hy > dbl_inf)
|
||||
{
|
||||
#ifdef HAVE_CONFIG_H
|
||||
errno = EDOM;
|
||||
#endif
|
||||
xw = 0xFFF8000000000000ULL;
|
||||
__builtin_memcpy(&x, &xw, sizeof xw);
|
||||
return x;
|
||||
}
|
||||
yh = 0.5 * y;
|
||||
if (v > yh)
|
||||
{
|
||||
v -= y;
|
||||
if (v >= yh)
|
||||
{
|
||||
v -= y;
|
||||
}
|
||||
else if (v == 0.0)
|
||||
{
|
||||
v = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sx)
|
||||
{
|
||||
v = -v;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/* The float format: bit 31 the sign, bits 30..23 the exponent biased by
|
||||
* 127, bits 22..0 the fraction. */
|
||||
static float
|
||||
remainder_f(float x, float y)
|
||||
{
|
||||
const unsigned int flt_inf = 0x7F800000U;
|
||||
unsigned int xw;
|
||||
unsigned int yw;
|
||||
unsigned int hx;
|
||||
unsigned int hy;
|
||||
unsigned int sx;
|
||||
float v;
|
||||
float yh;
|
||||
|
||||
__builtin_memcpy(&xw, &x, sizeof xw);
|
||||
__builtin_memcpy(&yw, &y, sizeof yw);
|
||||
sx = xw >> 31;
|
||||
hx = xw & 0x7FFFFFFFU;
|
||||
hy = yw & 0x7FFFFFFFU;
|
||||
|
||||
v = fabsf(x);
|
||||
y = fabsf(y);
|
||||
if (hy < 0x7F000000U)
|
||||
{
|
||||
v = fmodf(v, y + y); /* now v < 2y */
|
||||
if (v + v > y)
|
||||
{
|
||||
v -= y;
|
||||
if (v + v >= y)
|
||||
{
|
||||
v -= y;
|
||||
}
|
||||
else if (v == 0.0f)
|
||||
{
|
||||
v = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* x not finite or y a NaN */
|
||||
if (hx >= flt_inf || hy > flt_inf)
|
||||
{
|
||||
#ifdef HAVE_CONFIG_H
|
||||
errno = EDOM;
|
||||
#endif
|
||||
xw = 0xFFC00000U;
|
||||
__builtin_memcpy(&x, &xw, sizeof xw);
|
||||
return x;
|
||||
}
|
||||
yh = 0.5f * y;
|
||||
if (v > yh)
|
||||
{
|
||||
v -= y;
|
||||
if (v >= yh)
|
||||
{
|
||||
v -= y;
|
||||
}
|
||||
else if (v == 0.0f)
|
||||
{
|
||||
v = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sx)
|
||||
{
|
||||
v = -v;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/*
|
||||
* The x86 80-bit extended format: 64-bit significand m in bytes 0..7 (the
|
||||
* integer bit is explicit) and a sign/exponent word se in bytes 8..9 with
|
||||
* the sign in bit 15 and the exponent biased by 16383 in bits 14..0.
|
||||
*/
|
||||
struct remainder_ld_word
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
};
|
||||
|
||||
static long double
|
||||
remainder_l(long double x, long double y)
|
||||
{
|
||||
struct remainder_ld_word p;
|
||||
struct remainder_ld_word q;
|
||||
int sx;
|
||||
int ex;
|
||||
int ey;
|
||||
long double v;
|
||||
long double yh;
|
||||
|
||||
__builtin_memcpy(&p, &x, sizeof p);
|
||||
__builtin_memcpy(&q, &y, sizeof q);
|
||||
sx = (int)(p.se >> 15);
|
||||
ex = p.se & 0x7fff;
|
||||
ey = q.se & 0x7fff;
|
||||
|
||||
v = fabsl(x);
|
||||
y = fabsl(y);
|
||||
if (ey < 0x7ffe)
|
||||
{
|
||||
v = fmodl(v, y + y); /* now v < 2y */
|
||||
if (v + v > y)
|
||||
{
|
||||
v -= y;
|
||||
if (v + v >= y)
|
||||
{
|
||||
v -= y;
|
||||
}
|
||||
else if (v == 0.0L)
|
||||
{
|
||||
v = 0.0L;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* x not finite or y a NaN */
|
||||
if (ex == 0x7fff || (ey == 0x7fff && (q.m & 0x7FFFFFFFFFFFFFFFULL) != 0))
|
||||
{
|
||||
#ifdef HAVE_CONFIG_H
|
||||
errno = EDOM;
|
||||
#endif
|
||||
p.m = 0xC000000000000000ULL;
|
||||
p.se = 0xFFFF;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
yh = 0.5L * y;
|
||||
if (v > yh)
|
||||
{
|
||||
v -= y;
|
||||
if (v >= yh)
|
||||
{
|
||||
v -= y;
|
||||
}
|
||||
else if (v == 0.0L)
|
||||
{
|
||||
v = 0.0L;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sx)
|
||||
{
|
||||
v = -v;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/*
|
||||
* remainder for a float argument.
|
||||
*/
|
||||
float
|
||||
remainderf(float x, float y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return remainder_f(x, y);
|
||||
}
|
||||
|
||||
/*
|
||||
* remainder for a double argument.
|
||||
*/
|
||||
double
|
||||
remainder(double x, double y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return remainder_d(x, y);
|
||||
}
|
||||
|
||||
/*
|
||||
* remainder for a long double argument.
|
||||
*/
|
||||
long double
|
||||
remainderl(long double x, long double y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return remainder_l(x, y);
|
||||
}
|
||||
@@ -0,0 +1,364 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* The IEEE remainder x - n*y with n = x/y rounded to nearest, ties to
|
||||
* even (C23 7.12.10.2), plus the signed low bits of the integer quotient
|
||||
* n stored through quo (C23 7.12.10.3), all three precisions.
|
||||
*
|
||||
* The structure mirrors the host glibc remainder/remquo implementation
|
||||
* (glibc 2.44, measured byte-for-byte), because remquo's quotient is not
|
||||
* the full n: glibc tracks only the three lowest quotient bits and still
|
||||
* returns a remainder in the exact IEEE rounding, so the two functions
|
||||
* share one reduce-then-round shape. x is first reduced by fmod(x, 8y)
|
||||
* whenever |y| is small enough for 8y to be finite, which makes |x| < 8y
|
||||
* and therefore bounds x/y below 8; the tracked quotient cquo collects
|
||||
* the one 4y subtraction, the one 2y subtraction and the final rounding's
|
||||
* up-to-two y subtractions, which together hold the three lowest bits of
|
||||
* n. The final compare against y/2 (using doubled compares when y is so
|
||||
* small that halving could lose precision) rounds x to the nearest
|
||||
* multiple of y, ties to even via the natural 0.5 boundary, and each
|
||||
* subtraction that fires is exact (Sterbenz), so the returned remainder
|
||||
* is exact.
|
||||
*
|
||||
* The x86-64 host collapses the remainder and quotient signs the same
|
||||
* way the code below does: the magnitude result is computed from |x| and
|
||||
* |y| and then negated when x < 0, and *quo carries the sign of x/y.
|
||||
* Domain errors -- |y| == 0, x not finite, y a NaN -- return a NaN (the
|
||||
* classic indefinite pattern) and leave *quo untouched, as glibc does.
|
||||
* Like glibc, remquo never touches errno.
|
||||
*/
|
||||
|
||||
/* The double format: bit 63 the sign, bits 62..52 the exponent biased by
|
||||
* 1023, bits 51..0 the fraction. */
|
||||
static double
|
||||
remquo_d(double x, double y, int *quo)
|
||||
{
|
||||
const unsigned long long dbl_inf = 0x7FF0000000000000ULL;
|
||||
unsigned long long xw;
|
||||
unsigned long long yw;
|
||||
unsigned long long hx;
|
||||
unsigned long long hy;
|
||||
unsigned long long sx;
|
||||
unsigned long long sy;
|
||||
double ax;
|
||||
double ay;
|
||||
double yh;
|
||||
int cquo;
|
||||
int qneg;
|
||||
|
||||
__builtin_memcpy(&xw, &x, sizeof xw);
|
||||
__builtin_memcpy(&yw, &y, sizeof yw);
|
||||
sx = xw >> 63;
|
||||
sy = yw >> 63;
|
||||
qneg = (int)(sx ^ sy);
|
||||
hx = xw & 0x7FFFFFFFFFFFFFFFULL;
|
||||
hy = yw & 0x7FFFFFFFFFFFFFFFULL;
|
||||
|
||||
if (hy == 0 || hx >= dbl_inf || hy > dbl_inf)
|
||||
{
|
||||
xw = 0xFFF8000000000000ULL;
|
||||
__builtin_memcpy(&x, &xw, sizeof xw);
|
||||
return x;
|
||||
}
|
||||
|
||||
if (hx == hy)
|
||||
{
|
||||
*quo = qneg ? -1 : 1;
|
||||
return x * 0.0;
|
||||
}
|
||||
|
||||
ax = fabs(x);
|
||||
ay = fabs(y);
|
||||
if (hy <= 0x7FBFFFFFFFFFFFFFULL)
|
||||
{
|
||||
ax = fmod(ax, 8.0 * ay); /* now ax < 8 * ay */
|
||||
}
|
||||
|
||||
cquo = 0;
|
||||
if (hy <= 0x7FCFFFFFFFFFFFFFULL && ax >= 4.0 * ay)
|
||||
{
|
||||
ax -= 4.0 * ay;
|
||||
cquo += 4;
|
||||
}
|
||||
if (hy <= 0x7FDFFFFFFFFFFFFFULL && ax >= 2.0 * ay)
|
||||
{
|
||||
ax -= 2.0 * ay;
|
||||
cquo += 2;
|
||||
}
|
||||
|
||||
if (hy < 0x0020000000000000ULL)
|
||||
{
|
||||
if (ax + ax > ay)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
if (ax + ax >= ay)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
yh = 0.5 * ay;
|
||||
if (ax > yh)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
if (ax >= yh)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*quo = qneg ? -cquo : cquo;
|
||||
if (ax == 0.0)
|
||||
{
|
||||
ax = 0.0;
|
||||
}
|
||||
if (sx)
|
||||
{
|
||||
ax = -ax;
|
||||
}
|
||||
return ax;
|
||||
}
|
||||
|
||||
/* The float format: bit 31 the sign, bits 30..23 the exponent biased by
|
||||
* 127, bits 22..0 the fraction. */
|
||||
static float
|
||||
remquo_f(float x, float y, int *quo)
|
||||
{
|
||||
const unsigned int flt_inf = 0x7F800000U;
|
||||
unsigned int xw;
|
||||
unsigned int yw;
|
||||
unsigned int hx;
|
||||
unsigned int hy;
|
||||
unsigned int sx;
|
||||
unsigned int sy;
|
||||
float ax;
|
||||
float ay;
|
||||
float yh;
|
||||
int cquo;
|
||||
int qneg;
|
||||
|
||||
__builtin_memcpy(&xw, &x, sizeof xw);
|
||||
__builtin_memcpy(&yw, &y, sizeof yw);
|
||||
sx = xw >> 31;
|
||||
sy = yw >> 31;
|
||||
qneg = (int)(sx ^ sy);
|
||||
hx = xw & 0x7FFFFFFFU;
|
||||
hy = yw & 0x7FFFFFFFU;
|
||||
|
||||
if (hy == 0 || hx >= flt_inf || hy > flt_inf)
|
||||
{
|
||||
xw = 0xFFC00000U;
|
||||
__builtin_memcpy(&x, &xw, sizeof xw);
|
||||
return x;
|
||||
}
|
||||
|
||||
if (hx == hy)
|
||||
{
|
||||
*quo = qneg ? -1 : 1;
|
||||
return x * 0.0f;
|
||||
}
|
||||
|
||||
ax = fabsf(x);
|
||||
ay = fabsf(y);
|
||||
if (hy <= 0x7DFFFFFFU)
|
||||
{
|
||||
ax = fmodf(ax, 8.0f * ay); /* now ax < 8 * ay */
|
||||
}
|
||||
|
||||
cquo = 0;
|
||||
if (hy <= 0x7E7FFFFFU && ax >= 4.0f * ay)
|
||||
{
|
||||
ax -= 4.0f * ay;
|
||||
cquo += 4;
|
||||
}
|
||||
if (hy <= 0x7EFFFFFFU && ax >= 2.0f * ay)
|
||||
{
|
||||
ax -= 2.0f * ay;
|
||||
cquo += 2;
|
||||
}
|
||||
|
||||
if (hy < 0x01000000U)
|
||||
{
|
||||
if (ax + ax > ay)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
if (ax + ax >= ay)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
yh = 0.5f * ay;
|
||||
if (ax > yh)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
if (ax >= yh)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*quo = qneg ? -cquo : cquo;
|
||||
if (ax == 0.0f)
|
||||
{
|
||||
ax = 0.0f;
|
||||
}
|
||||
if (sx)
|
||||
{
|
||||
ax = -ax;
|
||||
}
|
||||
return ax;
|
||||
}
|
||||
|
||||
/*
|
||||
* The x86 80-bit extended format: 64-bit significand m in bytes 0..7 (the
|
||||
* integer bit is explicit) and a sign/exponent word se in bytes 8..9 with
|
||||
* the sign in bit 15 and the exponent biased by 16383 in bits 14..0.
|
||||
*/
|
||||
struct remquo_ld_word
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
};
|
||||
|
||||
static long double
|
||||
remquo_l(long double x, long double y, int *quo)
|
||||
{
|
||||
struct remquo_ld_word p;
|
||||
struct remquo_ld_word q;
|
||||
int ex;
|
||||
int ey;
|
||||
int qneg;
|
||||
int sx;
|
||||
int sy;
|
||||
long double ax;
|
||||
long double ay;
|
||||
long double yh;
|
||||
int cquo;
|
||||
|
||||
__builtin_memcpy(&p, &x, sizeof p);
|
||||
__builtin_memcpy(&q, &y, sizeof q);
|
||||
sx = (int)(p.se >> 15);
|
||||
sy = (int)(q.se >> 15);
|
||||
qneg = sx ^ sy;
|
||||
ex = p.se & 0x7fff;
|
||||
ey = q.se & 0x7fff;
|
||||
|
||||
if ((q.m == 0 && ey == 0) || ex == 0x7fff ||
|
||||
(ey == 0x7fff && (q.m & 0x7FFFFFFFFFFFFFFFULL) != 0))
|
||||
{
|
||||
p.m = 0xC000000000000000ULL;
|
||||
p.se = 0xFFFF;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
|
||||
if (ex == ey && p.m == q.m)
|
||||
{
|
||||
*quo = qneg ? -1 : 1;
|
||||
return x * 0.0L;
|
||||
}
|
||||
|
||||
ax = fabsl(x);
|
||||
ay = fabsl(y);
|
||||
if (ey <= 0x7ffb)
|
||||
{
|
||||
ax = fmodl(ax, 8.0L * ay); /* now ax < 8 * ay */
|
||||
}
|
||||
|
||||
cquo = 0;
|
||||
if (ey <= 0x7ffc && ax >= 4.0L * ay)
|
||||
{
|
||||
ax -= 4.0L * ay;
|
||||
cquo += 4;
|
||||
}
|
||||
if (ey <= 0x7ffd && ax >= 2.0L * ay)
|
||||
{
|
||||
ax -= 2.0L * ay;
|
||||
cquo += 2;
|
||||
}
|
||||
|
||||
if (ey < 0x0002)
|
||||
{
|
||||
if (ax + ax > ay)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
if (ax + ax >= ay)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
yh = 0.5L * ay;
|
||||
if (ax > yh)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
if (ax >= yh)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*quo = qneg ? -cquo : cquo;
|
||||
if (ax == 0.0L)
|
||||
{
|
||||
ax = 0.0L;
|
||||
}
|
||||
if (sx)
|
||||
{
|
||||
ax = -ax;
|
||||
}
|
||||
return ax;
|
||||
}
|
||||
|
||||
/*
|
||||
* remquo for a float argument.
|
||||
*/
|
||||
float
|
||||
remquof(float x, float y, int *quo) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return remquo_f(x, y, quo);
|
||||
}
|
||||
|
||||
/*
|
||||
* remquo for a double argument.
|
||||
*/
|
||||
double
|
||||
remquo(double x, double y, int *quo) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return remquo_d(x, y, quo);
|
||||
}
|
||||
|
||||
/*
|
||||
* remquo for a long double argument.
|
||||
*/
|
||||
long double
|
||||
remquol(long double x, long double y, int *quo) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return remquo_l(x, y, quo);
|
||||
}
|
||||
+243
@@ -0,0 +1,243 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* Integral value nearest to x in the current rounding direction (C23
|
||||
* 7.12.9.4), all three precisions. rint differs from nearbyint only in
|
||||
* that it may raise the inexact exception; never raising it is also
|
||||
* conforming, and the const attribute promises no errno path.
|
||||
*
|
||||
* The __builtin_rint forms are tempting (they fold to in-line code on
|
||||
* this target), but GCC warns -Winfinite-recursion when the enclosing
|
||||
* function carries the same name as the library symbol the builtin would
|
||||
* fall back to (a function named rint whose body is __builtin_rint),
|
||||
* which the -Wall -Wextra -pedantic build gate forbids. Each function is
|
||||
* therefore implemented directly on the IEEE 754 bit pattern with
|
||||
* round-to-nearest-even, exactly like nearbyint (see nearbyint.c): no
|
||||
* <fenv.h> exists in vlibc yet, so the hardware default round-to-nearest-
|
||||
* even is the only reachable rounding mode. rint(±0) is ±0, rint(-0.5)
|
||||
* is -0.0 (tie to even zero), and ±Inf/NaN pass through unchanged.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The double format: bit 63 is the sign, bits 62..52 the exponent biased
|
||||
* by 1023, bits 51..0 the fraction. For e in [1023, 1074] the low
|
||||
* (1075 - e) bits of the fraction word are the fractional part; the
|
||||
* significand is 53 bits wide, so in the e == 1023 binade the tie-even
|
||||
* test looks at the implicit bit (the only half-way value there, 1.5,
|
||||
* rounds up to 2).
|
||||
*/
|
||||
static double
|
||||
rint_d(double x)
|
||||
{
|
||||
unsigned long long bits;
|
||||
unsigned long long kept;
|
||||
unsigned long long frac;
|
||||
unsigned long long half;
|
||||
int e;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
e = (int)((bits >> 52) & 0x7ff);
|
||||
|
||||
if (e >= 1075)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e < 1023)
|
||||
{
|
||||
if ((bits & ~(1ULL << 63)) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e == 1022)
|
||||
{
|
||||
/* [1/2, 1): 0.5 itself is a tie toward even zero; anything
|
||||
* above it rounds to ±1. */
|
||||
if ((bits & 0xFFFFFFFFFFFFFULL) == 0)
|
||||
{
|
||||
bits &= 1ULL << 63;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
bits = (bits & (1ULL << 63)) | 0x3FF0000000000000ULL;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
bits &= 1ULL << 63;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
shift = 1075 - e;
|
||||
half = 1ULL << (shift - 1);
|
||||
frac = bits & ((1ULL << shift) - 1ULL);
|
||||
kept = bits & ~((1ULL << shift) - 1ULL);
|
||||
if (frac > half || (frac == half && (shift == 52 || ((kept >> shift) & 1ULL) != 0)))
|
||||
{
|
||||
kept += 1ULL << shift;
|
||||
}
|
||||
__builtin_memcpy(&x, &kept, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* The float format: bit 31 is the sign, bits 30..23 the exponent biased
|
||||
* by 127, bits 22..0 the fraction; the significand is 24 bits wide. For
|
||||
* e in [127, 149] the low (150 - e) fraction bits are fractional, and in
|
||||
* the e == 127 binade the tie-even test looks at the implicit bit.
|
||||
*/
|
||||
static float
|
||||
rint_f(float x)
|
||||
{
|
||||
unsigned int bits;
|
||||
unsigned int kept;
|
||||
unsigned int frac;
|
||||
unsigned int half;
|
||||
int e;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
e = (int)((bits >> 23) & 0xff);
|
||||
|
||||
if (e >= 150)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e < 127)
|
||||
{
|
||||
if ((bits & ~(1U << 31)) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e == 126)
|
||||
{
|
||||
if ((bits & 0x7FFFFFU) == 0)
|
||||
{
|
||||
bits &= 1U << 31;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
bits = (bits & (1U << 31)) | 0x3F800000U;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
bits &= 1U << 31;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
shift = 150 - e;
|
||||
half = 1U << (shift - 1);
|
||||
frac = bits & ((1U << shift) - 1U);
|
||||
kept = bits & ~((1U << shift) - 1U);
|
||||
if (frac > half || (frac == half && (shift == 23 || ((kept >> shift) & 1U) != 0)))
|
||||
{
|
||||
kept += 1U << shift;
|
||||
}
|
||||
__builtin_memcpy(&x, &kept, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* The x86 80-bit extended format: 64 significand bits m (the integer bit
|
||||
* is explicit, so the tie-even test is always m's kept LSB) and a
|
||||
* sign/exponent word se. For e in [16383, 16445] the low (16446 - e)
|
||||
* bits of m are fractional. The step may overflow m when the kept
|
||||
* significand is all ones; the carry then moves the value to the next
|
||||
* binade.
|
||||
*/
|
||||
static long double
|
||||
rint_ld(long double x)
|
||||
{
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
unsigned long long frac;
|
||||
unsigned long long half;
|
||||
int e;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&p, &x, sizeof p);
|
||||
e = p.se & 0x7fff;
|
||||
|
||||
if (e >= 16446)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e < 16383)
|
||||
{
|
||||
if (p.m == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e == 16382)
|
||||
{
|
||||
if (p.m == 0x8000000000000000ULL)
|
||||
{
|
||||
/* Exactly 0.5: tie toward even zero. */
|
||||
p.m = 0;
|
||||
p.se &= 0x8000;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
p.m = 0x8000000000000000ULL;
|
||||
p.se = (p.se & 0x8000) | 16383;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
p.m = 0;
|
||||
p.se &= 0x8000;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
|
||||
shift = 16446 - e;
|
||||
half = 1ULL << (shift - 1);
|
||||
frac = p.m & ((1ULL << shift) - 1ULL);
|
||||
p.m &= ~((1ULL << shift) - 1ULL);
|
||||
if (frac > half || (frac == half && ((p.m >> shift) & 1ULL) != 0))
|
||||
{
|
||||
p.m += 1ULL << shift;
|
||||
if (p.m == 0)
|
||||
{
|
||||
/* Kept significand was all ones: carry to the next binade. */
|
||||
p.m = 0x8000000000000000ULL;
|
||||
p.se = (p.se & 0x8000) | (unsigned short)(e + 1);
|
||||
}
|
||||
}
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* As rint, for a float argument.
|
||||
*/
|
||||
float
|
||||
rintf(float x)
|
||||
{
|
||||
return rint_f(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* As rint, for a double argument.
|
||||
*/
|
||||
double
|
||||
rint(double x)
|
||||
{
|
||||
return rint_d(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* As rint, for a long double argument.
|
||||
*/
|
||||
long double
|
||||
rintl(long double x)
|
||||
{
|
||||
return rint_ld(x);
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* Integral value nearest to x, halfway cases rounded away from zero
|
||||
* (C23 7.12.9.6), all three precisions.
|
||||
*
|
||||
* GCC never folds the __builtin_round forms on this target (no SSE4.1 in
|
||||
* the default -march, so the roundsd expansion is unavailable and every
|
||||
* __builtin_round call becomes an external round@PLT call at every
|
||||
* optimization level), so each function below rounds directly on the
|
||||
* IEEE 754 bit pattern. A negative argument with a nonzero dropped
|
||||
* fraction must round away from zero, so the rounding is applied to the
|
||||
* magnitude (sign bit untouched): drop the fractional bits, and when the
|
||||
* dropped part is >= half an ulp step the kept significand up by one.
|
||||
* The step is a plain integer add that carries into the exponent field
|
||||
* when the kept fraction is all ones, which renormalizes exactly.
|
||||
* round(±0) is ±0, round(-0.5) is -1.0 (half away from zero), and ±Inf
|
||||
* and NaN pass through unchanged. Pure: no rounding mode consulted and no
|
||||
* exception raised.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The double format: bit 63 is the sign, bits 62..52 the exponent biased
|
||||
* by 1023, bits 51..0 the fraction. A finite value with exponent e has
|
||||
* fractional bits only when e - 1023 < 52, i.e. e < 1075; for e in
|
||||
* [1023, 1074] exactly the low (1075 - e) bits of the fraction word are
|
||||
* the fractional part.
|
||||
*/
|
||||
static double
|
||||
round_d(double x)
|
||||
{
|
||||
unsigned long long bits;
|
||||
unsigned long long kept;
|
||||
unsigned long long frac;
|
||||
unsigned long long half;
|
||||
int e;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
e = (int)((bits >> 52) & 0x7ff);
|
||||
|
||||
/* |x| >= 2^52 is already integral; Inf (e == 0x7ff) and NaN pass
|
||||
* through unchanged as well. */
|
||||
if (e >= 1075)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
/* |x| < 1: round(±0) is ±0 and any other value with |x| >= 1/2 goes
|
||||
* to ±1 while smaller magnitudes collapse to ±0, keeping the sign. */
|
||||
if (e < 1023)
|
||||
{
|
||||
if ((bits & ~(1ULL << 63)) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e == 1022)
|
||||
{
|
||||
bits = (bits & (1ULL << 63)) | 0x3FF0000000000000ULL;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
bits &= 1ULL << 63;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
shift = 1075 - e;
|
||||
half = 1ULL << (shift - 1);
|
||||
frac = bits & ((1ULL << shift) - 1ULL);
|
||||
kept = bits & ~((1ULL << shift) - 1ULL);
|
||||
if (frac >= half)
|
||||
{
|
||||
kept += 1ULL << shift;
|
||||
}
|
||||
__builtin_memcpy(&x, &kept, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* The float format: bit 31 is the sign, bits 30..23 the exponent biased
|
||||
* by 127, bits 22..0 the fraction. Fraction bits exist exactly when the
|
||||
* exponent e is in [127, 149]; the low (150 - e) bits are fractional.
|
||||
*/
|
||||
static float
|
||||
round_f(float x)
|
||||
{
|
||||
unsigned int bits;
|
||||
unsigned int kept;
|
||||
unsigned int frac;
|
||||
unsigned int half;
|
||||
int e;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
e = (int)((bits >> 23) & 0xff);
|
||||
|
||||
if (e >= 150)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e < 127)
|
||||
{
|
||||
if ((bits & ~(1U << 31)) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e == 126)
|
||||
{
|
||||
bits = (bits & (1U << 31)) | 0x3F800000U;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
bits &= 1U << 31;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
shift = 150 - e;
|
||||
half = 1U << (shift - 1);
|
||||
frac = bits & ((1U << shift) - 1U);
|
||||
kept = bits & ~((1U << shift) - 1U);
|
||||
if (frac >= half)
|
||||
{
|
||||
kept += 1U << shift;
|
||||
}
|
||||
__builtin_memcpy(&x, &kept, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* The x86 80-bit extended format: 64 significand bits m (the integer bit
|
||||
* is explicit) in bytes 0..7 and a sign/exponent word se in bytes 8..9,
|
||||
* with the sign in bit 15 and the exponent (biased by 16383) in bits
|
||||
* 14..0. A value m * 2^(e - 16446) has fractional bits only when
|
||||
* e - 16383 < 63, i.e. e < 16446. The half-away step may overflow the
|
||||
* 64-bit m when the kept significand is all ones; the carry then moves
|
||||
* the value to the next binade (e + 1, integer bit alone).
|
||||
*/
|
||||
static long double
|
||||
round_ld(long double x)
|
||||
{
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
unsigned long long frac;
|
||||
unsigned long long half;
|
||||
int e;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&p, &x, sizeof p);
|
||||
e = p.se & 0x7fff;
|
||||
|
||||
if (e >= 16446)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e < 16383)
|
||||
{
|
||||
if (p.m == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e == 16382)
|
||||
{
|
||||
p.m = 0x8000000000000000ULL;
|
||||
p.se = (p.se & 0x8000) | 16383;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
p.m = 0;
|
||||
p.se &= 0x8000;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
|
||||
shift = 16446 - e;
|
||||
half = 1ULL << (shift - 1);
|
||||
frac = p.m & ((1ULL << shift) - 1ULL);
|
||||
p.m &= ~((1ULL << shift) - 1ULL);
|
||||
if (frac >= half)
|
||||
{
|
||||
p.m += 1ULL << shift;
|
||||
if (p.m == 0)
|
||||
{
|
||||
/* Kept significand was all ones: carry to the next binade. */
|
||||
p.m = 0x8000000000000000ULL;
|
||||
p.se = (p.se & 0x8000) | (unsigned short)(e + 1);
|
||||
}
|
||||
}
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* As round, for a float argument.
|
||||
*/
|
||||
float
|
||||
roundf(float x)
|
||||
{
|
||||
return round_f(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* As round, for a double argument.
|
||||
*/
|
||||
double
|
||||
round(double x)
|
||||
{
|
||||
return round_d(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* As round, for a long double argument.
|
||||
*/
|
||||
long double
|
||||
roundl(long double x)
|
||||
{
|
||||
return round_ld(x);
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "math_impl.h"
|
||||
|
||||
/*
|
||||
* x * FLT_RADIX^n with FLT_RADIX 2 (C23 7.12.6.6), all three precisions
|
||||
* and both exponent-argument types. scalbn takes an int n, scalbln a long
|
||||
* n; otherwise the two families are semantically identical to ldexp (they
|
||||
* share the exact same scale cores and overflow-to-+-Inf-with-ERANGE
|
||||
* behavior, and subnormal inputs are handled exactly like subnormal
|
||||
* outputs). The scalbln functions clamp the long exponent to +-20000
|
||||
* first: any magnitude beyond that saturates every result to +-Inf or +-0
|
||||
* in all three formats, and clamping keeps the arithmetic inside int range
|
||||
* with no shift by a huge count.
|
||||
*/
|
||||
|
||||
/*
|
||||
* As ldexp (see ldexp.c) for a double x and an int n.
|
||||
*/
|
||||
double
|
||||
scalbn(double x, int n)
|
||||
{
|
||||
int overflowed = 0;
|
||||
double r = vl_scale2_d(x, n, &overflowed);
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
if (overflowed)
|
||||
{
|
||||
errno = ERANGE;
|
||||
}
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
float
|
||||
scalbnf(float x, int n)
|
||||
{
|
||||
int overflowed = 0;
|
||||
float r = vl_scale2_f(x, n, &overflowed);
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
if (overflowed)
|
||||
{
|
||||
errno = ERANGE;
|
||||
}
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
long double
|
||||
scalbnl(long double x, int n)
|
||||
{
|
||||
int overflowed = 0;
|
||||
long double r = vl_scale2_ld(x, n, &overflowed);
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
if (overflowed)
|
||||
{
|
||||
errno = ERANGE;
|
||||
}
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* The scalbln family: as scalbn with the exponent given as a long.
|
||||
* n is first clamped into [-20000, 20000]; anything beyond saturates every
|
||||
* precision's range (the largest meaningful long-double exponent is below
|
||||
* 16446 in magnitude), so no precision is lost by the clamp.
|
||||
*/
|
||||
double
|
||||
scalbln(double x, long n)
|
||||
{
|
||||
if (n > 20000)
|
||||
{
|
||||
n = 20000;
|
||||
}
|
||||
else if (n < -20000)
|
||||
{
|
||||
n = -20000;
|
||||
}
|
||||
return scalbn(x, (int)n);
|
||||
}
|
||||
|
||||
float
|
||||
scalblnf(float x, long n)
|
||||
{
|
||||
if (n > 20000)
|
||||
{
|
||||
n = 20000;
|
||||
}
|
||||
else if (n < -20000)
|
||||
{
|
||||
n = -20000;
|
||||
}
|
||||
return scalbnf(x, (int)n);
|
||||
}
|
||||
|
||||
long double
|
||||
scalblnl(long double x, long n)
|
||||
{
|
||||
if (n > 20000)
|
||||
{
|
||||
n = 20000;
|
||||
}
|
||||
else if (n < -20000)
|
||||
{
|
||||
n = -20000;
|
||||
}
|
||||
return scalbnl(x, (int)n);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
#include <math.h>
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#define SET_ERANGE() (errno = ERANGE)
|
||||
#define SET_EDOM() (errno = EDOM)
|
||||
#else
|
||||
#define SET_ERANGE() \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
#define SET_EDOM() \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
/* sqrt is the one function IEEE 754 requires to be correctly rounded. The
|
||||
* sqrtsd/sqrtss/fsqrt hardware instructions are; inline asm keeps the call
|
||||
* self-contained (GCC's __builtin_sqrt would emit a libm call for NaN
|
||||
* inputs and a false recursion warning inside a function named sqrt). */
|
||||
double
|
||||
sqrt(double x)
|
||||
{
|
||||
double y;
|
||||
|
||||
if (x < 0.0)
|
||||
SET_EDOM();
|
||||
__asm__("sqrtsd %1, %0" : "=x"(y) : "x"(x));
|
||||
return y;
|
||||
}
|
||||
|
||||
float
|
||||
sqrtf(float x)
|
||||
{
|
||||
float y;
|
||||
|
||||
if (x < 0.0f)
|
||||
SET_EDOM();
|
||||
__asm__("sqrtss %1, %0" : "=x"(y) : "x"(x));
|
||||
return y;
|
||||
}
|
||||
|
||||
long double
|
||||
sqrtl(long double x)
|
||||
{
|
||||
if (x < 0.0L)
|
||||
SET_EDOM();
|
||||
__asm__("fsqrt" : "+t"(x));
|
||||
return x;
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* Integral value nearest to x in the direction of zero (C23 7.12.9.3),
|
||||
* all three precisions.
|
||||
*
|
||||
* GCC does not fold the __builtin_trunc forms on this target (see
|
||||
* floor.c), so each function below simply clears the fractional mantissa
|
||||
* bits on the IEEE 754 bit pattern. Rounding toward zero never rounds, so
|
||||
* no rounding mode or exception is involved; trunc(-0.3) is -0.0 (the
|
||||
* sign bit survives), and ±Inf/NaN pass through unchanged.
|
||||
*/
|
||||
|
||||
static double
|
||||
trunc_d(double x)
|
||||
{
|
||||
unsigned long long bits;
|
||||
int e;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
e = (int)((bits >> 52) & 0x7ff);
|
||||
|
||||
/* |x| >= 2^52 is already integral; Inf (e == 0x7ff) and NaN pass
|
||||
* through unchanged as well. */
|
||||
if (e >= 1075)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e < 1023)
|
||||
{
|
||||
/* |x| < 1: truncation is ±0, keeping the sign bit. */
|
||||
bits &= 1ULL << 63;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
bits &= ~((1ULL << (1075 - e)) - 1ULL);
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
static float
|
||||
trunc_f(float x)
|
||||
{
|
||||
unsigned int bits;
|
||||
int e;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
e = (int)((bits >> 23) & 0xff);
|
||||
|
||||
if (e >= 150)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e < 127)
|
||||
{
|
||||
bits &= 1U << 31;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
bits &= ~((1U << (150 - e)) - 1U);
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
static long double
|
||||
trunc_ld(long double x)
|
||||
{
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
int e;
|
||||
|
||||
__builtin_memcpy(&p, &x, sizeof p);
|
||||
e = p.se & 0x7fff;
|
||||
|
||||
if (e >= 16446)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e < 16383)
|
||||
{
|
||||
/* |x| < 1: truncation is ±0, keeping the sign bit. */
|
||||
p.m = 0;
|
||||
p.se &= 0x8000;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
|
||||
p.m &= ~((1ULL << (63 - (e - 16383))) - 1ULL);
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* As trunc, for a float argument.
|
||||
*/
|
||||
float
|
||||
truncf(float x)
|
||||
{
|
||||
return trunc_f(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* As trunc, for a double argument.
|
||||
*/
|
||||
double
|
||||
trunc(double x)
|
||||
{
|
||||
return trunc_d(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* As trunc, for a long double argument.
|
||||
*/
|
||||
long double
|
||||
truncl(long double x)
|
||||
{
|
||||
return trunc_ld(x);
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* pathconf() / fpathconf(): configurable limits and options of the
|
||||
* filesystem holding a path or an open descriptor.
|
||||
*
|
||||
* Both functions first validate the object with statfs/fstatfs (so a
|
||||
* missing path or a closed descriptor yields -1 with errno from the
|
||||
* syscall layer) and then answer from the resulting filesystem data plus
|
||||
* the fixed value table below. Only _PC_NAME_MAX and the three
|
||||
* block-size-derived keys are truly per-filesystem; the rest are vlibc's
|
||||
* fixed Linux values (see the pathconf() documentation in
|
||||
* include/unistd.h). Unknown keys and keys whose option this filesystem
|
||||
* does not provide return -1 WITHOUT setting errno.
|
||||
*
|
||||
* The name argument (one of the _PC_* keys) is forwarded to the value
|
||||
* table unchanged; POSIX does not define per-file-type answers for the
|
||||
* keys vlibc supports, so the object type is not inspected beyond the
|
||||
* statfs validation.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Linux struct statfs (kernel ABI, x86_64; linux/statfs.h). Layout and
|
||||
* sizes are LP64 facts: f_type..f_ffree are longs, f_fsid is an int pair,
|
||||
* then f_namelen/f_frsize/f_flags/f_spare[4] complete the 120 bytes the
|
||||
* kernel copies out. Only f_bsize and f_namelen are read here.
|
||||
*/
|
||||
struct vlibc_statfs
|
||||
{
|
||||
long f_type;
|
||||
long f_bsize;
|
||||
long f_blocks;
|
||||
long f_bfree;
|
||||
long f_bavail;
|
||||
long f_files;
|
||||
long f_ffree;
|
||||
int f_fsid[2];
|
||||
long f_namelen;
|
||||
long f_frsize;
|
||||
long f_flags;
|
||||
long f_spare[4];
|
||||
};
|
||||
|
||||
/* Answer key name from the filesystem data in fs; -1 for unknown keys and
|
||||
* for keys whose option this filesystem does not provide, without setting
|
||||
* errno in either case. */
|
||||
static long
|
||||
pathconf_value(const struct vlibc_statfs *fs, int name)
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case _PC_LINK_MAX:
|
||||
return 32000;
|
||||
case _PC_MAX_CANON:
|
||||
case _PC_MAX_INPUT:
|
||||
return 255;
|
||||
case _PC_NAME_MAX:
|
||||
return fs->f_namelen;
|
||||
case _PC_PATH_MAX:
|
||||
case _PC_PIPE_BUF:
|
||||
return 4096;
|
||||
case _PC_CHOWN_RESTRICTED:
|
||||
case _PC_NO_TRUNC:
|
||||
case _PC_2_SYMLINKS:
|
||||
return 1;
|
||||
case _PC_VDISABLE:
|
||||
return 0;
|
||||
case _PC_FILESIZEBITS:
|
||||
return 64;
|
||||
case _PC_REC_MIN_XFER_SIZE:
|
||||
case _PC_REC_XFER_ALIGN:
|
||||
case _PC_ALLOC_SIZE_MIN:
|
||||
return fs->f_bsize;
|
||||
default:
|
||||
/* _PC_ASYNC_IO, _PC_SYNC_IO, _PC_PRIO_IO, _PC_SOCK_MAXBUF,
|
||||
* _PC_REC_INCR_XFER_SIZE, _PC_REC_MAX_XFER_SIZE, _PC_SYMLINK_MAX
|
||||
* and unknown keys. */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
long
|
||||
pathconf(const char *path, int name)
|
||||
{
|
||||
struct vlibc_statfs fs = {0};
|
||||
|
||||
if (syscall_ret(__syscall2(SYS_statfs, (long)path, (long)&fs)) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return pathconf_value(&fs, name);
|
||||
}
|
||||
|
||||
long
|
||||
fpathconf(int fildes, int name) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
struct vlibc_statfs fs = {0};
|
||||
|
||||
if (syscall_ret(__syscall2(SYS_fstatfs, fildes, (long)&fs)) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return pathconf_value(&fs, name);
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* sysconf(): system limits and options.
|
||||
*
|
||||
* Every supported key returns its value directly (see the sysconf()
|
||||
* documentation in include/unistd.h for the full table). Unknown keys and
|
||||
* genuinely unsupported options return -1 WITHOUT setting errno — a call
|
||||
* must be able to probe arbitrary _SC_* values without clobbering a
|
||||
* caller's errno (the QA contract of todo 32).
|
||||
*
|
||||
* The fixed x86_64 clock-tick rate, defined ONCE here: USER_HZ is 100 on
|
||||
* every x86_64 Linux kernel. todo 35's times() reports in exactly these
|
||||
* ticks and consumes this constant rather than re-defining it.
|
||||
*/
|
||||
#define VLIBC_CLK_TCK 100
|
||||
|
||||
/* Kernel-UAPI values local to this file (include/fcntl.h owns the public
|
||||
* AT_FDCWD / O_RDONLY spellings; asm-generic/auxvec.h owns AT_PAGESZ). */
|
||||
#define VLIBC_SYSCONF_AT_FDCWD (-100)
|
||||
#define VLIBC_SYSCONF_AT_NULL 0
|
||||
#define VLIBC_SYSCONF_AT_PAGESZ 6
|
||||
#define VLIBC_SYSCONF_O_RDONLY 0
|
||||
#define VLIBC_SYSCONF_RLIMIT_NOFILE 7
|
||||
|
||||
/*
|
||||
* Linux struct sysinfo (kernel ABI, x86_64; linux/sysinfo.h). The totalram/
|
||||
* freeram/... fields are memory sizes counted in mem_unit-byte units
|
||||
* (current kernels report mem_unit == 1 with sizes in bytes). procs is the
|
||||
* number of live PROCESSES, not a CPU count — CPU counts come from
|
||||
* sched_getaffinity, not from here.
|
||||
*/
|
||||
struct vlibc_sysinfo
|
||||
{
|
||||
long uptime;
|
||||
unsigned long loads[3];
|
||||
unsigned long totalram;
|
||||
unsigned long freeram;
|
||||
unsigned long sharedram;
|
||||
unsigned long bufferram;
|
||||
unsigned long totalswap;
|
||||
unsigned long freeswap;
|
||||
unsigned short procs;
|
||||
unsigned short pad;
|
||||
unsigned long totalhigh;
|
||||
unsigned long freehigh;
|
||||
unsigned int mem_unit;
|
||||
};
|
||||
|
||||
/* RLIMIT_NOFILE as returned by prlimit64: current and maximum limits. */
|
||||
struct vlibc_rlimit
|
||||
{
|
||||
unsigned long cur;
|
||||
unsigned long max;
|
||||
};
|
||||
|
||||
/*
|
||||
* The memory page size, read from the AT_PAGESZ entry of the auxiliary
|
||||
* vector. The vlibc startup code (todo 3) does not save the auxv, so it is
|
||||
* re-read from /proc/self/auxv on first use and cached; a static cache is
|
||||
* safe because no threads exist yet (todo 45 must revisit). On any failure
|
||||
* — auxv unreadable or AT_PAGESZ absent — the x86_64 true page size 4096
|
||||
* is used as the documented fallback (this is what AT_PAGESZ reports on
|
||||
* every x86_64 kernel anyway; the value is never hardcoded as the primary
|
||||
* source).
|
||||
*/
|
||||
static long
|
||||
query_pagesize(void)
|
||||
{
|
||||
static long cached;
|
||||
static int done;
|
||||
|
||||
if (!done)
|
||||
{
|
||||
unsigned char buf[1024] = {0};
|
||||
long fd;
|
||||
long n;
|
||||
|
||||
done = 1;
|
||||
cached = 4096;
|
||||
fd = __syscall3(SYS_openat, VLIBC_SYSCONF_AT_FDCWD, (long)"/proc/self/auxv",
|
||||
VLIBC_SYSCONF_O_RDONLY);
|
||||
if (fd < 0)
|
||||
{
|
||||
return cached;
|
||||
}
|
||||
n = __syscall3(SYS_read, fd, (long)buf, (long)sizeof buf);
|
||||
__syscall1(SYS_close, fd);
|
||||
for (long off = 0; n >= 16 && off <= n - 16; off += 16)
|
||||
{
|
||||
unsigned long type;
|
||||
unsigned long value;
|
||||
|
||||
/* The auxv is a sequence of (type, value) unsigned long pairs. */
|
||||
// NOLINTBEGIN(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
|
||||
__builtin_memcpy(&type, buf + off, sizeof type);
|
||||
__builtin_memcpy(&value, buf + off + sizeof type, sizeof value);
|
||||
// NOLINTEND(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
|
||||
if (type == VLIBC_SYSCONF_AT_NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (type == VLIBC_SYSCONF_AT_PAGESZ)
|
||||
{
|
||||
cached = (long)value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cached;
|
||||
}
|
||||
|
||||
/*
|
||||
* The open-descriptor limit: the RLIMIT_NOFILE soft limit via prlimit64.
|
||||
* prlimit64 cannot usefully fail on the calling process, but the raw
|
||||
* result is checked anyway so no errno write happens on the fallback path
|
||||
* (the probe must stay errno-clean: it may run under a seccomp filter).
|
||||
*/
|
||||
static long
|
||||
query_open_max(void)
|
||||
{
|
||||
struct vlibc_rlimit rlim = {0};
|
||||
long r;
|
||||
|
||||
r = __syscall4(SYS_prlimit64, 0, VLIBC_SYSCONF_RLIMIT_NOFILE, 0, (long)&rlim);
|
||||
if (r < 0 || rlim.cur == 0 || rlim.cur == ~0UL)
|
||||
{
|
||||
return 1024;
|
||||
}
|
||||
return (long)rlim.cur;
|
||||
}
|
||||
|
||||
/*
|
||||
* The online processor count, from the calling process's CPU affinity
|
||||
* mask via sched_getaffinity(pid 0): the kernel returns the task's allowed
|
||||
* CPUs, which is the count of CPUs this process can actually run on. The
|
||||
* configured count is reported as the same value (a cpuset could hide
|
||||
* CPUs, but on a system without hotplug the configured and online counts
|
||||
* agree; glibc/musl make the same simplification for the affinity path).
|
||||
*/
|
||||
static long
|
||||
query_nprocs(void)
|
||||
{
|
||||
unsigned long mask[16] = {0}; /* 1024 bits, enough for any CPU count */
|
||||
long count = 0;
|
||||
long r;
|
||||
|
||||
r = __syscall3(SYS_sched_getaffinity, 0, (long)sizeof mask, (long)mask);
|
||||
if (r < 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (r > (long)sizeof mask)
|
||||
{
|
||||
r = (long)sizeof mask;
|
||||
}
|
||||
for (long i = 0; i < r / (long)sizeof(unsigned long); i++)
|
||||
{
|
||||
count += (long)__builtin_popcountl(mask[i]);
|
||||
}
|
||||
return count > 0 ? count : 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Physical (avail == 0) or available (avail != 0) memory in pages, from
|
||||
* SYS_sysinfo. Sizes arrive in mem_unit-byte units, so the byte count is
|
||||
* totalram * mem_unit (overflow is not a concern: the product stays far
|
||||
* below 2^64 for any real machine). A sysinfo failure is reported as -1,
|
||||
* which the caller treats like an unsupported key.
|
||||
*/
|
||||
static long
|
||||
query_mem_pages(int avail)
|
||||
{
|
||||
struct vlibc_sysinfo si = {0};
|
||||
unsigned long unit;
|
||||
unsigned long bytes;
|
||||
long r;
|
||||
|
||||
r = __syscall1(SYS_sysinfo, (long)&si);
|
||||
if (r < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
unit = si.mem_unit != 0 ? si.mem_unit : 1;
|
||||
bytes = (avail != 0 ? si.freeram : si.totalram) * unit;
|
||||
return (long)(bytes / (unsigned long)query_pagesize());
|
||||
}
|
||||
|
||||
long
|
||||
sysconf(int name)
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case _SC_ARG_MAX:
|
||||
return 2097152;
|
||||
case _SC_CHILD_MAX:
|
||||
return 65535;
|
||||
case _SC_CLK_TCK:
|
||||
return VLIBC_CLK_TCK;
|
||||
case _SC_NGROUPS_MAX:
|
||||
return 65536;
|
||||
case _SC_OPEN_MAX:
|
||||
return query_open_max();
|
||||
case _SC_PAGESIZE:
|
||||
case _SC_PAGE_SIZE:
|
||||
return query_pagesize();
|
||||
case _SC_NPROCESSORS_CONF:
|
||||
case _SC_NPROCESSORS_ONLN:
|
||||
return query_nprocs();
|
||||
case _SC_PHYS_PAGES:
|
||||
return query_mem_pages(0);
|
||||
case _SC_AVPHYS_PAGES:
|
||||
return query_mem_pages(1);
|
||||
case _SC_SYMLOOP_MAX:
|
||||
return 40;
|
||||
case _SC_STREAM_MAX:
|
||||
return 16;
|
||||
case _SC_TZNAME_MAX:
|
||||
return 6;
|
||||
case _SC_HOST_NAME_MAX:
|
||||
return 64;
|
||||
case _SC_LOGIN_NAME_MAX:
|
||||
return 256;
|
||||
case _SC_GETPW_R_SIZE_MAX:
|
||||
case _SC_IOV_MAX:
|
||||
return 1024;
|
||||
case _SC_LINE_MAX:
|
||||
return 2048;
|
||||
case _SC_RE_DUP_MAX:
|
||||
return 32767;
|
||||
case _SC_COLL_WEIGHTS_MAX:
|
||||
return 255;
|
||||
case _SC_EXPR_NEST_MAX:
|
||||
return 32;
|
||||
case _SC_XOPEN_VERSION:
|
||||
return 700;
|
||||
case _SC_JOB_CONTROL:
|
||||
case _SC_SAVED_IDS:
|
||||
case _SC_REGEXP:
|
||||
case _SC_SHELL:
|
||||
case _SC_XOPEN_UNIX:
|
||||
case _SC_XOPEN_CRYPT:
|
||||
case _SC_XOPEN_ENH_I18N:
|
||||
case _SC_XOPEN_SHM:
|
||||
return 1;
|
||||
case _SC_VERSION:
|
||||
case _SC_2_VERSION:
|
||||
case _SC_THREADS:
|
||||
case _SC_SEMAPHORES:
|
||||
case _SC_SHARED_MEMORY_OBJECTS:
|
||||
case _SC_MESSAGE_PASSING:
|
||||
case _SC_MONOTONIC_CLOCK:
|
||||
case _SC_TIMERS:
|
||||
case _SC_SYNCHRONIZED_IO:
|
||||
case _SC_THREAD_SAFE_FUNCTIONS:
|
||||
case _SC_THREAD_ATTR_STACKADDR:
|
||||
case _SC_THREAD_ATTR_STACKSIZE:
|
||||
case _SC_THREAD_PRIORITY_SCHEDULING:
|
||||
case _SC_THREAD_PRIO_INHERIT:
|
||||
case _SC_THREAD_PRIO_PROTECT:
|
||||
case _SC_THREAD_PROCESS_SHARED:
|
||||
case _SC_REALTIME_SIGNALS:
|
||||
case _SC_2_C_BIND:
|
||||
case _SC_2_C_DEV:
|
||||
case _SC_2_FORT_DEV:
|
||||
case _SC_2_FORT_RUN:
|
||||
case _SC_2_LOCALEDEF:
|
||||
case _SC_2_SW_DEV:
|
||||
case _SC_2_UPE:
|
||||
return 200809L;
|
||||
default:
|
||||
/* Unknown key or unsupported option: -1, errno untouched. */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,397 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <syslog.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* vlibc — openlog/syslog/closelog/vsyslog/setlogmask.
|
||||
*
|
||||
* syslog is XSI/BSD, not POSIX.1-2008 base, so the whole file is gated at
|
||||
* level 2 (whole-file-L2 rule); at level 1 this TU compiles empty.
|
||||
*
|
||||
* Wire format: each message is one SOCK_DGRAM datagram to the /dev/log
|
||||
* socket, shaped like
|
||||
*
|
||||
* <PRI>ident[pid]: message
|
||||
*
|
||||
* where PRI is the three-digit decimal priority code = facility | severity
|
||||
* (the facility constants in <syslog.h> are already code << 3, so the OR is
|
||||
* the wire value), ident and [pid] appear only when openlog received a
|
||||
* non-NULL ident / the LOG_PID option, and the datagram carries no
|
||||
* timestamp — the syslog daemon adds its own (glibc sends none either). The
|
||||
* message text is the caller's format expanded by vsnprintf (the library's
|
||||
* own, from src/stdio), with one syslog-specific addition: "%m" is replaced
|
||||
* by the strerror(errno) text before vsnprintf sees the format, because the
|
||||
* printf core deliberately has no %m conversion.
|
||||
*
|
||||
* The socket is opened lazily on the first syslog call (or immediately when
|
||||
* openlog gets LOG_NDELAY) and closed by closelog. Every failure — a socket
|
||||
* that cannot be created, a sendto that bounces (no daemon listening gives
|
||||
* ECONNREFUSED at once for an unconnected datagram socket, so nothing here
|
||||
* blocks), a console that cannot be opened — is silent, per the POSIX rule
|
||||
* that syslog must not terminate the process. sendto takes the destination
|
||||
* address per call, so a daemon restart that closes the daemon's socket
|
||||
* needs no reconnect; a single reopen-on-ENOTCONN retry is kept as a guard
|
||||
* for sockets that were implicitly connected.
|
||||
*
|
||||
* This is process-global state, deliberately not thread-synchronized: the
|
||||
* POSIX text does not require syslog to be thread-safe, and matching the
|
||||
* reference implementations' unsynchronized behavior keeps the hot path
|
||||
* free of locks.
|
||||
*/
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/* The /dev/log address: the classic BSD syslog datagram sink on Linux. */
|
||||
#define LOG_SOCK_PATH "/dev/log"
|
||||
#define LOG_CONSOLE_PATH "/dev/console"
|
||||
|
||||
/* Kernel ABI constants this TU needs but no public header provides yet. */
|
||||
#define LOG_AF_UNIX 1 /* struct sockaddr family for AF_UNIX */
|
||||
#define LOG_SOCK_DGRAM 2 /* SOCK_DGRAM */
|
||||
|
||||
/*
|
||||
* struct sockaddr_un layout for Linux x86_64, declared locally: the socket
|
||||
* layer (todo 57) does not exist yet, and pulling in a system header would
|
||||
* break the clean-room, host-header-free build.
|
||||
*/
|
||||
struct log_sockaddr
|
||||
{
|
||||
unsigned short sun_family;
|
||||
char sun_path[108];
|
||||
};
|
||||
|
||||
/* Bounded copy of the openlog ident; 256 covers every real tag. */
|
||||
static char log_ident[256];
|
||||
|
||||
/* The openlog option flags (LOG_PID/LOG_CONS/...). */
|
||||
static int log_option;
|
||||
|
||||
/* The facility (code << 3) used when a message carries no facility of its
|
||||
* own; POSIX's default when openlog was never called is LOG_USER. */
|
||||
static int log_facility = LOG_USER;
|
||||
|
||||
/* The setlogmask mask: one bit per severity. Default: everything (0xff). */
|
||||
static int log_mask = LOG_UPTO(LOG_DEBUG);
|
||||
|
||||
/* The /dev/log socket, or -1 while closed. */
|
||||
static int log_fd = -1;
|
||||
|
||||
/*
|
||||
* Open the /dev/log datagram socket unless one is already open. Failures are
|
||||
* silent and leave log_fd at -1; the next message simply tries again.
|
||||
*/
|
||||
static void
|
||||
log_open(void)
|
||||
{
|
||||
long r;
|
||||
|
||||
if (log_fd >= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
r = __syscall3(SYS_socket, LOG_AF_UNIX, LOG_SOCK_DGRAM, 0);
|
||||
if (r >= 0)
|
||||
{
|
||||
log_fd = (int)r;
|
||||
}
|
||||
}
|
||||
|
||||
/* Write the len bytes at s to standard error with a raw write; ignore errors. */
|
||||
static void
|
||||
log_write_stderr(const char *s, size_t len)
|
||||
{
|
||||
(void)__syscall3(SYS_write, 2, (long)s, (long)len);
|
||||
}
|
||||
|
||||
/*
|
||||
* LOG_CONS fallback: write the message to /dev/console. The console is opened
|
||||
* O_WRONLY|O_NOCTTY so a session without a controlling terminal is not given
|
||||
* one, and is closed again right away — the file is not kept.
|
||||
*/
|
||||
static void
|
||||
log_write_console(const char *s, size_t len)
|
||||
{
|
||||
long fd;
|
||||
|
||||
fd = __syscall4(SYS_openat, AT_FDCWD, (long)LOG_CONSOLE_PATH, (long)(O_WRONLY | O_NOCTTY), 0);
|
||||
if (fd < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
(void)__syscall3(SYS_write, fd, (long)s, (long)len);
|
||||
(void)__syscall3(SYS_write, fd, (long)"\n", 1);
|
||||
(void)__syscall1(SYS_close, fd);
|
||||
}
|
||||
|
||||
/* Render v in decimal into dst; return the number of characters written. */
|
||||
static size_t
|
||||
log_put_dec(char *dst, unsigned long v)
|
||||
{
|
||||
char tmp[20];
|
||||
size_t n = 0;
|
||||
|
||||
do
|
||||
{
|
||||
tmp[n++] = (char)('0' + (int)(v % 10));
|
||||
v /= 10;
|
||||
} while (v != 0);
|
||||
for (size_t i = 0; i < n; i++)
|
||||
{
|
||||
dst[i] = tmp[n - 1 - i];
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
void
|
||||
openlog(const char *ident, int option, int facility)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (ident == NULL)
|
||||
{
|
||||
log_ident[0] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; ident[i] != '\0' && i + 1 < sizeof(log_ident); i++)
|
||||
{
|
||||
log_ident[i] = ident[i];
|
||||
}
|
||||
log_ident[i] = '\0';
|
||||
}
|
||||
log_option = option;
|
||||
log_facility = facility;
|
||||
if ((log_option & LOG_NDELAY) != 0)
|
||||
{
|
||||
log_open();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
closelog(void)
|
||||
{
|
||||
if (log_fd >= 0)
|
||||
{
|
||||
(void)__syscall1(SYS_close, log_fd);
|
||||
log_fd = -1;
|
||||
}
|
||||
log_ident[0] = '\0';
|
||||
log_option = 0;
|
||||
}
|
||||
|
||||
int
|
||||
setlogmask(int maskpri)
|
||||
{
|
||||
int old = log_mask;
|
||||
|
||||
log_mask = maskpri;
|
||||
return old;
|
||||
}
|
||||
|
||||
void
|
||||
vsyslog(int priority, const char *format, va_list ap)
|
||||
{
|
||||
int errno_saved = errno;
|
||||
int pr = priority & LOG_PRIMASK;
|
||||
int facility = priority & LOG_FACMASK;
|
||||
char expanded[1024];
|
||||
char message[1024];
|
||||
char line[1400];
|
||||
char *o = line;
|
||||
const char *const oend = line + sizeof(line) - 1;
|
||||
const char *es = NULL; /* strerror text, resolved on first "%m" */
|
||||
size_t msglen;
|
||||
size_t linelen;
|
||||
long r;
|
||||
int retried = 0;
|
||||
|
||||
/* The mask filters on severity alone; facility never suppresses. */
|
||||
if ((log_mask & LOG_MASK(pr)) == 0)
|
||||
{
|
||||
errno = errno_saved;
|
||||
return;
|
||||
}
|
||||
if (facility == 0)
|
||||
{
|
||||
facility = log_facility;
|
||||
}
|
||||
|
||||
/*
|
||||
* Expand "%m" into the strerror text, doubling any "%" inside that text
|
||||
* so vsnprintf prints it literally; every other character — including
|
||||
* the other conversion specifiers and their argument parsing — passes
|
||||
* through untouched, so the va_list still lines up. "%%m" is a literal
|
||||
* "%m", not a substitution.
|
||||
*/
|
||||
{
|
||||
const char *src = format;
|
||||
char *dst = expanded;
|
||||
const char *const dstend = expanded + sizeof(expanded) - 1;
|
||||
|
||||
while (*src != '\0' && dst < dstend)
|
||||
{
|
||||
if (src[0] == '%' && src[1] == '%')
|
||||
{
|
||||
if (dst + 1 >= dstend)
|
||||
{
|
||||
break;
|
||||
}
|
||||
dst[0] = '%';
|
||||
dst[1] = '%';
|
||||
dst += 2;
|
||||
src += 2;
|
||||
}
|
||||
else if (src[0] == '%' && src[1] == 'm')
|
||||
{
|
||||
if (es == NULL)
|
||||
{
|
||||
es = strerror(errno_saved);
|
||||
}
|
||||
for (const char *q = es; *q != '\0' && dst < dstend - 1; q++)
|
||||
{
|
||||
if (*q == '%')
|
||||
{
|
||||
dst[0] = '%';
|
||||
dst[1] = '%';
|
||||
dst += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
dst[0] = *q;
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
src += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
dst[0] = *src;
|
||||
dst++;
|
||||
src++;
|
||||
}
|
||||
}
|
||||
*dst = '\0';
|
||||
}
|
||||
|
||||
msglen = (size_t)vsnprintf(message, sizeof(message), expanded, ap);
|
||||
|
||||
/* "<facility|severity>" — three digits, zero-padded (values <= 191). */
|
||||
{
|
||||
int code = facility | pr;
|
||||
|
||||
*o++ = '<';
|
||||
*o++ = (char)('0' + code / 100);
|
||||
*o++ = (char)('0' + (code / 10) % 10);
|
||||
*o++ = (char)('0' + code % 10);
|
||||
*o++ = '>';
|
||||
}
|
||||
|
||||
for (size_t i = 0; log_ident[i] != '\0' && o < oend; i++)
|
||||
{
|
||||
*o++ = log_ident[i];
|
||||
}
|
||||
|
||||
if ((log_option & LOG_PID) != 0)
|
||||
{
|
||||
long pid = __syscall0(SYS_getpid);
|
||||
char digits[20];
|
||||
size_t n;
|
||||
|
||||
if (pid < 0)
|
||||
{
|
||||
pid = 0;
|
||||
}
|
||||
n = log_put_dec(digits, (unsigned long)pid);
|
||||
if (o < oend)
|
||||
{
|
||||
*o++ = '[';
|
||||
}
|
||||
for (size_t i = 0; i < n && o < oend; i++)
|
||||
{
|
||||
*o++ = digits[i];
|
||||
}
|
||||
if (o < oend)
|
||||
{
|
||||
*o++ = ']';
|
||||
}
|
||||
}
|
||||
|
||||
if (o + 1 < oend)
|
||||
{
|
||||
*o++ = ':';
|
||||
*o++ = ' ';
|
||||
}
|
||||
if (msglen > sizeof(message) - 1)
|
||||
{
|
||||
msglen = sizeof(message) - 1;
|
||||
}
|
||||
for (size_t i = 0; i < msglen && o < oend; i++)
|
||||
{
|
||||
*o++ = message[i];
|
||||
}
|
||||
*o = '\0';
|
||||
linelen = (size_t)(o - line);
|
||||
|
||||
if ((log_option & LOG_PERROR) != 0)
|
||||
{
|
||||
log_write_stderr(line, linelen);
|
||||
log_write_stderr("\n", 1);
|
||||
}
|
||||
|
||||
if (log_fd < 0)
|
||||
{
|
||||
log_open();
|
||||
}
|
||||
if (log_fd >= 0)
|
||||
{
|
||||
struct log_sockaddr sa = {.sun_family = LOG_AF_UNIX, .sun_path = LOG_SOCK_PATH};
|
||||
|
||||
do
|
||||
{
|
||||
r = __syscall6(SYS_sendto, log_fd, (long)line, (long)linelen, 0, (long)&sa,
|
||||
(long)sizeof(sa));
|
||||
if (r >= 0 || retried != 0 || -r != ENOTCONN)
|
||||
{
|
||||
break;
|
||||
}
|
||||
/* Stale after a daemon restart: drop and recreate the socket once. */
|
||||
(void)__syscall1(SYS_close, log_fd);
|
||||
log_fd = -1;
|
||||
log_open();
|
||||
retried = 1;
|
||||
} while (log_fd >= 0);
|
||||
|
||||
if (r < 0 && (log_option & LOG_CONS) != 0)
|
||||
{
|
||||
log_write_console(line, linelen);
|
||||
}
|
||||
}
|
||||
else if ((log_option & LOG_CONS) != 0)
|
||||
{
|
||||
log_write_console(line, linelen);
|
||||
}
|
||||
|
||||
errno = errno_saved;
|
||||
}
|
||||
|
||||
void
|
||||
syslog(int priority, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
vsyslog(priority, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,99 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* uname/gethostname/sethostname/gethostid: system identification.
|
||||
*
|
||||
* Everything here is XSI or BSD — none of it is POSIX.1-2008 base — so the
|
||||
* whole file is gated at level 2 (whole-file-L2 rule; the wiring pass puts
|
||||
* it in VLIBC_LEVEL2_SRCS, and at level 1 this TU compiles empty).
|
||||
*
|
||||
* gethostname and sethostname are declared in <unistd.h>, gethostid too;
|
||||
* uname and struct utsname live in <sys/utsname.h>. struct utsname is the
|
||||
* kernel's struct new_utsname layout (six 65-byte arrays), so the kernel
|
||||
* writes it in place through SYS_uname.
|
||||
*/
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* Fill the buffer with the kernel's UTS namespace strings (the "name"
|
||||
* information of the running kernel and host). syscall_ret reports 0 on
|
||||
* success and -1 with EFAULT when the buffer is not writable.
|
||||
*/
|
||||
int
|
||||
uname(struct utsname *buf)
|
||||
{
|
||||
return syscall_ret(__syscall1(SYS_uname, (long)buf));
|
||||
}
|
||||
|
||||
/*
|
||||
* Store the NUL-terminated nodename (the host name) into name. The
|
||||
* nodename is read from the kernel through uname() into a stack buffer, so
|
||||
* nothing is allocated or leaked. When the name does not fit in len bytes
|
||||
* — including its terminator — -1 is returned with ENAMETOOLONG and name
|
||||
* is left untouched (glibc semantics; the POSIX text leaves the truncation
|
||||
* case implementation-defined).
|
||||
*
|
||||
* The attribute keeps GCC from recognizing the length loop as a strlen
|
||||
* call or the copy loop as memcpy (house idiom, see src/string): a
|
||||
* libc-owned symbol must not be pulled into an unplanned dependency.
|
||||
*/
|
||||
__attribute__((optimize("no-tree-loop-distribute-patterns"))) int
|
||||
gethostname(char *name, size_t len)
|
||||
{
|
||||
struct utsname u = {0}; /* kernel-written; zeroed for the analyzer */
|
||||
size_t n = 0;
|
||||
|
||||
if (uname(&u) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
while (u.nodename[n] != '\0' && n < sizeof(u.nodename))
|
||||
{
|
||||
n++;
|
||||
}
|
||||
if (n + 1 > len)
|
||||
{
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
for (size_t i = 0; i <= n; i++)
|
||||
{
|
||||
name[i] = u.nodename[i];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the kernel's host name (the uts nodename) to the len bytes at name.
|
||||
* Requires CAP_SYS_ADMIN in the initial user namespace. syscall_ret
|
||||
* reports 0 on success and -1 with EPERM/EINVAL/EFAULT otherwise.
|
||||
*/
|
||||
int
|
||||
sethostname(const char *name, size_t len)
|
||||
{
|
||||
return syscall_ret(__syscall2(SYS_sethostname, (long)name, (long)len));
|
||||
}
|
||||
|
||||
/*
|
||||
* The 32-bit host identifier. vlibc returns 0, matching musl: the classic
|
||||
* value was a hash of the host's primary address obtained through
|
||||
* gethostbyname, a lookup this library deliberately does not perform, and
|
||||
* POSIX marks the interface obsolescent [OB].
|
||||
*/
|
||||
long
|
||||
gethostid(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
+227
@@ -0,0 +1,227 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* The mmap family over the raw x86_64 syscalls. Every wrapper is a thin
|
||||
* pass-through: SYS_mmap, SYS_munmap, SYS_mprotect, SYS_msync, SYS_mlock,
|
||||
* SYS_munlock, SYS_mlockall, SYS_munlockall, SYS_madvise. Arguments are
|
||||
* cast to long because that is the shape of the syscall register file (the
|
||||
* wrappers in arch/x86_64/syscall_arch.h take long arguments).
|
||||
*
|
||||
* mmap is the one member that cannot use syscall_ret(): its success value
|
||||
* is an address, which syscall_ret() would truncate to int. The kernel
|
||||
* result is therefore checked against the -4095 MAX_ERRNO bound directly
|
||||
* (the same test malloc.c applies to its private mmap) and the full pointer
|
||||
* is preserved on success. The other members funnel their result through
|
||||
* syscall_ret(), which returns the value on success and -1 with errno set
|
||||
* on error.
|
||||
*
|
||||
* Level 2 adds madvise (the raw Linux interface behind posix_madvise) and
|
||||
* the POSIX shared-memory pair. shm_open/shm_unlink live on real files
|
||||
* under /dev/shm (see <sys/mman.h> for the naming rationale and the
|
||||
* deviation from POSIX's leading-slash requirement): the object name is
|
||||
* validated, rendered as "/dev/shm/" + name into a local buffer, and handed
|
||||
* to SYS_openat / SYS_unlinkat directly — no inter-object dependency on
|
||||
* the unistd wrappers, the same self-containment creat.c uses.
|
||||
*/
|
||||
|
||||
/* Length of the "/dev/shm/" prefix (VLIBC_SHM_PREFIX_LEN bytes + NUL). */
|
||||
#define VLIBC_SHM_PREFIX "/dev/shm/"
|
||||
#define VLIBC_SHM_PREFIX_LEN (sizeof(VLIBC_SHM_PREFIX) - 1)
|
||||
|
||||
/* Longest object name: /dev/shm is a tmpfs, whose NAME_MAX is 255. */
|
||||
#define VLIBC_SHM_NAME_MAX 255
|
||||
#define VLIBC_SHM_PATH_MAX (VLIBC_SHM_PREFIX_LEN + VLIBC_SHM_NAME_MAX + 1)
|
||||
|
||||
/*
|
||||
* Map len bytes starting at offset off of the file descriptor fildes into
|
||||
* the process address space and return the mapping address. On failure
|
||||
* MAP_FAILED is returned with errno set. len is rounded up to whole pages
|
||||
* by the kernel; addr is a hint unless MAP_FIXED is given.
|
||||
*/
|
||||
void *
|
||||
mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off)
|
||||
{
|
||||
long r = __syscall6(SYS_mmap, (long)addr, (long)len, prot, flags, fildes, (long)off);
|
||||
|
||||
if ((uintptr_t)r > (uintptr_t)-4096)
|
||||
{
|
||||
errno = (int)-r; /* the kernel returned -errno */
|
||||
return MAP_FAILED;
|
||||
}
|
||||
return (void *)r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove the mapping at addr covering len bytes; return 0, or -1 with errno
|
||||
* set. addr must be page-aligned.
|
||||
*/
|
||||
int
|
||||
munmap(void *addr, size_t len)
|
||||
{
|
||||
return syscall_ret(__syscall2(SYS_munmap, (long)addr, (long)len));
|
||||
}
|
||||
|
||||
/*
|
||||
* Change the protection of the pages at addr covering len bytes to prot;
|
||||
* return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
mprotect(void *addr, size_t len, int prot)
|
||||
{
|
||||
return syscall_ret(__syscall3(SYS_mprotect, (long)addr, (long)len, prot));
|
||||
}
|
||||
|
||||
/*
|
||||
* Flush the pages at addr covering len bytes of a MAP_SHARED mapping to
|
||||
* (or invalidate them from) the underlying object per flags; return 0, or
|
||||
* -1 with errno set.
|
||||
*/
|
||||
int
|
||||
msync(void *addr, size_t len, int flags)
|
||||
{
|
||||
return syscall_ret(__syscall3(SYS_msync, (long)addr, (long)len, flags));
|
||||
}
|
||||
|
||||
/*
|
||||
* Lock the pages at addr covering len bytes into memory so they are never
|
||||
* paged out; return 0, or -1 with errno set. munlock undoes the lock.
|
||||
*/
|
||||
int
|
||||
mlock(const void *addr, size_t len)
|
||||
{
|
||||
return syscall_ret(__syscall2(SYS_mlock, (long)addr, (long)len));
|
||||
}
|
||||
|
||||
int
|
||||
munlock(const void *addr, size_t len)
|
||||
{
|
||||
return syscall_ret(__syscall2(SYS_munlock, (long)addr, (long)len));
|
||||
}
|
||||
|
||||
/*
|
||||
* Lock all pages mapped by the process into memory per flags (MCL_CURRENT,
|
||||
* MCL_FUTURE); return 0, or -1 with errno set. munlockall undoes the lock.
|
||||
*/
|
||||
int
|
||||
mlockall(int flags)
|
||||
{
|
||||
return syscall_ret(__syscall1(SYS_mlockall, flags));
|
||||
}
|
||||
|
||||
int
|
||||
munlockall(void)
|
||||
{
|
||||
return syscall_ret(__syscall0(SYS_munlockall));
|
||||
}
|
||||
|
||||
/*
|
||||
* Give the kernel the POSIX_MADV_* advice about the pages at addr covering
|
||||
* len bytes; return 0, or -1 with errno set. The advice values (0-4) match
|
||||
* the Linux MADV_* values, so the call is a direct SYS_madvise.
|
||||
*/
|
||||
int
|
||||
posix_madvise(void *addr, size_t len, int advice)
|
||||
{
|
||||
return syscall_ret(__syscall3(SYS_madvise, (long)addr, (long)len, advice));
|
||||
}
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* Linux madvise: give the kernel the MADV_* advice about the pages at addr
|
||||
* covering len bytes; return 0, or -1 with errno set. XSI — posix_madvise
|
||||
* is the POSIX base form; this exposes the full Linux MADV_* set.
|
||||
*/
|
||||
int
|
||||
madvise(void *addr, size_t len, int advice)
|
||||
{
|
||||
return syscall_ret(__syscall3(SYS_madvise, (long)addr, (long)len, advice));
|
||||
}
|
||||
|
||||
/*
|
||||
* Validate name (no empty names, no '/', tmpfs NAME_MAX bound) and render
|
||||
* the /dev/shm path into buf (capacity VLIBC_SHM_PATH_MAX). Returns 0 and
|
||||
* fills buf on success; -1 with errno set (EINVAL, ENAMETOOLONG) on error.
|
||||
*/
|
||||
static int
|
||||
vlibc_shm_path(char *buf, const char *name)
|
||||
{
|
||||
size_t i;
|
||||
size_t n;
|
||||
|
||||
for (n = 0; name[n] != '\0'; n++)
|
||||
{
|
||||
if (name[n] == '/')
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (n == 0)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (n > VLIBC_SHM_NAME_MAX)
|
||||
{
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; i < VLIBC_SHM_PREFIX_LEN; i++)
|
||||
{
|
||||
buf[i] = VLIBC_SHM_PREFIX[i];
|
||||
}
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
buf[VLIBC_SHM_PREFIX_LEN + i] = name[i];
|
||||
}
|
||||
buf[VLIBC_SHM_PREFIX_LEN + n] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Open (creating with mode when oflag contains O_CREAT) the POSIX shared
|
||||
* memory object name and return a descriptor, or -1 with errno set. The
|
||||
* object is a real file under /dev/shm and persists until shm_unlink. See
|
||||
* <sys/mman.h> on the naming deviation.
|
||||
*/
|
||||
int
|
||||
shm_open(const char *name, int oflag, mode_t mode)
|
||||
{
|
||||
char path[VLIBC_SHM_PATH_MAX];
|
||||
|
||||
if (vlibc_shm_path(path, name) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return syscall_ret(__syscall4(SYS_openat, AT_FDCWD, (long)path, oflag, (long)mode));
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove the shared memory object name from /dev/shm; return 0, or -1 with
|
||||
* errno set. Open descriptors of the object stay valid until closed.
|
||||
*/
|
||||
int
|
||||
shm_unlink(const char *name)
|
||||
{
|
||||
char path[VLIBC_SHM_PATH_MAX];
|
||||
|
||||
if (vlibc_shm_path(path, name) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return syscall_ret(__syscall3(SYS_unlinkat, AT_FDCWD, (long)path, 0));
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,433 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <pwd.h>
|
||||
|
||||
/*
|
||||
* vlibc — password database (todo 37).
|
||||
*
|
||||
* getpwnam/getpwuid scan /etc/passwd with a fresh fopen per call (no
|
||||
* persistent cursor); getpwent walks one FILE kept open between calls,
|
||||
* which setpwent rewinds and endpwent closes. Every line is read with
|
||||
* getline, one at a time — the whole file is never cached.
|
||||
*
|
||||
* Storage model: two static buffers back every non-reentrant result. A
|
||||
* successful call returns a struct whose string members point into a
|
||||
* private result copy; the getline scan that produced the match ran in a
|
||||
* separate buffer, so a caller may feed the returned record's own name
|
||||
* back into a later lookup (the getpwnam(getpwuid(x)->pw_name)
|
||||
* round-trip) without the scan clobbering its key. The result copy is
|
||||
* replaced by the next call to any of getpwnam/getpwuid/getpwent (or a
|
||||
* realloc of the copy), matching POSIX result-lifetime rules. The _r
|
||||
* forms never use this static storage for their result: the caller's
|
||||
* struct and buffer receive the record, and the caller's *result is how
|
||||
* the outcome is reported.
|
||||
*
|
||||
* Record syntax: seven ':'-separated fields, name:passwd:uid:gid:gecos:
|
||||
* dir:shell. The last field (shell) keeps any further ':' characters as
|
||||
* data. Blank or comment-style lines and records whose uid/gid are not
|
||||
* decimal numbers are skipped like malformed input.
|
||||
*/
|
||||
|
||||
/* The database path; not overridable (no NSS, no shadow, POSIX only). */
|
||||
#define PWD_PATH "/etc/passwd"
|
||||
|
||||
/* Non-reentrant result: struct plus the private copy it points into. */
|
||||
static struct passwd pw_result;
|
||||
static char *pw_out;
|
||||
static size_t pw_out_cap;
|
||||
|
||||
/* The getline scan buffer shared by every lookup and stream walk. */
|
||||
static char *pw_line;
|
||||
static size_t pw_cap;
|
||||
|
||||
/* The persistent getpwent stream; NULL when closed. */
|
||||
static FILE *pw_stream;
|
||||
|
||||
/*
|
||||
* Parse the leading digit string of s as an unsigned id. Accepts only
|
||||
* plain decimal digits and rejects empty fields and overflow; returns 0
|
||||
* on success.
|
||||
*/
|
||||
static int
|
||||
pw_parse_id(const char *s, unsigned *out)
|
||||
{
|
||||
unsigned long v = 0;
|
||||
const char *p;
|
||||
|
||||
if (*s == '\0')
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
for (p = s; *p != '\0'; p++)
|
||||
{
|
||||
if (*p < '0' || *p > '9')
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
v = v * 10 + (unsigned long)(*p - '0');
|
||||
if (v > 0xffffffffUL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
*out = (unsigned)v;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Split the line (newline already stripped) into its seven fields in
|
||||
* place, replacing the first six ':' separators with NULs. On success the
|
||||
* struct fields point into line and 0 is returned; a record with fewer
|
||||
* than six separators or a non-numeric uid/gid yields -1.
|
||||
*/
|
||||
static int
|
||||
pw_split(struct passwd *pw, char *line)
|
||||
{
|
||||
char *fields[7];
|
||||
char *p;
|
||||
int i;
|
||||
|
||||
p = line;
|
||||
for (i = 0; i < 6; i++)
|
||||
{
|
||||
fields[i] = p;
|
||||
p = strchr(p, ':');
|
||||
if (p == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
*p = '\0';
|
||||
p++;
|
||||
}
|
||||
fields[6] = p;
|
||||
if (pw_parse_id(fields[2], &pw->pw_uid) != 0 || pw_parse_id(fields[3], &pw->pw_gid) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
pw->pw_name = fields[0];
|
||||
pw->pw_passwd = fields[1];
|
||||
pw->pw_gecos = fields[4];
|
||||
pw->pw_dir = fields[5];
|
||||
pw->pw_shell = fields[6];
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Strip a trailing newline (and CR) from the line just read by getline. */
|
||||
static void
|
||||
pw_chomp(char *line, ssize_t len)
|
||||
{
|
||||
while (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r'))
|
||||
{
|
||||
line[--len] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Make line — a chomped, already-split record in the scan buffer — the
|
||||
* non-reentrant result: copy it into the private result buffer and
|
||||
* re-split it there, so the returned struct's strings point into storage
|
||||
* the scanner never touches again. Returns the result struct, or NULL if
|
||||
* the result copy could not be grown.
|
||||
*/
|
||||
static struct passwd *
|
||||
pw_publish(char *line)
|
||||
{
|
||||
size_t len = strlen(line) + 1;
|
||||
|
||||
if (len > pw_out_cap)
|
||||
{
|
||||
char *nb = (char *)realloc(pw_out, len);
|
||||
|
||||
if (nb == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
pw_out = nb;
|
||||
pw_out_cap = len;
|
||||
}
|
||||
strcpy(pw_out, line);
|
||||
if (pw_split(&pw_result, pw_out) != 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return &pw_result;
|
||||
}
|
||||
|
||||
/* Match predicate over a parsed record, keyed by name or uid. */
|
||||
typedef int (*pw_matcher)(const struct passwd *pw, const void *key);
|
||||
|
||||
static int
|
||||
pw_match_name(const struct passwd *pw, const void *key)
|
||||
{
|
||||
return strcmp(pw->pw_name, (const char *)key) == 0;
|
||||
}
|
||||
|
||||
static int
|
||||
pw_match_uid(const struct passwd *pw, const void *key)
|
||||
{
|
||||
return pw->pw_uid == *(const uid_t *)key;
|
||||
}
|
||||
|
||||
/*
|
||||
* Non-reentrant lookup core: open the database, scan with getline until
|
||||
* the predicate matches, and hand back a pointer into the shared static
|
||||
* line buffer. NULL means "no such entry" (or an unopenable database).
|
||||
*/
|
||||
static struct passwd *
|
||||
pw_find(pw_matcher match, const void *key)
|
||||
{
|
||||
FILE *f = fopen(PWD_PATH, "r");
|
||||
struct passwd cur;
|
||||
ssize_t got;
|
||||
|
||||
if (f == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
got = getline(&pw_line, &pw_cap, f);
|
||||
if (got < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
pw_chomp(pw_line, got);
|
||||
if (pw_split(&cur, pw_line) == 0 && match(&cur, key))
|
||||
{
|
||||
(void)fclose(f);
|
||||
return pw_publish(pw_line);
|
||||
}
|
||||
}
|
||||
(void)fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reentrant serializer: copy the parsed record into the caller's struct
|
||||
* and buffer. Returns 0 on success, or ERANGE (as the POSIX _r functions
|
||||
* return, never touching errno) when the record needs more than bufsize
|
||||
* bytes.
|
||||
*/
|
||||
static int
|
||||
pw_store(const struct passwd *in, struct passwd *pw, char *buf, size_t bufsize)
|
||||
{
|
||||
const char *fields[5] = {
|
||||
in->pw_name, in->pw_passwd, in->pw_gecos, in->pw_dir, in->pw_shell,
|
||||
};
|
||||
char *p = buf;
|
||||
size_t need = 0;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < 5; i++)
|
||||
{
|
||||
need += strlen(fields[i]) + 1;
|
||||
}
|
||||
if (bufsize < need)
|
||||
{
|
||||
return ERANGE;
|
||||
}
|
||||
pw->pw_uid = in->pw_uid;
|
||||
pw->pw_gid = in->pw_gid;
|
||||
pw->pw_name = p;
|
||||
p += strlen(pw->pw_name) + 1;
|
||||
pw->pw_passwd = p;
|
||||
p += strlen(pw->pw_passwd) + 1;
|
||||
pw->pw_gecos = p;
|
||||
p += strlen(pw->pw_gecos) + 1;
|
||||
pw->pw_dir = p;
|
||||
p += strlen(pw->pw_dir) + 1;
|
||||
pw->pw_shell = p;
|
||||
strcpy(pw->pw_shell, in->pw_shell);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct passwd *
|
||||
getpwnam(const char *name)
|
||||
{
|
||||
if (name == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return pw_find(pw_match_name, name);
|
||||
}
|
||||
|
||||
struct passwd *
|
||||
getpwuid(uid_t uid)
|
||||
{
|
||||
return pw_find(pw_match_uid, &uid);
|
||||
}
|
||||
|
||||
struct passwd *
|
||||
getpwent(void)
|
||||
{
|
||||
struct passwd cur;
|
||||
ssize_t got;
|
||||
|
||||
if (pw_stream == NULL)
|
||||
{
|
||||
pw_stream = fopen(PWD_PATH, "r");
|
||||
if (pw_stream == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
got = getline(&pw_line, &pw_cap, pw_stream);
|
||||
if (got < 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
pw_chomp(pw_line, got);
|
||||
if (pw_split(&cur, pw_line) == 0)
|
||||
{
|
||||
return pw_publish(pw_line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
setpwent(void)
|
||||
{
|
||||
if (pw_stream != NULL)
|
||||
{
|
||||
rewind(pw_stream);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
endpwent(void)
|
||||
{
|
||||
if (pw_stream != NULL)
|
||||
{
|
||||
(void)fclose(pw_stream);
|
||||
pw_stream = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Reentrant lookup core: scan for the first matching record and serialize
|
||||
* it into the caller's storage. Returns 0 with *result pointing at the
|
||||
* filled struct on success, 0 with *result NULL when no record matches,
|
||||
* ERANGE when the record does not fit bufsize, and EINVAL for a NULL
|
||||
* result pointer.
|
||||
*/
|
||||
static int
|
||||
pw_find_r(pw_matcher match, const void *key, struct passwd *pw, char *buf, size_t bufsize,
|
||||
struct passwd **result)
|
||||
{
|
||||
FILE *f;
|
||||
struct passwd cur;
|
||||
ssize_t got;
|
||||
int rc;
|
||||
|
||||
if (result == NULL || pw == NULL)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
if (buf == NULL && bufsize != 0)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
*result = NULL;
|
||||
f = fopen(PWD_PATH, "r");
|
||||
if (f == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
got = getline(&pw_line, &pw_cap, f);
|
||||
if (got < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
pw_chomp(pw_line, got);
|
||||
if (pw_split(&cur, pw_line) != 0 || !match(&cur, key))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
rc = pw_store(&cur, pw, buf, bufsize);
|
||||
(void)fclose(f);
|
||||
if (rc == 0)
|
||||
{
|
||||
*result = pw;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
(void)fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
getpwnam_r(const char *name, struct passwd *pw, char *buf, size_t bufsize, struct passwd **result)
|
||||
{
|
||||
if (name == NULL)
|
||||
{
|
||||
if (result != NULL)
|
||||
{
|
||||
*result = NULL;
|
||||
}
|
||||
return EINVAL;
|
||||
}
|
||||
return pw_find_r(pw_match_name, name, pw, buf, bufsize, result);
|
||||
}
|
||||
|
||||
int
|
||||
getpwuid_r(uid_t uid, struct passwd *pw, char *buf, size_t bufsize, struct passwd **result)
|
||||
{
|
||||
return pw_find_r(pw_match_uid, &uid, pw, buf, bufsize, result);
|
||||
}
|
||||
|
||||
int
|
||||
getpwent_r(struct passwd *pw, char *buf, size_t bufsize, struct passwd **result)
|
||||
{
|
||||
struct passwd cur;
|
||||
ssize_t got;
|
||||
int rc;
|
||||
|
||||
if (result == NULL || pw == NULL)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
if (buf == NULL && bufsize != 0)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
*result = NULL;
|
||||
if (pw_stream == NULL)
|
||||
{
|
||||
pw_stream = fopen(PWD_PATH, "r");
|
||||
if (pw_stream == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
got = getline(&pw_line, &pw_cap, pw_stream);
|
||||
if (got < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
pw_chomp(pw_line, got);
|
||||
if (pw_split(&cur, pw_line) != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
rc = pw_store(&cur, pw, buf, bufsize);
|
||||
if (rc == 0)
|
||||
{
|
||||
*result = pw;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include "atfork_impl.h"
|
||||
|
||||
#include "../internal/malloc.h"
|
||||
|
||||
/*
|
||||
* The atfork hook table (todo 20).
|
||||
*
|
||||
* The list is a doubly linked chain in registration order (head = oldest
|
||||
* registration). fork() runs the phases through __vlibc_atfork_prepare/
|
||||
* parent/child around the syscall; pthread_atfork (todo 45) appends
|
||||
* entries via __vlibc_atfork_register. Nodes live on the malloc heap:
|
||||
* registration happens long after the allocator is up, and fork() itself
|
||||
* never allocates — the phases only walk the existing chain.
|
||||
*
|
||||
* Phase order (POSIX 1003.1-2008 pthread_atfork):
|
||||
* - prepare: reverse registration order (tail to head);
|
||||
* - parent and child: registration order (head to tail).
|
||||
*/
|
||||
|
||||
struct vlibc_atfork_entry *volatile __vlibc_atfork_list = 0;
|
||||
|
||||
// NOLINTBEGIN(bugprone-easily-swappable-parameters)
|
||||
int
|
||||
__vlibc_atfork_register(void (*prepare)(void), void (*parent)(void), void (*child)(void))
|
||||
{
|
||||
struct vlibc_atfork_entry *node;
|
||||
struct vlibc_atfork_entry *tail;
|
||||
|
||||
node = (struct vlibc_atfork_entry *)__libc_malloc(sizeof(*node));
|
||||
if (node == 0)
|
||||
{
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
node->prepare = prepare;
|
||||
node->parent = parent;
|
||||
node->child = child;
|
||||
node->prev = 0;
|
||||
node->next = 0;
|
||||
|
||||
/* Append at the tail so the chain stays in registration order. */
|
||||
if (__vlibc_atfork_list == 0)
|
||||
{
|
||||
__vlibc_atfork_list = node;
|
||||
return 0;
|
||||
}
|
||||
tail = __vlibc_atfork_list;
|
||||
while (tail->next != 0)
|
||||
{
|
||||
tail = tail->next;
|
||||
}
|
||||
tail->next = node;
|
||||
node->prev = tail;
|
||||
return 0;
|
||||
}
|
||||
// NOLINTEND(bugprone-easily-swappable-parameters)
|
||||
|
||||
void
|
||||
__vlibc_atfork_prepare(void)
|
||||
{
|
||||
struct vlibc_atfork_entry *node = __vlibc_atfork_list;
|
||||
|
||||
/* Reverse registration order: walk to the tail, then back. */
|
||||
while (node != 0 && node->next != 0)
|
||||
{
|
||||
node = node->next;
|
||||
}
|
||||
for (; node != 0; node = node->prev)
|
||||
{
|
||||
if (node->prepare != 0)
|
||||
{
|
||||
node->prepare();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
__vlibc_atfork_parent(void)
|
||||
{
|
||||
struct vlibc_atfork_entry *node;
|
||||
|
||||
/* Registration order. */
|
||||
for (node = __vlibc_atfork_list; node != 0; node = node->next)
|
||||
{
|
||||
if (node->parent != 0)
|
||||
{
|
||||
node->parent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
__vlibc_atfork_child(void)
|
||||
{
|
||||
struct vlibc_atfork_entry *node;
|
||||
|
||||
/* Registration order. */
|
||||
for (node = __vlibc_atfork_list; node != 0; node = node->next)
|
||||
{
|
||||
if (node->child != 0)
|
||||
{
|
||||
node->child();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
#ifndef VLIBC_PROCESS_ATFORK_IMPL_H
|
||||
#define VLIBC_PROCESS_ATFORK_IMPL_H
|
||||
|
||||
/*
|
||||
* vlibc — internal atfork hook table (todo 20).
|
||||
*
|
||||
* fork() runs three hook phases around the clone operation:
|
||||
*
|
||||
* 1. prepare — in the parent, before the fork, in REVERSE registration
|
||||
* order (POSIX 1003.1-2008 pthread_atfork: "the prepare handlers are
|
||||
* called in the reverse order in which they were registered");
|
||||
* 2. parent — in the parent, after the fork, in registration order;
|
||||
* 3. child — in the child, after the fork, in registration order
|
||||
* (POSIX: "the parent and child handlers are called in the order in
|
||||
* which they were registered").
|
||||
*
|
||||
* The list is a doubly linked chain in registration order (head = oldest
|
||||
* registration); the phases walk it head-to-tail or tail-to-head as the
|
||||
* ordering above demands. It starts NULL — with no threads and no
|
||||
* registrations every phase is a no-op, so fork() keeps working before
|
||||
* todo 45 (pthread_atfork) lands. Todo 45 appends entries through
|
||||
* __vlibc_atfork_register(); nothing else needs to change here.
|
||||
*
|
||||
* The names sit in the implementation-reserved namespace (this is the
|
||||
* library's private seam, never public API), so the corresponding
|
||||
* bugprone checks are waived per the house NOLINT convention.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "../internal/libc.h"
|
||||
|
||||
// NOLINTBEGIN(bugprone-reserved-identifier)
|
||||
|
||||
struct vlibc_atfork_entry
|
||||
{
|
||||
void (*prepare)(void);
|
||||
void (*parent)(void);
|
||||
void (*child)(void);
|
||||
struct vlibc_atfork_entry *prev; /* toward older registrations */
|
||||
struct vlibc_atfork_entry *next; /* toward newer registrations */
|
||||
};
|
||||
|
||||
/* Head of the registration list; NULL until the first registration. */
|
||||
hidden extern struct vlibc_atfork_entry *volatile __vlibc_atfork_list;
|
||||
|
||||
/*
|
||||
* Append a handler set to the list. Returns 0, or -1 with errno ENOMEM
|
||||
* when the node cannot be allocated (pthread_atfork maps that to its
|
||||
* error return). Only pthread_atfork (todo 45) calls this.
|
||||
*/
|
||||
hidden int
|
||||
__vlibc_atfork_register(void (*prepare)(void), void (*parent)(void), void (*child)(void));
|
||||
|
||||
/* The three phases; fork() calls these around the syscall. */
|
||||
hidden void
|
||||
__vlibc_atfork_prepare(void);
|
||||
|
||||
hidden void
|
||||
__vlibc_atfork_parent(void);
|
||||
|
||||
hidden void
|
||||
__vlibc_atfork_child(void);
|
||||
|
||||
// NOLINTEND(bugprone-reserved-identifier)
|
||||
|
||||
#endif /* VLIBC_PROCESS_ATFORK_IMPL_H */
|
||||
@@ -0,0 +1,50 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* execl: execve with the arguments collected from the varargs list, which
|
||||
* is terminated by (char *)NULL. POSIX sets no argument-count limit, so
|
||||
* the list is counted first and staged on a VLA; GCC supports VLA in
|
||||
* every mode vlibc targets (the C23 auto-VLA optionality does not bite
|
||||
* here). The POSIX signature is fixed; the adjacent-parameter check is
|
||||
* waived per the house NOLINT convention.
|
||||
*/
|
||||
// NOLINTBEGIN(bugprone-easily-swappable-parameters)
|
||||
int
|
||||
execl(const char *path, const char *arg0, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int argc;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
va_start(ap, arg0);
|
||||
argc = 1;
|
||||
while (va_arg(ap, const char *) != 0)
|
||||
{
|
||||
argc++;
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
{
|
||||
char *argv[argc + 1];
|
||||
|
||||
argv[0] = (char *)arg0;
|
||||
va_start(ap, arg0);
|
||||
for (i = 1; i <= argc; i++)
|
||||
{
|
||||
argv[i] = va_arg(ap, char *);
|
||||
}
|
||||
va_end(ap);
|
||||
ret = execve(path, argv, environ);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
// NOLINTEND(bugprone-easily-swappable-parameters)
|
||||
@@ -0,0 +1,50 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* execle: like execl, but the varargs list is terminated by (char *)NULL
|
||||
* followed by one final char *const envp[] argument. The argv staging
|
||||
* uses a VLA (see execl.c); envp is read directly from the varargs. The
|
||||
* POSIX signature is fixed; the adjacent-parameter check is waived per
|
||||
* the house NOLINT convention.
|
||||
*/
|
||||
// NOLINTBEGIN(bugprone-easily-swappable-parameters)
|
||||
int
|
||||
execle(const char *path, const char *arg0, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int argc;
|
||||
int i;
|
||||
char **envp;
|
||||
int ret;
|
||||
|
||||
va_start(ap, arg0);
|
||||
argc = 1;
|
||||
while (va_arg(ap, const char *) != 0)
|
||||
{
|
||||
argc++;
|
||||
}
|
||||
envp = va_arg(ap, char **);
|
||||
va_end(ap);
|
||||
|
||||
{
|
||||
char *argv[argc + 1];
|
||||
|
||||
argv[0] = (char *)arg0;
|
||||
va_start(ap, arg0);
|
||||
for (i = 1; i <= argc; i++)
|
||||
{
|
||||
argv[i] = va_arg(ap, char *);
|
||||
}
|
||||
va_end(ap);
|
||||
ret = execve(path, argv, envp);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
// NOLINTEND(bugprone-easily-swappable-parameters)
|
||||
@@ -0,0 +1,47 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* execlp: like execl, but the file is located through PATH (see
|
||||
* execvp.c). The argv staging uses a VLA; the POSIX signature is fixed
|
||||
* and the adjacent-parameter check is waived per the house NOLINT
|
||||
* convention.
|
||||
*/
|
||||
// NOLINTBEGIN(bugprone-easily-swappable-parameters)
|
||||
int
|
||||
execlp(const char *file, const char *arg0, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int argc;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
va_start(ap, arg0);
|
||||
argc = 1;
|
||||
while (va_arg(ap, const char *) != 0)
|
||||
{
|
||||
argc++;
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
{
|
||||
char *argv[argc + 1];
|
||||
|
||||
argv[0] = (char *)arg0;
|
||||
va_start(ap, arg0);
|
||||
for (i = 1; i <= argc; i++)
|
||||
{
|
||||
argv[i] = va_arg(ap, char *);
|
||||
}
|
||||
va_end(ap);
|
||||
ret = execvp(file, argv);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
// NOLINTEND(bugprone-easily-swappable-parameters)
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* execv: execve with the calling process's environment. environ is the
|
||||
* global installed by the startup code (src/start/environ.c); the internal
|
||||
* syscall layer is not needed here beyond execve itself.
|
||||
*/
|
||||
int
|
||||
execv(const char *path, char *const argv[])
|
||||
{
|
||||
return execve(path, argv, environ);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/* AT_EMPTY_PATH for execveat (fexecve): resolve the fd itself. */
|
||||
#define VLIBC_PROC_AT_EMPTY_PATH 0x1000
|
||||
|
||||
/*
|
||||
* execve: replace the process image with the file at path. Success never
|
||||
* returns; -1 with errno set otherwise.
|
||||
*/
|
||||
int
|
||||
execve(const char *path, char *const argv[], char *const envp[])
|
||||
{
|
||||
return syscall_ret(__syscall3(SYS_execve, (long)path, (long)argv, (long)envp));
|
||||
}
|
||||
|
||||
/*
|
||||
* fexecve: like execve, but the image is the open descriptor fd. Runs the
|
||||
* kernel's execveat with an empty path plus AT_EMPTY_PATH. A descriptor
|
||||
* opened with O_PATH cannot be exec'd on kernels before 6.3; a regular
|
||||
* open works everywhere.
|
||||
*/
|
||||
int
|
||||
fexecve(int fd, char *const argv[], char *const envp[])
|
||||
{
|
||||
return syscall_ret(
|
||||
__syscall5(SYS_execveat, fd, (long)"", (long)argv, (long)envp, VLIBC_PROC_AT_EMPTY_PATH));
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* execvp: search PATH for file and execve it with the caller's
|
||||
* environment. If file contains '/', execve is tried directly.
|
||||
*
|
||||
* PATH is read via getenv(); a missing PATH defaults to "/bin:/usr/bin"
|
||||
* (the traditional pathconf default). An empty entry — a leading,
|
||||
* trailing, or doubled colon — means the current directory, per POSIX.
|
||||
*
|
||||
* Error policy (POSIX XBD exec): ENOENT and ENOTDIR from a candidate are
|
||||
* skipped while the rest of PATH is searched, and EACCES is remembered;
|
||||
* any other error aborts immediately. When every candidate fails, errno
|
||||
* is EACCES if any candidate failed with EACCES, else ENOENT.
|
||||
*/
|
||||
// NOLINTBEGIN(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
|
||||
int
|
||||
execvp(const char *file, char *const argv[])
|
||||
{
|
||||
const char *path;
|
||||
const char *p;
|
||||
size_t flen;
|
||||
int saw_eacces;
|
||||
|
||||
if (file == 0 || file[0] == '\0')
|
||||
{
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
if (strchr(file, '/') != 0)
|
||||
{
|
||||
return execve(file, argv, environ);
|
||||
}
|
||||
path = getenv("PATH");
|
||||
if (path == 0)
|
||||
{
|
||||
path = "/bin:/usr/bin";
|
||||
}
|
||||
flen = strlen(file);
|
||||
saw_eacces = 0;
|
||||
|
||||
for (p = path; *p != '\0';)
|
||||
{
|
||||
const char *end = p;
|
||||
size_t dlen;
|
||||
|
||||
while (*end != '\0' && *end != ':')
|
||||
{
|
||||
end++;
|
||||
}
|
||||
dlen = (size_t)(end - p);
|
||||
|
||||
{
|
||||
char candidate[dlen + flen + 2]; /* dir + '/' + file + NUL */
|
||||
long r;
|
||||
|
||||
if (dlen == 0)
|
||||
{
|
||||
/* Empty PATH entry: the current directory. */
|
||||
memcpy(candidate, file, flen + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(candidate, p, dlen);
|
||||
candidate[dlen] = '/';
|
||||
memcpy(candidate + dlen + 1, file, flen + 1);
|
||||
}
|
||||
r = __syscall3(SYS_execve, (long)candidate, (long)argv, (long)environ);
|
||||
if (r < 0)
|
||||
{
|
||||
int e = (int)-r;
|
||||
|
||||
if (e == EACCES)
|
||||
{
|
||||
saw_eacces = 1;
|
||||
}
|
||||
else if (e != ENOENT && e != ENOTDIR)
|
||||
{
|
||||
/* A real error: stop the search and report it. */
|
||||
errno = e;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (*end == '\0')
|
||||
{
|
||||
break;
|
||||
}
|
||||
p = end + 1;
|
||||
}
|
||||
|
||||
errno = saw_eacces ? EACCES : ENOENT;
|
||||
return -1;
|
||||
}
|
||||
// NOLINTEND(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
|
||||
@@ -0,0 +1,77 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
#include "atfork_impl.h"
|
||||
|
||||
/*
|
||||
* fork(): create a child that is a copy of the caller.
|
||||
*
|
||||
* Hook invocation order (documented for todo 45, pthread_atfork):
|
||||
*
|
||||
* 1. __vlibc_atfork_prepare() — reverse registration order (POSIX),
|
||||
* before the syscall;
|
||||
* 2. SYS_fork;
|
||||
* 3. __vlibc_atfork_child() in the child, __vlibc_atfork_parent() in
|
||||
* the parent — both in registration order (POSIX).
|
||||
*
|
||||
* The hook list is NULL by default (no threads, no registrations), so a
|
||||
* bare fork() costs exactly the three no-op walker calls; with a static
|
||||
* link the compiler folds them to nothing.
|
||||
*
|
||||
* SYS_fork is sufficient here: it is the kernel's plain
|
||||
* clone(SIGCHLD, 0). There is no thread runtime yet (todo 45); once there
|
||||
* is, fork must switch to SYS_clone with a clearable child tid so that
|
||||
* pthread_join can reap without SIGCHLD races.
|
||||
*/
|
||||
pid_t
|
||||
fork(void)
|
||||
{
|
||||
pid_t ret;
|
||||
|
||||
__vlibc_atfork_prepare();
|
||||
ret = syscall_ret(__syscall0(SYS_fork));
|
||||
if (ret == 0)
|
||||
{
|
||||
/* Child: run the child phase (registration order). */
|
||||
__vlibc_atfork_child();
|
||||
}
|
||||
else if (ret > 0)
|
||||
{
|
||||
/* Parent: run the parent phase (registration order). */
|
||||
__vlibc_atfork_parent();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* vfork(): like fork(), but POSIX lets the child borrow the parent's
|
||||
* address space until it execs or exits (POSIX [OB], level 2).
|
||||
*
|
||||
* Implemented as fork(). Rationale (glibc makes the identical choice on
|
||||
* Linux): a C vfork cannot honor the "no function call between vfork and
|
||||
* exec" contract. The child's rsp is the parent's suspended rsp + 8
|
||||
* (the child has already popped vfork's return address), so the child's
|
||||
* first call — even into this library's own execve wrapper — writes its
|
||||
* return address exactly over the parent's suspended return-address
|
||||
* slot; when the parent resumes it returns through the clobbered slot
|
||||
* into the child's continuation. Verified empirically (the resumed
|
||||
* parent landed in the child's exec-failure path). The kernel's COW
|
||||
* fork already makes fork() as cheap as vfork for all practical
|
||||
* purposes, and the [OB] semantics differences (no parent suspension,
|
||||
* no shared stack) are explicitly tolerated by the plan. The atfork
|
||||
* hooks run, as they do for glibc's fork-based vfork.
|
||||
*/
|
||||
pid_t
|
||||
vfork(void)
|
||||
{
|
||||
return fork();
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* Supplementary group access.
|
||||
*
|
||||
* getgroups (POSIX base): fill grouplist with up to gidsetsize
|
||||
* supplementary group ids and return the count; with gidsetsize 0 only
|
||||
* the count is returned and nothing is written.
|
||||
*
|
||||
* setgroups (XSI, level 2): install the given supplementary group list.
|
||||
* Requires privilege (CAP_SETGID).
|
||||
*/
|
||||
int
|
||||
getgroups(int gidsetsize, gid_t grouplist[])
|
||||
{
|
||||
return syscall_ret(__syscall2(SYS_getgroups, gidsetsize, (long)grouplist));
|
||||
}
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
int
|
||||
setgroups(size_t gidsetsize, const gid_t *grouplist)
|
||||
{
|
||||
return syscall_ret(__syscall2(SYS_setgroups, (long)gidsetsize, (long)grouplist));
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* getpid/getppid: plain kernel reads.
|
||||
*
|
||||
* No const/pure attribute: the result is process state, not a pure
|
||||
* function of the arguments. In particular, const would let the compiler
|
||||
* hoist a getpid call across fork() and observe the parent's pid inside
|
||||
* the child.
|
||||
*/
|
||||
pid_t
|
||||
getpid(void)
|
||||
{
|
||||
return (pid_t)syscall_ret(__syscall0(SYS_getpid));
|
||||
}
|
||||
|
||||
pid_t
|
||||
getppid(void)
|
||||
{
|
||||
return (pid_t)syscall_ret(__syscall0(SYS_getppid));
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/* Foreground-process-group ioctls (x86_64 _IO/_IOR derivations). */
|
||||
#define VLIBC_PROC_TIOCGPGRP 0x540F
|
||||
#define VLIBC_PROC_TIOCSPGRP 0x5410
|
||||
|
||||
/*
|
||||
* Process group and session control (POSIX base): setpgid, getpgrp,
|
||||
* setsid. The XSI/obsolescent extras (setpgrp, getpgid, getsid,
|
||||
* tcgetpgrp, tcsetpgrp) are gated at level 2 below.
|
||||
*/
|
||||
|
||||
/*
|
||||
* setpgid: move pid (0 = the caller) into process group pgid (0 = pid's
|
||||
* own value). A process can change groups for itself and its children;
|
||||
* session leaders cannot change groups.
|
||||
*/
|
||||
int
|
||||
setpgid(pid_t pid, pid_t pgid)
|
||||
{
|
||||
return syscall_ret(__syscall2(SYS_setpgid, pid, pgid));
|
||||
}
|
||||
|
||||
/*
|
||||
* getpgrp: the caller's process group.
|
||||
*/
|
||||
pid_t
|
||||
getpgrp(void)
|
||||
{
|
||||
return (pid_t)syscall_ret(__syscall0(SYS_getpgrp));
|
||||
}
|
||||
|
||||
/*
|
||||
* setsid: make the caller the leader of a new session and process group,
|
||||
* detaching it from the controlling terminal. Returns the new session id
|
||||
* (the caller's pid), or -1 with errno EPERM when the caller is already
|
||||
* a process group leader.
|
||||
*/
|
||||
pid_t
|
||||
setsid(void)
|
||||
{
|
||||
return (pid_t)syscall_ret(__syscall0(SYS_setsid));
|
||||
}
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/* Level 2 (muslmimic): XSI and obsolescent session extras. */
|
||||
|
||||
/*
|
||||
* setpgrp (obsolescent): setpgid(0, 0) — the caller becomes the leader
|
||||
* of its own process group.
|
||||
*/
|
||||
int
|
||||
setpgrp(void)
|
||||
{
|
||||
return setpgid(0, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* getpgid (XSI): the process group of pid (0 = the caller).
|
||||
*/
|
||||
pid_t
|
||||
getpgid(pid_t pid)
|
||||
{
|
||||
return (pid_t)syscall_ret(__syscall1(SYS_getpgid, pid));
|
||||
}
|
||||
|
||||
/*
|
||||
* getsid (XSI): the session id of pid (0 = the caller).
|
||||
*/
|
||||
pid_t
|
||||
getsid(pid_t pid)
|
||||
{
|
||||
return (pid_t)syscall_ret(__syscall1(SYS_getsid, pid));
|
||||
}
|
||||
|
||||
/*
|
||||
* tcgetpgrp (XSI): the foreground process group of the terminal on
|
||||
* fildes, or -1 with errno set (ENOTTY when fildes is not a terminal).
|
||||
*/
|
||||
pid_t
|
||||
tcgetpgrp(int fildes)
|
||||
{
|
||||
int pgrp = 0;
|
||||
|
||||
if (syscall_ret(__syscall3(SYS_ioctl, fildes, VLIBC_PROC_TIOCGPGRP, (long)&pgrp)) < 0)
|
||||
{
|
||||
return (pid_t)-1;
|
||||
}
|
||||
return (pid_t)pgrp;
|
||||
}
|
||||
|
||||
/*
|
||||
* tcsetpgrp (XSI): make pgid the foreground process group of the
|
||||
* terminal on fildes.
|
||||
*/
|
||||
int
|
||||
tcsetpgrp(int fildes, pid_t pgid)
|
||||
{
|
||||
return syscall_ret(__syscall3(SYS_ioctl, fildes, VLIBC_PROC_TIOCSPGRP, pgid));
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,145 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* system(cmd): run "sh -c cmd" and return the shell's wait status.
|
||||
*
|
||||
* - NULL cmd: report whether a command processor exists. /bin/sh is
|
||||
* assumed here, as everywhere in POSIX, so this always returns 1.
|
||||
* - otherwise: fork, exec /bin/sh with {"sh", "-c", cmd}, wait for it,
|
||||
* and return the raw wait4 status (e.g. 768 == "exit 3").
|
||||
* - -1 with errno set when the child cannot be created or reaped.
|
||||
*
|
||||
* POSIX requires the parent to block SIGCHLD and ignore SIGINT/SIGQUIT
|
||||
* while the shell runs. There is no signal.h yet (todo 28): the mask and
|
||||
* dispositions are manipulated with raw rt_sigprocmask/rt_sigaction
|
||||
* syscalls on the kernel's one-word sigset_t, so this file takes no
|
||||
* dependency on the signal todo. The child restores the saved mask and
|
||||
* dispositions before exec, matching glibc/musl behavior.
|
||||
*
|
||||
* The child runs only async-signal-safe code between fork and exec: the
|
||||
* raw signal syscalls, execve, and the raw exit syscall. fork()'s atfork
|
||||
* hooks run (prepare/child), which is what pthread_atfork (todo 45)
|
||||
* wants. This fork-based implementation matches glibc; POSIX does not
|
||||
* require system() to be async-signal-safe itself.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Kernel sigaction layout (x86_64): handler, flags, restorer, and the
|
||||
* one-word sigset_t mask. This is the syscall ABI, NOT the <signal.h>
|
||||
* struct — todo 28 owns that one.
|
||||
*/
|
||||
struct vlibc_sys_sigaction
|
||||
{
|
||||
void (*handler)(int);
|
||||
unsigned long flags;
|
||||
void (*restorer)(void);
|
||||
unsigned long mask;
|
||||
};
|
||||
|
||||
#define VLIBC_PROC_SIG_IGN ((void (*)(int))1)
|
||||
#define VLIBC_PROC_SIG_BLOCK 0
|
||||
#define VLIBC_PROC_SIG_SETMASK 2
|
||||
#define VLIBC_PROC_SIGINT 2
|
||||
#define VLIBC_PROC_SIGQUIT 3
|
||||
#define VLIBC_PROC_SIGCHLD 17
|
||||
|
||||
int
|
||||
system(const char *string)
|
||||
{
|
||||
struct vlibc_sys_sigaction ignore;
|
||||
struct vlibc_sys_sigaction old_int;
|
||||
struct vlibc_sys_sigaction old_quit;
|
||||
unsigned long block_mask = 1UL << (VLIBC_PROC_SIGCHLD - 1);
|
||||
unsigned long old_mask = 0;
|
||||
pid_t pid;
|
||||
int status = 0;
|
||||
|
||||
if (string == 0)
|
||||
{
|
||||
/* A command processor is always available. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
ignore.handler = VLIBC_PROC_SIG_IGN;
|
||||
ignore.flags = 0;
|
||||
ignore.restorer = 0;
|
||||
ignore.mask = 0;
|
||||
|
||||
/* 1. Parent: ignore SIGINT/SIGQUIT, block SIGCHLD (all saved). */
|
||||
if (syscall_ret(
|
||||
__syscall4(SYS_rt_sigaction, VLIBC_PROC_SIGINT, (long)&ignore, (long)&old_int, 8)) < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (syscall_ret(__syscall4(SYS_rt_sigaction, VLIBC_PROC_SIGQUIT, (long)&ignore, (long)&old_quit,
|
||||
8)) < 0)
|
||||
{
|
||||
/* Undo the SIGINT change above. */
|
||||
(void)syscall_ret(__syscall4(SYS_rt_sigaction, VLIBC_PROC_SIGINT, (long)&old_int, 0, 8));
|
||||
return -1;
|
||||
}
|
||||
if (syscall_ret(__syscall4(SYS_rt_sigprocmask, VLIBC_PROC_SIG_BLOCK, (long)&block_mask,
|
||||
(long)&old_mask, 8)) < 0)
|
||||
{
|
||||
(void)syscall_ret(__syscall4(SYS_rt_sigaction, VLIBC_PROC_SIGINT, (long)&old_int, 0, 8));
|
||||
(void)syscall_ret(__syscall4(SYS_rt_sigaction, VLIBC_PROC_SIGQUIT, (long)&old_quit, 0, 8));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 2. Fork. */
|
||||
pid = fork();
|
||||
if (pid == 0)
|
||||
{
|
||||
char *sh_argv[4];
|
||||
|
||||
/* Child: restore the saved state, then exec the shell. */
|
||||
(void)__syscall4(SYS_rt_sigaction, VLIBC_PROC_SIGINT, (long)&old_int, 0, 8);
|
||||
(void)__syscall4(SYS_rt_sigaction, VLIBC_PROC_SIGQUIT, (long)&old_quit, 0, 8);
|
||||
(void)__syscall4(SYS_rt_sigprocmask, VLIBC_PROC_SIG_SETMASK, (long)&old_mask, 0, 8);
|
||||
sh_argv[0] = "sh";
|
||||
sh_argv[1] = "-c";
|
||||
sh_argv[2] = (char *)string;
|
||||
sh_argv[3] = 0;
|
||||
execve("/bin/sh", sh_argv, environ);
|
||||
__syscall1(SYS_exit_group, 127); /* exec failed */
|
||||
}
|
||||
if (pid < 0)
|
||||
{
|
||||
/* Restore the parent state; errno was set by fork(). */
|
||||
(void)__syscall4(SYS_rt_sigprocmask, VLIBC_PROC_SIG_SETMASK, (long)&old_mask, 0, 8);
|
||||
(void)__syscall4(SYS_rt_sigaction, VLIBC_PROC_SIGINT, (long)&old_int, 0, 8);
|
||||
(void)__syscall4(SYS_rt_sigaction, VLIBC_PROC_SIGQUIT, (long)&old_quit, 0, 8);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 3. Parent: wait for the shell (raw SYS_wait4; todo 23 owns the
|
||||
* public wrapper). */
|
||||
for (;;)
|
||||
{
|
||||
long r = __syscall4(SYS_wait4, pid, (long)&status, 0, 0);
|
||||
|
||||
if (r < 0 && -r == EINTR)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (r < 0)
|
||||
{
|
||||
status = syscall_ret(r); /* -1 + errno */
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* 4. Parent: restore the saved state. */
|
||||
(void)__syscall4(SYS_rt_sigprocmask, VLIBC_PROC_SIG_SETMASK, (long)&old_mask, 0, 8);
|
||||
(void)__syscall4(SYS_rt_sigaction, VLIBC_PROC_SIGINT, (long)&old_int, 0, 8);
|
||||
(void)__syscall4(SYS_rt_sigaction, VLIBC_PROC_SIGQUIT, (long)&old_quit, 0, 8);
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* Real/effective uid and gid accessors and mutators. None of the getters
|
||||
* carries a const/pure attribute: the values are process state, not a
|
||||
* pure function of the arguments, and the set functions change what the
|
||||
* get functions read.
|
||||
*/
|
||||
uid_t
|
||||
getuid(void)
|
||||
{
|
||||
return (uid_t)syscall_ret(__syscall0(SYS_getuid));
|
||||
}
|
||||
|
||||
uid_t
|
||||
geteuid(void)
|
||||
{
|
||||
return (uid_t)syscall_ret(__syscall0(SYS_geteuid));
|
||||
}
|
||||
|
||||
gid_t
|
||||
getgid(void)
|
||||
{
|
||||
return (gid_t)syscall_ret(__syscall0(SYS_getgid));
|
||||
}
|
||||
|
||||
gid_t
|
||||
getegid(void)
|
||||
{
|
||||
return (gid_t)syscall_ret(__syscall0(SYS_getegid));
|
||||
}
|
||||
|
||||
/*
|
||||
* setuid: set the real, effective, and saved ids (kernel SYS_setuid).
|
||||
* When unprivileged, uid must match the current real or effective id.
|
||||
*/
|
||||
int
|
||||
setuid(uid_t uid)
|
||||
{
|
||||
return syscall_ret(__syscall1(SYS_setuid, uid));
|
||||
}
|
||||
|
||||
/*
|
||||
* seteuid: set only the effective id. Linux has no seteuid syscall; the
|
||||
* kernel contract is setresuid(-1, euid, -1), which is what glibc and
|
||||
* musl issue too.
|
||||
*/
|
||||
int
|
||||
seteuid(uid_t euid)
|
||||
{
|
||||
return syscall_ret(__syscall3(SYS_setresuid, -1, euid, -1));
|
||||
}
|
||||
|
||||
int
|
||||
setgid(gid_t gid)
|
||||
{
|
||||
return syscall_ret(__syscall1(SYS_setgid, gid));
|
||||
}
|
||||
|
||||
int
|
||||
setegid(gid_t egid)
|
||||
{
|
||||
return syscall_ret(__syscall3(SYS_setresgid, -1, egid, -1));
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user