Add epoll and do more release readiness changes

This change also pays off some of the remaining technical debt with
stdio, file descriptors, and memory managemnt polyfills.
This commit is contained in:
Justine Tunney 2020-11-28 12:01:51 -08:00
parent a9ea949df8
commit 3e4fd4b0ad
271 changed files with 5706 additions and 1365 deletions

View file

@ -19,6 +19,8 @@
*/
#include "libc/stdio/stdio.h"
/* TODO(jart): Delete or rework */
/**
* Returns number of bytes available in stream buffer.
*/

View file

@ -37,10 +37,12 @@
*/
int fclose(FILE *f) {
int rc;
if (!f) return 0; /* good java behavior; glibc crashes */
if (!f) return 0;
_fflushunregister(f);
fflush(f);
free_s(&f->buf);
if (!f->nofree) {
free_s(&f->buf);
}
f->state = EOF;
if (f->noclose) {
f->fd = -1;

View file

@ -17,6 +17,8 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/calls/calls.h"
#include "libc/mem/mem.h"
#include "libc/stdio/internal.h"
#include "libc/stdio/stdio.h"
#include "libc/sysv/consts/o.h"
@ -31,14 +33,21 @@
* @error ENOMEM
*/
FILE *fdopen(int fd, const char *mode) {
FILE *res;
if ((res = fmemopen(NULL, BUFSIZ, mode))) {
res->fd = fd;
res->reader = freadbuf;
res->writer = fwritebuf;
if ((res->iomode & O_ACCMODE) != O_RDONLY) {
_fflushregister(res);
FILE *f;
if ((f = calloc(1, sizeof(FILE)))) {
f->fd = fd;
f->reader = __freadbuf;
f->writer = __fwritebuf;
f->bufmode = ischardev(fd) ? _IOLBF : _IOFBF;
f->iomode = fopenflags(mode);
f->size = BUFSIZ;
if ((f->buf = valloc(f->size))) {
if ((f->iomode & O_ACCMODE) != O_RDONLY) {
_fflushregister(f);
}
return f;
}
free(f);
}
return res;
return NULL;
}

View file

@ -19,6 +19,9 @@
*/
#include "libc/stdio/stdio.h"
/**
* Returns true if stream is in end-of-file state.
*/
int feof(FILE *f) {
return f->state == -1;
}

View file

@ -45,7 +45,6 @@ static struct StdioFlush g_fflush;
*
* @param f is the stream handle
* @return number of bytes written or -1 on error
* @see fwritebuf
*/
int fflush(FILE *f) {
size_t i;
@ -62,19 +61,14 @@ int fflush(FILE *f) {
}
}
}
} else if (f->fd != -1 && (f->iomode & O_WRONLY)) {
if (!f->state) {
while (f->beg != f->end) {
if ((wrote = fwritebuf(f)) != -1) {
res += wrote;
} else {
res = -1;
break;
}
} else if (f->fd != -1) {
while (!f->state && f->beg && !f->end) {
if ((wrote = __fwritebuf(f)) != -1) {
res += wrote;
}
} else if (f->state != -1) {
res = fseterr(f, f->state);
}
} else if (f->beg && f->beg < f->size) {
f->buf[f->beg] = 0;
}
return res;
}

View file

@ -17,11 +17,17 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/stdio/internal.h"
#include "libc/stdio/stdio.h"
/**
* Reads uint8_t from stream.
*/
int fgetc(FILE *f) {
return getc(f);
int c;
if (f->beg >= f->end) {
if (!f->reader) return __fseteof(f);
if (f->reader(f) == -1) return -1;
}
return f->buf[f->beg++];
}

View file

@ -17,12 +17,8 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/bits/bits.h"
#include "libc/bits/popcnt.h"
#include "libc/calls/calls.h"
#include "libc/errno.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/o.h"
@ -31,46 +27,46 @@
/**
* Opens buffer as stream.
*
* This function is the heart of the streams implementation, and it's
* truly magnificent for unit testing.
*
* @param buf becomes owned by this function, and is allocated if NULL
* @return new stream or NULL w/ errno
* @error ENOMEM, EINVAL
*/
FILE *fmemopen(void *buf, size_t size, const char *mode) {
FILE *res;
FILE *f;
char *p;
unsigned flags;
if (buf && !size) {
if (size && size > 0x7ffff000) {
einval();
return NULL;
}
if (size && popcnt(size) != 1) {
einval();
return NULL;
}
if (!(res = calloc(1, sizeof(FILE)))) {
if (!(f = calloc(1, sizeof(FILE)))) {
return NULL;
}
if (!buf) {
if (!size) size = FRAMESIZE;
if (!(buf = valloc(size))) {
free(res);
if (!size) size = BUFSIZ;
if (!(buf = calloc(1, size))) {
free(f);
return NULL;
}
} else {
f->nofree = true;
}
f->fd = -1;
f->buf = buf;
f->size = size;
f->end = size;
f->iomode = fopenflags(mode);
if (f->iomode & O_APPEND) {
if ((p = memchr(buf, '\0', size))) {
f->beg = p - (char *)buf;
} else {
f->beg = f->end;
}
}
res->fd = -1;
setbuffer(res, buf, size);
res->bufmode = res->buf ? _IOFBF : _IONBF;
flags = fopenflags(mode);
res->iomode = (flags & O_ACCMODE) == O_RDWR
? O_RDWR
: (flags & O_ACCMODE) == O_WRONLY ? O_WRONLY : O_RDONLY;
return res;
return f;
}

View file

@ -27,20 +27,19 @@
* @return c (as unsigned char) if written or -1 w/ errno
*/
noinstrument int fputc(int c, FILE *f) {
if (c != -1) {
if (f->beg < f->size) {
c &= 0xff;
f->buf[f->end] = c;
f->end = (f->end + 1) & (f->size - 1);
if (unlikely(f->beg == f->end || f->bufmode == _IONBF ||
(f->bufmode == _IOLBF && c == '\n'))) {
f->buf[f->beg++] = c;
if (f->beg == f->size || f->bufmode == _IONBF ||
(f->bufmode == _IOLBF && c == '\n')) {
if (f->writer) {
return f->writer(f);
} else if (f->beg == f->end) {
return fseteof(f);
if (f->writer(f) == -1) return -1;
} else if (f->beg == f->size) {
f->beg = 0;
}
}
return c;
} else {
return fseteof(f);
return __fseteof(f);
}
}

View file

@ -17,11 +17,9 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/limits.h"
#include "libc/stdio/internal.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/str/tpencode.internal.h"
#include "libc/str/tpenc.h"
/**
* Writes wide character to stream.
@ -29,15 +27,18 @@
* @return wc if written or -1 w/ errno
*/
wint_t fputwc(wchar_t wc, FILE *f) {
unsigned i, len;
char buf[MB_LEN_MAX];
uint64_t w;
if (wc != -1) {
len = tpencode(buf, sizeof(buf), wc, false);
for (i = 0; i < len; ++i) {
if (fputc(buf[i], f) == -1) return -1;
}
w = tpenc(wc);
do {
if (fputc(w & 0xff, f) != -1) {
w >>= 8;
} else {
return -1;
}
} while (w);
return wc;
} else {
return fseteof(f);
return __fseteof(f);
}
}

View file

@ -41,7 +41,7 @@ size_t fread(void *buf, size_t stride, size_t count, FILE *f) {
} else if (!(i % stride)) {
return i / stride;
} else {
return fseterr(f, EOVERFLOW);
return __fseterr(f, EOVERFLOW);
}
}
return count;

View file

@ -22,12 +22,12 @@
#include "libc/stdio/internal.h"
#include "libc/stdio/stdio.h"
int freadbuf(FILE *f) {
int __freadbuf(FILE *f) {
ssize_t got;
got = read(f->fd, f->buf, f->size - 1);
if (got == -1) return fseterrno(f);
if (got == 0) return fseteof(f);
got = read(f->fd, f->buf, f->size);
if (got == -1) return __fseterrno(f);
if (got == 0) return __fseteof(f);
f->beg = 0;
f->end = got & (f->size - 1);
f->end = got;
return got;
}

View file

@ -86,7 +86,7 @@ FILE *freopen(const char *pathname, const char *mode, FILE *stream) {
kNtFileAttributeNormal)) {
return stream;
} else {
winerr();
__winerr();
return NULL;
}
}

View file

@ -24,6 +24,8 @@
#include "libc/stdio/internal.h"
#include "libc/stdio/stdio.h"
/* TODO(jart): Delete or rework */
/**
* Fills empty space in buffer with whatever's available.
*
@ -34,11 +36,9 @@ int freplenish(FILE *f) {
ssize_t rc;
size_t got;
struct iovec iov[2];
if (f->beg == f->end) {
f->beg = f->end = 0;
}
if (f->beg <= f->end) {
if (f->beg) {
iov[0].iov_base = f->buf + f->end;
@ -57,14 +57,14 @@ int freplenish(FILE *f) {
if (rc != -1) {
if (rc) {
got = rc;
f->end = (f->end + got) & (f->size - 1);
f->end = (f->end + got) % f->size;
return got;
} else {
return fseteof(f);
return __fseteof(f);
}
} else if (errno == EINTR || errno == EAGAIN) {
return 0;
} else {
return fseterrno(f);
return __fseterrno(f);
}
}

View file

@ -21,14 +21,25 @@
#include "libc/stdio/stdio.h"
/**
* Stream decoder.
* @see libc/fmt/vcscanf.h
* Decodes data from stream.
*
* To read a line of data from a well-formed trustworthy file:
*
* int x, y;
* char text[256];
* fscanf(f, "%d %d %s\n", &x, &y, text);
*
* Please note that this function is brittle by default, which makes it
* a good fit for yolo coding. With some toil it can be used in a way
* that makes it reasonably hardened although getline() may be better.
*
* @see libc/fmt/vcscanf.c
*/
int(fscanf)(FILE *stream, const char *fmt, ...) {
int rc;
va_list va;
va_start(va, fmt);
rc = (vcscanf)((int (*)(void *))fgetc, stream, fmt, va);
rc = (vcscanf)((int (*)(void *))fgetc, (void *)ungetc, stream, fmt, va);
va_end(va);
return rc;
}

View file

@ -20,6 +20,7 @@
#include "libc/calls/calls.h"
#include "libc/errno.h"
#include "libc/stdio/stdio.h"
#include "libc/sysv/consts/o.h"
/**
* Repositions open file stream.
@ -29,20 +30,32 @@
* is in the EOF state, this function can be used to restore it without
* needing to reopen the file.
*
* @param stream is a non-null stream handle
* @param f is a non-null stream handle
* @param offset is the byte delta
* @param whence can be SEET_SET, SEEK_CUR, or SEEK_END
* @returns new offset or -1 on error
*/
long fseek(FILE *stream, long offset, int whence) {
int skew = fflush(stream);
if (whence == SEEK_CUR && skew != -1) offset -= skew;
long fseek(FILE *f, long offset, int whence) {
int skew;
int64_t newpos;
if ((newpos = lseek(stream->fd, offset, whence)) != -1) {
stream->state = 0;
return newpos;
if (f->fd != -1) {
if (whence == SEEK_CUR && f->beg < f->end) {
offset -= f->end - f->beg;
}
if (f->beg && !f->end) {
f->writer(f);
}
if ((newpos = lseek(f->fd, offset, whence)) != -1) {
f->state = 0;
f->beg = 0;
f->end = 0;
return newpos;
} else {
f->state = errno == ESPIPE ? EBADF : errno;
return -1;
}
} else {
stream->state = errno == ESPIPE ? EBADF : errno;
f->beg = offset % f->size;
return -1;
}
}

View file

@ -19,6 +19,6 @@
*/
#include "libc/stdio/internal.h"
long fseteof(FILE *f) {
return fseterr(f, -1);
long __fseteof(FILE *f) {
return __fseterr(f, -1);
}

View file

@ -21,7 +21,7 @@
#include "libc/errno.h"
#include "libc/stdio/internal.h"
long fseterr(FILE *f, int err) {
long __fseterr(FILE *f, int err) {
if (!err) err = -1;
f->state = f->state <= 0 ? err : f->state;
if (err > 0) errno = err;

View file

@ -20,4 +20,6 @@
#include "libc/errno.h"
#include "libc/stdio/internal.h"
long fseterrno(FILE *f) { return fseterr(f, errno); }
long __fseterrno(FILE *f) {
return __fseterr(f, errno);
}

View file

@ -26,4 +26,6 @@
* @param stream is a non-null stream handle
* @returns current byte offset from beginning of file, or -1
*/
long ftell(FILE *stream) { return fseek(stream, 0, SEEK_CUR); }
long ftell(FILE *stream) {
return fseek(stream, 0, SEEK_CUR);
}

View file

@ -37,7 +37,7 @@ size_t fwrite(const void *data, size_t stride, size_t count, FILE *f) {
if (!(i % stride)) {
return i / stride;
} else {
return fseterr(f, EOVERFLOW);
return __fseterr(f, EOVERFLOW);
}
}
}

View file

@ -18,28 +18,21 @@
02110-1301 USA
*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/nt/files.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/internal.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
/**
* One-shot writes data from stream buffer to underlying file or device.
*
* @param f is a non-null open stream handle
* @return number of bytes written or -1 on error
*/
int fwritebuf(FILE *f) {
ssize_t put;
unsigned bytes;
bytes = (f->beg < f->end ? f->end : f->size) - f->beg;
if ((put = write(f->fd, &f->buf[f->beg], bytes)) == -1) {
int __fwritebuf(FILE *f) {
ssize_t wrote;
if ((wrote = write(f->fd, f->buf, f->beg)) == -1) {
if (errno == EINTR) return 0;
return (int)fseterrno(f);
return __fseterrno(f);
}
f->beg = (unsigned)((f->beg + put) & (f->size - 1));
return bytes;
if (wrote == f->beg) {
f->beg = 0;
} else {
memcpy(f->buf, f->buf + wrote, f->beg - wrote);
f->beg -= wrote;
}
return wrote;
}

View file

@ -35,10 +35,8 @@
ezlea g_stderr_buf,cx
mov %rcx,24(%rax) # f.buf
movl $BUFSIZ,32(%rax) # f.size
ezlea fwritebuf,cx
ezlea fswritebuf,dx
testb IsMetal()
cmove %rcx,%rdx
ezlea __fwritebuf,cx
mov %rcx,%rdx
mov %rdx,48(%rax) # f.writer
mov %rax,stderr(%rip)
.init.end 400,_init_g_stderr,globl,hidden

View file

@ -31,10 +31,8 @@
ezlea g_stdin_buf,cx
mov %rcx,24(%rax) # f.buf
movl $BUFSIZ,32(%rax) # f.size
ezlea freadbuf,cx
ezlea fsreadbuf,dx
testb IsMetal()
cmove %rcx,%rdx
ezlea __freadbuf,cx
mov %rcx,%rdx
mov %rdx,40(%rax) # f.reader
mov %rax,stdin(%rip)
.init.end 400,_init_g_stdin,globl,hidden

View file

@ -33,10 +33,8 @@
ezlea g_stdout_buf,cx
mov %rcx,24(%rax) # f.buf
movl $BUFSIZ,32(%rax) # f.size
ezlea fwritebuf,cx
ezlea fswritebuf,dx
testb IsMetal()
cmovz %rcx,%rdx
ezlea __fwritebuf,cx
mov %rcx,%rdx
mov %rdx,48(%rax) # f.writer
mov %rax,stdout(%rip)
.init.end 400,_init_g_stdout,globl,hidden

View file

@ -1,32 +0,0 @@
/*-*- 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
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/stdio/internal.h"
#include "libc/stdio/stdio.h"
int __getc_moar(FILE *f) {
int b;
if (f->beg == f->end) {
if (!f->reader) return fseteof(f);
if (f->reader(f) == -1) return -1;
}
b = f->buf[f->beg];
f->beg = (f->beg + 1) & (f->size - 1);
return b;
}

View file

@ -51,7 +51,7 @@ ssize_t getdelim(char **line, size_t *n, int delim, FILE *f) {
break;
}
if (i + 2 >= *n && !__grow(line, n, 1, 0)) {
fseterrno(f);
__fseterrno(f);
break;
}
if (((*line)[i++] = c) == delim) {

View file

@ -12,13 +12,11 @@ extern unsigned char g_stderrbuf[BUFSIZ];
int _fflushregister(FILE *) hidden;
void _fflushunregister(FILE *) hidden;
int freadbuf(FILE *) hidden;
int fwritebuf(FILE *) hidden;
int fsreadbuf(FILE *) hidden;
int fswritebuf(FILE *) hidden;
long fseteof(FILE *) hidden;
long fseterrno(FILE *) hidden;
long fseterr(FILE *, int) hidden;
int __freadbuf(FILE *) hidden;
int __fwritebuf(FILE *) hidden;
long __fseteof(FILE *) hidden;
long __fseterrno(FILE *) hidden;
long __fseterr(FILE *, int) hidden;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -28,7 +28,7 @@ int(scanf)(const char *fmt, ...) {
int rc;
va_list va;
va_start(va, fmt);
rc = (vcscanf)((int (*)(void *))fgetc, stdin, fmt, va);
rc = (vcscanf)((int (*)(void *))fgetc, NULL, stdin, fmt, va);
va_end(va);
return rc;
}

View file

@ -1,56 +0,0 @@
/*-*- 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
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "ape/lib/pc.h"
#include "libc/nexgen32e/uart.internal.h"
#include "libc/stdio/internal.h"
#include "libc/stdio/stdio.h"
static void fin(FILE *f) {
f->buf[f->end] = inb(0x3F8);
f->end = (f->end + 1) & (f->size - 1);
}
static void fout(FILE *f) {
outb(0x3F8, f->buf[f->beg]);
f->beg = (f->beg + 1) & (f->size - 1);
}
static int serialstdio(FILE *f, unsigned char status, void action(FILE *)) {
int block = 1;
unsigned tally = 0;
while (f->end != f->beg) {
if (!(inb(0x3F8 + UART_LSR) & status)) {
if (!block) break;
asm("pause");
} else {
action(f);
tally++;
}
}
return (int)tally;
}
int fsreadbuf(FILE *f) {
return serialstdio(f, UART_TTYDA, fin);
}
int fswritebuf(FILE *f) {
return serialstdio(f, UART_TTYTXR, fout);
}

View file

@ -18,7 +18,8 @@ typedef struct FILE {
uint32_t beg; // 0x10
uint32_t end; // 0x14
uint8_t *buf; // 0x18
size_t size; // 0x20
uint32_t size; // 0x20
uint32_t nofree; // 0x24
int (*reader)(struct FILE *); // 0x28
int (*writer)(struct FILE *); // 0x30
} FILE;
@ -99,10 +100,8 @@ int vfscanf(FILE *, const char *, va_list);
cosmopolitan § standard i/o » optimizations
*/
int __getc_moar(FILE *);
#define putc(c, f) fputc(c, f)
#define getc(f) (f->beg + 1 < f->end ? f->buf[f->beg++] : __getc_moar(f))
#define getc(f) (f->beg < f->end ? f->buf[f->beg++] : fgetc(f))
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
#define printf(FMT, ...) (printf)(PFLINK(FMT), ##__VA_ARGS__)

View file

@ -20,7 +20,7 @@
#include "libc/stdio/stdio.h"
int ungetc(int c, FILE *f) {
f->beg = (f->beg - 1) & (f->size - 1);
f->buf[f->beg] = c;
f->beg = (f->beg - 1u) & (f->size - 1);
return c;
}

View file

@ -25,5 +25,5 @@
* @see libc/fmt/vcscanf.h
*/
int(vfscanf)(FILE *stream, const char *fmt, va_list ap) {
return (vcscanf)((int (*)(void *))fgetc, stream, fmt, ap);
return (vcscanf)((void *)fgetc, (void *)ungetc, stream, fmt, ap);
}

View file

@ -25,5 +25,5 @@
* @see libc/fmt/vcscanf.h
*/
int(vscanf)(const char *fmt, va_list ap) {
return (vcscanf)((int (*)(void *))fgetc, stdin, fmt, ap);
return (vcscanf)((int (*)(void *))fgetc, (void *)ungetc, stdin, fmt, ap);
}