Improve Libc by making Python work even better

Actually Portable Python is now outperforming the Python binaries
that come bundled with Linux distros, at things like HTTP serving.
You can now have a fully featured Python install in just one .com
file that runs on six operating systems and is about 10mb in size.
With tuning, the tiniest is ~1mb. We've got most of the libraries
working, including pysqlite, and the repl now feels very pleasant.
The things you can't do quite yet are: threads and shared objects
but that can happen in the future, if the community falls in love
with this project and wants to see it developed further. Changes:

- Add siginterrupt()
- Add sqlite3 to Python
- Add issymlink() helper
- Make GetZipCdir() faster
- Add tgamma() and finite()
- Add legacy function lutimes()
- Add readlink() and realpath()
- Use heap allocations when appropriate
- Reorganize Python into two-stage build
- Save Lua / Python shell history to dotfile
- Integrate Python Lib embedding into linkage
- Make isregularfile() and isdirectory() go faster
- Make Python shell auto-completion work perfectly
- Make crash reports work better if changed directory
- Fix Python+NT open() / access() flag overflow error
- Disable Python tests relating to \N{LONG NAME} syntax
- Have Python REPL copyright() show all notice embeddings

The biggest technical challenge at the moment is working around
when Python tries to be too clever about filenames.
This commit is contained in:
Justine Tunney 2021-08-18 14:21:30 -07:00
parent 98ccbf44b1
commit 8af197560e
179 changed files with 6728 additions and 10430 deletions

View file

@ -71,6 +71,7 @@ bool fileexists(const char *);
bool isdirectory(const char *);
bool isexecutable(const char *);
bool isregularfile(const char *);
bool issymlink(const char *);
bool32 isatty(int) nosideeffect;
bool32 ischardev(int) nosideeffect;
char *commandv(const char *, char[hasatleast PATH_MAX]);
@ -128,6 +129,7 @@ int killpg(int, int);
int link(const char *, const char *) nothrow;
int linkat(int, const char *, int, const char *, uint32_t);
int lstat(const char *, struct stat *);
int lutimes(const char *, const struct timeval[2]);
int madvise(void *, uint64_t, int);
int mkdir(const char *, uint32_t);
int mkdirat(int, const char *, uint32_t);
@ -152,7 +154,6 @@ int posix_fadvise(int, uint64_t, uint64_t, int);
int posix_madvise(void *, uint64_t, int);
int prctl();
int raise(int);
int readlink(const char *, char *, size_t);
int reboot(int);
int remove(const char *);
int rename(const char *, const char *);
@ -176,6 +177,7 @@ int setsid(void);
int setuid(uint32_t);
int sigaction(int, const struct sigaction *, struct sigaction *);
int sigignore(int);
int siginterrupt(int, int);
int sigprocmask(int, const struct sigset *, struct sigset *);
int sigqueue(int, int, const union sigval);
int sigsuspend(const struct sigset *);
@ -213,6 +215,7 @@ ssize_t pwrite(int, const void *, size_t, int64_t);
ssize_t pwritev(int, const struct iovec *, int, int64_t);
ssize_t read(int, void *, size_t);
ssize_t readansi(int, char *, size_t);
ssize_t readlink(const char *, char *, size_t);
ssize_t readlinkat(int, const char *, char *, size_t);
ssize_t splice(int, int64_t *, int, int64_t *, size_t, uint32_t);
ssize_t vmsplice(int, const struct iovec *, int64_t, uint32_t);

View file

@ -23,6 +23,7 @@
#include "libc/errno.h"
#include "libc/nt/files.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/errfuns.h"
/**
@ -38,7 +39,7 @@ bool fileexists(const char *path) {
uint16_t path16[PATH_MAX];
if (!IsWindows()) {
olderr = errno;
rc = stat(path, &st);
rc = __sys_fstatat(AT_FDCWD, path, &st, 0);
if (rc == -1 && (errno == ENOENT || errno == ENOTDIR)) {
errno = olderr;
}

View file

@ -20,13 +20,50 @@
#include "libc/macros.internal.h"
#include "libc/nt/files.h"
#include "libc/str/str.h"
#include "libc/str/tpenc.h"
#include "libc/str/utf16.h"
#include "libc/sysv/errfuns.h"
textwindows char *sys_getcwd_nt(char *buf, size_t size) {
char16_t name16[PATH_MAX];
if (GetCurrentDirectory(ARRAYLEN(name16), name16)) {
tprecode16to8(buf, size, name16);
return buf;
uint64_t w;
wint_t x, y;
uint32_t n, i, j;
char16_t name16[PATH_MAX + 1];
if ((n = GetCurrentDirectory(ARRAYLEN(name16), name16))) {
if (n <= PATH_MAX) {
tprecode16to8(buf, size, name16);
for (j = i = 0; i < n;) {
x = name16[i++] & 0xffff;
if (!IsUcs2(x)) {
if (i < n) {
y = name16[i++] & 0xffff;
x = MergeUtf16(x, y);
} else {
x = 0xfffd;
}
}
if (x < 0200) {
if (x == '\\') {
x = '/';
}
w = x;
} else {
w = tpenc(x);
}
do {
if (j < size) {
buf[j++] = w;
}
w >>= 8;
} while (w);
}
if (j < size) {
buf[j] = 0;
return buf;
}
}
erange();
return NULL;
} else {
__winerr();
return NULL;

View file

@ -26,7 +26,7 @@ textwindows int gethostname_nt(char *name, size_t len) {
uint32_t nSize;
char16_t name16[256];
nSize = ARRAYLEN(name16);
if (GetComputerNameEx(kNtComputerNameDnsHostname, name16, &nSize)) {
if (GetComputerNameEx(kNtComputerNamePhysicalDnsHostname, name16, &nSize)) {
tprecode16to8(name, len, name16);
return 0;
} else {

View file

@ -199,6 +199,8 @@ i64 sys_ptrace(int, i32, void *, void *) hidden;
i64 sys_pwrite(i32, const void *, u64, i64, i64) hidden;
i64 sys_pwritev(i32, const struct iovec *, i32, i64, i64) hidden;
i64 sys_read(i32, void *, u64) hidden;
i64 sys_readlink(const char *, char *, u64) hidden;
i64 sys_readlinkat(int, const char *, char *, u64) hidden;
i64 sys_sendfile(i32, i32, i64 *, u64) hidden;
i64 sys_splice(i32, i64 *, i32, i64 *, u64, u32) hidden;
i64 sys_vmsplice(i32, const struct iovec *, i64, u32) hidden;
@ -290,6 +292,7 @@ int sys_utimensat_nt(int, const char *, const struct timespec *, int) hidden;
ssize_t sys_open_nt(int, const char *, u32, i32) nodiscard hidden;
ssize_t sys_read_nt(struct Fd *, const struct iovec *, size_t, ssize_t) hidden;
ssize_t sys_write_nt(struct Fd *, const struct iovec *, size_t, ssize_t) hidden;
ssize_t sys_readlinkat_nt(int, const char *, char *, size_t) hidden;
/*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § syscalls » windows nt » support
@ -312,6 +315,9 @@ unsigned __wincrash_nt(struct NtExceptionPointers *);
ssize_t sys_readv_nt(struct Fd *, const struct iovec *, int) hidden;
ssize_t sys_writev_nt(struct Fd *, const struct iovec *, int) hidden;
char16_t *CreatePipeName(char16_t *) hidden;
bool isdirectory_nt(const char *) hidden;
bool isregularfile_nt(const char *) hidden;
bool issymlink_nt(const char *) hidden;
/*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § syscalls » metal

View file

@ -51,10 +51,12 @@ int ioctl(int, uint64_t, ...);
ReZ = ioctl_siocgifdstaddr(FD, __VA_ARGS__); \
} else if (CMP(REQUEST, SIOCGIFFLAGS)) { \
ReZ = ioctl_siocgifflags(FD, __VA_ARGS__); \
} else if (CMP(REQUEST, FIONBIO)) { \
ReZ = ioctl_default(FD, REQUEST, __VA_ARGS__); \
} else if (CMP(REQUEST, FIOCLEX)) { \
ReZ = ioctl_fioclex(FD); \
ReZ = ioctl_fioclex(FD, REQUEST); \
} else if (CMP(REQUEST, FIONCLEX)) { \
ReZ = ioctl_fionclex(FD); \
ReZ = ioctl_fioclex(FD, REQUEST); \
} else { \
ReZ = DEFAULT; \
} \
@ -76,8 +78,7 @@ int ioctl_siocgifnetmask(int, void *);
int ioctl_siocgifbrdaddr(int, void *);
int ioctl_siocgifflags(int, void *);
int ioctl_default(int, uint64_t, void *);
int ioctl_fioclex(int);
int ioctl_fionclex(int);
int ioctl_fioclex(int, int);
#endif /* GNUC && !ANSI */
COSMOPOLITAN_C_END_

View file

@ -18,12 +18,17 @@
*/
#include "libc/calls/internal.h"
#include "libc/calls/ioctl.h"
#include "libc/sysv/consts/fio.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/errfuns.h"
textwindows int ioctl_fioclex_nt(int fd) {
textwindows int ioctl_fioclex_nt(int fd, int req) {
if (__isfdopen(fd)) {
g_fds.p[fd].flags |= O_CLOEXEC;
if (req == FIOCLEX) {
g_fds.p[fd].flags |= O_CLOEXEC;
} else {
g_fds.p[fd].flags &= ~O_CLOEXEC;
}
return 0;
} else {
return ebadf();

View file

@ -20,17 +20,17 @@
#include "libc/calls/ioctl.h"
#include "libc/dce.h"
int ioctl_fioclex_nt(int);
int ioctl_fioclex_nt(int, int);
/**
* Sets "close on exec" on file descriptor the fast way.
*
* @see ioctl(fd, FIOCLEX, 0) dispatches here
*/
int ioctl_fioclex(int fd) {
int ioctl_fioclex(int fd, int req) {
if (!IsWindows()) {
return sys_ioctl(fd, FIOCLEX, 0);
return sys_ioctl(fd, req, 0);
} else {
return ioctl_fioclex_nt(fd);
return ioctl_fioclex_nt(fd, req);
}
}

View file

@ -16,21 +16,21 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/ioctl.h"
#include "libc/dce.h"
int ioctl_fionclex_nt(int);
#include "libc/nt/enum/fileflagandattributes.h"
#include "libc/nt/files.h"
/**
* Clears "close on exec" on file descriptor the fast way.
*
* @see ioctl(fd, FIONCLEX, 0) dispatches here
* Returns true if file exists and is a directory on Windows NT.
*/
int ioctl_fionclex(int fd) {
if (!IsWindows()) {
return sys_ioctl(fd, FIONCLEX, 0);
bool isdirectory_nt(const char *path) {
uint32_t x;
char16_t path16[PATH_MAX];
if (__mkntpath(path, path16) == -1) return -1;
if ((x = GetFileAttributes(path16)) != -1u) {
return !!(x & kNtFileAttributeDirectory);
} else {
return ioctl_fionclex_nt(fd);
return false;
}
}

View file

@ -17,8 +17,12 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/stat.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/nt/files.h"
#include "libc/sysv/consts/at.h"
/**
* Returns true if file exists and is a directory.
@ -26,8 +30,12 @@
bool isdirectory(const char *path) {
struct stat st;
int rc, olderr;
olderr = errno;
rc = stat(path, &st);
if (rc == -1 && (errno == ENOENT || errno == ENOTDIR)) errno = olderr;
return rc != -1 && S_ISDIR(st.st_mode);
if (!IsWindows()) {
olderr = errno;
rc = sys_fstatat(AT_FDCWD, path, &st, AT_SYMLINK_NOFOLLOW);
if (rc == -1 && (errno == ENOENT || errno == ENOTDIR)) errno = olderr;
return rc != -1 && S_ISDIR(st.st_mode);
} else {
return isdirectory_nt(path);
}
}

View file

@ -0,0 +1,36 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2021 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/nt/enum/fileflagandattributes.h"
#include "libc/nt/files.h"
/**
* Returns true if file exists and is a regular file on Windows NT.
*/
bool isregularfile_nt(const char *path) {
uint32_t x;
char16_t path16[PATH_MAX];
if (__mkntpath(path, path16) == -1) return -1;
if ((x = GetFileAttributes(path16)) != -1u) {
return !(x & (kNtFileAttributeDirectory | kNtFileAttributeReparsePoint));
} else {
return false;
}
}

View file

@ -16,20 +16,27 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/errno.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/stat.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/sysv/consts/at.h"
/**
* Returns true if file exists and is a regular file
* Returns true if file exists and is a regular file.
*/
bool isregularfile(const char *path) {
/* TODO(jart): Use fast path on NT? */
struct stat st;
int olderr = errno;
int rc = stat(path, &st);
if (rc == -1 && (errno == ENOENT || errno == ENOTDIR)) {
errno = olderr;
int rc, olderr;
if (!IsWindows()) {
olderr = errno;
rc = sys_fstatat(AT_FDCWD, path, &st, AT_SYMLINK_NOFOLLOW);
if (rc == -1 && (errno == ENOENT || errno == ENOTDIR)) {
errno = olderr;
}
return rc != -1 && S_ISREG(st.st_mode);
} else {
return isregularfile_nt(path);
}
return rc != -1 && S_ISREG(st.st_mode);
}

View file

@ -16,16 +16,21 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/ioctl.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/errfuns.h"
#include "libc/nt/enum/fileflagandattributes.h"
#include "libc/nt/files.h"
textwindows int ioctl_fionclex_nt(int fd) {
if (__isfdopen(fd)) {
g_fds.p[fd].flags &= ~O_CLOEXEC;
return 0;
/**
* Returns true if file exists and is a symbolic link on Windows NT.
*/
bool issymlink_nt(const char *path) {
uint32_t x;
char16_t path16[PATH_MAX];
if (__mkntpath(path, path16) == -1) return -1;
if ((x = GetFileAttributes(path16)) != -1u) {
return !!(x & kNtFileAttributeReparsePoint);
} else {
return ebadf();
return false;
}
}

41
libc/calls/issymlink.c Normal file
View file

@ -0,0 +1,41 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/stat.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/nt/files.h"
#include "libc/sysv/consts/at.h"
/**
* Returns true if file exists and is a symbolic link.
*/
bool issymlink(const char *path) {
struct stat st;
int rc, olderr;
if (!IsWindows()) {
olderr = errno;
rc = sys_fstatat(AT_FDCWD, path, &st, AT_SYMLINK_NOFOLLOW);
if (rc == -1 && (errno == ENOENT || errno == ENOTDIR)) errno = olderr;
return rc != -1 && S_ISLNK(st.st_mode);
} else {
return issymlink_nt(path);
}
}

37
libc/calls/lutimes.c Normal file
View file

@ -0,0 +1,37 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2021 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/sysv/consts/at.h"
#include "libc/time/time.h"
/**
* Changes file timestamps, the legacy way.
*/
int lutimes(const char *filename, const struct timeval tv[2]) {
struct timespec ts[2];
if (tv) {
ts[0].tv_sec = tv[0].tv_sec;
ts[0].tv_nsec = tv[0].tv_usec * 1000;
ts[1].tv_sec = tv[1].tv_sec;
ts[1].tv_nsec = tv[1].tv_usec * 1000;
return utimensat(AT_FDCWD, filename, ts, AT_SYMLINK_NOFOLLOW);
} else {
return utimensat(AT_FDCWD, filename, 0, AT_SYMLINK_NOFOLLOW);
}
}

View file

@ -25,11 +25,15 @@
#include "libc/sysv/consts/o.h"
#include "libc/sysv/errfuns.h"
static inline bool IsSlash(char c) {
return c == '/' || c == '\\';
}
textwindows static const char *FixNtMagicPath(const char *path,
unsigned flags) {
const struct NtMagicPaths *mp = &kNtMagicPaths;
asm("" : "+r"(mp));
if (path[0] != '/') return path;
if (!IsSlash(path[0])) return path;
if (strcmp(path, mp->devtty) == 0) {
if ((flags & O_ACCMODE) == O_RDONLY) {
return mp->conin;
@ -79,8 +83,8 @@ textwindows int __mkntpath2(const char *path,
p = path16;
q = path;
z = PATH_MAX - 16;
if (q[0] == '/' && q[1] == 't' && q[2] == 'm' && q[3] == 'p' &&
(q[4] == '/' || !q[4])) {
if (IsSlash(q[0]) && q[1] == 't' && q[2] == 'm' && q[3] == 'p' &&
(IsSlash(q[4]) || !q[4])) {
m = GetTempPath(z, p);
if (!q[4]) return m;
q += 5;

43
libc/calls/readlink.c Normal file
View file

@ -0,0 +1,43 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2021 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/sysv/consts/at.h"
/**
* Reads symbolic link.
*
* This does *not* nul-terminate the buffer.
*
* It is recommended that malloc() be linked into your program when
* using this function. Otherwise the buffer should be larger. It should
* also be noted that, without malloc, long names with many astral plane
* characters might not decode properly.
*
* @param path must be a symbolic link pathname
* @param buf will receive symbolic link contents, and won't be modified
* unless the function succeeds (with the exception of no-malloc nt)
* @return number of bytes written to buf, or -1 w/ errno; if the
* return is equal to bufsiz then truncation may have occurred
* @see readlinkat(AT_FDCWD, ...) for modern version of this
* @error EINVAL if path isn't a symbolic link
* @asyncsignalsafe
*/
ssize_t readlink(const char *path, char *buf, size_t bufsiz) {
return readlinkat(AT_FDCWD, path, buf, bufsiz);
}

124
libc/calls/readlinkat-nt.c Normal file
View file

@ -0,0 +1,124 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2021 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/bits.h"
#include "libc/bits/weaken.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/mem/mem.h"
#include "libc/nexgen32e/bsr.h"
#include "libc/nt/createfile.h"
#include "libc/nt/enum/creationdisposition.h"
#include "libc/nt/enum/fileflagandattributes.h"
#include "libc/nt/enum/fsctl.h"
#include "libc/nt/enum/io.h"
#include "libc/nt/errors.h"
#include "libc/nt/files.h"
#include "libc/nt/runtime.h"
#include "libc/nt/struct/reparsedatabuffer.h"
#include "libc/str/tpenc.h"
#include "libc/str/utf16.h"
#include "libc/sysv/errfuns.h"
static textwindows ssize_t sys_readlinkat_nt_error(void) {
uint32_t e;
if ((e = GetLastError()) == kNtErrorNotAReparsePoint) {
return einval();
} else {
errno = e;
return -1;
}
}
textwindows ssize_t sys_readlinkat_nt(int dirfd, const char *path, char *buf,
size_t bufsiz) {
int64_t h;
ssize_t rc;
uint64_t w;
wint_t x, y;
void *freeme;
uint32_t e, i, j, n, mem;
char16_t path16[PATH_MAX], *p;
struct NtReparseDataBuffer *rdb;
if (weaken(malloc)) {
mem = 16384;
rdb = weaken(malloc)(mem);
freeme = rdb;
} else if (bufsiz >= sizeof(struct NtReparseDataBuffer) + 16) {
mem = bufsiz;
rdb = (struct NtReparseDataBuffer *)buf;
freeme = 0;
} else {
return enomem();
}
if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1;
if ((h = CreateFile(path16, 0, 0, 0, kNtOpenExisting,
kNtFileFlagOpenReparsePoint | kNtFileFlagBackupSemantics,
0)) != -1) {
if (DeviceIoControl(h, kNtFsctlGetReparsePoint, 0, 0, rdb, mem, &n, 0)) {
if (rdb->ReparseTag == kNtIoReparseTagSymlink) {
i = 0;
j = 0;
n = rdb->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(char16_t);
p = (char16_t *)((char *)rdb->SymbolicLinkReparseBuffer.PathBuffer +
rdb->SymbolicLinkReparseBuffer.PrintNameOffset);
while (i < n) {
x = p[i++] & 0xffff;
if (!IsUcs2(x)) {
if (i < n) {
y = p[i++] & 0xffff;
x = MergeUtf16(x, y);
} else {
x = 0xfffd;
}
}
if (x < 0200) {
if (x == '\\') {
x = '/';
}
w = x;
} else {
w = tpenc(x);
}
do {
if (j < bufsiz) {
buf[j++] = w;
}
w >>= 8;
} while (w);
}
if (freeme || (intptr_t)(buf + j) <= (intptr_t)(p + i)) {
rc = j;
} else {
rc = enametoolong();
}
} else {
rc = einval();
}
} else {
rc = sys_readlinkat_nt_error();
}
CloseHandle(h);
} else {
rc = sys_readlinkat_nt_error();
}
if (freeme && weaken(free)) {
weaken(free)(freeme);
}
return rc;
}

59
libc/calls/readlinkat.c Normal file
View file

@ -0,0 +1,59 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2021 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/weaken.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/errfuns.h"
#include "libc/zipos/zipos.internal.h"
/**
* Reads symbolic link.
*
* This does *not* nul-terminate the buffer.
*
* It is recommended that malloc() be linked into your program when
* using this function. Otherwise the buffer should be larger. It should
* also be noted that, without malloc, long names with many astral plane
* characters might not decode properly.
*
* @param dirfd is normally AT_FDCWD but if it's an open directory and
* file is a relative path, then file is opened relative to dirfd
* @param path must be a symbolic link pathname
* @param buf will receive symbolic link contents, and won't be modified
* unless the function succeeds (with the exception of no-malloc nt)
* @return number of bytes written to buf, or -1 w/ errno; if the
* return is equal to bufsiz then truncation may have occurred
* @error EINVAL if path isn't a symbolic link
* @asyncsignalsafe
*/
ssize_t readlinkat(int dirfd, const char *path, char *buf, size_t bufsiz) {
struct ZiposUri zipname;
if (IsAsan() && !__asan_is_valid(buf, bufsiz)) return efault();
if (weaken(__zipos_open) && weaken(__zipos_parseuri)(path, &zipname) != -1) {
return einval();
}
if (!IsWindows()) {
return sys_readlinkat(dirfd, path, buf, bufsiz);
} else {
return sys_readlinkat_nt(dirfd, path, buf, bufsiz);
}
}

View file

@ -1,25 +1,225 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
Musl Libc
Copyright © 2005-2014 Rich Felker, et al.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/bits.h"
#include "libc/bits/safemacros.internal.h"
#include "libc/bits/weaken.h"
#include "libc/calls/calls.h"
#include "libc/errno.h"
#include "libc/limits.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h"
#include "libc/sysv/errfuns.h"
char *realpath(const char *path, char *resolved_path) {
enotsup();
return NULL;
#define SYMLOOP_MAX 40
asm(".ident\t\"\\n\\n\
Musl libc (MIT License)\\n\
Copyright 2005-2014 Rich Felker, et. al.\"");
asm(".include \"libc/disclaimer.inc\"");
/* clang-format off */
static inline bool IsSlash(char c)
{
return c == '/' || c == '\\';
}
static size_t GetSlashLen(const char *s)
{
const char *s0 = s;
while (IsSlash(*s)) s++;
return s-s0;
}
static char *ResolvePath(char *d, const char *s, size_t n)
{
if (d || (weaken(malloc) && (d = weaken(malloc)(n+1)))) {
return memmove(d, s, n+1);
} else {
enomem();
return 0;
}
}
/**
* Returns absolute pathname.
*
* This function removes `/./` and `/../` components. IF the path is a
* symbolic link then it's resolved.
*
* @param resolved needs PATH_MAX bytes or NULL to use malloc()
*/
char *realpath(const char *filename, char *resolved)
{
ssize_t k;
int up, check_dir=0;
size_t p, q, l, l0, cnt=0, nup=0;
char output[PATH_MAX], stack[PATH_MAX+1], *z;
if (!filename) {
einval();
return 0;
}
l = strnlen(filename, sizeof stack);
if (!l) {
enoent();
return 0;
}
if (l >= PATH_MAX) goto toolong;
if (l > 4 && (READ32LE(filename) == READ32LE("zip:") ||
READ32LE(filename) == READ32LE("zip!"))) {
return ResolvePath(resolved, filename, l);
}
p = sizeof stack - l - 1;
q = 0;
memcpy(stack+p, filename, l+1);
/* Main loop. Each iteration pops the next part from stack of
* remaining path components and consumes any slashes that follow.
* If not a link, it's moved to output; if a link, contents are
* pushed to the stack. */
restart:
for (; ; p+=GetSlashLen(stack+p)) {
/* If stack starts with /, the whole component is / or //
* and the output state must be reset. */
if (IsSlash(stack[p])) {
check_dir=0;
nup=0;
q=0;
output[q++] = '/';
p++;
/* Initial // is special. */
if (IsSlash(stack[p]) && !IsSlash(stack[p+1]))
output[q++] = '/';
continue;
}
z = (char *)min((intptr_t)strchrnul(stack+p, '/'),
(intptr_t)strchrnul(stack+p, '\\'));
l0 = l = z-(stack+p);
if (!l && !check_dir) break;
/* Skip any . component but preserve check_dir status. */
if (l==1 && stack[p]=='.') {
p += l;
continue;
}
/* Copy next component onto output at least temporarily, to
* call readlink, but wait to advance output position until
* determining it's not a link. */
if (q && !IsSlash(output[q-1])) {
if (!p) goto toolong;
stack[--p] = '/';
l++;
}
if (q+l >= PATH_MAX) goto toolong;
memcpy(output+q, stack+p, l);
output[q+l] = 0;
p += l;
up = 0;
if (l0==2 && stack[p-2]=='.' && stack[p-1]=='.') {
up = 1;
/* Any non-.. path components we could cancel start
* after nup repetitions of the 3-byte string "../";
* if there are none, accumulate .. components to
* later apply to cwd, if needed. */
if (q <= 3*nup) {
nup++;
q += l;
continue;
}
/* When previous components are already known to be
* directories, processing .. can skip readlink. */
if (!check_dir) goto skip_readlink;
}
k = readlink(output, stack, p);
if (k==p) goto toolong;
if (!k) {
errno = ENOENT;
return 0;
}
if (k<0) {
if (errno != EINVAL) return 0;
skip_readlink:
check_dir = 0;
if (up) {
while(q && !IsSlash(output[q-1])) q--;
if (q>1 && (q>2 || !IsSlash(output[0]))) q--;
continue;
}
if (l0) q += l;
check_dir = stack[p];
continue;
}
if (++cnt == SYMLOOP_MAX) {
errno = ELOOP;
return 0;
}
/* If link contents end in /, strip any slashes already on
* stack to avoid /->// or //->/// or spurious toolong. */
if (IsSlash(stack[k-1])) {
while (IsSlash(stack[p]))
p++;
}
p -= k;
memmove(stack+p, stack, k);
/* Skip the stack advancement in case we have a new
* absolute base path. */
goto restart;
}
output[q] = 0;
if (!IsSlash(output[0])) {
if (!getcwd(stack, sizeof(stack))) return 0;
l = strlen(stack);
/* Cancel any initial .. components. */
p = 0;
while (nup--) {
while(l>1 && !IsSlash(stack[l-1])) l--;
if (l>1) l--;
p += 2;
if (p<q) p++;
}
if (q-p && !IsSlash(stack[l-1])) stack[l++] = '/';
if (l + (q-p) + 1 >= PATH_MAX) goto toolong;
memmove(output + l, output + p, q - p + 1);
memcpy(output, stack, l);
q = l + q-p;
}
return ResolvePath(resolved, output, q);
toolong:
enametoolong();
return 0;
}

38
libc/calls/siginterrupt.c Normal file
View file

@ -0,0 +1,38 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2021 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/sysv/consts/sa.h"
/**
* Tunes whether signal can interrupt restartable system calls.
*/
int siginterrupt(int sig, int flag) {
struct sigaction sa;
if (sigaction(sig, 0, &sa) != -1) {
if (flag) {
sa.sa_flags &= ~SA_RESTART;
} else {
sa.sa_flags |= SA_RESTART;
}
return sigaction(sig, &sa, 0);
} else {
return -1;
}
}

View file

@ -27,6 +27,7 @@
#define CBRK CEOL
#define CRPRNT CREPRINT
#define CFLUSH CDISCARD
#define CEOL 255
#define CMIN 1
#define CTIME 0