Compare commits

...
14 Commits
Author SHA1 Message Date
Justine Tunney 4072e40bb8 Cosmopolitan 0.1.2 2021-01-30 10:13:13 -08:00
Justine Tunney 393a642c22 Increase Windows stack size (#32)
We're now using the same values CMD.EXE uses.
2021-01-30 10:08:28 -08:00
Justine Tunney 95173645a1 Implement getcwd() for XNU 2021-01-30 08:54:12 -08:00
Justine Tunney 417797d218 Support dirfd relative iops on Windows
We always favor calling functions like openat(), fstatat(), etc. because
Linux, XNU, FreeBSD, and OpenBSD all elected to support them, while some
systems like Android love them so much, that they stopped supporting the
old interfaces.

This change ensures that when dirfd is actually a dirfd and not AT_FDCWD
we'll do the right thing on Windows NT. We use an API that's been around
since Vista to accomplish that.

This change also adds exponential backoff to chdir() on Windows since it
seems almost as flaky on Windows 7 as the rmdir() function.
2021-01-30 01:49:43 -08:00
Justine Tunney b8d26e2418 Fix fork() on Windows after chdir() 2021-01-29 23:19:29 -08:00
Justine Tunney 21e1023d28 Reduce memory requirements for execve() 2021-01-29 22:00:10 -08:00
Justine Tunney eaca5b3e81 Add exponential backoff to rmdir() on Windows 2021-01-29 21:48:40 -08:00
Justine Tunney bf8b1623c8 Normalize mkdir() error codes 2021-01-29 21:21:06 -08:00
Justine Tunney 6070319f84 Create FUNDING.yml 2021-01-29 14:56:22 -08:00
Justine Tunney 36b4bf74f7 Cosmopolitan 0.1.1
This fixes a few bugs that slipped through the 0.1 release process.
2021-01-29 01:30:51 -08:00
Justine Tunney caee314a50 Make PATH search do the right thing w/ empty path 2021-01-29 01:27:09 -08:00
Justine Tunney a5f3456333 Refix readdir() on OpenBSD
We now have better tests which are catching these kinds of bugs.
2021-01-29 01:25:14 -08:00
Justine Tunney f7c7b949fd Explore comfort level with posix_spawn
Justine says nay for the time being. Only XNU implements this. It's not
clear what ABI XNU is using but it's obviously not the one in the POSIX
documentation link below. Since all platforms implement vfork, it might
be better to empirically gauge the intersection of consensus which will
have better performance than this interface.

https://pubs.opengroup.org/onlinepubs/009696699/xrat/xsh_chap03.html
2021-01-29 01:24:54 -08:00
Justine Tunney b2260c2278 Remove printfs accidentally in release :( 2021-01-29 00:07:53 -08:00
98 changed files with 1158 additions and 475 deletions

No files matched your search

+3
View File
@@ -0,0 +1,3 @@
# These are supported funding model platforms
github: [jart]
+2 -2
View File
@@ -20,7 +20,7 @@ Here's how to get started with the freestanding hermetically-sealed
monolithic source repository:
```sh
tar xf cosmopolitan-0.1.tar.gz # see our releases page
tar xf cosmopolitan-0.1.2.tar.gz # see our releases page
cd cosmo
make -j12
o//examples/hello.com
@@ -30,7 +30,7 @@ Here's how to get started with the amalgamated binaries, which let you
bring your own build system:
```sh
unzip cosmopolitan-amalgamated-0.1.zip # see our releases page
unzip cosmopolitan-amalgamated-0.1.2.zip # see our releases page
echo 'main() { printf("hello world\n"); }' >hello.c
gcc -g -O -static -fno-pie -no-pie -mno-red-zone -nostdlib -nostdinc \
-o hello.com.dbg hello.c -Wl,--gc-sections -Wl,-z,max-page-size=0x1000 -fuse-ld=bfd \
+2 -2
View File
@@ -783,8 +783,8 @@ ape.pe: .ascin "PE",4
.long 0 # Checksum
.short v_ntsubsystem # Subsystem: 0=Neutral,2=GUI,3=Console
.short .LDLLEXE # DllCharacteristics
.quad 0x0000000000020000 # StackReserve
.quad 0x0000000000020000 # StackCommit
.quad 0x0000000000100000 # StackReserve
.quad 0x00000000000fc000 # StackCommit
.quad 0 # HeapReserve
.quad 0 # HeapCommit
.long 0 # LoaderFlags
+21 -3
View File
@@ -11,12 +11,31 @@
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/errfuns.h"
#include "third_party/xed/x86.h"
/**
* @fileoverview x86 instruction length decoder by way of hex pipe.
*/
int fgethex(FILE *f) {
int o, t = -1;
while (!((o = fgetc(f)) & ~0xFF)) {
switch (t) {
case -1:
t = isxdigit(o) ? hextoint(o) : -1;
break;
default:
if (isxdigit(o)) {
return t * 16 + hextoint(o);
}
break;
}
}
if (t >= 0) return einval();
return -1;
}
int main(int argc, char *argv[argc]) {
unsigned c, i, j, l;
enum XedError err;
@@ -42,9 +61,8 @@ int main(int argc, char *argv[argc]) {
l = xedd.length;
if (l <= 0 || l > i) abort();
for (j = 0; j < l; ++j) {
if (fputhex(buf[j], stdout) == -1) {
return errno;
}
fputc("0123456789ABCDEF"[(buf[j] & 0xf0) >> 4], stdout);
fputc("0123456789ABCDEF"[(buf[j] & 0x0f) >> 0], stdout);
}
putchar('\n');
memcpy(&buf[0], &buf[l], i -= l);
+1 -11
View File
@@ -17,10 +17,7 @@
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/dce.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/errfuns.h"
/**
* Checks if effective user can access path in particular ways.
@@ -31,12 +28,5 @@
* @asyncsignalsafe
*/
int access(const char *path, int mode) {
char16_t path16[PATH_MAX];
if (!path) return efault();
if (!IsWindows()) {
return faccessat$sysv(AT_FDCWD, path, mode, 0);
} else {
if (__mkntpath(path, path16) == -1) return -1;
return ntaccesscheck(path16, mode);
}
return faccessat(AT_FDCWD, path, mode, 0);
}
+5 -4
View File
@@ -230,10 +230,7 @@ long ptrace(int, int, void *, void *);
int chroot(const char *);
int prctl();
int sysctl(const int *, unsigned, void *, size_t *, void *, size_t);
#define getcwd(BUF, SIZE) \
(__builtin_constant_p(BUF) && (&(BUF)[0] == NULL) ? get_current_dir_name() \
: getcwd(BUF, SIZE))
int fchdir(int);
/*───────────────────────────────────────────────────────────────────────────│─╗
│ cosmopolitan § system calls » formatting ─╬─│┼
@@ -247,6 +244,10 @@ int vdprintf(int, const char *, va_list) paramsnonnull();
╚────────────────────────────────────────────────────────────────────────────│*/
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
#define getcwd(BUF, SIZE) \
(__builtin_constant_p(BUF) && !(BUF) ? get_current_dir_name() \
: getcwd(BUF, SIZE))
void _init_onntconsoleevent(void);
void _init_wincrash(void);
+25 -7
View File
@@ -17,21 +17,39 @@
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/internal.h"
#include "libc/nt/errors.h"
#include "libc/nt/files.h"
#include "libc/nt/runtime.h"
#include "libc/nt/synchronization.h"
#include "libc/sysv/errfuns.h"
textwindows int chdir$nt(const char *path) {
int len;
int e, ms, len;
char16_t path16[PATH_MAX];
if ((len = __mkntpath(path, path16)) == -1) return -1;
if (path16[len - 1] != u'/' && path16[len - 1] != u'\\') {
path16[len + 0] = u'/';
if (path16[len - 1] != u'\\') {
if (len + 1 + 1 > PATH_MAX) return enametoolong();
path16[len + 0] = u'\\';
path16[len + 1] = u'\0';
}
if (SetCurrentDirectory(path16)) {
return 0;
} else {
return __winerr();
/*
* chdir() seems flaky on windows 7
* in a similar way to rmdir() sigh
*/
for (ms = 1;; ms *= 2) {
if (SetCurrentDirectory(path16)) {
return 0;
} else {
e = GetLastError();
if (ms <= 512 &&
(e == kNtErrorFileNotFound || e == kNtErrorAccessDenied)) {
Sleep(ms);
continue;
} else {
break;
}
}
}
errno = e;
return -1;
}
+2 -2
View File
@@ -23,11 +23,11 @@
/**
* Sets current directory.
*
* @asyncsignalsafe
* @syscall
* @see fchdir()
*/
int chdir(const char *path) {
if (!path) return efault();
if (!IsWindows()) {
return chdir$sysv(path);
} else {
-2
View File
@@ -32,9 +32,7 @@
* @see /etc/passwd for user ids
* @see /etc/group for group ids
* @asyncsignalsafe
* @syscall
*/
int chown(const char *pathname, uint32_t uid, uint32_t gid) {
if (!pathname) return efault();
return fchownat$sysv(AT_FDCWD, pathname, uid, gid, 0);
}
+17 -10
View File
@@ -27,7 +27,9 @@ static bool AccessCommand(char path[hasatleast PATH_MAX], const char *name,
size_t namelen, size_t pathlen) {
if (pathlen + 1 + namelen + 1 + 4 + 1 > PATH_MAX) return -1;
if (pathlen && (path[pathlen - 1] != '/' && path[pathlen - 1] != '\\')) {
path[pathlen++] = '/';
path[pathlen] =
!IsWindows() ? '/' : memchr(path, '\\', pathlen) ? '\\' : '/';
pathlen++;
}
memcpy(path + pathlen, name, namelen + 1);
if (isexecutable(path)) return true;
@@ -43,15 +45,18 @@ static bool SearchPath(char path[hasatleast PATH_MAX], const char *name,
size_t i;
const char *p;
p = firstnonnull(emptytonull(getenv("PATH")), "/bin:/usr/local/bin:/usr/bin");
for (;; p += i) {
while (*p == ':' || *p == ';') ++p;
if (!*p) break;
for (i = 0; i < PATH_MAX && p[i] && p[i] != ':' && p[i] != ';'; ++i) {
path[i] = p[i];
for (;;) {
for (i = 0; p[i] && p[i] != ':' && p[i] != ';'; ++i) {
if (i < PATH_MAX) path[i] = p[i];
}
if (AccessCommand(path, name, namelen, i)) {
return true;
}
if (p[i] == ':' || p[i] == ';') {
p += i + 1;
} else {
break;
}
}
return false;
}
@@ -71,9 +76,12 @@ char *commandv(const char *name, char pathbuf[hasatleast PATH_MAX]) {
int rc, olderr;
if (!(namelen = strlen(name))) return PROGN(enoent(), NULL);
if (namelen + 1 > PATH_MAX) return PROGN(enametoolong(), NULL);
if (name[0] == '/' || name[0] == '\\') {
memcpy(pathbuf, name, namelen + 1);
return pathbuf;
if (strchr(name, '/') || strchr(name, '\\')) {
if (AccessCommand(strcpy(pathbuf, ""), name, namelen, 0)) {
return pathbuf;
} else {
return NULL;
}
}
olderr = errno;
if ((IsWindows() &&
@@ -81,7 +89,6 @@ char *commandv(const char *name, char pathbuf[hasatleast PATH_MAX]) {
stpcpy(pathbuf, kNtSystemDirectory) - pathbuf) ||
AccessCommand(pathbuf, name, namelen,
stpcpy(pathbuf, kNtWindowsDirectory) - pathbuf))) ||
AccessCommand(strcpy(pathbuf, ""), name, namelen, 0) ||
SearchPath(pathbuf, name, namelen)) {
errno = olderr;
return pathbuf;
+2 -5
View File
@@ -47,11 +47,8 @@ textwindows int execve$nt(const char *program, char *const argv[],
close(i);
}
}
for (i = 0; argv[i];) ++i;
i = (i + 1) * sizeof(char *);
argv = memcpy(alloca(i), argv, i);
memcpy(argv, &program, sizeof(program));
rc = ntspawn(argv, envp, NULL, NULL, true, 0, NULL, &startinfo, &procinfo);
rc = ntspawn(program, argv, envp, NULL, NULL, true, 0, NULL, &startinfo,
&procinfo);
if (rc == -1) return -1;
CloseHandle(procinfo.hThread);
do {
+4 -3
View File
@@ -25,15 +25,16 @@
#include "libc/str/str.h"
int execve$sysv(const char *prog, char *const argv[], char *const envp[]) {
size_t i, n;
char **shargs, bash[PATH_MAX];
size_t i;
char **shargs;
if (__execve$sysv(prog, argv, envp) != -1) return 0;
if (errno != ENOEXEC) return -1;
for (i = 0; argv[i];) ++i;
shargs = alloca((i + 2) * sizeof(char *));
memcpy(shargs + 2, argv + 1, i * sizeof(char *));
shargs[0] = !IsFreebsd() ? _PATH_BSHELL
: firstnonnull(commandv("bash", bash), _PATH_BSHELL);
: firstnonnull(commandv("bash", alloca(PATH_MAX)),
_PATH_BSHELL);
shargs[1] = prog;
return __execve$sysv(shargs[0], shargs, envp);
}
+1 -2
View File
@@ -22,7 +22,6 @@
int faccessat$nt(int dirfd, const char *path, int mode, uint32_t flags) {
char16_t path16[PATH_MAX];
if (dirfd != AT_FDCWD || flags) return einval();
if (__mkntpath(path, path16) == -1) return -1;
if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1;
return ntaccesscheck(path16, mode);
}
+4 -3
View File
@@ -25,12 +25,13 @@
/**
* Checks if effective user can access path in particular ways.
*
* @param dirfd is usually AT_FDCWD
* @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 is a filename or directory
* @param flags can be R_OK, W_OK, X_OK, F_OK
* @param mode can be R_OK, W_OK, X_OK, F_OK
* @param flags should be 0
* @return 0 if ok, or -1 and sets errno
* @asyncsignalsafe
* @syscall
*/
int faccessat(int dirfd, const char *path, int mode, uint32_t flags) {
if (!path) return efault();
+41
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/dce.h"
#include "libc/nt/files.h"
#include "libc/sysv/errfuns.h"
textwindows int fchdir$nt(int dirfd) {
uint32_t len;
char16_t dir[PATH_MAX];
if (!__isfdkind(dirfd, kFdFile)) return ebadf();
len = GetFinalPathNameByHandle(g_fds.p[dirfd].handle, dir, ARRAYLEN(dir),
kNtFileNameNormalized | kNtVolumeNameDos);
if (len + 1 + 1 > ARRAYLEN(dir)) return enametoolong();
if (dir[len - 1] != u'\\') {
dir[len + 0] = u'\\';
dir[len + 1] = u'\0';
}
if (SetCurrentDirectory(dir)) {
return 0;
} else {
return __winerr();
}
}
+13 -2
View File
@@ -18,7 +18,18 @@
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/dce.h"
textwindows int lstat$nt(const char *pathname, struct stat *st) {
return stat$nt(pathname, st); /* todo(jart) */
/**
* Sets current directory based on file descriptor.
*
* @see open(path, O_DIRECTORY)
* @asyncsignalsafe
*/
int fchdir(int dirfd) {
if (!IsWindows()) {
return fchdir$sysv(dirfd);
} else {
return fchdir$nt(dirfd);
}
}
-2
View File
@@ -31,10 +31,8 @@
* @see /etc/passwd for user ids
* @see /etc/group for group ids
* @asyncsignalsafe
* @syscall
*/
int fchownat(int dirfd, const char *pathname, uint32_t uid, uint32_t gid,
uint32_t flags) {
/* TODO(jart): Windows? */
return fchownat$sysv(dirfd, pathname, uid, gid, flags);
}
@@ -26,11 +26,12 @@
#include "libc/nt/runtime.h"
#include "libc/runtime/runtime.h"
textwindows int stat$nt(const char *path, struct stat *st) {
textwindows int fstatat$nt(int dirfd, const char *path, struct stat *st,
uint32_t flags) {
int rc;
int64_t fh;
uint16_t path16[PATH_MAX];
if (__mkntpath(path, path16) == -1) return -1;
if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1;
if ((fh = CreateFile(
path16, kNtFileReadAttributes,
kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete, NULL,
+15 -17
View File
@@ -16,33 +16,31 @@
│ 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/errno.h"
#include "libc/sysv/consts/at.h"
#include "libc/zipos/zipos.internal.h"
/**
* Performs stat() w/ features for threaded apps.
* Returns information about thing.
*
* @param dirfd can be AT_FDCWD or an open directory descriptor, and is
* ignored if pathname is absolute
* @param dirfd is normally AT_FDCWD but if it's an open directory and
* file is a relative path, then file becomes relative to dirfd
* @param st is where result is stored
* @param flags can have AT_{EMPTY_PATH,NO_AUTOMOUNT,SYMLINK_NOFOLLOW}
* @return 0 on success, or -1 w/ errno
* @see S_ISDIR(st.st_mode), S_ISREG()
* @asyncsignalsafe
*/
int fstatat(int dirfd, const char *pathname, struct stat *st, uint32_t flags) {
int olderr = errno;
int rc = fstatat$sysv(dirfd, pathname, st, flags);
if (rc != -1) {
stat2linux(st);
} else if (errno == ENOSYS && dirfd == AT_FDCWD) {
if (!flags) {
errno = olderr;
rc = stat(pathname, st);
} else if (flags == AT_SYMLINK_NOFOLLOW) {
errno = olderr;
rc = lstat(pathname, st);
}
int fstatat(int dirfd, const char *path, struct stat *st, uint32_t flags) {
struct ZiposUri zipname;
if (weaken(__zipos_stat) && weaken(__zipos_parseuri)(path, &zipname) != -1) {
return weaken(__zipos_stat)(&zipname, st);
} else if (!IsWindows()) {
return fstatat$sysv(dirfd, path, st, flags);
} else {
return fstatat$nt(dirfd, path, st, flags);
}
return rc;
}
+2 -2
View File
@@ -23,12 +23,12 @@
#include "libc/sysv/errfuns.h"
textwindows char *getcwd$nt(char *buf, size_t size) {
uint16_t name16[PATH_MAX + 1];
uint16_t name16[PATH_MAX];
if (GetCurrentDirectory(ARRAYLEN(name16), name16)) {
tprecode16to8(buf, size, name16);
return buf;
} else {
__winerr();
return NULL;
}
return NULL;
}
+57
View File
@@ -0,0 +1,57 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 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/calls/struct/stat.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/errfuns.h"
#define XNU_F_GETPATH 50
#define XNU_MAXPATHLEN 1024
char *getcwd$xnu(char *res, size_t size) {
int fd;
struct stat st[2];
char buf[XNU_MAXPATHLEN], *ret = NULL;
if ((fd = openat$sysv(AT_FDCWD, ".", O_RDONLY | O_DIRECTORY)) != -1) {
if (fstat$sysv(fd, &st[0]) != -1) {
if (st[0].st_dev && st[0].st_ino) {
if (fcntl$sysv(fd, XNU_F_GETPATH, buf) != -1) {
if (fstatat$sysv(AT_FDCWD, buf, &st[1], 0) != -1) {
if (st[0].st_dev == st[1].st_dev && st[0].st_ino == st[1].st_ino) {
if (memccpy(res, buf, '\0', size)) {
ret = res;
} else {
erange();
}
} else {
einval();
}
}
}
} else {
einval();
}
}
close(fd);
}
return ret;
}
+8 -22
View File
@@ -19,8 +19,6 @@
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/sysv/errfuns.h"
/**
* Returns current working directory.
@@ -34,28 +32,16 @@
* @error ERANGE, EINVAL
*/
char *(getcwd)(char *buf, size_t size) {
if (buf) {
buf[0] = '\0';
if (!IsWindows()) {
int olderr = errno;
if (getcwd$sysv(buf, size) != NULL) {
return buf;
} else if (IsXnu() && errno == ENOSYS) {
if (size >= 2) {
buf[0] = '.'; /* XXX: could put forth more effort */
buf[1] = '\0';
errno = olderr;
return buf;
} else {
erange();
}
}
return NULL;
if (buf && size) buf[0] = '\0';
if (!IsWindows()) {
if (IsXnu()) {
return getcwd$xnu(buf, size);
} else if (getcwd$sysv(buf, size) != (void *)-1) {
return buf;
} else {
return getcwd$nt(buf, size);
return NULL;
}
} else {
efault();
return NULL;
return getcwd$nt(buf, size);
}
}
+26 -5
View File
@@ -73,16 +73,27 @@ struct dirent$bsd {
char d_name[256];
};
/**
* OpenBSD getdents() ABI.
*/
struct dirent$openbsd {
uint64_t d_fileno;
int64_t d_off;
uint16_t d_reclen;
uint8_t d_type;
uint8_t d_namlen;
uint8_t __zomg[4];
char d_name[256];
};
static textwindows noinline DIR *opendir$nt(const char *name) {
int len;
DIR *res;
char16_t name16[PATH_MAX];
if ((len = __mkntpath(name, name16)) == -1) return NULL;
if (len + 2 + 1 > PATH_MAX) return PROGN(enametoolong(), NULL);
if (name16[len - 1] == u'/' || name16[len - 1] == u'\\') {
name16[--len] = u'\0';
}
name16[len++] = u'/';
while (name16[len - 1] == u'\\') name16[--len] = u'\0';
name16[len++] = u'\\';
name16[len++] = u'*';
name16[len] = u'\0';
if (!(res = calloc(1, sizeof(DIR)))) return NULL;
@@ -194,6 +205,7 @@ struct dirent *readdir(DIR *dir) {
long basep;
struct dirent *ent;
struct dirent$bsd *bsd;
struct dirent$openbsd *obsd;
if (!IsWindows()) {
if (dir->buf_pos >= dir->buf_end) {
basep = dir->tell; /* <- what does xnu do */
@@ -202,10 +214,19 @@ struct dirent *readdir(DIR *dir) {
dir->buf_pos = 0;
dir->buf_end = rc;
}
if (IsLinux() || IsOpenbsd()) {
if (IsLinux()) {
ent = (struct dirent *)(dir->buf + dir->buf_pos);
dir->buf_pos += ent->d_reclen;
dir->tell = ent->d_off;
} else if (IsOpenbsd()) {
obsd = (struct dirent$openbsd *)(dir->buf + dir->buf_pos);
dir->buf_pos += obsd->d_reclen;
ent = &dir->ent;
ent->d_ino = obsd->d_fileno;
ent->d_off = obsd->d_off;
ent->d_reclen = obsd->d_reclen;
ent->d_type = obsd->d_type;
memcpy(ent->d_name, obsd->d_name, obsd->d_namlen + 1);
} else {
bsd = (struct dirent$bsd *)(dir->buf + dir->buf_pos);
dir->buf_pos += bsd->d_reclen;
+5 -1
View File
@@ -22,7 +22,9 @@
#include "libc/calls/internal.h"
#include "libc/calls/ntspawn.h"
#include "libc/fmt/itoa.h"
#include "libc/macros.h"
#include "libc/nexgen32e/nt2sysv.h"
#include "libc/nt/dll.h"
#include "libc/nt/enum/filemapflags.h"
#include "libc/nt/enum/pageflags.h"
#include "libc/nt/enum/startf.h"
@@ -119,6 +121,7 @@ textwindows void WinMainForked(void) {
textwindows int fork$nt(void) {
jmp_buf jb;
int i, rc, pid;
char exe[PATH_MAX];
int64_t reader, writer;
char *p, buf[21 + 1 + 21 + 1];
struct NtStartupInfo startinfo;
@@ -137,7 +140,8 @@ textwindows int fork$nt(void) {
startinfo.hStdInput = g_fds.p[0].handle;
startinfo.hStdOutput = g_fds.p[1].handle;
startinfo.hStdError = g_fds.p[2].handle;
if (ntspawn(g_argv, environ, &kNtIsInheritable, NULL, true, 0, NULL,
GetModuleFileNameA(0, exe, ARRAYLEN(exe));
if (ntspawn(exe, g_argv, environ, &kNtIsInheritable, NULL, true, 0, NULL,
&startinfo, &procinfo) != -1) {
CloseHandle(reader);
CloseHandle(procinfo.hThread);
-1
View File
@@ -32,7 +32,6 @@
*/
nodiscard char *get_current_dir_name(void) {
char *buf, *res;
if ((res = getenv("PWD"))) return strdup(res);
if (!(buf = malloc(PATH_MAX))) return NULL;
if (!(res = (getcwd)(buf, PATH_MAX))) free(buf);
return res;
+12 -8
View File
@@ -99,6 +99,7 @@ forceinline size_t clampio(size_t size) {
╚────────────────────────────────────────────────────────────────────────────│*/
char *getcwd$sysv(char *, u64) hidden;
char *getcwd$xnu(char *, u64) hidden;
i32 __dup3$sysv(i32, i32, i32) hidden;
i32 __execve$sysv(const char *, char *const[], char *const[]) hidden;
i32 __fstat$sysv(i32, struct stat *) hidden;
@@ -115,11 +116,12 @@ i32 execve$sysv(const char *, char *const[], char *const[]) hidden;
i32 faccessat$sysv(i32, const char *, i32, u32) hidden;
i32 fadvise$sysv(i32, i64, i64, i32) hidden;
i32 fallocate$sysv(i64, i32, i64, i64) hidden;
i32 fchdir$sysv(i32) hidden;
i32 fchmod$sysv(i32, u32) hidden;
i32 fchmodat$sysv(i32, const char *, u32, u32) hidden;
i32 fchown$sysv(i64, u32, u32) hidden;
i32 fchownat$sysv(i32, const char *, u32, u32, u32) hidden;
i32 fcntl$sysv(i32, i32, i32) hidden;
i32 fcntl$sysv(i32, i32, ...) hidden;
i32 fdatasync$sysv(i32) hidden;
i32 flock$sysv(i32, i32) hidden;
i32 fork$sysv(void) hidden;
@@ -150,7 +152,7 @@ i32 mprotect$sysv(void *, u64, i32) hidden;
i32 msync$sysv(void *, u64, i32) hidden;
i32 munmap$sysv(void *, u64) hidden;
i32 nanosleep$sysv(const struct timespec *, struct timespec *) hidden;
i32 openat$sysv(i32, const char *, i32, i32) hidden;
i32 openat$sysv(i32, const char *, i32, ...) hidden;
i32 pause$sysv(void) hidden;
i32 pipe$sysv(i32[hasatleast 2]) hidden;
i32 pipe2$sysv(i32[hasatleast 2], u32) hidden;
@@ -230,11 +232,13 @@ int dup$nt(int, int, int) hidden;
int execve$nt(const char *, char *const[], char *const[]) hidden;
int faccessat$nt(int, const char *, int, uint32_t) hidden;
int fadvise$nt(int, u64, u64, int) hidden;
int fchdir$nt(int) hidden;
int fcntl$nt(int, int, unsigned) hidden;
int fdatasync$nt(int) hidden;
int flock$nt(int, int) hidden;
int fork$nt(void) hidden;
int fstat$nt(i64, struct stat *) hidden;
int fstatat$nt(int, const char *, struct stat *, uint32_t) hidden;
int ftruncate$nt(int, u64) hidden;
int getpriority$nt(int) hidden;
int getrusage$nt(int, struct rusage *) hidden;
@@ -243,22 +247,21 @@ int kill$nt(int, int) hidden;
int link$nt(const char *, const char *) hidden;
int lstat$nt(const char *, struct stat *) hidden;
int madvise$nt(void *, size_t, int) hidden;
int mkdirat$nt(int, const char *, uint32_t) hidden;
int msync$nt(void *, size_t, int) hidden;
int nanosleep$nt(const struct timespec *, struct timespec *) hidden;
int pipe$nt(int[hasatleast 2], unsigned) hidden;
int rename$nt(const char *, const char *) hidden;
int rmdir$nt(const char *) hidden;
int renameat$nt(int, const char *, int, const char *) hidden;
int sched_yield$nt(void) hidden;
int setitimer$nt(int, const struct itimerval *, struct itimerval *) hidden;
int setpriority$nt(int) hidden;
int stat$nt(const char *, struct stat *) hidden;
int symlink$nt(const char *, const char *) hidden;
int symlinkat$nt(const char *, int, const char *) hidden;
int sync$nt(void) hidden;
int sysinfo$nt(struct sysinfo *) hidden;
int truncate$nt(const char *, u64) hidden;
int unlink$nt(const char *) hidden;
int unlinkat$nt(int, const char *, int) hidden;
int utimensat$nt(int, const char *, const struct timespec *, int) hidden;
ssize_t open$nt(const char *, u32, i32) nodiscard hidden;
ssize_t open$nt(int, const char *, u32, i32) nodiscard hidden;
ssize_t read$nt(struct Fd *, const struct iovec *, size_t, ssize_t) hidden;
ssize_t write$nt(struct Fd *, const struct iovec *, size_t, ssize_t) hidden;
@@ -278,6 +281,7 @@ int ntaccesscheck(const char16_t *, u32) paramsnonnull() hidden;
int64_t __winerr(void) nocallback privileged;
int __mkntpath(const char *, char16_t[hasatleast PATH_MAX - 16]) hidden;
int __mkntpath2(const char *, char16_t[hasatleast PATH_MAX - 16], int) hidden;
int __mkntpathat(int, const char *, int, char16_t[PATH_MAX]) hidden;
unsigned __wincrash$nt(struct NtExceptionPointers *);
/*───────────────────────────────────────────────────────────────────────────│─╗
-1
View File
@@ -31,6 +31,5 @@
* @see /etc/group for group ids
*/
int lchown(const char *pathname, uint32_t uid, uint32_t gid) {
/* TODO(jart): Windows? */
return fchownat$sysv(AT_FDCWD, pathname, uid, gid, AT_SYMLINK_NOFOLLOW);
}
+1 -7
View File
@@ -16,8 +16,6 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/dce.h"
#include "libc/calls/internal.h"
#include "libc/calls/calls.h"
#include "libc/sysv/consts/at.h"
@@ -26,9 +24,5 @@
* @asyncsignalsafe
*/
int lstat(const char *pathname, struct stat *st) {
if (!IsWindows()) {
return fstatat$sysv(AT_FDCWD, pathname, st, AT_SYMLINK_NOFOLLOW);
} else {
return lstat$nt(pathname, st);
}
return fstatat(AT_FDCWD, pathname, st, AT_SYMLINK_NOFOLLOW);
}
+9 -17
View File
@@ -24,30 +24,22 @@
#include "libc/sysv/consts/at.h"
#include "libc/sysv/errfuns.h"
static textwindows noinline int mkdir$nt(const char *path, uint32_t mode) {
uint16_t path16[PATH_MAX];
if (__mkntpath(path, path16) == -1) return -1;
if (CreateDirectory(path16, NULL)) {
return 0;
} else {
return __winerr();
}
}
/**
* Creates directory a.k.a. folder.
*
* mkdir o → 0
* mkdir o/yo/yo/yo → -1 w/ ENOENT
* if o/yo is file → -1 w/ ENOTDIR
* if o/yo/yo/yo is dir → -1 w/ EEXIST
* if o/yo/yo/yo is file → -1 w/ EEXIST
*
* @param path is a UTF-8 string, preferably relative w/ forward slashes
* @param mode can be, for example, 0755
* @return 0 on success or -1 w/ errno
* @error EEXIST, ENOTDIR, ENAMETOOLONG, EACCES
* @asyncsignalsafe
* @see makedirs()
*/
int mkdir(const char *path, uint32_t mode) {
if (!path) return efault();
if (!IsWindows()) {
return mkdirat$sysv(AT_FDCWD, path, mode);
} else {
return mkdir$nt(path, mode);
}
int mkdir(const char *path, unsigned mode) {
return mkdirat(AT_FDCWD, path, mode);
}
+49
View File
@@ -0,0 +1,49 @@
/*-*- 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/internal.h"
#include "libc/errno.h"
#include "libc/nt/enum/fileflagandattributes.h"
#include "libc/nt/files.h"
#include "libc/nt/runtime.h"
#include "libc/str/str.h"
static textwindows bool SubpathExistsThatsNotDirectory(char16_t *path) {
char16_t *p;
uint32_t attrs;
while ((p = strrchr16(path, '\\'))) {
*p = u'\0';
if ((attrs = GetFileAttributes(path)) != -1u) {
if (attrs & kNtFileAttributeDirectory) return false;
return true;
}
}
return false;
}
textwindows int mkdirat$nt(int dirfd, const char *path, uint32_t mode) {
int e;
char16_t *p, path16[PATH_MAX];
if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1;
if (CreateDirectory(path16, NULL)) return 0;
e = GetLastError();
/* WIN32 doesn't distinguish between ENOTDIR and ENOENT */
if (e == ENOTDIR && !SubpathExistsThatsNotDirectory(path16)) e = ENOENT;
errno = e;
return -1;
}
+17 -4
View File
@@ -19,11 +19,24 @@
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/errfuns.h"
int mkdirat(int dirfd, const char *pathname, unsigned mode) {
if (dirfd == AT_FDCWD) {
return mkdir(pathname, mode);
/**
* Creates directory a.k.a. folder.
*
* @param dirfd is normally AT_FDCWD but if it's an open directory and
* path is relative, then path becomes relative to dirfd
* @param path is a UTF-8 string, preferably relative w/ forward slashes
* @param mode can be, for example, 0755
* @return 0 on success or -1 w/ errno
* @error EEXIST, ENOTDIR, ENAMETOOLONG, EACCES, ENOENT
* @asyncsignalsafe
* @see makedirs()
*/
int mkdirat(int dirfd, const char *path, unsigned mode) {
if (!IsWindows()) {
return mkdirat$sysv(dirfd, path, mode);
} else {
return mkdirat$sysv(dirfd, pathname, mode);
return mkdirat$nt(dirfd, path, mode);
}
}
+11 -7
View File
@@ -30,24 +30,28 @@
* GetDosArgv() or GetDosArgv(). This function does NOT escape
* command interpreter syntax, e.g. $VAR (sh), %VAR% (cmd).
*
* @param cmdline is output buffer
* @param prog is used as argv[0]
* @param argv is an a NULL-terminated array of UTF-8 strings
* @return freshly allocated lpCommandLine or NULL w/ errno
* @kudos Daniel Colascione for teaching how to quote
* @see libc/runtime/dosargv.c
*/
textwindows int mkntcmdline(char16_t cmdline[ARG_MAX], char *const argv[]) {
textwindows int mkntcmdline(char16_t cmdline[ARG_MAX], const char *prog,
char *const argv[]) {
char *arg;
uint64_t w;
wint_t x, y;
int slashes, n;
size_t i, j, k;
bool needsquote;
char16_t cbuf[2];
for (k = i = 0; argv[i]; ++i) {
for (arg = prog, k = i = 0; arg; arg = argv[++i]) {
if (i) {
cmdline[k++] = u' ';
if (k == ARG_MAX) return e2big();
}
needsquote = !*argv[i] || argv[i][strcspn(argv[i], " \t\n\v\"")];
needsquote = !arg[0] || arg[strcspn(arg, " \t\n\v\"")];
if (needsquote) {
cmdline[k++] = u'"';
if (k == ARG_MAX) return e2big();
@@ -55,20 +59,20 @@ textwindows int mkntcmdline(char16_t cmdline[ARG_MAX], char *const argv[]) {
for (j = 0;;) {
if (needsquote) {
slashes = 0;
while (argv[i][j] && argv[i][j] == '\\') slashes++, j++;
while (arg[j] && arg[j] == '\\') slashes++, j++;
slashes <<= 1;
if (argv[i][j] == '"') slashes++;
if (arg[j] == '"') slashes++;
while (slashes--) {
cmdline[k++] = u'\\';
if (k == ARG_MAX) return e2big();
}
}
x = argv[i][j++] & 0xff;
x = arg[j++] & 0xff;
if (x >= 0300) {
n = ThomPikeLen(x);
x = ThomPikeByte(x);
while (--n) {
if ((y = argv[i][j++] & 0xff)) {
if ((y = arg[j++] & 0xff)) {
x = ThomPikeMerge(x, y);
} else {
x = 0;
+44
View File
@@ -0,0 +1,44 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 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/internal.h"
#include "libc/macros.h"
#include "libc/nt/files.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/errfuns.h"
int __mkntpathat(int dirfd, const char *path, int flags,
char16_t file[PATH_MAX]) {
char16_t dir[PATH_MAX];
uint32_t dirlen, filelen;
if ((filelen = __mkntpath2(path, file, flags)) == -1) return -1;
if (file[0] != u'\\' && dirfd != AT_FDCWD) { /* ProTip: \\?\C:\foo */
if (!__isfdkind(dirfd, kFdFile)) return ebadf();
dirlen = GetFinalPathNameByHandle(g_fds.p[dirfd].handle, dir, ARRAYLEN(dir),
kNtFileNameNormalized | kNtVolumeNameDos);
if (!dirlen) return __winerr();
if (dirlen + 1 + filelen + 1 > ARRAYLEN(dir)) return enametoolong();
dir[dirlen] = u'\\';
memcpy(dir + dirlen + 1, file, (filelen + 1) * sizeof(char16_t));
memcpy(file, dir, (dirlen + 1 + filelen + 1) * sizeof(char16_t));
return dirlen + 1 + filelen;
} else {
return filelen;
}
}
+2 -2
View File
@@ -58,7 +58,7 @@ struct SpawnBlock {
* @see spawnve() which abstracts this function
*/
textwindows int ntspawn(
char *const argv[], char *const envp[],
const char *prog, char *const argv[], char *const envp[],
struct NtSecurityAttributes *opt_lpProcessAttributes,
struct NtSecurityAttributes *opt_lpThreadAttributes, bool32 bInheritHandles,
uint32_t dwCreationFlags, const char16_t *opt_lpCurrentDirectory,
@@ -80,7 +80,7 @@ textwindows int ntspawn(
(block =
MapViewOfFileExNuma(handle, kNtFileMapRead | kNtFileMapWrite, 0, 0,
blocksize, NULL, kNtNumaNoPreferredNode))) {
if (mkntcmdline(block->cmdline, argv) != -1 &&
if (mkntcmdline(block->cmdline, prog, argv) != -1 &&
mkntenvblock(block->envvars, envp) != -1) {
if (CreateProcess(NULL, block->cmdline, opt_lpProcessAttributes,
opt_lpThreadAttributes, bInheritHandles,
+5 -4
View File
@@ -6,11 +6,12 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
int mkntcmdline(char16_t[ARG_MAX], char *const[]) hidden;
int mkntcmdline(char16_t[ARG_MAX], const char *, char *const[]) hidden;
int mkntenvblock(char16_t[ARG_MAX], char *const[]) hidden;
int ntspawn(char *const[], char *const[], struct NtSecurityAttributes *,
struct NtSecurityAttributes *, bool32, uint32_t, const char16_t *,
const struct NtStartupInfo *, struct NtProcessInformation *) hidden;
int ntspawn(const char *, char *const[], char *const[],
struct NtSecurityAttributes *, struct NtSecurityAttributes *,
bool32, uint32_t, const char16_t *, const struct NtStartupInfo *,
struct NtProcessInformation *) hidden;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
-3
View File
@@ -48,13 +48,10 @@ textwindows bool32 __onntconsoleevent(uint32_t CtrlType) {
}
switch ((rva = __sighandrvas[sig])) {
case (uintptr_t)SIG_DFL:
dprintf(2, "__onntconsoleevent ExitProcess\n");
ExitProcess(128 + sig);
case (uintptr_t)SIG_IGN:
dprintf(2, "__onntconsoleevent SIG_IGN\n");
return true;
default:
dprintf(2, "__onntconsoleevent %#x\n", rva);
memset(&info, 0, sizeof(info));
info.si_signo = sig;
((sigaction_f)(_base + rva))(sig, &info, NULL);
+29 -22
View File
@@ -30,18 +30,19 @@
#include "libc/nt/files.h"
#include "libc/nt/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/errfuns.h"
static textwindows int64_t open$nt$impl(const char *file, uint32_t flags,
int32_t mode) {
static textwindows int64_t open$nt$impl(int dirfd, const char *path,
uint32_t flags, int32_t mode) {
uint32_t br;
int64_t handle;
char16_t file16[PATH_MAX];
if (__mkntpath2(file, file16, flags) == -1) return -1;
char16_t path16[PATH_MAX];
if (__mkntpathat(dirfd, path, flags, path16) == -1) return -1;
if ((handle = CreateFile(
file16,
path16,
(flags & 0xf000000f) |
(/* this is needed if we mmap(rwx+cow)
nt is choosy about open() access */
@@ -82,11 +83,18 @@ static textwindows int64_t open$nt$impl(const char *file, uint32_t flags,
*/
DeviceIoControl(handle, kNtFsctlSetSparse, NULL, 0, NULL, 0, &br, NULL);
}
return handle;
} else if (GetLastError() == kNtErrorFileExists &&
((flags & O_CREAT) &&
(flags & O_TRUNC))) { /* TODO(jart): What was this? */
return eisdir();
} else {
return __winerr();
}
return handle;
}
static textwindows ssize_t open$nt$console(const struct NtMagicPaths *mp,
static textwindows ssize_t open$nt$console(int dirfd,
const struct NtMagicPaths *mp,
uint32_t flags, int32_t mode,
size_t fd) {
if (GetFileType(g_fds.p[STDIN_FILENO].handle) == kNtFileTypeChar &&
@@ -94,39 +102,38 @@ static textwindows ssize_t open$nt$console(const struct NtMagicPaths *mp,
g_fds.p[fd].handle = g_fds.p[STDIN_FILENO].handle;
g_fds.p[fd].extra = g_fds.p[STDOUT_FILENO].handle;
} else if ((g_fds.p[fd].handle = open$nt$impl(
mp->conin, (flags & ~O_ACCMODE) | O_RDONLY, mode)) != -1) {
dirfd, mp->conin, (flags & ~O_ACCMODE) | O_RDONLY, mode)) !=
-1) {
g_fds.p[fd].extra =
open$nt$impl(mp->conout, (flags & ~O_ACCMODE) | O_WRONLY, mode);
open$nt$impl(dirfd, mp->conout, (flags & ~O_ACCMODE) | O_WRONLY, mode);
assert(g_fds.p[fd].extra != -1);
} else {
return __winerr();
return -1;
}
g_fds.p[fd].kind = kFdConsole;
g_fds.p[fd].flags = flags;
return fd;
}
static textwindows ssize_t open$nt$file(const char *file, uint32_t flags,
int32_t mode, size_t fd) {
if ((g_fds.p[fd].handle = open$nt$impl(file, flags, mode)) != -1) {
static textwindows ssize_t open$nt$file(int dirfd, const char *file,
uint32_t flags, int32_t mode,
size_t fd) {
if ((g_fds.p[fd].handle = open$nt$impl(dirfd, file, flags, mode)) != -1) {
g_fds.p[fd].kind = kFdFile;
g_fds.p[fd].flags = flags;
return fd;
} else if (GetLastError() == kNtErrorFileExists &&
((flags & O_CREAT) && (flags & O_TRUNC))) {
return eisdir();
} else {
return __winerr();
return -1;
}
}
textwindows ssize_t open$nt(const char *file, uint32_t flags, int32_t mode) {
textwindows ssize_t open$nt(int dirfd, const char *file, uint32_t flags,
int32_t mode) {
size_t fd;
if ((fd = __getemptyfd()) == -1) return -1;
if ((flags & O_ACCMODE) == O_RDWR &&
strcmp(file, kNtMagicPaths.devtty) == 0) {
return open$nt$console(&kNtMagicPaths, flags, mode, fd);
if ((flags & O_ACCMODE) == O_RDWR && !strcmp(file, kNtMagicPaths.devtty)) {
return open$nt$console(dirfd, &kNtMagicPaths, flags, mode, fd);
} else {
return open$nt$file(file, flags, mode, fd);
return open$nt$file(dirfd, file, flags, mode, fd);
}
}
+1 -16
View File
@@ -16,14 +16,8 @@
│ 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/str/str.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/errfuns.h"
#include "libc/zipos/zipos.internal.h"
/**
* Opens file.
@@ -34,23 +28,14 @@
* @param mode is an octal user/group/other permission signifier, that's
* ignored if O_CREAT or O_TMPFILE weren't passed
* @return number needing close(), or -1 w/ errno
* @note don't call open() from signal handlers
* @asyncsignalsafe
* @vforksafe
*/
nodiscard int open(const char *file, int flags, ...) {
va_list va;
unsigned mode;
struct ZiposUri zipname;
va_start(va, flags);
mode = va_arg(va, unsigned);
va_end(va);
if (!file) return efault();
if (weaken(__zipos_open) && weaken(__zipos_parseuri)(file, &zipname) != -1) {
return weaken(__zipos_open)(&zipname, flags, mode);
} else if (!IsWindows()) {
return openat$sysv(AT_FDCWD, file, flags, mode);
} else {
return open$nt(file, flags, mode);
}
return openat(AT_FDCWD, file, flags, mode);
}
+19 -7
View File
@@ -16,15 +16,20 @@
│ 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/str/str.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/errfuns.h"
#include "libc/zipos/zipos.internal.h"
/**
* Opens file, the modern way.
* Opens file.
*
* @param dirfd is normally AT_FDCWD or an open relative directory thing
* @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 file is a UTF-8 string, preferably relative w/ forward slashes
* @param flags should be O_RDONLY, O_WRONLY, or O_RDWR, and can be or'd
* with O_CREAT, O_TRUNC, O_APPEND, O_EXCL, O_CLOEXEC, O_TMPFILE
@@ -33,16 +38,23 @@
* @return number needing close(), or -1 w/ errno
* @asyncsignalsafe
*/
int openat(int dirfd, const char *pathname, int flags, ...) {
nodiscard int openat(int dirfd, const char *file, int flags, ...) {
va_list va;
unsigned mode;
struct ZiposUri zipname;
va_start(va, flags);
mode = va_arg(va, unsigned);
va_end(va);
if (!pathname) return efault();
if (dirfd == AT_FDCWD) {
return open(pathname, flags, mode);
if (!file) return efault();
if (weaken(__zipos_open) && weaken(__zipos_parseuri)(file, &zipname) != -1) {
if (dirfd == AT_FDCWD) {
return weaken(__zipos_open)(&zipname, flags, mode);
} else {
return eopnotsupp(); /* TODO */
}
} else if (!IsWindows()) {
return openat$sysv(dirfd, file, flags, mode);
} else {
return openat$sysv(dirfd, pathname, flags, mode);
return open$nt(dirfd, file, flags, mode);
}
}
+1 -9
View File
@@ -17,10 +17,7 @@
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/dce.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/errfuns.h"
/**
* Moves file the Unix way.
@@ -29,10 +26,5 @@
* @asyncsignalsafe
*/
int rename(const char *oldpathname, const char *newpathname) {
if (!oldpathname || !newpathname) return efault();
if (!IsWindows()) {
return renameat$sysv(AT_FDCWD, oldpathname, AT_FDCWD, newpathname);
} else {
return rename$nt(oldpathname, newpathname);
}
return renameat(AT_FDCWD, oldpathname, AT_FDCWD, newpathname);
}
@@ -23,11 +23,12 @@
#include "libc/str/str.h"
#include "libc/sysv/errfuns.h"
textwindows int rename$nt(const char *oldpath, const char *newpath) {
textwindows int renameat$nt(int olddirfd, const char *oldpath, int newdirfd,
const char *newpath) {
char16_t oldpath16[PATH_MAX];
char16_t newpath16[PATH_MAX];
if (__mkntpath(oldpath, oldpath16) == -1 ||
__mkntpath(newpath, newpath16) == -1) {
if (__mkntpathat(olddirfd, oldpath, 0, oldpath16) == -1 ||
__mkntpathat(newdirfd, newpath, 0, newpath16) == -1) {
return -1;
}
if (MoveFileEx(oldpath16, newpath16, kNtMovefileReplaceExisting)) {
+11 -4
View File
@@ -19,16 +19,23 @@
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/errfuns.h"
/**
* Renames files relative to directories.
*
* @param olddirfd is normally AT_FDCWD but if it's an open directory
* and oldpath is relative, then oldpath become relative to dirfd
* @param newdirfd is normally AT_FDCWD but if it's an open directory
* and newpath is relative, then newpath become relative to dirfd
* @return 0 on success, or -1 w/ errno
*/
int renameat(int olddirfd, const char *oldpath, int newdirfd,
const char *newpath) {
unsigned mode;
if (olddirfd == AT_FDCWD && newdirfd == AT_FDCWD) {
return rename(oldpath, newpath);
} else {
if (!oldpath || !newpath) return efault();
if (!IsWindows()) {
return renameat$sysv(olddirfd, oldpath, newdirfd, newpath);
} else {
return renameat$nt(olddirfd, oldpath, newdirfd, newpath);
}
}
+2 -11
View File
@@ -16,11 +16,7 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/dce.h"
#include "libc/nt/files.h"
#include "libc/str/str.h"
#include "libc/calls/internal.h"
#include "libc/sysv/errfuns.h"
#include "libc/calls/calls.h"
#include "libc/sysv/consts/at.h"
/**
@@ -29,10 +25,5 @@
* @return 0 on success or -1 w/ errno on error
*/
int rmdir(const char *path) {
if (!path) return efault();
if (!IsWindows()) {
return unlinkat$sysv(AT_FDCWD, path, AT_REMOVEDIR);
} else {
return rmdir$nt(path);
}
return unlinkat(AT_FDCWD, path, AT_REMOVEDIR);
}
+14
View File
@@ -0,0 +1,14 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_SCHED_H_
#define COSMOPOLITAN_LIBC_CALLS_SCHED_H_
#include "libc/calls/struct/sched_param.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
int sched_setscheduler(int, int, const struct sched_param *);
int sched_getscheduler(int);
int sched_setparam(int, const struct sched_param *);
int sched_getparam(int, struct sched_param *);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_CALLS_SCHED_H_ */
+3 -16
View File
@@ -16,29 +16,16 @@
│ 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/str/str.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/errfuns.h"
#include "libc/zipos/zipos.internal.h"
/**
* Returns information about thing.
*
* @param st is where result is stored
* @see S_ISDIR(st.st_mode), S_ISREG(), etc.
* @asyncsignalsafe
*/
int stat(const char *pathname, struct stat *st) {
struct ZiposUri zipname;
if (weaken(__zipos_stat) &&
weaken(__zipos_parseuri)(pathname, &zipname) != -1) {
return weaken(__zipos_stat)(&zipname, st);
} else if (!IsWindows()) {
return fstatat$sysv(AT_FDCWD, pathname, st, 0);
} else {
return stat$nt(pathname, st);
}
int stat(const char *path, struct stat *st) {
return fstatat(AT_FDCWD, path, st, 0);
}
+1 -1
View File
@@ -2,7 +2,7 @@
#define COSMOPOLITAN_LIBC_CALLS_STRUCT_DIRENT_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
struct dirent { /* openbsd and linux getdents64 abi */
struct dirent { /* linux getdents64 abi */
uint64_t d_ino; /* inode number */
int64_t d_off; /* implementation-dependent location number */
uint16_t d_reclen; /* byte length of this whole struct and string */
+1 -9
View File
@@ -17,10 +17,7 @@
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/dce.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/errfuns.h"
/**
* Creates symbolic link.
@@ -36,10 +33,5 @@
* @asyncsignalsafe
*/
int symlink(const char *target, const char *linkpath) {
if (!target || !linkpath) return efault();
if (!IsWindows()) {
return symlinkat$sysv(target, AT_FDCWD, linkpath);
} else {
return symlink$nt(target, linkpath);
}
return symlinkat(target, AT_FDCWD, linkpath);
}
@@ -20,10 +20,13 @@
#include "libc/calls/internal.h"
#include "libc/nt/files.h"
textwindows int symlink$nt(const char *target, const char *linkpath) {
char16_t linkpath16[PATH_MAX], target16[PATH_MAX];
uint32_t flags = isdirectory(target) ? kNtSymbolicLinkFlagDirectory : 0;
if (__mkntpath(linkpath, linkpath16) == -1) return -1;
textwindows int symlinkat$nt(const char *target, int newdirfd,
const char *linkpath) {
uint32_t flags;
char16_t target16[PATH_MAX];
char16_t linkpath16[PATH_MAX];
flags = isdirectory(target) ? kNtSymbolicLinkFlagDirectory : 0;
if (__mkntpathat(newdirfd, linkpath, 0, linkpath16) == -1) return -1;
if (__mkntpath(target, target16) == -1) return -1;
if (CreateSymbolicLink(linkpath16, target16, flags)) {
return 0;
+43
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 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/dce.h"
#include "libc/sysv/consts/at.h"
/**
* Creates symbolic link.
*
* This is like link() but adds a tiny indirection to make the fact that
* the file is a link obvious. It also enables certain other features,
* like the ability to be broken.
*
* @param target can be relative and needn't exist
* @param linkpath is what gets created
* @return 0 on success, or -1 w/ errno
* @note Windows NT only lets admins do this
* @asyncsignalsafe
*/
int symlinkat(const char *target, int newdirfd, const char *linkpath) {
if (!IsWindows()) {
return symlinkat$sysv(target, newdirfd, linkpath);
} else {
return symlinkat$nt(target, newdirfd, linkpath);
}
}
-1
View File
@@ -30,6 +30,5 @@
int touch(const char *file, uint32_t mode) {
int fd;
if ((fd = open(file, O_CREAT | O_WRONLY, mode)) == -1) return -1;
fsync(fd); /* TODO(jart): do we need it? */
return close(fd);
}
+2 -11
View File
@@ -16,12 +16,8 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/internal.h"
#include "libc/dce.h"
#include "libc/nt/files.h"
#include "libc/str/str.h"
#include "libc/calls/calls.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/errfuns.h"
/**
* Deletes file.
@@ -35,10 +31,5 @@
* @asyncsignalsafe
*/
int unlink(const char *name) {
if (!name) return efault();
if (!IsWindows()) {
return unlinkat$sysv(AT_FDCWD, name, 0);
} else {
return unlink$nt(name);
}
return unlinkat(AT_FDCWD, name, 0);
}
+65
View File
@@ -0,0 +1,65 @@
/*-*- 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/internal.h"
#include "libc/errno.h"
#include "libc/nt/errors.h"
#include "libc/nt/files.h"
#include "libc/nt/runtime.h"
#include "libc/nt/synchronization.h"
#include "libc/sysv/consts/at.h"
static textwindows int unlink$nt(const char16_t *path) {
if (DeleteFile(path)) {
return 0;
} else {
return __winerr();
}
}
static textwindows int rmdir$nt(const char16_t *path) {
int e, ms;
for (ms = 1;; ms *= 2) {
if (RemoveDirectory(path)) return 0;
/*
* Files can linger, for absolutely no reason.
* Possibly some Windows Defender bug on Win7.
* Sleep for up to one second w/ expo backoff.
* Alternative is use Microsoft internal APIs.
* Never could have imagined it'd be this bad.
*/
if ((e = GetLastError()) == kNtErrorDirNotEmpty && ms <= 512) {
Sleep(ms);
continue;
} else {
break;
}
}
errno = e;
return -1;
}
textwindows int unlinkat$nt(int dirfd, const char *path, int flags) {
uint16_t path16[PATH_MAX];
if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1;
if (flags & AT_REMOVEDIR) {
return rmdir$nt(path16);
} else {
return unlink$nt(path16);
}
}
+14 -4
View File
@@ -18,12 +18,22 @@
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/dce.h"
#include "libc/sysv/consts/at.h"
int unlinkat(int dirfd, const char *pathname, int flags) {
if (dirfd == AT_FDCWD) {
return unlink(pathname);
/**
* Deletes inode and maybe the file too.
*
* @param dirfd is normally AT_FDCWD but if it's an open directory and
* path is relative, then path becomes relative to dirfd
* @param path is the thing to delete
* @param flags can have AT_REMOVEDIR
* @return 0 on success, or -1 w/ errno
*/
int unlinkat(int dirfd, const char *path, int flags) {
if (!IsWindows()) {
return unlinkat$sysv(dirfd, path, flags);
} else {
return unlinkat$sysv(dirfd, pathname, flags);
return unlinkat$nt(dirfd, path, flags);
}
}
+5 -20
View File
@@ -35,25 +35,12 @@ textwindows int utimensat$nt(int dirfd, const char *path,
const struct timespec ts[2], int flags) {
int i, rc;
int64_t fh;
bool closeme;
uint16_t path16[PATH_MAX];
struct NtFileTime ft[2], *ftp[2];
if (flags) return einval();
if (path) {
if (dirfd == AT_FDCWD) {
if (__mkntpath(path, path16) == -1) return -1;
if ((fh = CreateFile(path16, kNtFileWriteAttributes, kNtFileShareRead,
NULL, kNtOpenExisting, kNtFileAttributeNormal, 0)) !=
-1) {
closeme = true;
} else {
return __winerr();
}
} else {
return einval();
}
} else {
return ebadf();
if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1;
if ((fh = CreateFile(path16, kNtFileWriteAttributes, kNtFileShareRead, NULL,
kNtOpenExisting, kNtFileAttributeNormal, 0)) == -1) {
return __winerr();
}
if (!ts || ts[0].tv_nsec == UTIME_NOW || ts[1].tv_nsec == UTIME_NOW) {
GetSystemTimeAsFileTime(ft);
@@ -78,8 +65,6 @@ textwindows int utimensat$nt(int dirfd, const char *path,
} else {
rc = __winerr();
}
if (closeme) {
CloseHandle(fh);
}
CloseHandle(fh);
return rc;
}
+2
View File
@@ -46,6 +46,8 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
const struct StackFrame *frame;
const char *debugbin, *p1, *p2, *p3, *addr2line;
char buf[kBacktraceBufSize], *argv[kBacktraceMaxFrames];
if (IsOpenbsd()) return -1;
if (IsWindows()) return -1;
if (!(debugbin = FindDebugBinary()) || !(addr2line = GetAddr2linePath())) {
return -1;
}
+1
View File
@@ -32,6 +32,7 @@ int64_t LoadLibraryEx(const char16_t *lpLibFileName, int64_t hFile,
uint32_t dwFlags);
uint32_t GetModuleFileName(int64_t hModule, char16_t *lpFilename,
uint32_t nSize);
uint32_t GetModuleFileNameA(int64_t hModule, char *lpFilename, uint32_t nSize);
intptr_t GetModuleHandle(const char *opt_lpModuleName);
intptr_t GetModuleHandleW(const char16_t *opt_lpModuleName);
void *GetProcAddress(int64_t hModule, const char *lpProcName);
+9
View File
@@ -199,6 +199,15 @@ bool32 WriteFileGather(int64_t hFileOpenedWithOverlappedAndNoBuffering,
uint32_t nNumberOfBytesToWrite, uint32_t *lpReserved,
struct NtOverlapped inout_lpOverlapped) paramsnonnull();
#define kNtFileNameNormalized 0x0
#define kNtFileNameOpened 0x8
#define kNtVolumeNameDos 0x0 /* e.g. \\?\C:\Users\jart */
#define kNtVolumeNameGuid 0x1 /* e.g. \\?\Volume{ea38-etc.}\Users\jart */
#define kNtVolumeNameNt 0x2 /* e.g. \Device\HarddiskVolume4\Users\jart */
#define kNtVolumeNameNone 0x4 /* e.g. \Users\jart */
uint32_t GetFinalPathNameByHandle(int64_t hFile, char16_t *out_path,
uint32_t arraylen, uint32_t flags);
#if ShouldUseMsabiAttribute()
#include "libc/nt/thunk/files.inc"
#endif /* ShouldUseMsabiAttribute() */
@@ -1,2 +1,12 @@
.include "o/libc/nt/codegen.inc"
.imp kernel32,__imp_GetFinalPathNameByHandleA,GetFinalPathNameByHandleA,0
.text.windows
GetFinalPathNameByHandleA:
push %rbp
mov %rsp,%rbp
.profilable
mov __imp_GetFinalPathNameByHandleA(%rip),%rax
jmp __sysv2nt
.endfn GetFinalPathNameByHandleA,globl
.previous
@@ -1,2 +1,12 @@
.include "o/libc/nt/codegen.inc"
.imp kernel32,__imp_GetFinalPathNameByHandleW,GetFinalPathNameByHandleW,0
.text.windows
GetFinalPathNameByHandle:
push %rbp
mov %rsp,%rbp
.profilable
mov __imp_GetFinalPathNameByHandleW(%rip),%rax
jmp __sysv2nt
.endfn GetFinalPathNameByHandle,globl
.previous
+2 -2
View File
@@ -2176,8 +2176,8 @@ imp 'GetFileVersionInfoSizeExA' GetFileVersionInfoSizeExA KernelBase 561
imp 'GetFileVersionInfoSizeEx' GetFileVersionInfoSizeExW KernelBase 562
imp 'GetFileVersionInfoSize' GetFileVersionInfoSizeW KernelBase 563
imp 'GetFileVersionInfo' GetFileVersionInfoW KernelBase 564
imp 'GetFinalPathNameByHandleA' GetFinalPathNameByHandleA kernel32 0 # KernelBase
imp 'GetFinalPathNameByHandle' GetFinalPathNameByHandleW kernel32 0 # KernelBase
imp 'GetFinalPathNameByHandleA' GetFinalPathNameByHandleA kernel32 0 4 # KernelBase
imp 'GetFinalPathNameByHandle' GetFinalPathNameByHandleW kernel32 0 4 # KernelBase
imp 'GetFirmwareEnvironmentVariableA' GetFirmwareEnvironmentVariableA kernel32 597
imp 'GetFirmwareEnvironmentVariableExA' GetFirmwareEnvironmentVariableExA kernel32 598
imp 'GetFirmwareEnvironmentVariableEx' GetFirmwareEnvironmentVariableExW kernel32 599
+26 -13
View File
@@ -17,6 +17,7 @@
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/bits/bits.h"
#include "libc/bits/safemacros.h"
#include "libc/calls/calls.h"
#include "libc/errno.h"
#include "libc/macros.h"
@@ -31,19 +32,31 @@
* @return path to debug binary, or -1 w/ errno
*/
const char *FindDebugBinary(void) {
static char buf[PATH_MAX];
if (buf[0]) return &buf[0];
const char *const trybins[] = {program_invocation_name,
(const char *)getauxval(AT_EXECFN)};
for (unsigned i = 0; i < ARRAYLEN(trybins); ++i) {
const char *res = trybins[i];
unsigned len = strlen(res);
if (4 < len && len < sizeof(buf) - 5) {
if (strcmp(res + len - 4, ".dbg") == 0) return res;
/* try suffixing extension, e.g. .com → .com.dbg */
memcpy(mempcpy(buf, res, len), ".dbg", 5);
if (fileexists(buf)) return &buf[0];
buf[0] = '\0';
unsigned i, len;
char buf[2][PATH_MAX];
static char res[PATH_MAX];
const char *bins[4], *pwd;
bins[0] = program_invocation_name;
bins[1] = (const char *)getauxval(AT_EXECFN);
pwd = emptytonull(getenv("PWD"));
for (i = 0; i < 2; ++i) {
if (pwd && bins[i] && bins[i][0] != '/' && bins[i][0] != '\\' &&
strlen(pwd) + 1 + strlen(bins[i]) + 1 <= ARRAYLEN(buf[0])) {
strcpy(buf[i], pwd);
strcat(buf[i], "/");
strcat(buf[i], bins[i]);
bins[i + 2] = buf[i];
}
}
for (i = 0; i < 4; ++i) {
if (!bins[i]) continue;
len = strlen(bins[i]);
memcpy(res, bins[i], len + 1);
if (!endswith(res, ".dbg") && len + 3 + 1 <= ARRAYLEN(res)) {
strcat(res, ".dbg");
}
if (fileexists(res)) {
return res;
}
}
errno = ENOENT;
+4 -2
View File
@@ -49,6 +49,8 @@
* TODO: How can we ensure we never overlap with KERNEL32.DLL?
*/
#define WINSTACK 0x100000
struct WinArgs {
char *argv[4096];
char *envp[4096];
@@ -107,7 +109,7 @@ static textwindows wontreturn void WinMainNew(void) {
NormalizeCmdExe();
*(/*unconst*/ int *)&__hostos = WINDOWS;
addr = NtGetVersion() < kNtVersionWindows10 ? 0xff00000 : 0x777000000000;
size = ROUNDUP(STACKSIZE + sizeof(struct WinArgs), FRAMESIZE);
size = ROUNDUP(WINSTACK + sizeof(struct WinArgs), FRAMESIZE);
_mmi.p[0].h =
__mmap$nt((char *)addr, size, PROT_READ | PROT_WRITE | PROT_EXEC, -1, 0)
.maphandle;
@@ -128,7 +130,7 @@ static textwindows wontreturn void WinMainNew(void) {
FreeEnvironmentStrings(env16);
auxv[0][0] = pushpop(0L);
auxv[0][1] = pushpop(0L);
_jmpstack((char *)addr + STACKSIZE, _executive, count, wa->argv, wa->envp,
_jmpstack((char *)addr + WINSTACK, _executive, count, wa->argv, wa->envp,
auxv);
}
-2
View File
@@ -52,8 +52,6 @@ int putchar(int);
int puts(const char *) paramsnonnull();
ssize_t getline(char **, size_t *, FILE *) paramsnonnull();
ssize_t getdelim(char **, size_t *, int, FILE *) paramsnonnull();
int fputhex(int, FILE *) paramsnonnull();
int fgethex(FILE *) paramsnonnull();
FILE *fopen(const char *, const char *) paramsnonnull() nodiscard;
FILE *fdopen(int, const char *) paramsnonnull() nodiscard;
FILE *fmemopen(void *, size_t, const char *) paramsnonnull((3)) nodiscard;
+1 -1
View File
@@ -24,7 +24,7 @@
* This API was thought to be nearly extinct until recent versions
* of Clang (c. 2019) started generating synthetic calls to it.
*
* @return unsigned char subtraction at stop index
* @return 0 if a and b have equal contents, otherwise non-zero
* @asyncsignalsafe
*/
int bcmp(const void *a, const void *b, size_t n) {
+3
View File
@@ -151,10 +151,13 @@ int strcasecmpzbw(const uint16_t *, const char *) strlenesque;
char *stpcpy(char *, const char *) memcpyesque;
char *stpncpy(char *, const char *, size_t) memcpyesque;
char *strcat(char *, const char *) memcpyesque;
char16_t *strcat16(char16_t *, const char16_t *) memcpyesque;
wchar_t *wcscat(wchar_t *, const wchar_t *) memcpyesque;
size_t strlcpy(char *, const char *, size_t);
size_t strlcat(char *, const char *, size_t);
char *strcpy(char *, const char *) memcpyesque;
char16_t *strcpy16(char16_t *, const char16_t *) memcpyesque;
wchar_t *wcscpy(wchar_t *, const wchar_t *) memcpyesque;
char *strncat(char *, const char *, size_t) memcpyesque;
char *strncpy(char *, const char *, size_t) memcpyesque;
char *strtok(char *, const char *) paramsnonnull((2)) libcesque;
+11 -11
View File
@@ -16,16 +16,16 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/internal.h"
#include "libc/nt/files.h"
#include "libc/nt/runtime.h"
#include "libc/str/str.h"
textwindows int unlink$nt(const char *name) {
uint16_t name16[PATH_MAX];
if (__mkntpath(name, name16) == -1) return -1;
if (DeleteFile(name16)) {
return 0;
} else {
return __winerr();
}
/**
* Appends 𝑠 to 𝑑.
*
* @param 𝑑 is a NUL-terminated 16-bit string buffer
* @param 𝑠 is a NUL-terminated 16-bit string
* @return 𝑑
* @asyncsignalsafe
*/
char16_t *strcat16(char16_t *d, const char16_t *s) {
return strcpy16(d + strlen16(d), s);
}
-1
View File
@@ -18,7 +18,6 @@
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/intrin/pcmpeqb.h"
#include "libc/intrin/pmovmskb.h"
#include "libc/limits.h"
#include "libc/str/str.h"
/**
+1 -1
View File
@@ -28,5 +28,5 @@
* @return original dest
*/
char16_t *strcpy16(char16_t *dest, const char16_t *src) {
return memcpy(dest, src, (strlen16(src) + 1) << 1);
return memcpy(dest, src, (strlen16(src) + 1) * sizeof(char16_t));
}
+11 -6
View File
@@ -16,11 +16,16 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
int fputhex(int c, FILE *f) {
return (fputc("0123456789ABCDEF"[(c / 16) & 0xF], f) >= 0 &&
fputc("0123456789ABCDEF"[(c % 16) & 0xF], f) >= 0)
? c
: -1;
/**
* Appends 𝑠 to 𝑑.
*
* @param 𝑑 is a NUL-terminated 32-bit string
* @param 𝑠 is a NUL-terminated 32-bit string
* @return 𝑑
* @asyncsignalsafe
*/
wchar_t *wcscat(wchar_t *d, const wchar_t *s) {
return wcscpy(d + wcslen(d), s);
}
+13 -11
View File
@@ -16,16 +16,18 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/internal.h"
#include "libc/nt/files.h"
#include "libc/nt/runtime.h"
#include "libc/str/str.h"
textwindows int rmdir$nt(const char *path) {
uint16_t path16[PATH_MAX];
if (__mkntpath(path, path16) == -1) return -1;
if (RemoveDirectory(path16)) {
return 0;
} else {
return __winerr();
}
/**
* Copies NUL-terminated wide character string.
*
* 𝑑 and 𝑠 must not overlap unless 𝑑 ≤ 𝑠.
*
* @param 𝑑 is destination memory
* @param 𝑠 is a NUL-terminated string
* @return original dest
* @asyncsignalsafe
*/
wchar_t *wcscpy(wchar_t *d, const wchar_t *s) {
return memcpy(d, s, (wcslen(s) + 1) * sizeof(wchar_t));
}
+2
View File
@@ -0,0 +1,2 @@
.include "o/libc/sysv/macros.internal.inc"
.scall fchdir$sysv 0x000d000d200d0051 globl hidden
-2
View File
@@ -1,2 +0,0 @@
.include "o/libc/sysv/macros.internal.inc"
.scall fchdir 0x000d000d200d0051 globl
+1 -1
View File
@@ -1,2 +1,2 @@
.include "o/libc/sysv/macros.internal.inc"
.scall mknodat 0x014022ffffff0103 globl
.scall mknodat 0x0140022fffff0103 globl
+2
View File
@@ -0,0 +1,2 @@
.include "o/libc/sysv/macros.internal.inc"
.scall posix_spawn$sysv 0xffffffff20f4ffff globl hidden
-2
View File
@@ -1,2 +0,0 @@
.include "o/libc/sysv/macros.internal.inc"
.scall posix_spawn 0xffffffff20f4ffff globl hidden
+3 -15
View File
@@ -25,7 +25,7 @@ dir=libc/sysv/consts
# group name GNU/Systemd XNU's Not UNIX FreeBSD OpenBSD XENIX Commentary
syscon errno ENOSYS 38 78 78 78 1 # bsd consensus & kNtErrorInvalidFunction
syscon errno EPERM 1 1 1 1 12 # unix consensus & kNtErrorInvalidAccess (should be kNtErrorNotOwner but is that mutex only??)
syscon errno ENOENT 2 2 2 2 2 # unix consensus & kNtErrorFileNot_FOUND
syscon errno ENOENT 2 2 2 2 2 # unix consensus & kNtErrorFileNotFound
syscon errno ESRCH 3 3 3 3 566 # "no such process" & kNtErrorThreadNotInProcess (cf. kNtErrorInvalidHandle)
syscon errno EINTR 4 4 4 4 10004 # unix consensus & WSAEINTR
syscon errno EIO 5 5 5 5 1117 # unix consensus & kNtErrorIoDevice
@@ -44,7 +44,7 @@ syscon errno EBUSY 16 16 16 16 170 # unix consensus & kNtErrorBusy
syscon errno EEXIST 17 17 17 17 183 # unix consensus & kNtErrorAlreadyExists (should be kNtErrorFileExists too)
syscon errno EXDEV 18 18 18 18 17 # unix consensus & kNtErrorNotSameDevice
syscon errno ENODEV 19 19 19 19 1200 # unix consensus & kNtErrorBadDevice
syscon errno ENOTDIR 20 20 20 20 3 # unix consensus & kNtErrorPathNotFound (TODO)
syscon errno ENOTDIR 20 20 20 20 3 # unix consensus & kNtErrorPathNotFound
syscon errno EISDIR 21 21 21 21 267 # unix consensus & kNtErrorDirectoryNotSupported
syscon errno EINVAL 22 22 22 22 87 # unix consensus & kNtErrorInvalidParameter
syscon errno ENFILE 23 23 23 23 331 # unix consensus & kNtErrorTooManyDescriptors
@@ -63,7 +63,7 @@ syscon errno ERANGE 34 34 34 34 -1 # bsd consensus
syscon errno EDEADLK 35 11 11 11 1131 # bsd consensus & kNtErrorPossibleDeadlock
syscon errno ENAMETOOLONG 36 63 63 63 0x274f # bsd consensus & WSAENAMETOOLONG
syscon errno ENOLCK 37 77 77 77 -1 # bsd consensus
syscon errno ENOTEMPTY 39 66 66 66 0x2752 # bsd consensus & WSAENOTEMPTY
syscon errno ENOTEMPTY 39 66 66 66 145 # bsd consensus & kNtErrorDirNotEmpty (TODO: What is WSAENOTEMPTY? 0x2752)
syscon errno ELOOP 40 62 62 62 0x274e # bsd consensus & WSAELOOP
syscon errno ENOMSG 42 91 83 90 -1
syscon errno EIDRM 43 90 82 89 -1
@@ -280,18 +280,6 @@ syscon madv MADV_HWPOISON 100 0 0 0 0 # TODO(jart): what is it?
syscon madv MADV_REMOVE 9 0 0 0 0 # TODO(jart): what is it?
syscon fadv POSIX_FADV_NOREUSE 5 0 5 0 0 # wut
# posix_spawn()
# only native on xnu
#
# group name GNU/Systemd XNU's Not UNIX FreeBSD OpenBSD XENIX Commentary
syscon spawn POSIX_SPAWN_RESETIDS 1 1 1 1 0 # unix consensus
syscon spawn POSIX_SPAWN_SETPGROUP 2 2 2 2 0 # unix consensus
syscon spawn POSIX_SPAWN_SETSIGDEF 4 4 0x10 0x10 0
syscon spawn POSIX_SPAWN_SETSIGMASK 8 8 0x20 0x20 0
syscon spawn POSIX_SPAWN_SETSCHEDPARAM 0x10 0 4 4 0
syscon spawn POSIX_SPAWN_SETSCHEDULER 0x20 0 8 8 0
syscon spawn POSIX_SPAWN_USEVFORK 0x40 0 0 0 0
# mmap(), mprotect(), etc.
# digital restrictions management for the people
#
+1 -1
View File
@@ -1,2 +1,2 @@
.include "libc/sysv/consts/syscon.inc"
.syscon errno ENOTEMPTY 39 66 66 66 0x2752
.syscon errno ENOTEMPTY 39 66 66 66 145
-2
View File
@@ -1,2 +0,0 @@
.include "libc/sysv/consts/syscon.inc"
.syscon spawn POSIX_SPAWN_RESETIDS 1 1 1 1 0
-2
View File
@@ -1,2 +0,0 @@
.include "libc/sysv/consts/syscon.inc"
.syscon spawn POSIX_SPAWN_SETPGROUP 2 2 2 2 0
@@ -1,2 +0,0 @@
.include "libc/sysv/consts/syscon.inc"
.syscon spawn POSIX_SPAWN_SETSCHEDPARAM 0x10 0 4 4 0
@@ -1,2 +0,0 @@
.include "libc/sysv/consts/syscon.inc"
.syscon spawn POSIX_SPAWN_SETSCHEDULER 0x20 0 8 8 0
-2
View File
@@ -1,2 +0,0 @@
.include "libc/sysv/consts/syscon.inc"
.syscon spawn POSIX_SPAWN_SETSIGDEF 4 4 0x10 0x10 0
@@ -1,2 +0,0 @@
.include "libc/sysv/consts/syscon.inc"
.syscon spawn POSIX_SPAWN_SETSIGMASK 8 8 0x20 0x20 0
-2
View File
@@ -1,2 +0,0 @@
.include "libc/sysv/consts/syscon.inc"
.syscon spawn POSIX_SPAWN_USEVFORK 0x40 0 0 0 0
+13 -27
View File
@@ -1,26 +1,6 @@
#ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS_POSIX_H_
#define COSMOPOLITAN_LIBC_SYSV_CONSTS_POSIX_H_
#include "libc/runtime/symbolic.h"
#define POSIX_FADV_DONTNEED SYMBOLIC(POSIX_FADV_DONTNEED)
#define POSIX_FADV_NOREUSE SYMBOLIC(POSIX_FADV_NOREUSE)
#define POSIX_FADV_NORMAL SYMBOLIC(POSIX_FADV_NORMAL)
#define POSIX_FADV_RANDOM SYMBOLIC(POSIX_FADV_RANDOM)
#define POSIX_FADV_SEQUENTIAL SYMBOLIC(POSIX_FADV_SEQUENTIAL)
#define POSIX_FADV_WILLNEED SYMBOLIC(POSIX_FADV_WILLNEED)
#define POSIX_MADV_DONTNEED SYMBOLIC(POSIX_MADV_DONTNEED)
#define POSIX_MADV_NORMAL SYMBOLIC(POSIX_MADV_NORMAL)
#define POSIX_MADV_RANDOM SYMBOLIC(POSIX_MADV_RANDOM)
#define POSIX_MADV_SEQUENTIAL SYMBOLIC(POSIX_MADV_SEQUENTIAL)
#define POSIX_MADV_WILLNEED SYMBOLIC(POSIX_MADV_WILLNEED)
#define POSIX_SPAWN_RESETIDS SYMBOLIC(POSIX_SPAWN_RESETIDS)
#define POSIX_SPAWN_SETPGROUP SYMBOLIC(POSIX_SPAWN_SETPGROUP)
#define POSIX_SPAWN_SETSCHEDPARAM SYMBOLIC(POSIX_SPAWN_SETSCHEDPARAM)
#define POSIX_SPAWN_SETSCHEDULER SYMBOLIC(POSIX_SPAWN_SETSCHEDULER)
#define POSIX_SPAWN_SETSIGDEF SYMBOLIC(POSIX_SPAWN_SETSIGDEF)
#define POSIX_SPAWN_SETSIGMASK SYMBOLIC(POSIX_SPAWN_SETSIGMASK)
#define POSIX_SPAWN_USEVFORK SYMBOLIC(POSIX_SPAWN_USEVFORK)
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
@@ -35,14 +15,20 @@ hidden extern const long POSIX_MADV_NORMAL;
hidden extern const long POSIX_MADV_RANDOM;
hidden extern const long POSIX_MADV_SEQUENTIAL;
hidden extern const long POSIX_MADV_WILLNEED;
hidden extern const long POSIX_SPAWN_RESETIDS;
hidden extern const long POSIX_SPAWN_SETPGROUP;
hidden extern const long POSIX_SPAWN_SETSCHEDPARAM;
hidden extern const long POSIX_SPAWN_SETSCHEDULER;
hidden extern const long POSIX_SPAWN_SETSIGDEF;
hidden extern const long POSIX_SPAWN_SETSIGMASK;
hidden extern const long POSIX_SPAWN_USEVFORK;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#define POSIX_FADV_DONTNEED SYMBOLIC(POSIX_FADV_DONTNEED)
#define POSIX_FADV_NOREUSE SYMBOLIC(POSIX_FADV_NOREUSE)
#define POSIX_FADV_NORMAL SYMBOLIC(POSIX_FADV_NORMAL)
#define POSIX_FADV_RANDOM SYMBOLIC(POSIX_FADV_RANDOM)
#define POSIX_FADV_SEQUENTIAL SYMBOLIC(POSIX_FADV_SEQUENTIAL)
#define POSIX_FADV_WILLNEED SYMBOLIC(POSIX_FADV_WILLNEED)
#define POSIX_MADV_DONTNEED SYMBOLIC(POSIX_MADV_DONTNEED)
#define POSIX_MADV_NORMAL SYMBOLIC(POSIX_MADV_NORMAL)
#define POSIX_MADV_RANDOM SYMBOLIC(POSIX_MADV_RANDOM)
#define POSIX_MADV_SEQUENTIAL SYMBOLIC(POSIX_MADV_SEQUENTIAL)
#define POSIX_MADV_WILLNEED SYMBOLIC(POSIX_MADV_WILLNEED)
#endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_POSIX_H_ */
+3 -3
View File
@@ -91,7 +91,7 @@ scall 'setsockopt$sysv' 0x0069006920690036 globl hidden
scall 'getsockopt$sysv' 0x0076007620760037 globl hidden
scall '__fork$sysv' 0x0002000220020039 globl hidden # xnu needs eax=~-edx b/c eax always holds pid and edx is 0 for parent and 1 for child
#scall vfork 0x004200422042003a globl # this syscall is from the moon so we implement it by hand in libc/calls/hefty/vfork.S
scall posix_spawn 0xffffffff20f4ffff globl hidden # TODO: put in spawnve()
scall 'posix_spawn$sysv' 0xffffffff20f4ffff globl hidden # good luck figuring out how xnu defines this
scall '__execve$sysv' 0x003b003b203b003b globl hidden
scall 'wait4$sysv' 0x000b00072007003d globl hidden
scall 'kill$sysv' 0x007a00252025003e globl hidden # kill(pid, sig, 1) b/c xnu
@@ -118,7 +118,7 @@ scall '__truncate$sysv' 0x00c801df20c8004c globl hidden # openbsd:pad
scall '__ftruncate$sysv' 0x00c901e020c9004d globl hidden # openbsd:pad
scall 'getcwd$sysv' 0x01300146ffff004f globl hidden
scall 'chdir$sysv' 0x000c000c200c0050 globl hidden
scall fchdir 0x000d000d200d0051 globl
scall 'fchdir$sysv' 0x000d000d200d0051 globl hidden
scall 'rename$sysv' 0x0080008020800052 globl hidden
scall 'mkdir$sysv' 0x0088008820880053 globl hidden
scall 'rmdir$sysv' 0x0089008920890054 globl hidden
@@ -164,7 +164,7 @@ scall sigpending 0x003400342034007f globl
scall 'sigsuspend$sysv' 0x006f0155206f0082 globl hidden
scall sigaltstack 0x0120003520350083 globl
scall 'mknod$sysv' 0x000e000e200e0085 globl hidden
scall mknodat 0x014022ffffff0103 globl # FreeBSD 12+
scall mknodat 0x0140022fffff0103 globl # FreeBSD 12+
scall 'mkfifo$sysv' 0x008400842084ffff globl hidden
scall mkfifoat 0x013f01f1ffffffff globl
scall statfs 0x003f022b21590089 globl
+47
View File
@@ -0,0 +1,47 @@
/*-*- 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/errno.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h"
#include "libc/x/x.h"
/**
* Recursively creates directory a.k.a. folder.
*
* @param path is a UTF-8 string, preferably relative w/ forward slashes
* @param mode can be, for example, 0755
* @return 0 on success or -1 w/ errno
* @see mkdir()
*/
int makedirs(const char *path, unsigned mode) {
int rc;
char *dir;
if (mkdir(path, mode) != -1) return 0;
if (errno != ENOENT) return -1;
dir = xdirname(path);
if (strcmp(dir, path)) {
rc = makedirs(dir, mode);
} else {
rc = -1;
}
free(dir);
if (rc == -1) return -1;
return mkdir(path, mode);
}
+65
View File
@@ -0,0 +1,65 @@
/*-*- 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/dirent.h"
#include "libc/calls/struct/stat.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/dt.h"
#include "libc/x/x.h"
static int rmrfdir(const char *dirpath) {
int rc;
DIR *d;
char *path;
struct dirent *e;
if (!(d = opendir(dirpath))) return -1;
while ((e = readdir(d))) {
if (!strcmp(e->d_name, ".")) continue;
if (!strcmp(e->d_name, "..")) continue;
if (strchr(e->d_name, '/')) abort();
path = xjoinpaths(dirpath, e->d_name);
if (e->d_type == DT_DIR) {
rc = rmrfdir(path);
} else {
rc = unlink(path);
}
if (rc == -1) {
free(path);
closedir(d);
return -1;
}
free(path);
}
rc = closedir(d);
rc |= rmdir(dirpath);
return rc;
}
/**
* Recursively removes file or directory.
*/
int rmrf(const char *path) {
struct stat st;
if (stat(path, &st) == -1) return -1;
if (!S_ISDIR(st.st_mode)) return unlink(path);
return rmrfdir(path);
}
+8 -1
View File
@@ -45,9 +45,16 @@ char *xstrdup(const char *) _XPNN _XMAL;
char *xstrndup(const char *, size_t) _XPNN _XMAL;
char *xstrcat(const char *, ...) paramsnonnull((1)) nullterminated() _XMAL;
char *xstrmul(const char *, size_t) paramsnonnull((1)) _XMAL;
char *xinet_ntop(int, const void *) _XPNN _XMAL;
/*───────────────────────────────────────────────────────────────────────────│─╗
│ cosmopolitan § eXtended apis » files ─╬─│┼
╚────────────────────────────────────────────────────────────────────────────│*/
int rmrf(const char *);
int makedirs(const char *, unsigned);
char *xdirname(const char *) paramsnonnull() _XMAL;
char *xjoinpaths(const char *, const char *) paramsnonnull() _XMAL;
char *xinet_ntop(int, const void *) _XPNN _XMAL;
/*───────────────────────────────────────────────────────────────────────────│─╗
│ cosmopolitan § eXtended apis » time ─╬─│┼
+1
View File
@@ -34,6 +34,7 @@ LIBC_X_A_CHECKS = \
LIBC_X_A_DIRECTDEPS = \
LIBC_CALLS \
LIBC_CALLS_HEFTY \
LIBC_FMT \
LIBC_INTRIN \
LIBC_MEM \
+56 -29
View File
@@ -16,52 +16,79 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/bits/safemacros.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/dirent.h"
#include "libc/calls/struct/stat.h"
#include "libc/dce.h"
#include "libc/fmt/fmt.h"
#include "libc/log/check.h"
#include "libc/mem/mem.h"
#include "libc/runtime/gc.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/dt.h"
#include "libc/testlib/testlib.h"
#include "libc/x/x.h"
uint64_t i;
char pathbuf[PATH_MAX];
const char *oldpath, *bindir, *homedir, *binsh, *sh;
char pathbuf[PATH_MAX], testdir[PATH_MAX], *oldpath;
TEST(commandv_00, todo) { /* TODO(jart): Improve this on Windows. */
if (IsWindows()) exit(0);
void SetUp(void) {
sprintf(testdir, "o/tmp/%s.%d", program_invocation_short_name, getpid());
makedirs(testdir, 0755);
CHECK_NE(-1, chdir(testdir));
mkdir("bin", 0755);
mkdir("home", 0755);
oldpath = strdup(nulltoempty(getenv("PATH")));
CHECK_NE(-1, setenv("PATH", "bin", true));
}
TEST(commandv_001, setupFiles) {
mkdir("o", 0755);
mkdir("o/tmp", 0755);
oldpath = strdup(getenv("PATH"));
homedir = xasprintf("o/tmp/home.%d", getpid());
bindir = xasprintf("o/tmp/bin.%d", getpid());
binsh = xasprintf("%s/sh.com", bindir);
ASSERT_NE(-1, mkdir(homedir, 0755));
ASSERT_NE(-1, mkdir(bindir, 0755));
ASSERT_NE(-1, touch(binsh, 0755));
ASSERT_NE(-1, setenv("PATH", bindir, true));
void TearDown(void) {
CHECK_NE(-1, setenv("PATH", oldpath, true));
CHECK_NE(-1, chdir("../../.."));
CHECK_NE(-1, rmrf(testdir));
}
TEST(commandv_010, testSlashes_wontSearchPath_butChecksAccess) {
sh = defer(unlink, gc(xasprintf("%s/sh.com", homedir)));
EXPECT_NE(-1, touch(sh, 0755));
TEST(commandv, testPathSearch) {
EXPECT_NE(-1, touch("bin/sh", 0755));
EXPECT_STREQ("bin/sh", commandv("sh", pathbuf));
}
TEST(commandv, testPathSearch_appendsComExtension) {
EXPECT_NE(-1, touch("bin/sh.com", 0755));
EXPECT_STREQ("bin/sh.com", commandv("sh", pathbuf));
}
TEST(commandv, testSlashes_wontSearchPath_butChecksAccess) {
EXPECT_NE(-1, touch("home/sh", 0755));
i = g_syscount;
EXPECT_STREQ(sh, commandv(sh, pathbuf));
if (!IsWindows()) EXPECT_EQ(i + 1 /* access() */, g_syscount);
EXPECT_STREQ("home/sh", commandv("home/sh", pathbuf));
if (!IsWindows()) EXPECT_EQ(i + 1, g_syscount);
}
TEST(commandv_999, teardown) {
setenv("PATH", oldpath, true);
unlink(binsh);
rmdir(bindir);
rmdir(homedir);
free(bindir);
free(binsh);
free(homedir);
free(oldpath);
TEST(commandv, testSlashes_wontSearchPath_butStillAppendsComExtension) {
EXPECT_NE(-1, touch("home/sh.com", 0755));
i = g_syscount;
EXPECT_STREQ("home/sh.com", commandv("home/sh", pathbuf));
if (!IsWindows()) EXPECT_EQ(i + 2, g_syscount);
}
TEST(commandv, testSameDir_doesntHappenByDefault) {
EXPECT_NE(-1, touch("bog", 0755));
EXPECT_EQ(NULL, commandv("bog", pathbuf));
}
TEST(commandv, testSameDir_willHappenWithColonBlank) {
CHECK_NE(-1, setenv("PATH", "bin:", true));
EXPECT_NE(-1, touch("bog", 0755));
EXPECT_STREQ("bog", commandv("bog", pathbuf));
}
TEST(commandv, testSameDir_willHappenWithColonBlank2) {
CHECK_NE(-1, setenv("PATH", ":bin", true));
EXPECT_NE(-1, touch("bog", 0755));
EXPECT_STREQ("bog", commandv("bog", pathbuf));
}
+55
View File
@@ -0,0 +1,55 @@
/*-*- 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/errno.h"
#include "libc/fmt/conv.h"
#include "libc/fmt/fmt.h"
#include "libc/log/check.h"
#include "libc/macros.h"
#include "libc/runtime/gc.h"
#include "libc/testlib/testlib.h"
#include "libc/x/x.h"
char basedir[PATH_MAX];
char testdir[PATH_MAX];
void SetUp(void) {
getcwd(basedir, ARRAYLEN(basedir));
sprintf(testdir, "o/tmp/%s.%d", program_invocation_short_name, getpid());
makedirs(testdir, 0755);
CHECK_NE(-1, chdir(testdir));
}
void TearDown(void) {
CHECK_NE(-1, chdir(basedir));
CHECK_NE(-1, rmrf(testdir));
}
TEST(getcwd, test) {
char buf[PATH_MAX];
EXPECT_NE(-1, mkdir("subdir", 0755));
EXPECT_NE(-1, chdir("subdir"));
EXPECT_STREQ("subdir", basename(getcwd(buf, ARRAYLEN(buf))));
}
TEST(getcwd, testNullBuf_allocatesResult) {
EXPECT_NE(-1, mkdir("subdir", 0755));
EXPECT_NE(-1, chdir("subdir"));
EXPECT_STREQ("subdir", basename(gc(getcwd(NULL, 0))));
}
+55
View File
@@ -0,0 +1,55 @@
/*-*- 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/fmt/fmt.h"
#include "libc/log/check.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/o.h"
#include "libc/testlib/testlib.h"
#include "libc/x/x.h"
char testdir[PATH_MAX];
void SetUp(void) {
sprintf(testdir, "o/tmp/%s.%d", program_invocation_short_name, getpid());
makedirs(testdir, 0755);
CHECK_NE(-1, chdir(testdir));
}
void TearDown(void) {
CHECK_NE(-1, chdir("../../.."));
CHECK_NE(-1, rmrf(testdir));
}
TEST(lseek, wat) {
int fd, pid;
char buf[8] = {0};
ASSERT_NE(-1, (fd = open("wut", O_RDWR | O_CREAT, 0644)));
ASSERT_EQ(3, write(fd, "wut", 3));
ASSERT_NE(-1, lseek(fd, 0, SEEK_SET));
if (!(pid = fork())) {
lseek(fd, 1, SEEK_SET);
_exit(0);
}
EXPECT_NE(-1, waitpid(pid, 0, 0));
EXPECT_EQ(1, read(fd, buf, 1));
EXPECT_EQ('u', buf[0]); /* wat?! */
EXPECT_NE(-1, close(fd));
}
+81
View File
@@ -0,0 +1,81 @@
/*-*- 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/errno.h"
#include "libc/fmt/fmt.h"
#include "libc/log/check.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/o.h"
#include "libc/testlib/testlib.h"
#include "libc/x/x.h"
char testdir[PATH_MAX];
void SetUp(void) {
sprintf(testdir, "o/tmp/%s.%d", program_invocation_short_name, getpid());
makedirs(testdir, 0755);
CHECK_NE(-1, chdir(testdir));
}
void TearDown(void) {
CHECK_NE(-1, chdir("../../.."));
CHECK_NE(-1, rmrf(testdir));
}
TEST(mkdir, testNothingExists_ENOENT) {
EXPECT_EQ(-1, mkdir("yo/yo/yo", 0755));
EXPECT_EQ(ENOENT, errno);
}
TEST(mkdir, testDirectoryComponentIsFile_ENOTDIR) {
EXPECT_NE(-1, touch("yo", 0644));
EXPECT_EQ(-1, mkdir("yo/yo/yo", 0755));
EXPECT_EQ(ENOTDIR, errno);
}
TEST(mkdir, testPathIsFile_EEXIST) {
EXPECT_NE(-1, mkdir("yo", 0755));
EXPECT_NE(-1, mkdir("yo/yo", 0755));
EXPECT_NE(-1, touch("yo/yo/yo", 0644));
EXPECT_EQ(-1, mkdir("yo/yo/yo", 0755));
EXPECT_EQ(EEXIST, errno);
}
TEST(mkdir, testPathIsDirectory_EEXIST) {
EXPECT_NE(-1, mkdir("yo", 0755));
EXPECT_NE(-1, mkdir("yo/yo", 0755));
EXPECT_NE(-1, mkdir("yo/yo/yo", 0755));
EXPECT_EQ(-1, mkdir("yo/yo/yo", 0755));
EXPECT_EQ(EEXIST, errno);
}
TEST(makedirs, testEmptyString_ENOENT) {
EXPECT_EQ(-1, makedirs("", 0755));
EXPECT_EQ(ENOENT, errno);
}
TEST(mkdirat, testRelativePath_opensRelativeToDirFd) {
int dirfd;
ASSERT_NE(-1, mkdir("foo", 0755));
ASSERT_NE(-1, (dirfd = open("foo", O_RDONLY | O_DIRECTORY)));
EXPECT_NE(-1, mkdirat(dirfd, "bar", 0755));
EXPECT_TRUE(isdirectory("foo/bar"));
EXPECT_EQ(-1, makedirs("", 0755));
EXPECT_NE(-1, close(dirfd));
}
+9 -9
View File
@@ -27,49 +27,49 @@ char16_t cmdline[ARG_MAX];
TEST(mkntcmdline, emptyArgvList_isEmpty) {
char *argv[] = {NULL};
EXPECT_NE(-1, mkntcmdline(cmdline, argv));
EXPECT_NE(-1, mkntcmdline(cmdline, argv[0], argv));
EXPECT_STREQ(u"", cmdline);
}
TEST(mkntcmdline, emptyArg_getsQuoted) {
char *argv[] = {"", NULL};
EXPECT_NE(-1, mkntcmdline(cmdline, argv));
EXPECT_NE(-1, mkntcmdline(cmdline, argv[0], argv));
EXPECT_STREQ(u"\"\"", cmdline);
}
TEST(mkntcmdline, ignoranceIsBliss) {
char *argv[] = {"echo", "hello", "world", NULL};
EXPECT_NE(-1, mkntcmdline(cmdline, argv));
EXPECT_NE(-1, mkntcmdline(cmdline, argv[0], argv));
EXPECT_STREQ(u"echo hello world", cmdline);
}
TEST(mkntcmdline, spaceInArgument_getQuotesWrappedAround) {
char *argv[] = {"echo", "hello there", "world", NULL};
EXPECT_NE(-1, mkntcmdline(cmdline, argv));
EXPECT_NE(-1, mkntcmdline(cmdline, argv[0], argv));
EXPECT_STREQ(u"echo \"hello there\" world", cmdline);
}
TEST(mkntcmdline, justQuote) {
char *argv[] = {"\"", NULL};
EXPECT_NE(-1, mkntcmdline(cmdline, argv));
EXPECT_NE(-1, mkntcmdline(cmdline, argv[0], argv));
EXPECT_STREQ(u"\"\\\"\"", cmdline);
}
TEST(mkntcmdline, justSlash) {
char *argv[] = {"\\", NULL};
EXPECT_NE(-1, mkntcmdline(cmdline, argv));
EXPECT_NE(-1, mkntcmdline(cmdline, argv[0], argv));
EXPECT_STREQ(u"\\", cmdline);
}
TEST(mkntcmdline, justSlashQuote) {
char *argv[] = {"\\\"", NULL};
EXPECT_NE(-1, mkntcmdline(cmdline, argv));
EXPECT_NE(-1, mkntcmdline(cmdline, argv[0], argv));
EXPECT_STREQ(u"\"\\\\\\\"\"" /* "\\\"" */, cmdline);
}
TEST(mkntcmdline, basicQuoting) {
char *argv[] = {"a\"b c", "d", NULL};
EXPECT_NE(-1, mkntcmdline(cmdline, argv));
EXPECT_NE(-1, mkntcmdline(cmdline, argv[0], argv));
EXPECT_STREQ(u"\"a\\\"b c\" d" /* "a\"b c" d */, cmdline);
}
@@ -79,7 +79,7 @@ TEST(mkntcmdline, testUnicode) {
strdup("要依法治国是赞美那些谁是公义的和惩罚恶人。 - 韩非"),
NULL,
};
EXPECT_NE(-1, mkntcmdline(cmdline, argv1));
EXPECT_NE(-1, mkntcmdline(cmdline, argv1[0], argv1));
EXPECT_STREQ(u"(╯°□°)╯ \"要依法治国是赞美那些谁是公义的和惩罚恶人。 - 韩非\"",
cmdline);
}
+1
View File
@@ -30,4 +30,5 @@ TEST(dirname, test) {
EXPECT_STREQ(".", dirname(gc(strdup("hello"))));
EXPECT_STREQ(".", dirname(gc(strdup("."))));
EXPECT_STREQ(".", dirname(gc(strdup(".."))));
EXPECT_STREQ("", dirname(gc(strdup(""))));
}
@@ -1,7 +1,7 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2020 Justine Alexandra Roberts Tunney │
│ Copyright 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 │
@@ -16,24 +16,20 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/errfuns.h"
#include "libc/testlib/ezbench.h"
#include "libc/testlib/testlib.h"
int fgethex(FILE *f) {
int o, t = -1;
while (!((o = fgetc(f)) & ~0xFF)) {
switch (t) {
case -1:
t = isxdigit(o) ? hextoint(o) : -1;
break;
default:
if (isxdigit(o)) {
return t * 16 + hextoint(o);
}
break;
}
}
if (t >= 0) return einval();
return -1;
TEST(strcasecmp, test) {
EXPECT_EQ(0, strcasecmp("HELLO", "hello"));
EXPECT_EQ(-17, strcasecmp("HELLO", "yello"));
EXPECT_EQ(-17, strcasecmp("HELLO", "YELLO"));
EXPECT_EQ(+17, strcasecmp("yello", "HELLO"));
EXPECT_EQ(+17, strcasecmp("YELLO", "HELLO"));
}
BENCH(strcasecmp, bench) {
EZBENCH2("strcasecmp 16 eq", donothing,
EXPROPRIATE(
strcasecmp(VEIL("r", "abcdefghijklmnop"), "ABCDEFGHIJKLMNOP")));
}
+5 -1
View File
@@ -393,7 +393,11 @@ int Serve(void) {
if (!Poll() && (!g_timeout || g_interrupted)) break;
}
close(g_servfd);
LOGF("timeout expired, shutting down");
if (!g_timeout) {
LOGF("timeout expired, shutting down");
} else {
LOGF("got ctrl-c, shutting down");
}
return 0;
}