Implement getcwd() for XNU

This commit is contained in:
Justine Tunney
2021-01-30 08:54:12 -08:00
parent 417797d218
commit 95173645a1
17 changed files with 239 additions and 77 deletions

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);

View File

@ -232,10 +232,6 @@ int prctl();
int sysctl(const int *, unsigned, void *, size_t *, void *, size_t);
int fchdir(int);
#define getcwd(BUF, SIZE) \
(__builtin_constant_p(BUF) && (&(BUF)[0] == NULL) ? get_current_dir_name() \
: getcwd(BUF, SIZE))
/*───────────────────────────────────────────────────────────────────────────│─╗
│ cosmopolitan § system calls » formatting ─╬─│┼
╚────────────────────────────────────────────────────────────────────────────│*/
@ -248,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);

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
libc/calls/getcwd-xnu.c Normal file
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;
}

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);
}
}

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;

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;
@ -120,7 +121,7 @@ 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;
@ -151,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;

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;

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;

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) {

View File

@ -151,11 +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 *);
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;

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"
/**

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));
}

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);
}

View File

@ -16,24 +16,18 @@
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"
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;
/**
* 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));
}

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))));
}

View File

@ -0,0 +1,35 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 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/str/str.h"
#include "libc/testlib/ezbench.h"
#include "libc/testlib/testlib.h"
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")));
}