Make fixes and improvements

- Fix handling of precision in hex float formatting
- Enhance the cocmd interpreter for system() and popen()
- Manually ran the Lua unit tests, which are now passing
- Let stdio i/o operations happen when file is in error state
- We're now saving and restoring xmm in ftrace out of paranoia
This commit is contained in:
Justine Tunney 2023-07-09 05:11:25 -07:00
parent 95fbdb4f76
commit 41396ff48a
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
43 changed files with 495 additions and 261 deletions

View file

@ -218,7 +218,9 @@ privileged void _klog(const char *b, size_t n) {
__imp_WriteFile(h, b, n, &wrote, 0);
__imp_SetLastError(e);
} else if (IsMetal()) {
if (_weaken(_klog_vga)) _weaken(_klog_vga)(b, n);
if (_weaken(_klog_vga)) {
_weaken(_klog_vga)(b, n);
}
for (i = 0; i < n; ++i) {
for (;;) {
dx = 0x3F8 + UART_LSR;

View file

@ -2,5 +2,5 @@
#define _FTW_H
#include "libc/calls/weirdtypes.h"
#include "libc/sysv/consts/s.h"
#include "third_party/musl/ftw.h"
#include "libc/stdio/ftw.h"
#endif /* _FTW_H */

View file

@ -58,7 +58,7 @@ relegated wontreturn void __die(void) {
_Exitr(77);
} else if (owner == me) {
kprintf("die failed while dying\n");
_Exitr(78);
_Exitr(79);
} else {
_Exit1(79);
}

View file

@ -36,20 +36,18 @@ __nt2sysv:
mov %rsp,%rbp
// TODO(jart): We should probably find some way to use our own
// stack when Windows delivers signals ;_;
sub $0x100,%rsp
sub $256,%rsp
push %rbx
push %rdi
push %rsi
pushf # TODO(jart): Do we need it?
lea -0x80(%rbp),%rdi
call _savexmm
pushf // TODO(jart): Do we need it?
call __xmm_save
mov %rcx,%rdi
mov %rdx,%rsi
mov %r8,%rdx
mov %r9,%rcx
call *%rax
lea -0x80(%rbp),%rdi
call _loadxmm
call __xmm_load
popf
pop %rsi
pop %rdi

View file

@ -17,28 +17,48 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
.privileged
// Loads XMM registers from buffer.
//
// @param %rdi points to &(forcealign(16) uint8_t[256])[128]
// @note modern cpus have out-of-order execution engines
_loadxmm:
__xmm_save:
lea -128(%rbp),%rdi
.leafprologue
movaps -0x80(%rdi),%xmm0
movaps -0x70(%rdi),%xmm1
movaps -0x60(%rdi),%xmm2
movaps -0x50(%rdi),%xmm3
movaps -0x40(%rdi),%xmm4
movaps -0x30(%rdi),%xmm5
movaps -0x20(%rdi),%xmm6
movaps -0x10(%rdi),%xmm7
movaps 0x00(%rdi),%xmm8
movaps 0x10(%rdi),%xmm9
movaps 0x20(%rdi),%xmm10
movaps 0x30(%rdi),%xmm11
movaps 0x40(%rdi),%xmm12
movaps 0x50(%rdi),%xmm13
movaps 0x60(%rdi),%xmm14
movaps 0x70(%rdi),%xmm15
movdqu %xmm0,-0x80(%rdi)
movdqu %xmm1,-0x70(%rdi)
movdqu %xmm2,-0x60(%rdi)
movdqu %xmm3,-0x50(%rdi)
movdqu %xmm4,-0x40(%rdi)
movdqu %xmm5,-0x30(%rdi)
movdqu %xmm6,-0x20(%rdi)
movdqu %xmm7,-0x10(%rdi)
movdqu %xmm8,0x00(%rdi)
movdqu %xmm9,0x10(%rdi)
movdqu %xmm10,0x20(%rdi)
movdqu %xmm11,0x30(%rdi)
movdqu %xmm12,0x40(%rdi)
movdqu %xmm13,0x50(%rdi)
movdqu %xmm14,0x60(%rdi)
movdqu %xmm15,0x70(%rdi)
.leafepilogue
.endfn _loadxmm,globl,hidden
.endfn __xmm_save,globl,hidden
__xmm_load:
lea -128(%rbp),%rdi
.leafprologue
movdqu -0x80(%rdi),%xmm0
movdqu -0x70(%rdi),%xmm1
movdqu -0x60(%rdi),%xmm2
movdqu -0x50(%rdi),%xmm3
movdqu -0x40(%rdi),%xmm4
movdqu -0x30(%rdi),%xmm5
movdqu -0x20(%rdi),%xmm6
movdqu -0x10(%rdi),%xmm7
movdqu 0x00(%rdi),%xmm8
movdqu 0x10(%rdi),%xmm9
movdqu 0x20(%rdi),%xmm10
movdqu 0x30(%rdi),%xmm11
movdqu 0x40(%rdi),%xmm12
movdqu 0x50(%rdi),%xmm13
movdqu 0x60(%rdi),%xmm14
movdqu 0x70(%rdi),%xmm15
.leafepilogue
.endfn __xmm_load,globl,hidden

View file

@ -27,15 +27,19 @@
#include "libc/fmt/magnumstrs.internal.h"
#include "libc/intrin/_getenv.internal.h"
#include "libc/intrin/bits.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/weaken.h"
#include "libc/macros.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/ok.h"
#include "libc/sysv/consts/s.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/consts/timer.h"
#include "third_party/awk/cmd.h"
#include "third_party/getopt/getopt.internal.h"
#include "third_party/musl/glob.h"
#include "third_party/sed/cmd.h"
#include "third_party/tr/cmd.h"
@ -61,7 +65,6 @@
static char *p;
static char *q;
static char *r;
static int envi;
static int vari;
static size_t n;
static char *cmd;
@ -75,6 +78,9 @@ static const char *prog;
static char argbuf[ARG_MAX];
static bool unsupported[256];
static int ShellSpawn(void);
static int ShellExec(void);
static ssize_t Write(int fd, const char *s) {
return write(fd, s, strlen(s));
}
@ -87,34 +93,19 @@ static wontreturn void UnsupportedSyntax(unsigned char c) {
_Exit(4);
}
static wontreturn void SysExit(int rc, const char *call, const char *thing) {
int err;
char ibuf[12];
const char *estr;
err = errno;
FormatInt32(ibuf, err);
estr = _strerdoc(err);
if (!estr) estr = "EUNKNOWN";
tinyprint(2, thing, ": ", call, "() failed: ", estr, " (", ibuf, ")\n", NULL);
_Exit(rc);
}
static void Open(const char *path, int fd, int flags) {
const char *err;
close(fd);
if (open(path, flags, 0644) == -1) {
SysExit(7, "open", path);
perror(path);
_Exit(1);
}
}
static wontreturn void Exec(void) {
_unassert(args[0][0]);
if (!n) {
tinyprint(2, prog, ": error: too few args\n", NULL);
_Exit(5);
}
static int SystemExec(void) {
execvpe(args[0], args, envs);
SysExit(127, "execve", args[0]);
perror(args[0]);
return (n = 0), 127;
}
static int GetSignalByName(const char *s) {
@ -133,6 +124,18 @@ static void PutEnv(char **p, const char *kv) {
if (!e.s) p[e.i + 1] = 0;
}
static void UnsetEnv(char **p, const char *k) {
int i;
struct Env e;
if ((e = _getenv(p, k)).s) {
p[e.i] = 0;
for (i = e.i + 1; p[i]; ++i) {
p[i - 1] = p[i];
p[i] = 0;
}
}
}
static void Append(int c) {
_npassert(q + 1 < argbuf + sizeof(argbuf));
*q++ = c;
@ -163,28 +166,53 @@ static wontreturn void Exit(void) {
_Exit(n > 1 ? atoi(args[1]) : 0);
}
static int Waiter(int pid) {
int ws;
n = 0;
if (waitpid(pid, &ws, 0) == -1) {
perror("wait");
return 1;
}
if (WIFEXITED(ws)) {
return WEXITSTATUS(ws);
} else {
return 128 + WTERMSIG(ws);
}
}
static int Wait(void) {
char ibuf[12];
int e, rc, ws, pid;
int e, ws, pid;
if (n > 1) {
if (waitpid(atoi(args[1]), &ws, 0) == -1) {
SysExit(22, "waitpid", prog);
if ((pid = atoi(args[1])) <= 0) {
tinyprint(2, "wait: bad pid\n", NULL);
return 1;
}
rc = WIFEXITED(ws) ? WEXITSTATUS(ws) : 128 + WTERMSIG(ws);
exitstatus = rc;
return Waiter(pid);
} else {
for (e = errno;;) {
for (n = 0, e = errno;;) {
if (waitpid(-1, &ws, 0) == -1) {
if (errno == ECHILD) {
errno = e;
break;
}
SysExit(22, "waitpid", prog);
perror("wait");
return 1;
}
}
rc = 0;
return 0;
}
return rc;
}
static const char *GetOptArg(int c, int *i, int j) {
if (args[*i][j] == c) {
if (args[*i][j + 1]) {
return args[*i] + j + 1;
} else if (*i + 1 < n) {
return args[++*i];
}
}
return 0;
}
static int Echo(void) {
@ -229,29 +257,60 @@ static int Read(void) {
return rc;
}
static int NeedArgument(const char *prog) {
tinyprint(2, prog, ": missing argument\n", NULL);
return 1;
}
static int Cd(void) {
const char *s;
if ((s = n > 1 ? args[1] : _getenv(envs, "HOME").s)) {
if (!chdir(s)) {
return 0;
} else {
tinyprint(2, "chdir: ", s, ": ", _strerdoc(errno), "\n", NULL);
perror(s);
return 1;
}
} else {
tinyprint(2, "chdir: missing argument\n", NULL);
return 1;
return NeedArgument("cd");
}
}
static int Mkdir(void) {
int i = 1;
int (*f)(const char *, unsigned) = mkdir;
if (n >= 3 && !strcmp(args[1], "-p")) ++i, f = makedirs;
int i, j;
int mode = 0755;
const char *arg;
int (*mkdir_impl)(const char *, unsigned) = mkdir;
for (i = 1; i < n; ++i) {
if (args[i][0] == '-' && args[i][1]) {
if (args[i][1] == '-' && !args[i][2]) {
++i; // rm -- terminates option parsing
break;
}
for (j = 1; j < args[i][j]; ++j) {
if (args[i][j] == 'p') {
mkdir_impl = makedirs; // mkdir -p creates parents
continue;
}
if ((arg = GetOptArg('m', &i, j))) {
mode = strtol(arg, 0, 8); // mkdir -m OCTAL sets mode
break;
}
char option[2] = {args[i][j]};
tinyprint(2, "mkdir", ": illegal option -- ", option, "\n", NULL);
return 1;
}
} else {
break;
}
}
if (i == n) {
return NeedArgument("mkdir");
}
for (; i < n; ++i) {
if (f(args[i], 0755)) {
tinyprint(2, "mkdir: ", args[i], ": ", _strerdoc(errno), "\n", NULL);
return errno;
if (mkdir_impl(args[i], mode)) {
perror(args[i]);
return 1;
}
}
return 0;
@ -267,7 +326,7 @@ static int Kill(void) {
}
for (; i < n; ++i) {
if (kill(atoi(args[i]), sig)) {
tinyprint(2, "kill: ", args[i], ": ", _strerdoc(errno), "\n", NULL);
perror("kill");
rc = 1;
}
}
@ -321,33 +380,52 @@ static int Test(void) {
}
static int Rm(void) {
int i;
if (n > 1 && args[1][0] != '-') {
for (i = 1; i < n; ++i) {
if (unlink(args[i])) {
tinyprint(2, "rm: ", args[i], ": ", _strerdoc(errno), "\n", NULL);
int i, j;
bool force = false;
for (i = 1; i < n; ++i) {
if (args[i][0] == '-' && args[i][1]) {
if (args[i][1] == '-' && !args[i][2]) {
++i; // rm -- terminates option parsing
break;
}
for (j = 1; j < args[i][j]; ++j) {
if (args[i][j] == 'f') {
force = true; // rm -f forces removal
continue;
}
char option[2] = {args[i][j]};
tinyprint(2, "rm", ": illegal option -- ", option, "\n", NULL);
return 1;
}
} else {
break;
}
return 0;
} else {
return -1; // fall back to system rm command
}
if (i == n) {
return NeedArgument("rm");
}
for (; i < n; ++i) {
struct stat st;
if ((!force && (lstat(args[i], &st) ||
(!S_ISLNK(st.st_mode) && access(args[i], W_OK)))) ||
unlink(args[i])) {
if (force && errno == ENOENT) continue;
perror(args[i]);
return 1;
}
}
return 0;
}
static int Rmdir(void) {
int i;
if (n > 1 && args[1][0] != '-') {
for (i = 1; i < n; ++i) {
if (rmdir(args[i])) {
tinyprint(2, "rmdir: ", args[i], ": ", _strerdoc(errno), "\n", NULL);
return 1;
}
for (i = 1; i < n; ++i) {
if (rmdir(args[i])) {
perror(args[i]);
return 1;
}
return 0;
} else {
return -1; // fall back to system rmdir command
}
return 0;
}
static int Touch(void) {
@ -355,7 +433,7 @@ static int Touch(void) {
if (n > 1 && args[1][0] != '-') {
for (i = 1; i < n; ++i) {
if (touch(args[i], 0644)) {
tinyprint(2, "touch: ", args[i], ": ", _strerdoc(errno), "\n", NULL);
perror(args[i]);
return 1;
}
}
@ -365,17 +443,77 @@ static int Touch(void) {
}
}
static int Shift(int i) {
if (i <= n) {
memmove(args, args + i, (n - i + 1) * sizeof(*args));
n -= i;
}
return 0;
}
static int Fake(int main(int, char **)) {
int exitstatus, ws, pid;
if ((pid = fork()) == -1) SysExit(21, "vfork", prog);
int pid;
if ((pid = fork()) == -1) {
perror("fork");
return 127;
}
if (!pid) {
// TODO(jart): Maybe nuke stdio state somehow?
// TODO(jart): Maybe nuke stdio too?
if (_weaken(optind)) {
*_weaken(optind) = 1;
}
environ = envs;
exit(main(n, args));
}
if (waitpid(pid, &ws, 0) == -1) SysExit(22, "waitpid", prog);
exitstatus = WIFEXITED(ws) ? WEXITSTATUS(ws) : 128 + WTERMSIG(ws);
return n = 0, exitstatus;
return Waiter(pid);
}
static int Env(void) {
int i, j;
const char *arg;
char term = '\n';
for (i = 1; i < n; ++i) {
if (args[i][0] == '-') {
if (!args[i][1]) {
envs[0] = 0; // env - clears environment
continue;
}
for (j = 1; j < args[i][j]; ++j) {
if (args[i][j] == 'i') {
envs[0] = 0; // env -i clears environment
continue;
}
if (args[i][j] == '0') {
term = 0; // env -0 uses '\0' line separator
continue;
}
if ((arg = GetOptArg('u', &i, j))) {
UnsetEnv(envs, arg); // env -u VAR removes variable
break;
}
char option[2] = {args[i][j]};
tinyprint(2, "env", ": illegal option -- ", option, "\n", NULL);
return 1;
}
continue;
}
if (strchr(args[i], '=')) {
PutEnv(envs, args[i]);
} else {
Shift(i);
return ShellSpawn();
}
}
for (i = 0; envs[i]; ++i) {
Write(1, envs[i]);
write(1, &term, 1);
}
return 0;
}
static int Exec(void) {
Shift(1);
return ShellExec();
}
static int TryBuiltin(void) {
@ -384,6 +522,8 @@ static int TryBuiltin(void) {
if (!strcmp(args[0], "cd")) return Cd();
if (!strcmp(args[0], "rm")) return Rm();
if (!strcmp(args[0], "[")) return Test();
if (!strcmp(args[0], "env")) return Env();
if (!strcmp(args[0], "exec")) return Exec();
if (!strcmp(args[0], "wait")) return Wait();
if (!strcmp(args[0], "echo")) return Echo();
if (!strcmp(args[0], "read")) return Read();
@ -403,43 +543,64 @@ static int TryBuiltin(void) {
return -1;
}
static wontreturn void Launch(void) {
static int ShellExec(void) {
int rc;
if ((rc = TryBuiltin()) != -1) _Exit(rc);
Exec();
if ((rc = TryBuiltin()) == -1) {
rc = SystemExec();
}
return (n = 0), rc;
}
static void Pipe(void) {
int pid, pfds[2];
if (pipe2(pfds, O_CLOEXEC)) SysExit(8, "pipe2", prog);
if ((pid = fork()) == -1) SysExit(9, "vfork", prog);
if (pipe2(pfds, O_CLOEXEC)) {
perror("pipe");
_Exit(127);
}
if ((pid = fork()) == -1) {
perror("fork");
_Exit(127);
}
if (!pid) {
_unassert(dup2(pfds[1], 1) == 1);
// we can't rely on cloexec because builtins
if (pfds[0] != 1) _unassert(!close(pfds[0]));
if (pfds[1] != 1) _unassert(!close(pfds[1]));
Launch();
_Exit(ShellExec());
}
_unassert(!dup2(pfds[0], 0));
if (pfds[1]) _unassert(!close(pfds[1]));
n = 0;
}
static int Run(void) {
int exitstatus, ws, pid;
if ((exitstatus = TryBuiltin()) == -1) {
if ((pid = vfork()) == -1) SysExit(21, "vfork", prog);
if (!pid) Exec();
if (waitpid(pid, &ws, 0) == -1) SysExit(22, "waitpid", prog);
exitstatus = WIFEXITED(ws) ? WEXITSTATUS(ws) : 128 + WTERMSIG(ws);
static int ShellSpawn(void) {
int rc, ws, pid;
if ((rc = TryBuiltin()) == -1) {
switch ((pid = fork())) {
case 0:
_Exit(SystemExec());
default:
rc = Waiter(pid);
break;
case -1:
perror("fork");
rc = 127;
break;
}
}
n = 0;
return exitstatus;
return (n = 0), rc;
}
static void Async(void) {
if ((lastchild = fork()) == -1) SysExit(21, "vfork", prog);
if (!lastchild) Launch();
static void ShellSpawnAsync(void) {
switch ((lastchild = fork())) {
case 0:
_Exit(ShellExec());
default:
break;
case -1:
perror("fork");
break;
}
n = 0;
}
@ -535,7 +696,7 @@ static char *Tokenize(void) {
if (q > r) {
return Finish();
} else if (p[1] == '|') {
rc = Run();
rc = ShellSpawn();
if (!rc) {
_Exit(0);
} else {
@ -550,7 +711,7 @@ static char *Tokenize(void) {
if (q > r) {
return Finish();
} else {
exitstatus = Run();
exitstatus = ShellSpawn();
t = STATE_WHITESPACE;
}
} else if (*p == '>') {
@ -562,7 +723,7 @@ static char *Tokenize(void) {
if (q > r) {
return Finish();
} else if (p[1] == '&') {
rc = Run();
rc = ShellSpawn();
if (!rc) {
++p;
t = STATE_WHITESPACE;
@ -570,7 +731,7 @@ static char *Tokenize(void) {
_Exit(rc);
}
} else {
Async();
ShellSpawnAsync();
t = STATE_WHITESPACE;
}
} else {
@ -691,7 +852,7 @@ int _cocmd(int argc, char **argv, char **envp) {
}
// copy environment variables
envi = 0;
int envi = 0;
if (envp) {
for (; envp[envi]; ++envi) {
_npassert(envi + 1 < ARRAYLEN(envs));
@ -748,5 +909,5 @@ int _cocmd(int argc, char **argv, char **envp) {
}
}
Launch();
return ShellExec();
}

View file

@ -31,6 +31,7 @@ ftrace_hook:
push %rbp
mov %rsp,%rbp
and $-16,%rsp
sub $256,%rsp
push %rax
push %rdi
push %rsi
@ -44,7 +45,9 @@ ftrace_hook:
push %r13
push %r14
push %r15
call __xmm_save
call ftracer
call __xmm_load
pop %r15
pop %r14
pop %r13
@ -67,7 +70,7 @@ ftrace_hook:
ldr w16,[x16,#:lo12:__ftrace]
cmp w16,0
ble 1f
stp x29,x30,[sp,-256]!
stp x29,x30,[sp,-384]!
mov x29,sp
stp x0,x1,[sp,16]
@ -84,6 +87,10 @@ ftrace_hook:
stp x24,x25,[sp,208]
stp x26,x27,[sp,224]
str x28,[sp,240]
stp q0,q1,[sp,256]
stp q2,q3,[sp,288]
stp q4,q5,[sp,320]
stp q6,q7,[sp,352]
bl ftracer
@ -101,8 +108,12 @@ ftrace_hook:
ldp x24,x25,[sp,208]
ldp x26,x27,[sp,224]
ldr x28,[sp,240]
ldp q0,q1,[sp,256]
ldp q2,q3,[sp,288]
ldp q4,q5,[sp,320]
ldp q6,q7,[sp,352]
ldp x29,x30,[sp],256
ldp x29,x30,[sp],384
1: ret
#endif /* __x86_64__ */

View file

@ -18,14 +18,13 @@
*/
#include "libc/fmt/itoa.h"
#include "libc/intrin/kprintf.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
#include "libc/runtime/symbols.internal.h"
void ftrace_hook(void);
_Hide int ftrace_stackdigs;
textstartup int ftrace_install(void) {
if (GetSymbolTable()) {
ftrace_stackdigs = LengthInt64Thousands(GetStackSize());

View file

@ -1,7 +1,7 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
/*-*- 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 2023 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,29 +16,6 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
#include "libc/runtime/internal.h"
// Stores XMM registers to buffer.
//
// @param %rdi points to &(forcealign(16) uint8_t[256])[128]
// @note modern cpus have out-of-order execution engines
_savexmm:
.leafprologue
movaps %xmm0,-0x80(%rdi)
movaps %xmm1,-0x70(%rdi)
movaps %xmm2,-0x60(%rdi)
movaps %xmm3,-0x50(%rdi)
movaps %xmm4,-0x40(%rdi)
movaps %xmm5,-0x30(%rdi)
movaps %xmm6,-0x20(%rdi)
movaps %xmm7,-0x10(%rdi)
movaps %xmm8,0x00(%rdi)
movaps %xmm9,0x10(%rdi)
movaps %xmm10,0x20(%rdi)
movaps %xmm11,0x30(%rdi)
movaps %xmm12,0x40(%rdi)
movaps %xmm13,0x50(%rdi)
movaps %xmm14,0x60(%rdi)
movaps %xmm15,0x70(%rdi)
.leafepilogue
.endfn _savexmm,globl,hidden
int ftrace_stackdigs;

View file

@ -46,7 +46,6 @@
#define DETOUR_SKEW 8
#endif
extern _Hide int ftrace_stackdigs;
static struct CosmoFtrace g_ftrace;
static privileged inline int GetNestingLevelImpl(struct StackFrame *frame) {

View file

@ -12,6 +12,7 @@
COSMOPOLITAN_C_START_
extern int __pid;
extern int ftrace_stackdigs;
extern uint32_t __ntconsolemode[3];
extern const char v_ntsubsystem[] __attribute__((__weak__));
extern const uintptr_t __fini_array_end[] __attribute__((__weak__));

View file

@ -114,8 +114,6 @@ void _Exitr(int) libcesque wontreturn;
void _Exit1(int) libcesque wontreturn;
void _restorewintty(void);
void __paginate(int, const char *);
void _loadxmm(void *);
void _savexmm(void *);
long _missingno();
/* memory management */
void _weakfree(void *);

View file

@ -727,7 +727,6 @@ haveinc:
* - `%e` double (expo formatting)
* - `%f` double (ugly formatting)
* - `%a` double (hex formatting)
* - `%Lg` long double
*
* Size Modifiers
*
@ -738,6 +737,7 @@ haveinc:
* - `%lx` unsigned long (64-bit hexadecimal)
* - `%jd` intmax_t (64-bit)
* - `%jjd` int128_t (128-bit)
* - `%Lg` long double
*
* Width Modifiers
*
@ -1251,19 +1251,23 @@ _Hide int __fmt(void *fn, void *arg, const char *format, va_list va) {
__FMT_PUT(*s++);
if (prec || (flags & FLAGS_HASH)) __FMT_PUT('.');
while (--prec >= 0) {
if ((c = *s))
if ((c = *s)) {
s++;
else
} else {
c = '0';
}
__FMT_PUT(c);
}
__FMT_PUT(d);
if (decpt < 0) {
__FMT_PUT('-');
decpt = -decpt;
} else
} else {
__FMT_PUT('+');
for (c = 2, k = 10; 10 * k <= decpt; c++, k *= 10) donothing;
}
for (c = 2, k = 10; 10 * k <= decpt; c++, k *= 10) {
donothing;
}
for (;;) {
i1 = decpt / k;
__FMT_PUT(i1 + '0');
@ -1271,7 +1275,9 @@ _Hide int __fmt(void *fn, void *arg, const char *format, va_list va) {
decpt -= i1 * k;
decpt *= 10;
}
while (--width >= 0) __FMT_PUT(' ');
while (--width >= 0) {
__FMT_PUT(' ');
}
freedtoa(s0);
break;
@ -1321,7 +1327,7 @@ _Hide int __fmt(void *fn, void *arg, const char *format, va_list va) {
}
if (sign) __FMT_PUT(sign);
__FMT_PUT('0');
__FMT_PUT(alphabet[17]);
__FMT_PUT(alphabet[17]); // x or X
if ((flags & FLAGS_ZEROPAD) && width > 0 && !(flags & FLAGS_LEFT)) {
do __FMT_PUT('0');
while (--width > 0);
@ -1329,29 +1335,31 @@ _Hide int __fmt(void *fn, void *arg, const char *format, va_list va) {
i1 = prec1 & 7;
k = prec1 >> 3;
__FMT_PUT(alphabet[(fpb.bits[k] >> 4 * i1) & 0xf]);
if (prec1 > 0 || (flags & FLAGS_HASH)) __FMT_PUT('.');
if (prec1 > 0) {
prec -= prec1;
while (prec1 > 0) {
if (--i1 < 0) {
if (--k < 0) break;
i1 = 7;
}
__FMT_PUT(alphabet[(fpb.bits[k] >> 4 * i1) & 0xf]);
--prec1;
}
if ((flags & FLAGS_HASH) && prec > 0) {
do __FMT_PUT(0);
while (--prec > 0);
}
if (prec1 > 0 || prec > 0) {
__FMT_PUT('.');
}
__FMT_PUT(alphabet[16]);
while (prec1 > 0) {
if (--i1 < 0) {
if (--k < 0) break;
i1 = 7;
}
__FMT_PUT(alphabet[(fpb.bits[k] >> 4 * i1) & 0xf]);
--prec1;
--prec;
}
while (prec-- > 0) {
__FMT_PUT('0');
}
__FMT_PUT(alphabet[16]); // p or P
if (bex < 0) {
__FMT_PUT('-');
bex = -bex;
} else
} else {
__FMT_PUT('+');
for (c = 1; 10 * c <= bex; c *= 10) donothing;
}
for (c = 1; 10 * c <= bex; c *= 10) {
donothing;
}
for (;;) {
i1 = bex / c;
__FMT_PUT('0' + i1);
@ -1359,7 +1367,9 @@ _Hide int __fmt(void *fn, void *arg, const char *format, va_list va) {
bex -= i1 * c;
bex *= 10;
}
while (--width >= 0) __FMT_PUT(' ');
while (--width >= 0) {
__FMT_PUT(' ');
}
break;
case '%':

View file

@ -43,7 +43,7 @@ size_t fread_unlocked(void *buf, size_t stride, size_t count, FILE *f) {
ssize_t rc;
size_t n, m;
struct iovec iov[2];
if (f->state) {
if (!stride) {
return 0;
}
if ((f->iomode & O_ACCMODE) == O_WRONLY) {

53
libc/stdio/ftw.c Normal file
View file

@ -0,0 +1,53 @@
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi
Musl Libc
Copyright © 2005-2014 Rich Felker, et al.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "libc/stdio/ftw.h"
asm(".ident\t\"\\n\\n\
Musl libc (MIT License)\\n\
Copyright 2005-2014 Rich Felker, et. al.\"");
asm(".include \"libc/disclaimer.inc\"");
// clang-format off
/**
* Walks file tree.
*
* @return 0 on success, -1 on error, or non-zero `fn` result
* @see examples/walk.c for example
* @see nftw()
*/
int ftw(const char *dirpath,
int fn(const char *fpath,
const struct stat *st,
int typeflag),
int fd_limit)
{
/* The following cast assumes that calling a function with one
* argument more than it needs behaves as expected. This is
* actually undefined, but works on all real-world machines. */
return nftw(dirpath, (int (*)())fn, fd_limit, FTW_PHYS);
}

90
libc/stdio/ftw.h Normal file
View file

@ -0,0 +1,90 @@
#ifndef COSMOPOLITAN_THIRD_PARTY_MUSL_FTW_H_
#define COSMOPOLITAN_THIRD_PARTY_MUSL_FTW_H_
#include "libc/calls/struct/stat.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
/**
* Type for file.
*/
#define FTW_F 1
/**
* Type for directory.
*/
#define FTW_D 2
/**
* Type for directory that cannot be read.
*/
#define FTW_DNR 3
/**
* Type for stat() failed and not a symbolic link.
*/
#define FTW_NS 4
/**
* Type for symbolic link when `FTW_PHYS` is in flags.
*/
#define FTW_SL 5
/**
* Directory and `FTW_DEPTH` in flags.
*/
#define FTW_DP 6
/**
* Type for broken symbolic link when `FTW_PHYS` is not in flags.
*/
#define FTW_SLN 7
/**
* Flag to prevent following symbolic links (recommended).
* @see nftw() flags
*/
#define FTW_PHYS 1
/**
* Flag to prevent crossing mount points.
* @see nftw() flags
*/
#define FTW_MOUNT 2
/**
* Unsupported.
* @see nftw() flags
*/
#define FTW_CHDIR 4
/**
* Flag for post-order traversal.
*
* 1. Will use `FTW_DP` instead of `FTW_D` as type.
* 2. Directory callback happens *after* rather than before.
*
* @see nftw() flags
*/
#define FTW_DEPTH 8
struct FTW {
/**
* Byte offset of basename component in `fpath` passed to callback.
*/
int base;
/**
* Depth relative to `dirpath` whose level is zero.
*/
int level;
};
int ftw(const char *, int (*)(const char *, const struct stat *, int), int);
int nftw(const char *,
int (*)(const char *, const struct stat *, int, struct FTW *), int,
int);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_THIRD_PARTY_MUSL_FTW_H_ */

View file

@ -45,7 +45,7 @@ size_t fwrite_unlocked(const void *data, size_t stride, size_t count, FILE *f) {
size_t n, m;
const char *p;
struct iovec iov[2];
if (f->state) {
if (!stride) {
return 0;
}
if ((f->iomode & O_ACCMODE) == O_RDONLY) {

189
libc/stdio/nftw.c Normal file
View file

@ -0,0 +1,189 @@
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi
Musl Libc
Copyright © 2005-2014 Rich Felker, et al.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/struct/dirent.h"
#include "libc/calls/weirdtypes.h"
#include "libc/errno.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/s.h"
#include "libc/thread/thread.h"
#include "libc/stdio/ftw.h"
#define PATH_MAXIMUS 4096
asm(".ident\t\"\\n\\n\
Musl libc (MIT License)\\n\
Copyright 2005-2014 Rich Felker, et. al.\"");
asm(".include \"libc/disclaimer.inc\"");
// clang-format off
struct history
{
struct history *chain;
dev_t dev;
ino_t ino;
int level;
int base;
};
static int do_nftw(char *path,
int fn(const char *, const struct stat *, int, struct FTW *),
int fd_limit,
int flags,
struct history *h)
{
size_t l = strlen(path), j = l && path[l-1]=='/' ? l-1 : l;
struct stat st;
struct history new;
int type;
int r;
int dfd=-1;
int err=0;
struct FTW lev;
if ((flags & FTW_PHYS) ? lstat(path, &st) : stat(path, &st) < 0) {
if (!(flags & FTW_PHYS) && errno==ENOENT && !lstat(path, &st))
type = FTW_SLN;
else if (errno != EACCES) return -1;
else type = FTW_NS;
} else if (S_ISDIR(st.st_mode)) {
if (flags & FTW_DEPTH) type = FTW_DP;
else type = FTW_D;
} else if (S_ISLNK(st.st_mode)) {
if (flags & FTW_PHYS) type = FTW_SL;
else type = FTW_SLN;
} else {
type = FTW_F;
}
if ((flags & FTW_MOUNT) && h && st.st_dev != h->dev)
return 0;
new.chain = h;
new.dev = st.st_dev;
new.ino = st.st_ino;
new.level = h ? h->level+1 : 0;
new.base = j+1;
lev.level = new.level;
if (h) {
lev.base = h->base;
} else {
size_t k;
for (k=j; k && path[k]=='/'; k--);
for (; k && path[k-1]!='/'; k--);
lev.base = k;
}
if (type == FTW_D || type == FTW_DP) {
dfd = open(path, O_RDONLY | O_DIRECTORY);
err = errno;
if (dfd < 0 && err == EACCES) type = FTW_DNR;
if (!fd_limit) close(dfd);
}
if (!(flags & FTW_DEPTH) && (r=fn(path, &st, type, &lev)))
return r;
for (; h; h = h->chain)
if (h->dev == st.st_dev && h->ino == st.st_ino)
return 0;
if ((type == FTW_D || type == FTW_DP) && fd_limit) {
if (dfd < 0) {
errno = err;
return -1;
}
DIR *d = fdopendir(dfd);
if (d) {
struct dirent *de;
while ((de = readdir(d))) {
if (de->d_name[0] == '.'
&& (!de->d_name[1]
|| (de->d_name[1]=='.'
&& !de->d_name[2]))) continue;
if (strlen(de->d_name) >= PATH_MAXIMUS-l) {
errno = ENAMETOOLONG;
closedir(d);
return -1;
}
path[j]='/';
strcpy(path+j+1, de->d_name);
if ((r=do_nftw(path, fn, fd_limit-1, flags, &new))) {
closedir(d);
return r;
}
}
closedir(d);
} else {
close(dfd);
return -1;
}
}
path[l] = 0;
if ((flags & FTW_DEPTH) && (r=fn(path, &st, type, &lev)))
return r;
return 0;
}
/**
* Walks file tree.
*
* @return 0 on success, -1 on error, or non-zero `fn` result
* @see examples/walk.c for example
*/
int nftw(const char *dirpath,
int fn(const char *fpath,
const struct stat *st,
int typeflag,
struct FTW *ftwbuf),
int fd_limit,
int flags)
{
int r, cs;
size_t l;
char pathbuf[PATH_MAXIMUS+1];
if (fd_limit <= 0) return 0;
l = strlen(dirpath);
if (l > PATH_MAXIMUS) {
errno = ENAMETOOLONG;
return -1;
}
memcpy(pathbuf, dirpath, l+1);
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
r = do_nftw(pathbuf, fn, fd_limit, flags, NULL);
pthread_setcancelstate(cs, 0);
return r;
}

View file

@ -203,6 +203,7 @@ typedef const int *wctrans_t;
wctrans_t wctrans(const char *);
wint_t towctrans(wint_t, wctrans_t);
int getsubopt(char **, char *const *, char **) paramsnonnull();
char *strsignal(int) returnsnonnull libcesque;
char *strsignal_r(int, char[hasatleast 15]) returnsnonnull libcesque;
char *strerror(int) returnsnonnull dontthrow nocallback;

View file

@ -20,7 +20,7 @@
#include "libc/errno.h"
#include "libc/sysv/errfuns.h"
#include "libc/x/x.h"
#include "third_party/musl/ftw.h"
#include "libc/stdio/ftw.h"
static int rmrf_callback(const char *fpath, //
const struct stat *st, //