mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-03-03 07:29:23 +00:00
Make major improvements to stdio
Buffering now has optimal performance, bugs have been fixed, and some missing apis have been introduced. This implementation is also now more production worthy since it's less brittle now in terms of system errors. That's going to help redbean since lua i/o is all based on stdio. See #97
This commit is contained in:
parent
09bcfa23d5
commit
da36e7e256
69 changed files with 1595 additions and 735 deletions
|
@ -19,6 +19,7 @@
|
|||
#include "libc/alg/arraylist.internal.h"
|
||||
#include "libc/dns/dns.h"
|
||||
#include "libc/dns/hoststxt.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sock/sock.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
|
@ -43,28 +44,28 @@
|
|||
* @see hoststxtsort() which is the logical next step
|
||||
*/
|
||||
int parsehoststxt(struct HostsTxt *ht, FILE *f) {
|
||||
int rc;
|
||||
char *line;
|
||||
size_t linesize;
|
||||
rc = 0;
|
||||
struct HostsTxtEntry entry;
|
||||
char *addr, *name, *tok, *comment;
|
||||
line = NULL;
|
||||
linesize = 0;
|
||||
while ((getline(&line, &linesize, f)) != -1) {
|
||||
struct HostsTxtEntry entry;
|
||||
char *addr, *name, *tok, *comment;
|
||||
if ((comment = strchr(line, '#'))) *comment = '\0';
|
||||
if ((addr = strtok_r(line, " \t\r\n\v", &tok)) &&
|
||||
inet_pton(AF_INET, addr, entry.ip) == 1) {
|
||||
entry.canon = ht->strings.i;
|
||||
while ((name = strtok_r(NULL, " \t\r\n\v", &tok))) {
|
||||
entry.name = ht->strings.i;
|
||||
if (concat(&ht->strings, name, strnlen(name, DNS_NAME_MAX) + 1) == -1 ||
|
||||
append(&ht->entries, &entry) == -1) {
|
||||
rc = -1;
|
||||
}
|
||||
concat(&ht->strings, name, strnlen(name, DNS_NAME_MAX) + 1);
|
||||
append(&ht->entries, &entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
free(line);
|
||||
return rc | ferror(f);
|
||||
if (ferror(f)) {
|
||||
errno = ferror(f);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ void vflogf_onfail(FILE *f) {
|
|||
struct stat st;
|
||||
if (IsTiny()) return;
|
||||
err = ferror(f);
|
||||
if ((err == ENOSPC || err == EDQUOT || err == EFBIG) &&
|
||||
if (fileno(f) != -1 && (err == ENOSPC || err == EDQUOT || err == EFBIG) &&
|
||||
(fstat(fileno(f), &st) == -1 || st.st_size > kNontrivialSize)) {
|
||||
ftruncate(fileno(f), 0);
|
||||
fseek(f, SEEK_SET, 0);
|
||||
|
@ -91,6 +91,7 @@ void vflogf_onfail(FILE *f) {
|
|||
*/
|
||||
void(vflogf)(unsigned level, const char *file, int line, FILE *f,
|
||||
const char *fmt, va_list va) {
|
||||
int bufmode;
|
||||
struct tm tm;
|
||||
long double t2;
|
||||
const char *prog;
|
||||
|
@ -98,7 +99,7 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f,
|
|||
char buf32[32], *buf32p;
|
||||
int64_t secs, nsec, dots;
|
||||
if (!f) f = __log_file;
|
||||
if (fileno(f) == -1) return;
|
||||
if (!f) return;
|
||||
t2 = nowl();
|
||||
secs = t2;
|
||||
nsec = (t2 - secs) * 1e9L;
|
||||
|
@ -114,6 +115,8 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f,
|
|||
buf32p = "--------------------";
|
||||
}
|
||||
prog = basename(program_invocation_name);
|
||||
bufmode = f->bufmode;
|
||||
if (bufmode == _IOLBF) f->bufmode = _IOFBF;
|
||||
if ((fprintf)(f, "%c%s%06ld:%s:%d:%.*s:%d] ", vflogf_loglevel2char(level),
|
||||
buf32p, rem1000000int64(div1000int64(dots)), file, line,
|
||||
strchrnul(prog, '.') - prog, prog, getpid()) <= 0) {
|
||||
|
@ -122,6 +125,10 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f,
|
|||
(vfprintf)(f, fmt, va);
|
||||
va_end(va);
|
||||
fputs("\n", f);
|
||||
if (bufmode == _IOLBF) {
|
||||
f->bufmode = _IOLBF;
|
||||
fflush(f);
|
||||
}
|
||||
if (level == kLogFatal) {
|
||||
__start_fatal(file, line);
|
||||
strcpy(buf32, "unknown");
|
||||
|
|
|
@ -53,7 +53,7 @@ static char g_buf[512];
|
|||
static const char *g_lastsymbol;
|
||||
static struct SymbolTable *g_symbols;
|
||||
|
||||
forceinline int GetNestingLevel(struct StackFrame *frame) {
|
||||
static noasan int GetNestingLevel(struct StackFrame *frame) {
|
||||
int nesting = -2;
|
||||
while (frame) {
|
||||
++nesting;
|
||||
|
@ -69,7 +69,7 @@ forceinline int GetNestingLevel(struct StackFrame *frame) {
|
|||
* prologues of other functions. We assume those functions behave
|
||||
* according to the System Five NexGen32e ABI.
|
||||
*/
|
||||
privileged void ftrace(void) {
|
||||
privileged noasan void ftrace(void) {
|
||||
size_t i, j, nesting;
|
||||
const char *symbol;
|
||||
struct StackFrame *frame;
|
||||
|
|
|
@ -35,8 +35,6 @@ FILE *fdopen(int fd, const char *mode) {
|
|||
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;
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "libc/bits/bits.h"
|
||||
#include "libc/bits/pushpop.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
|
@ -36,28 +37,26 @@
|
|||
*/
|
||||
int fflush(FILE *f) {
|
||||
size_t i;
|
||||
int rc;
|
||||
rc = 0;
|
||||
ssize_t rc;
|
||||
if (!f) {
|
||||
for (i = __fflush.handles.i; i; --i) {
|
||||
if ((f = __fflush.handles.p[i - 1])) {
|
||||
if (fflush(f) == -1) {
|
||||
rc = -1;
|
||||
break;
|
||||
}
|
||||
if (fflush(f) == -1) return -1;
|
||||
}
|
||||
}
|
||||
} else if (f->fd != -1) {
|
||||
while (f->beg && !f->end) {
|
||||
if (__fwritebuf(f) == -1) {
|
||||
rc = -1;
|
||||
break;
|
||||
while (f->beg && !f->end && (f->iomode & O_ACCMODE) != O_RDONLY) {
|
||||
if ((rc = write(f->fd, f->buf, f->beg)) == -1) {
|
||||
f->state = errno;
|
||||
return -1;
|
||||
}
|
||||
if (rc != f->beg) abort();
|
||||
f->beg = 0;
|
||||
}
|
||||
} else if (f->beg && f->beg < f->size) {
|
||||
f->buf[f->beg] = 0;
|
||||
}
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
textstartup int __fflush_register(FILE *f) {
|
||||
|
|
|
@ -16,22 +16,18 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/stdio/internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
|
||||
static noinline int __fgetc(FILE *f) {
|
||||
if (!f->reader) return __fseteof(f);
|
||||
if (f->reader(f) == -1) return -1;
|
||||
return f->buf[f->beg++];
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads uint8_t from stream.
|
||||
* Reads byte from stream.
|
||||
* @return byte in range 0..255, or -1 w/ errno
|
||||
*/
|
||||
int fgetc(FILE *f) {
|
||||
unsigned char b;
|
||||
if (f->beg < f->end) {
|
||||
return f->buf[f->beg++];
|
||||
return f->buf[f->beg++] & 0xff;
|
||||
} else {
|
||||
return __fgetc(f);
|
||||
if (!fread(&b, 1, 1, f)) return -1;
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,16 +17,35 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/thompike.h"
|
||||
#include "libc/str/tpdecodecb.internal.h"
|
||||
|
||||
/**
|
||||
* Reads UTF-8 character from stream.
|
||||
*
|
||||
* @return wide character or -1 on EOF or error
|
||||
*/
|
||||
wint_t fgetwc(FILE *f) {
|
||||
wint_t res;
|
||||
res = -1;
|
||||
tpdecodecb(&res, fgetc(f), (void *)fgetc, f);
|
||||
return res;
|
||||
int c, n;
|
||||
wint_t b, x, y;
|
||||
if (f->beg < f->end) {
|
||||
b = f->buf[f->beg++] & 0xff;
|
||||
} else if ((c = fgetc(f)) != -1) {
|
||||
b = c;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
if (b < 0300) return b;
|
||||
n = ThomPikeLen(b);
|
||||
x = ThomPikeByte(b);
|
||||
while (--n) {
|
||||
if ((c = fgetc(f)) == -1) return -1;
|
||||
y = c;
|
||||
if (ThomPikeCont(y)) {
|
||||
x = ThomPikeMerge(x, y);
|
||||
} else {
|
||||
ungetc(y, f);
|
||||
return b;
|
||||
}
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
|
|
@ -24,10 +24,10 @@
|
|||
* Reads UTF-8 content from stream into UTF-32 buffer.
|
||||
*/
|
||||
wchar_t *fgetws(wchar_t *s, int size, FILE *f) {
|
||||
wint_t c;
|
||||
wchar_t *p = s;
|
||||
if (size > 0) {
|
||||
while (--size > 0) {
|
||||
wint_t c;
|
||||
if ((c = fgetwc(f)) == -1) {
|
||||
if (ferror(f) == EINTR) continue;
|
||||
break;
|
||||
|
|
|
@ -32,32 +32,27 @@ FILE *fmemopen(void *buf, size_t size, const char *mode) {
|
|||
FILE *f;
|
||||
char *p;
|
||||
unsigned flags;
|
||||
|
||||
if (size && size > 0x7ffff000) {
|
||||
einval();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(f = calloc(1, sizeof(FILE)))) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!buf) {
|
||||
if (buf) {
|
||||
f->nofree = true;
|
||||
} else {
|
||||
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->size = size;
|
||||
f->iomode = fopenflags(mode);
|
||||
|
||||
if (f->iomode & O_APPEND) {
|
||||
if ((p = memchr(buf, '\0', size))) {
|
||||
f->beg = p - (char *)buf;
|
||||
|
@ -65,6 +60,5 @@ FILE *fmemopen(void *buf, size_t size, const char *mode) {
|
|||
f->beg = f->end;
|
||||
}
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
|
|
|
@ -17,33 +17,22 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/stdio/internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
|
||||
/**
|
||||
* Writes byte to stream.
|
||||
* @return c (as unsigned char) if written or -1 w/ errno
|
||||
* @see putc() if called within loop
|
||||
*
|
||||
* @param c is byte to buffer or write, which is masked
|
||||
* @return c as unsigned char if written or -1 w/ errno
|
||||
*/
|
||||
noinstrument int fputc(int c, FILE *f) {
|
||||
if ((f->iomode & O_ACCMODE) != O_RDONLY) {
|
||||
if (f->beg < f->size) {
|
||||
f->buf[f->beg++] = c;
|
||||
if (f->beg == f->size || f->bufmode == _IONBF ||
|
||||
(f->bufmode == _IOLBF && c == '\n')) {
|
||||
if (f->writer) {
|
||||
if (f->writer(f) == -1) return -1;
|
||||
} else if (f->beg == f->size) {
|
||||
f->beg = 0;
|
||||
}
|
||||
}
|
||||
return c & 0xff;
|
||||
} else {
|
||||
return __fseteof(f);
|
||||
}
|
||||
int fputc(int c, FILE *f) {
|
||||
unsigned char b;
|
||||
if (c != '\n' && f->beg < f->size && f->bufmode != _IONBF) {
|
||||
f->buf[f->beg++] = c;
|
||||
return c & 0xff;
|
||||
} else {
|
||||
return __fseterr(f, EBADF);
|
||||
b = c;
|
||||
if (!fwrite(&b, 1, 1, f)) return -1;
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,9 +16,6 @@
|
|||
│ 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/macros.internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
|
@ -31,17 +28,12 @@
|
|||
*
|
||||
* @param s is a NUL-terminated string that's non-NULL
|
||||
* @param f is an open stream
|
||||
* @return strlen(s) or -1 w/ errno on error
|
||||
* @return bytes written, or -1 w/ errno
|
||||
*/
|
||||
int fputs(const char *s, FILE *f) {
|
||||
int i, n, m;
|
||||
size_t n, r;
|
||||
n = strlen(s);
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (putc(s[i], f) == -1) {
|
||||
if (ferror(f) == EINTR) continue;
|
||||
if (feof(f)) errno = f->state = EPIPE;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
r = fwrite(s, 1, n, f);
|
||||
if (!r && n) return -1;
|
||||
return r;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/stdio/internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/tpenc.h"
|
||||
|
||||
|
@ -30,14 +29,12 @@ wint_t fputwc(wchar_t wc, FILE *f) {
|
|||
if (wc != -1) {
|
||||
w = tpenc(wc);
|
||||
do {
|
||||
if (fputc(w & 0xff, f) != -1) {
|
||||
w >>= 8;
|
||||
} else {
|
||||
if (fputc(w, f) == -1) {
|
||||
return -1;
|
||||
}
|
||||
} while (w);
|
||||
} while ((w >>= 8));
|
||||
return wc;
|
||||
} else {
|
||||
return __fseteof(f);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,12 +16,19 @@
|
|||
│ 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/iovec.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sock/sock.h"
|
||||
#include "libc/stdio/internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/internal.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Reads data from stream.
|
||||
|
@ -31,17 +38,62 @@
|
|||
* @return count on success, [0,count) on eof, or 0 on error or count==0
|
||||
*/
|
||||
size_t fread(void *buf, size_t stride, size_t count, FILE *f) {
|
||||
int c;
|
||||
size_t i, n;
|
||||
unsigned char *p;
|
||||
for (n = stride * count, p = buf, i = 0; i < n; ++i) {
|
||||
if ((c = getc(f)) != -1) {
|
||||
p[i] = c & 0xff;
|
||||
} else if (!(i % stride)) {
|
||||
return i / stride;
|
||||
} else {
|
||||
return __fseterr(f, EOVERFLOW);
|
||||
}
|
||||
char *p;
|
||||
ssize_t rc;
|
||||
size_t n, m;
|
||||
struct iovec iov[2];
|
||||
if ((f->iomode & O_ACCMODE) == O_WRONLY) {
|
||||
f->state = errno = EBADF;
|
||||
return 0;
|
||||
}
|
||||
if (f->beg > f->end) {
|
||||
f->state = errno = EINVAL;
|
||||
return 0;
|
||||
}
|
||||
p = buf;
|
||||
n = stride * count;
|
||||
m = f->end - f->beg;
|
||||
memcpy(p, f->buf + f->beg, MIN(n, m));
|
||||
if (n < m) {
|
||||
f->beg += n;
|
||||
return count;
|
||||
}
|
||||
if (n == m) {
|
||||
f->beg = f->end = 0;
|
||||
return count;
|
||||
}
|
||||
if (f->fd == -1) {
|
||||
f->beg = 0;
|
||||
f->end = 0;
|
||||
f->state = -1;
|
||||
return m / stride;
|
||||
}
|
||||
iov[0].iov_base = p + m;
|
||||
iov[0].iov_len = n - m;
|
||||
if (f->bufmode != _IONBF && n < f->size) {
|
||||
iov[1].iov_base = f->buf;
|
||||
if (f->size > PUSHBACK) {
|
||||
iov[1].iov_len = f->size - PUSHBACK;
|
||||
} else {
|
||||
iov[1].iov_len = f->size;
|
||||
}
|
||||
} else {
|
||||
iov[1].iov_base = NULL;
|
||||
iov[1].iov_len = 0;
|
||||
}
|
||||
if ((rc = readv(f->fd, iov, 2)) == -1) {
|
||||
f->state = errno;
|
||||
return 0;
|
||||
}
|
||||
n = rc;
|
||||
f->beg = 0;
|
||||
f->end = 0;
|
||||
if (n > iov[0].iov_len) {
|
||||
f->end += n - iov[0].iov_len;
|
||||
return count;
|
||||
} else {
|
||||
n = (m + n) / stride;
|
||||
if (n < count) f->state = -1;
|
||||
return n;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
|
|
@ -16,10 +16,7 @@
|
|||
│ 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/stdio/stdio.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
|
||||
/**
|
||||
* Repositions open file stream.
|
||||
|
@ -32,28 +29,8 @@
|
|||
* @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
|
||||
* @returns 0 on success or -1 on error
|
||||
*/
|
||||
long fseek(FILE *f, long offset, int whence) {
|
||||
int64_t pos;
|
||||
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 (lseek(f->fd, offset, whence) != -1) {
|
||||
f->state = 0;
|
||||
f->beg = 0;
|
||||
f->end = 0;
|
||||
return 0;
|
||||
} else {
|
||||
f->state = errno == ESPIPE ? EBADF : errno;
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
f->beg = (offset & 0xffffffff) % f->size;
|
||||
return -1;
|
||||
}
|
||||
int fseek(FILE *f, long offset, int whence) {
|
||||
return fseeko(f, offset, whence);
|
||||
}
|
||||
|
|
83
libc/stdio/fseeko.c
Normal file
83
libc/stdio/fseeko.c
Normal file
|
@ -0,0 +1,83 @@
|
|||
/*-*- 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/stdio/stdio.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
|
||||
/**
|
||||
* Repositions open file stream.
|
||||
*
|
||||
* This function flushes the buffer (unless it's currently in the EOF
|
||||
* state) and then calls lseek() on the underlying file. If the stream
|
||||
* is in the EOF state, this function can be used to restore it without
|
||||
* needing to reopen the file.
|
||||
*
|
||||
* @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 0 on success or -1 on error
|
||||
*/
|
||||
int fseeko(FILE *f, int64_t offset, int whence) {
|
||||
ssize_t rc;
|
||||
int64_t pos;
|
||||
if (f->fd != -1) {
|
||||
if (f->beg && !f->end && (f->iomode & O_ACCMODE) != O_RDONLY) {
|
||||
if ((rc = write(f->fd, f->buf, f->beg)) == -1) {
|
||||
f->state = errno;
|
||||
return -1;
|
||||
}
|
||||
if (rc != f->beg) abort();
|
||||
f->beg = 0;
|
||||
}
|
||||
if (whence == SEEK_CUR && f->beg < f->end) {
|
||||
offset -= f->end - f->beg;
|
||||
}
|
||||
if (lseek(f->fd, offset, whence) != -1) {
|
||||
f->beg = 0;
|
||||
f->end = 0;
|
||||
return 0;
|
||||
} else {
|
||||
f->state = errno == ESPIPE ? EBADF : errno;
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
switch (whence) {
|
||||
case SEEK_SET:
|
||||
pos = offset;
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
pos = f->beg + offset;
|
||||
break;
|
||||
case SEEK_END:
|
||||
pos = f->end + offset;
|
||||
break;
|
||||
default:
|
||||
pos = -1;
|
||||
break;
|
||||
}
|
||||
if (0 <= pos && pos <= f->end) {
|
||||
f->beg = pos;
|
||||
return 0;
|
||||
} else {
|
||||
f->state = errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,33 +16,14 @@
|
|||
│ 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/stdio/stdio.h"
|
||||
|
||||
/**
|
||||
* Returns current position of stream.
|
||||
*
|
||||
* @param stream is a non-null stream handle
|
||||
* @returns current byte offset from beginning of file, or -1
|
||||
* @returns current byte offset from beginning, or -1 w/ errno
|
||||
*/
|
||||
long ftell(FILE *f) {
|
||||
int64_t pos;
|
||||
if (f->fd != -1) {
|
||||
if (f->beg && !f->end) {
|
||||
f->writer(f);
|
||||
}
|
||||
if ((pos = lseek(f->fd, 0, SEEK_CUR)) != -1) {
|
||||
f->state = 0;
|
||||
f->beg = 0;
|
||||
f->end = 0;
|
||||
return pos;
|
||||
} else {
|
||||
f->state = errno == ESPIPE ? EBADF : errno;
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
errno = f->state;
|
||||
return -1;
|
||||
}
|
||||
return ftello(f);
|
||||
}
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
/*-*- 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│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ 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/macros.internal.h"
|
||||
.source __FILE__
|
||||
|
||||
ftello: jmp ftell
|
||||
.endfn ftello,globl
|
54
libc/stdio/ftello.c
Normal file
54
libc/stdio/ftello.c
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*-*- 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/errno.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
|
||||
/**
|
||||
* Returns current position of stream.
|
||||
*
|
||||
* @param stream is a non-null stream handle
|
||||
* @returns current byte offset from beginning, or -1 w/ errno
|
||||
*/
|
||||
int64_t ftello(FILE *f) {
|
||||
ssize_t rc;
|
||||
int64_t pos;
|
||||
uint32_t skew;
|
||||
if (f->fd != -1) {
|
||||
if (f->beg && !f->end && (f->iomode & O_ACCMODE) != O_RDONLY) {
|
||||
if ((rc = write(f->fd, f->buf, f->beg)) == -1) {
|
||||
f->state = errno;
|
||||
return -1;
|
||||
}
|
||||
if (rc != f->beg) abort();
|
||||
f->beg = 0;
|
||||
}
|
||||
if ((pos = lseek(f->fd, 0, SEEK_CUR)) != -1) {
|
||||
if (f->beg < f->end) pos -= f->end - f->beg;
|
||||
return pos;
|
||||
} else {
|
||||
f->state = errno == ESPIPE ? EBADF : errno;
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
return f->beg;
|
||||
}
|
||||
}
|
|
@ -16,10 +16,17 @@
|
|||
│ 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/iovec.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sock/sock.h"
|
||||
#include "libc/stdio/internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
|
||||
/**
|
||||
* Writes data to stream.
|
||||
|
@ -29,10 +36,54 @@
|
|||
* @return count on success, [0,count) on EOF, 0 on error or count==0
|
||||
*/
|
||||
size_t fwrite(const void *data, size_t stride, size_t count, FILE *f) {
|
||||
size_t i, n;
|
||||
const unsigned char *p;
|
||||
for (n = stride * count, p = data, i = 0; i < n; ++i) {
|
||||
if (fputc(p[i], f) == -1) return -1;
|
||||
ldiv_t d;
|
||||
ssize_t rc;
|
||||
size_t n, m;
|
||||
const char *p;
|
||||
struct iovec iov[2];
|
||||
if ((f->iomode & O_ACCMODE) == O_RDONLY) {
|
||||
f->state = errno = EBADF;
|
||||
return 0;
|
||||
}
|
||||
n = stride * count;
|
||||
m = f->size - f->beg;
|
||||
if (n <= m && f->bufmode != _IONBF) {
|
||||
memcpy(f->buf + f->beg, data, n);
|
||||
f->beg += n;
|
||||
if (f->fd != -1 && f->bufmode == _IOLBF &&
|
||||
(p = memrchr(f->buf, '\n', f->beg))) {
|
||||
n = p + 1 - f->buf;
|
||||
if ((rc = write(f->fd, f->buf, n)) == -1) {
|
||||
if (errno == EINTR || errno == EAGAIN) return count;
|
||||
f->state = errno;
|
||||
return 0;
|
||||
}
|
||||
n = rc;
|
||||
memmove(f->buf, f->buf + n, f->beg - n);
|
||||
f->beg -= n;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
if (f->fd == -1) {
|
||||
n = MIN(n, m);
|
||||
d = ldiv(n, stride);
|
||||
n -= d.rem;
|
||||
memcpy(f->buf + f->beg, data, n);
|
||||
f->beg += n;
|
||||
f->state = -1;
|
||||
return d.quot;
|
||||
}
|
||||
iov[0].iov_base = f->buf;
|
||||
iov[0].iov_len = f->beg;
|
||||
iov[1].iov_base = data;
|
||||
iov[1].iov_len = n;
|
||||
n += f->beg;
|
||||
if ((rc = writev(f->fd, iov, 2)) == -1) {
|
||||
f->state = errno;
|
||||
return 0;
|
||||
}
|
||||
m = rc;
|
||||
if (n != m) abort();
|
||||
f->beg = 0;
|
||||
return count;
|
||||
}
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
/*-*- 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│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ 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/macros.internal.h"
|
||||
#include "libc/notice.inc"
|
||||
.source __FILE__
|
||||
|
||||
getc: jmp fgetc
|
||||
.endfn getc,globl
|
|
@ -16,13 +16,12 @@
|
|||
│ 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/stdio/internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
|
||||
long __fseterr(FILE *f, int err) {
|
||||
if (!err) err = -1;
|
||||
f->state = f->state <= 0 ? err : f->state;
|
||||
if (err > 0) errno = err;
|
||||
return -1;
|
||||
/**
|
||||
* Reads byte from stream.
|
||||
* @return byte in range 0..255, or -1 w/ errno
|
||||
*/
|
||||
int(getc)(FILE *f) {
|
||||
return fgetc(f);
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
/*-*- 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│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ 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/macros.internal.h"
|
||||
.source __FILE__
|
||||
|
||||
// Reads uint8_t from standard input.
|
||||
// @return %al has result w/ rest of %rax cleared
|
||||
getchar:mov stdin(%rip),%rdi
|
||||
jmp fgetc
|
||||
.endfn getchar,globl
|
27
libc/stdio/getchar.c
Normal file
27
libc/stdio/getchar.c
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*-*- 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/stdio/stdio.h"
|
||||
|
||||
/**
|
||||
* Reads byte from stream.
|
||||
* @return byte in range 0..255, or -1 w/ errno
|
||||
*/
|
||||
int getchar(void) {
|
||||
return fgetc(stdin);
|
||||
}
|
|
@ -17,54 +17,63 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/stdio/internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
|
||||
/**
|
||||
* Reads string from stream.
|
||||
*
|
||||
* @param line is the caller's buffer (in/out) which is extended
|
||||
* automatically. *line may be NULL but only if *n is 0;
|
||||
* NUL-termination is guaranteed FTMP
|
||||
* @param n is the capacity of line (in/out)
|
||||
* @param s is the caller's buffer (in/out) which is extended or
|
||||
* allocated automatically, also NUL-terminated is guaranteed
|
||||
* @param n is the capacity of s (in/out)
|
||||
* @param delim is the stop char (and NUL is implicitly too)
|
||||
* @return number of bytes read, including delim, excluding NUL, or -1
|
||||
* w/ errno on EOF or error; see ferror() and feof()
|
||||
* @note this function can't punt EINTR to caller
|
||||
* @see getline(), gettok_r()
|
||||
*/
|
||||
ssize_t getdelim(char **line, size_t *n, int delim, FILE *f) {
|
||||
assert((*line && *n) || (!*line && !*n));
|
||||
ssize_t rc = -1;
|
||||
size_t i = 0;
|
||||
char *p2;
|
||||
size_t n2;
|
||||
int c;
|
||||
for (;;) {
|
||||
if ((c = getc(f)) == -1) {
|
||||
if (ferror(f) == EINTR) continue;
|
||||
if (feof(f) && i) rc = i;
|
||||
break;
|
||||
}
|
||||
if (i + 2 > *n) {
|
||||
n2 = MAX(11, *n);
|
||||
n2 += n2 >> 1;
|
||||
if ((p2 = realloc(*line, n2))) {
|
||||
*line = p2;
|
||||
*n = n2;
|
||||
} else {
|
||||
__fseterrno(f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (((*line)[i++] = c) == delim) {
|
||||
rc = i;
|
||||
ssize_t getdelim(char **s, size_t *n, int delim, FILE *f) {
|
||||
char *p;
|
||||
ssize_t rc;
|
||||
size_t i, m;
|
||||
if ((f->iomode & O_ACCMODE) == O_WRONLY) {
|
||||
f->state = errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
if (f->beg > f->end || f->bufmode == _IONBF) {
|
||||
f->state = errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (!*s) *n = 0;
|
||||
for (i = 0;; i += m) {
|
||||
m = f->end - f->beg;
|
||||
if ((p = memchr(f->buf + f->beg, delim, m))) m = p + 1 - (f->buf + f->beg);
|
||||
if (i + m + 1 > *n && !(*s = realloc(*s, (*n = i + m + 1)))) abort();
|
||||
memcpy(*s + i, f->buf + f->beg, m);
|
||||
(*s)[i + m] = '\0';
|
||||
if ((f->beg += m) == f->end) f->beg = f->end = 0;
|
||||
if (p) {
|
||||
return i + m;
|
||||
} else if (f->fd == -1) {
|
||||
break;
|
||||
} else if ((rc = read(f->fd, f->buf, f->size)) != -1) {
|
||||
if (!rc) break;
|
||||
f->end = rc;
|
||||
} else if (errno != EINTR) {
|
||||
f->state = errno;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (*line && i < *n) (*line)[i] = '\0';
|
||||
return rc;
|
||||
f->state = -1;
|
||||
if (i + m) {
|
||||
return i + m;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
/*-*- 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│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ 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/macros.internal.h"
|
||||
.source __FILE__
|
||||
|
||||
getwc: jmp fgetwc
|
||||
.endfn getwc,globl
|
|
@ -16,9 +16,12 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/errno.h"
|
||||
#include "libc/stdio/internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
|
||||
long __fseterrno(FILE *f) {
|
||||
return __fseterr(f, errno);
|
||||
/**
|
||||
* Reads UTF-8 character from stream.
|
||||
* @return wide character or -1 on EOF or error
|
||||
*/
|
||||
wint_t(getwc)(FILE *f) {
|
||||
return fgetwc(f);
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
/*-*- 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│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ 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/macros.internal.h"
|
||||
.source __FILE__
|
||||
|
||||
// Reads Thompson-Pike encoded varint from standard input.
|
||||
// @return %eax has result w/ rest of %rax cleared
|
||||
getwchar:
|
||||
mov stdin(%rip),%rdi
|
||||
jmp fgetwc
|
||||
.endfn getwchar,globl
|
|
@ -16,8 +16,12 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/stdio/internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
|
||||
long __fseteof(FILE *f) {
|
||||
return __fseterr(f, -1);
|
||||
/**
|
||||
* Reads UTF-8 character from stream.
|
||||
* @return wide character or -1 on EOF or error
|
||||
*/
|
||||
wint_t getwchar(void) {
|
||||
return fgetwc(stdin);
|
||||
}
|
|
@ -1,23 +1,19 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_STDIO_INTERNAL_H_
|
||||
#define COSMOPOLITAN_LIBC_STDIO_INTERNAL_H_
|
||||
#include "libc/stdio/stdio.h"
|
||||
|
||||
#define PUSHBACK 12
|
||||
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
extern unsigned char g_stdinbuf[BUFSIZ];
|
||||
extern unsigned char g_stdoutbuf[BUFSIZ];
|
||||
extern unsigned char g_stderrbuf[BUFSIZ];
|
||||
extern char g_stdinbuf[BUFSIZ];
|
||||
extern char g_stdoutbuf[BUFSIZ];
|
||||
extern char g_stderrbuf[BUFSIZ];
|
||||
|
||||
int __fflush_register(FILE *) hidden;
|
||||
void __fflush_unregister(FILE *) hidden;
|
||||
|
||||
int __freadbuf(FILE *) hidden;
|
||||
int __fwritebuf(FILE *) hidden;
|
||||
long __fseteof(FILE *) hidden;
|
||||
long __fseterrno(FILE *) hidden;
|
||||
long __fseterr(FILE *, int) hidden;
|
||||
void __fclosepid(FILE *) hidden;
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_STDIO_INTERNAL_H_ */
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
/*-*- 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│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ 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/macros.internal.h"
|
||||
.source __FILE__
|
||||
|
||||
putc: jmp fputc
|
||||
.endfn putc,globl
|
|
@ -16,17 +16,14 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
|
||||
int __freadbuf(FILE *f) {
|
||||
ssize_t got;
|
||||
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;
|
||||
return got;
|
||||
/**
|
||||
* Writes byte to stream.
|
||||
*
|
||||
* @param c is byte to buffer or write, which is masked
|
||||
* @return c as unsigned char if written or -1 w/ errno
|
||||
*/
|
||||
int(putc)(int c, FILE *f) {
|
||||
return fputc(c, f);
|
||||
}
|
|
@ -22,9 +22,17 @@
|
|||
* Writes string w/ trailing newline to stdout.
|
||||
*/
|
||||
int puts(const char *s) {
|
||||
FILE *f = stdout;
|
||||
int rc, res;
|
||||
if ((res = rc = fputs(s, f)) == -1) return -1;
|
||||
if ((rc = fputc('\n', f)) == -1) return -1;
|
||||
return res + 1;
|
||||
FILE *f;
|
||||
size_t n, r;
|
||||
f = stdout;
|
||||
if ((n = strlen(s))) {
|
||||
r = fwrite(s, 1, n, f);
|
||||
if (!r) return -1;
|
||||
if (r < n) return r;
|
||||
}
|
||||
if (fputc('\n', f) == -1) {
|
||||
if (feof(f)) return n;
|
||||
return -1;
|
||||
}
|
||||
return n + 1;
|
||||
}
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
/*-*- 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│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ 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/macros.internal.h"
|
||||
.source __FILE__
|
||||
|
||||
// Writes wide character to stream.
|
||||
// @param %edi is the wide character
|
||||
// @param %rsi is the FILE stream pointer
|
||||
// @return %eax is set to %edi param or -1 on error
|
||||
putwc: jmp fputwc
|
||||
.endfn putwc,globl
|
|
@ -1,5 +1,5 @@
|
|||
/*-*- 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 │
|
||||
│ │
|
||||
|
@ -16,8 +16,13 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
.source __FILE__
|
||||
#include "libc/stdio/stdio.h"
|
||||
|
||||
fseeko: jmp fseek
|
||||
.endfn fseeko,globl
|
||||
/**
|
||||
* Writes wide character to stream.
|
||||
*
|
||||
* @return wc if written or -1 w/ errno
|
||||
*/
|
||||
wint_t(putwc)(wchar_t wc, FILE *f) {
|
||||
return fputwc(wc, f);
|
||||
}
|
|
@ -20,7 +20,8 @@
|
|||
|
||||
/**
|
||||
* Writes wide character to stdout.
|
||||
*
|
||||
* @return wc if written or -1 w/ errno
|
||||
*/
|
||||
wint_t putwchar(wchar_t wc) { return fputwc(wc, stdout); }
|
||||
wint_t putwchar(wchar_t wc) {
|
||||
return fputwc(wc, stdout);
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
* Like fseek(), this function can be used to restore a stream from the
|
||||
* EOF state, without reopening it.
|
||||
*/
|
||||
void rewind(FILE *stream) {
|
||||
fseek(stream, 0, SEEK_SET);
|
||||
void rewind(FILE *f) {
|
||||
fseek(f, 0, SEEK_SET);
|
||||
f->state = 0;
|
||||
}
|
||||
|
|
|
@ -16,10 +16,12 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Sets buffer on stdio stream.
|
||||
*/
|
||||
void setbuf(FILE *f, char *buf) { setbuffer(f, buf, BUFSIZ); }
|
||||
void setbuf(FILE *f, char *buf) {
|
||||
setvbuf(f, buf, buf ? _IOFBF : _IONBF, BUFSIZ);
|
||||
}
|
||||
|
|
|
@ -16,20 +16,18 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Sets buffer on stdio stream.
|
||||
*
|
||||
* @param buf may optionally be non-NULL to set the stream's underlying
|
||||
* buffer, which the stream will own, but won't free
|
||||
* @param size is ignored if buf is NULL
|
||||
*/
|
||||
void setbuffer(FILE *f, char *buf, size_t size) {
|
||||
if (buf && f->buf != (unsigned char *)buf) {
|
||||
free_s(&f->buf);
|
||||
if (!size) size = BUFSIZ;
|
||||
f->buf = (unsigned char *)buf;
|
||||
}
|
||||
if (size) {
|
||||
f->size = size;
|
||||
}
|
||||
setvbuf(f, buf, buf ? _IOFBF : _IONBF, size);
|
||||
}
|
||||
|
|
27
libc/stdio/setlinebuf.c
Normal file
27
libc/stdio/setlinebuf.c
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*-*- 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/stdio/stdio.h"
|
||||
|
||||
/**
|
||||
* Puts stream in line-buffering mode.
|
||||
*/
|
||||
void setlinebuf(FILE *f) {
|
||||
setvbuf(f, NULL, _IOLBF, 0);
|
||||
}
|
|
@ -24,12 +24,19 @@
|
|||
*
|
||||
* @param mode may be _IOFBF, _IOLBF, or _IONBF
|
||||
* @param buf may optionally be non-NULL to set the stream's underlying
|
||||
* buffer, which the stream will own, but won't free
|
||||
* @param size must be a two power if buf is provided
|
||||
* buffer, which the stream will own, but won't free, otherwise the
|
||||
* existing buffer is used
|
||||
* @param size is ignored if buf is NULL
|
||||
* @return 0 on success or -1 on error
|
||||
*/
|
||||
int setvbuf(FILE *f, char *buf, int mode, size_t size) {
|
||||
setbuffer(f, buf, size);
|
||||
if (buf) {
|
||||
if (!size) size = BUFSIZ;
|
||||
if (!f->nofree && f->buf != buf) free_s(&f->buf);
|
||||
f->buf = buf;
|
||||
f->size = size;
|
||||
f->nofree = true;
|
||||
}
|
||||
f->bufmode = mode;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -18,5 +18,5 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/stdio/internal.h"
|
||||
|
||||
unsigned char g_stdoutbuf[BUFSIZ] hidden;
|
||||
unsigned char g_stderrbuf[BUFSIZ] hidden;
|
||||
char g_stdoutbuf[BUFSIZ] hidden;
|
||||
char g_stderrbuf[BUFSIZ] hidden;
|
||||
|
|
|
@ -34,8 +34,5 @@
|
|||
ezlea __stderr_buf,cx
|
||||
mov %rcx,24(%rax) #→ f.buf
|
||||
movl $BUFSIZ,32(%rax) #→ f.size
|
||||
ezlea __fwritebuf,cx
|
||||
mov %rcx,%rdx
|
||||
mov %rdx,48(%rax) #→ f.writer
|
||||
mov %rax,stderr(%rip)
|
||||
.init.end 400,_init_stderr,globl,hidden
|
||||
|
|
|
@ -30,8 +30,5 @@
|
|||
ezlea __stdin_buf,cx
|
||||
mov %rcx,24(%rax) #→ f.buf
|
||||
movl $BUFSIZ,32(%rax) #→ f.size
|
||||
ezlea __freadbuf,cx
|
||||
mov %rcx,%rdx
|
||||
mov %rdx,40(%rax) #→ f.reader
|
||||
mov %rax,stdin(%rip)
|
||||
.init.end 400,_init_stdin,globl,hidden
|
||||
|
|
|
@ -13,19 +13,17 @@ COSMOPOLITAN_C_START_
|
|||
╚────────────────────────────────────────────────────────────────────────────│*/
|
||||
|
||||
typedef struct FILE {
|
||||
uint8_t bufmode; /* 0x00 _IOFBF, etc. (ignored if fd=-1) */
|
||||
bool noclose; /* 0x01 for fake dup() */
|
||||
uint32_t iomode; /* 0x04 O_RDONLY, etc. (ignored if fd=-1) */
|
||||
int32_t state; /* 0x08 0=OK, -1=EOF, >0=errno */
|
||||
int fd; /* 0x0c ≥0=fd, -1=closed|buffer */
|
||||
uint32_t beg; /* 0x10 */
|
||||
uint32_t end; /* 0x14 */
|
||||
uint8_t *buf; /* 0x18 */
|
||||
uint32_t size; /* 0x20 */
|
||||
uint32_t nofree; /* 0x24 */
|
||||
int (*reader)(struct FILE *); /* 0x28 */
|
||||
int (*writer)(struct FILE *); /* 0x30 */
|
||||
int pid; /* 0x34 */
|
||||
uint8_t bufmode; /* 0x00 _IOFBF, etc. (ignored if fd=-1) */
|
||||
bool noclose; /* 0x01 for fake dup() */
|
||||
uint32_t iomode; /* 0x04 O_RDONLY, etc. (ignored if fd=-1) */
|
||||
int32_t state; /* 0x08 0=OK, -1=EOF, >0=errno */
|
||||
int fd; /* 0x0c ≥0=fd, -1=closed|buffer */
|
||||
uint32_t beg; /* 0x10 */
|
||||
uint32_t end; /* 0x14 */
|
||||
char *buf; /* 0x18 */
|
||||
uint32_t size; /* 0x20 */
|
||||
uint32_t nofree; /* 0x24 */
|
||||
int pid; /* 0x28 */
|
||||
} FILE;
|
||||
|
||||
extern FILE *stdin;
|
||||
|
@ -46,10 +44,13 @@ int fputs(const char *, FILE *) paramsnonnull();
|
|||
int fputws(const wchar_t *, FILE *) paramsnonnull();
|
||||
char *fgets(char *, int, FILE *) paramsnonnull();
|
||||
wchar_t *fgetws(wchar_t *, int, FILE *) paramsnonnull();
|
||||
wint_t putwc(wchar_t, FILE *) paramsnonnull();
|
||||
wint_t fputwc(wchar_t, FILE *) paramsnonnull();
|
||||
wint_t putwchar(wchar_t);
|
||||
wint_t getwchar(void);
|
||||
wint_t getwc(FILE *) paramsnonnull();
|
||||
wint_t fgetwc(FILE *) paramsnonnull();
|
||||
wint_t ungetwc(wint_t, FILE *) paramsnonnull();
|
||||
int getchar(void);
|
||||
int putchar(int);
|
||||
int puts(const char *) paramsnonnull();
|
||||
|
@ -59,12 +60,14 @@ 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;
|
||||
FILE *freopen(const char *, const char *, FILE *) paramsnonnull((2, 3));
|
||||
size_t fread(void *, size_t, size_t, FILE *) paramsnonnull();
|
||||
size_t fwrite(const void *, size_t, size_t, FILE *) paramsnonnull();
|
||||
size_t fread(void *, size_t, size_t, FILE *) paramsnonnull((4));
|
||||
size_t fwrite(const void *, size_t, size_t, FILE *) paramsnonnull((4));
|
||||
int fclose(FILE *);
|
||||
int fclose_s(FILE **) paramsnonnull();
|
||||
long fseek(FILE *, long, int) paramsnonnull();
|
||||
int fseek(FILE *, long, int) paramsnonnull();
|
||||
long ftell(FILE *) paramsnonnull();
|
||||
int fseeko(FILE *, int64_t, int) paramsnonnull();
|
||||
int64_t ftello(FILE *) paramsnonnull();
|
||||
void rewind(FILE *) paramsnonnull();
|
||||
int fopenflags(const char *) paramsnonnull();
|
||||
void setbuf(FILE *, char *);
|
||||
|
@ -77,8 +80,6 @@ typedef uint64_t fpos_t;
|
|||
compatfn char *gets(char *) paramsnonnull();
|
||||
compatfn int fgetpos(FILE *, fpos_t *) paramsnonnull();
|
||||
compatfn int fsetpos(FILE *, const fpos_t *) paramsnonnull();
|
||||
compatfn int64_t fseeko(FILE *, long, int) paramsnonnull();
|
||||
compatfn int64_t ftello(FILE *) paramsnonnull();
|
||||
|
||||
int system(const char *);
|
||||
int systemexec(const char *);
|
||||
|
@ -102,8 +103,10 @@ int vfscanf(FILE *, const char *, va_list);
|
|||
│ cosmopolitan § standard i/o » optimizations ─╬─│┼
|
||||
╚────────────────────────────────────────────────────────────────────────────│*/
|
||||
|
||||
#define getc(f) fgetc(f)
|
||||
#define putc(c, f) fputc(c, f)
|
||||
#define getc(f) fgetc(f)
|
||||
#define getwc(f) fgetwc(f)
|
||||
#define putc(c, f) fputc(c, f)
|
||||
#define putwc(c, f) fputwc(c, f)
|
||||
|
||||
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
|
||||
#define printf(FMT, ...) (printf)(PFLINK(FMT), ##__VA_ARGS__)
|
||||
|
|
|
@ -32,8 +32,5 @@
|
|||
ezlea __stdout_buf,cx
|
||||
mov %rcx,24(%rax) #→ f.buf
|
||||
movl $BUFSIZ,32(%rax) #→ f.size
|
||||
ezlea __fwritebuf,cx
|
||||
mov %rcx,%rdx
|
||||
mov %rdx,48(%rax) #→ f.writer
|
||||
mov %rax,stdout(%rip)
|
||||
.init.end 400,_init_stdout,globl,hidden
|
||||
|
|
|
@ -17,13 +17,20 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
/**
|
||||
* Pushes 𝑐 back to stream.
|
||||
* Pushes byte back to stream.
|
||||
*/
|
||||
int ungetc(int c, FILE *f) {
|
||||
uint32_t i;
|
||||
if (c == -1) return c;
|
||||
if (f->beg) f->buf[--f->beg] = c;
|
||||
return c;
|
||||
if (c == -1) return -1;
|
||||
if (f->beg) {
|
||||
f->buf[--f->beg] = c;
|
||||
} else if (f->end < f->size) {
|
||||
memmove(f->buf + 1, f->buf, f->end++);
|
||||
f->buf[0] = c;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
return c & 0xff;
|
||||
}
|
||||
|
|
|
@ -16,22 +16,32 @@
|
|||
│ 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/stdio/internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/str/tpenc.h"
|
||||
|
||||
int __fwritebuf(FILE *f) {
|
||||
ssize_t wrote;
|
||||
if ((wrote = write(f->fd, f->buf, f->beg)) == -1) {
|
||||
if (errno == EINTR) return 0;
|
||||
return __fseterrno(f);
|
||||
}
|
||||
if (wrote == f->beg) {
|
||||
f->beg = 0;
|
||||
/**
|
||||
* Pushes wide character back to stream.
|
||||
*/
|
||||
wint_t ungetwc(wint_t c, FILE *f) {
|
||||
char b[6];
|
||||
unsigned n;
|
||||
uint64_t w;
|
||||
if (c == -1) return -1;
|
||||
n = 0;
|
||||
w = tpenc(c);
|
||||
do {
|
||||
b[n++] = w;
|
||||
} while ((w >>= 8));
|
||||
if (f->beg >= n) {
|
||||
f->beg -= n;
|
||||
memcpy(f->buf + f->beg, b, n);
|
||||
} else if (f->beg + f->end + n <= f->size) {
|
||||
memmove(f->buf + f->beg + n, f->buf + f->beg, f->end - f->beg);
|
||||
memcpy(f->buf + f->beg, b, n);
|
||||
f->end += n;
|
||||
} else {
|
||||
memcpy(f->buf, f->buf + wrote, f->beg - wrote);
|
||||
f->beg -= wrote;
|
||||
return -1;
|
||||
}
|
||||
return wrote;
|
||||
return c;
|
||||
}
|
|
@ -60,9 +60,10 @@ axdx_t tprecode8to16(char16_t *dst, size_t dstsize, const char *src) {
|
|||
r.ax = 0;
|
||||
r.dx = 0;
|
||||
for (;;) {
|
||||
if (!IsTiny() && !((uintptr_t)(src + r.dx) & 15)) {
|
||||
tprecode8to16_sse2(dst, dstsize, src, r);
|
||||
}
|
||||
/* TODO(jart): Why is it now so much slower refactored? */
|
||||
/* if (!IsTiny() && !((uintptr_t)(src + r.dx) & 15)) { */
|
||||
/* tprecode8to16_sse2(dst, dstsize, src, r); */
|
||||
/* } */
|
||||
x = src[r.dx++] & 0xff;
|
||||
if (ThomPikeCont(x)) continue;
|
||||
if (!isascii(x)) {
|
||||
|
|
|
@ -27,44 +27,130 @@
|
|||
* HTML entities and forward slash are escaped too for added safety.
|
||||
*
|
||||
* We assume the UTF-8 is well-formed and can be represented as UTF-16.
|
||||
* Things that can't be decoded or encoded will be replaced with invalid
|
||||
* code-point markers. This function is agnostic to numbers that have
|
||||
* been used with malicious intent in the past under buggy software.
|
||||
* Noncanonical encodings such as overlong NUL are canonicalized as NUL.
|
||||
* Things that can't be decoded will fall back to binary. Things that
|
||||
* can't be encoded will use invalid codepoint markers. This function is
|
||||
* agnostic to numbers that have been used with malicious intent in the
|
||||
* past under buggy software. Noncanonical encodings such as overlong
|
||||
* NUL are canonicalized as NUL.
|
||||
*/
|
||||
struct EscapeResult EscapeJsStringLiteral(const char *data, size_t size) {
|
||||
char *p;
|
||||
size_t i;
|
||||
unsigned n;
|
||||
uint64_t w;
|
||||
wint_t x, y;
|
||||
unsigned i, n;
|
||||
wint_t x, a, b;
|
||||
const char *d, *e;
|
||||
struct EscapeResult r;
|
||||
d = data;
|
||||
e = data + size;
|
||||
p = r.data = xmalloc(size * 6 + 6 + 1);
|
||||
for (i = 0; i < size;) {
|
||||
x = data[i++] & 0xff;
|
||||
if (x >= 0200) {
|
||||
if (x >= 0300) {
|
||||
n = ThomPikeLen(x);
|
||||
x = ThomPikeByte(x);
|
||||
while (--n) {
|
||||
if (i < size) {
|
||||
y = data[i++] & 0xff;
|
||||
if (ThomPikeCont(y)) {
|
||||
x = ThomPikeMerge(x, y);
|
||||
} else {
|
||||
x = 0xFFFD;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
x = 0xFFFD;
|
||||
while (d < e) {
|
||||
x = *d++ & 0xff;
|
||||
if (x >= 0300) {
|
||||
a = ThomPikeByte(x);
|
||||
n = ThomPikeLen(x) - 1;
|
||||
if (d + n <= e) {
|
||||
for (i = 0;;) {
|
||||
b = d[i] & 0xff;
|
||||
if (!ThomPikeCont(b)) break;
|
||||
a = ThomPikeMerge(a, b);
|
||||
if (++i == n) {
|
||||
x = a;
|
||||
d += i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
x = 0xFFFD;
|
||||
}
|
||||
}
|
||||
switch (x) {
|
||||
case ' ':
|
||||
case '!':
|
||||
case '#':
|
||||
case '$':
|
||||
case '%':
|
||||
case '(':
|
||||
case ')':
|
||||
case '*':
|
||||
case '+':
|
||||
case ',':
|
||||
case '-':
|
||||
case '.':
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
case ':':
|
||||
case ';':
|
||||
case '?':
|
||||
case '@':
|
||||
case 'A':
|
||||
case 'B':
|
||||
case 'C':
|
||||
case 'D':
|
||||
case 'E':
|
||||
case 'F':
|
||||
case 'G':
|
||||
case 'H':
|
||||
case 'I':
|
||||
case 'J':
|
||||
case 'K':
|
||||
case 'L':
|
||||
case 'M':
|
||||
case 'N':
|
||||
case 'O':
|
||||
case 'P':
|
||||
case 'Q':
|
||||
case 'R':
|
||||
case 'S':
|
||||
case 'T':
|
||||
case 'U':
|
||||
case 'V':
|
||||
case 'W':
|
||||
case 'X':
|
||||
case 'Y':
|
||||
case 'Z':
|
||||
case '[':
|
||||
case ']':
|
||||
case '^':
|
||||
case '_':
|
||||
case '`':
|
||||
case 'a':
|
||||
case 'b':
|
||||
case 'c':
|
||||
case 'd':
|
||||
case 'e':
|
||||
case 'f':
|
||||
case 'g':
|
||||
case 'h':
|
||||
case 'i':
|
||||
case 'j':
|
||||
case 'k':
|
||||
case 'l':
|
||||
case 'm':
|
||||
case 'n':
|
||||
case 'o':
|
||||
case 'p':
|
||||
case 'q':
|
||||
case 'r':
|
||||
case 's':
|
||||
case 't':
|
||||
case 'u':
|
||||
case 'v':
|
||||
case 'w':
|
||||
case 'x':
|
||||
case 'y':
|
||||
case 'z':
|
||||
case '{':
|
||||
case '|':
|
||||
case '}':
|
||||
case '~':
|
||||
*p++ = x;
|
||||
break;
|
||||
case '\t':
|
||||
p[0] = '\\';
|
||||
p[1] = 't';
|
||||
|
@ -105,24 +191,19 @@ struct EscapeResult EscapeJsStringLiteral(const char *data, size_t size) {
|
|||
p[1] = '\'';
|
||||
p += 2;
|
||||
break;
|
||||
default:
|
||||
if (0x20 <= x && x < 0x7F) {
|
||||
*p++ = x;
|
||||
break;
|
||||
}
|
||||
/* fallthrough */
|
||||
case '<':
|
||||
case '>':
|
||||
case '&':
|
||||
case '=':
|
||||
default:
|
||||
w = EncodeUtf16(x);
|
||||
do {
|
||||
p[0] = '\\';
|
||||
p[1] = 'u';
|
||||
p[2] = "0123456789ABCDEF"[(w & 0xF000) >> 014];
|
||||
p[3] = "0123456789ABCDEF"[(w & 0x0F00) >> 010];
|
||||
p[4] = "0123456789ABCDEF"[(w & 0x00F0) >> 004];
|
||||
p[5] = "0123456789ABCDEF"[(w & 0x000F) >> 000];
|
||||
p[2] = "0123456789abcdef"[(w & 0xF000) >> 014];
|
||||
p[3] = "0123456789abcdef"[(w & 0x0F00) >> 010];
|
||||
p[4] = "0123456789abcdef"[(w & 0x00F0) >> 004];
|
||||
p[5] = "0123456789abcdef"[(w & 0x000F) >> 000];
|
||||
p += 6;
|
||||
} while ((w >>= 16));
|
||||
break;
|
||||
|
|
|
@ -31,7 +31,7 @@ Content-Md5, kHttpContentMd5
|
|||
Content-Range, kHttpContentRange
|
||||
Content-Type, kHttpContentType
|
||||
Date, kHttpDate
|
||||
Etag, kHttpEtag
|
||||
ETag, kHttpEtag
|
||||
Expires, kHttpExpires
|
||||
From, kHttpFrom
|
||||
Host, kHttpHost
|
||||
|
@ -58,5 +58,10 @@ Retry-After, kHttpRetryAfter
|
|||
Server, kHttpServer
|
||||
Vary, kHttpVary
|
||||
Warning, kHttpWarning
|
||||
Www-Authenticate, kHttpWwwAuthenticate
|
||||
WWW-Authenticate, kHttpWwwAuthenticate
|
||||
Last-Modified, kHttpLastModified
|
||||
Cookie, kHttpCookie
|
||||
Trailer, kHttpTrailer
|
||||
TE, kHttpTe
|
||||
DNT, kHttpDnt
|
||||
Expect, kHttpExpect
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* ANSI-C code produced by gperf version 3.1 */
|
||||
/* Command-line: gperf gethttpheader.gperf */
|
||||
/* Computed positions: -k'1,9,$' */
|
||||
/* Computed positions: -k'3-4,10' */
|
||||
|
||||
#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
|
||||
&& ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
|
||||
|
@ -37,12 +37,12 @@
|
|||
#line 12 "gethttpheader.gperf"
|
||||
struct HttpHeaderSlot { char *name; char code; };
|
||||
|
||||
#define TOTAL_KEYWORDS 49
|
||||
#define MIN_WORD_LENGTH 3
|
||||
#define TOTAL_KEYWORDS 54
|
||||
#define MIN_WORD_LENGTH 2
|
||||
#define MAX_WORD_LENGTH 19
|
||||
#define MIN_HASH_VALUE 5
|
||||
#define MAX_HASH_VALUE 72
|
||||
/* maximum key range = 68, duplicates = 0 */
|
||||
#define MIN_HASH_VALUE 2
|
||||
#define MAX_HASH_VALUE 89
|
||||
/* maximum key range = 88, duplicates = 0 */
|
||||
|
||||
#ifndef GPERF_DOWNCASE
|
||||
#define GPERF_DOWNCASE 1
|
||||
|
@ -101,52 +101,55 @@ hash (register const char *str, register size_t len)
|
|||
{
|
||||
static const unsigned char asso_values[] =
|
||||
{
|
||||
73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
|
||||
73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
|
||||
73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
|
||||
73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
|
||||
73, 73, 73, 73, 73, 0, 73, 73, 73, 73,
|
||||
73, 73, 73, 0, 73, 73, 73, 73, 73, 73,
|
||||
73, 73, 73, 73, 73, 5, 20, 0, 15, 0,
|
||||
0, 45, 20, 5, 73, 10, 35, 5, 5, 5,
|
||||
20, 73, 5, 30, 0, 0, 15, 20, 73, 15,
|
||||
73, 73, 73, 73, 73, 73, 73, 5, 20, 0,
|
||||
15, 0, 0, 45, 20, 5, 73, 10, 35, 5,
|
||||
5, 5, 20, 73, 5, 30, 0, 0, 15, 20,
|
||||
73, 15, 73, 73, 73, 73, 73, 73, 73, 73,
|
||||
73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
|
||||
73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
|
||||
73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
|
||||
73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
|
||||
73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
|
||||
73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
|
||||
73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
|
||||
73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
|
||||
73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
|
||||
73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
|
||||
73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
|
||||
73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
|
||||
73, 73, 73, 73, 73, 73
|
||||
90, 90, 90, 90, 90, 90, 90, 90, 90, 90,
|
||||
90, 90, 90, 90, 90, 90, 90, 90, 90, 90,
|
||||
90, 90, 90, 90, 90, 90, 90, 90, 90, 90,
|
||||
90, 90, 90, 90, 90, 90, 90, 90, 90, 90,
|
||||
90, 90, 90, 90, 90, 30, 90, 90, 90, 90,
|
||||
90, 90, 90, 90, 90, 90, 90, 90, 90, 90,
|
||||
90, 90, 90, 90, 90, 5, 5, 30, 50, 0,
|
||||
35, 30, 0, 5, 90, 40, 0, 30, 0, 20,
|
||||
45, 90, 0, 5, 10, 5, 0, 15, 20, 25,
|
||||
90, 90, 90, 90, 90, 90, 90, 5, 5, 30,
|
||||
50, 0, 35, 30, 0, 5, 90, 40, 0, 30,
|
||||
0, 20, 45, 90, 0, 5, 10, 5, 0, 15,
|
||||
20, 25, 90, 90, 90, 90, 90, 90, 90, 90,
|
||||
90, 90, 90, 90, 90, 90, 90, 90, 90, 90,
|
||||
90, 90, 90, 90, 90, 90, 90, 90, 90, 90,
|
||||
90, 90, 90, 90, 90, 90, 90, 90, 90, 90,
|
||||
90, 90, 90, 90, 90, 90, 90, 90, 90, 90,
|
||||
90, 90, 90, 90, 90, 90, 90, 90, 90, 90,
|
||||
90, 90, 90, 90, 90, 90, 90, 90, 90, 90,
|
||||
90, 90, 90, 90, 90, 90, 90, 90, 90, 90,
|
||||
90, 90, 90, 90, 90, 90, 90, 90, 90, 90,
|
||||
90, 90, 90, 90, 90, 90, 90, 90, 90, 90,
|
||||
90, 90, 90, 90, 90, 90, 90, 90, 90, 90,
|
||||
90, 90, 90, 90, 90, 90, 90, 90, 90, 90,
|
||||
90, 90, 90, 90, 90, 90, 90, 90, 90, 90,
|
||||
90, 90, 90, 90, 90, 90
|
||||
};
|
||||
register unsigned int hval = len;
|
||||
|
||||
switch (hval)
|
||||
{
|
||||
default:
|
||||
hval += asso_values[(unsigned char)str[8]];
|
||||
hval += asso_values[(unsigned char)str[9]];
|
||||
/*FALLTHROUGH*/
|
||||
case 9:
|
||||
case 8:
|
||||
case 7:
|
||||
case 6:
|
||||
case 5:
|
||||
case 4:
|
||||
hval += asso_values[(unsigned char)str[3]];
|
||||
/*FALLTHROUGH*/
|
||||
case 3:
|
||||
hval += asso_values[(unsigned char)str[2]];
|
||||
/*FALLTHROUGH*/
|
||||
case 2:
|
||||
case 1:
|
||||
hval += asso_values[(unsigned char)str[0]];
|
||||
break;
|
||||
}
|
||||
return hval + asso_values[(unsigned char)str[len - 1]];
|
||||
return hval;
|
||||
}
|
||||
|
||||
const struct HttpHeaderSlot *
|
||||
|
@ -154,116 +157,134 @@ LookupHttpHeader (register const char *str, register size_t len)
|
|||
{
|
||||
static const struct HttpHeaderSlot wordlist[] =
|
||||
{
|
||||
{""}, {""}, {""}, {""}, {""},
|
||||
#line 23 "gethttpheader.gperf"
|
||||
{"Close", kHttpClose},
|
||||
{""},
|
||||
#line 52 "gethttpheader.gperf"
|
||||
{"Upgrade", kHttpUpgrade},
|
||||
{""}, {""},
|
||||
#line 65 "gethttpheader.gperf"
|
||||
{"TE", kHttpTe},
|
||||
#line 18 "gethttpheader.gperf"
|
||||
{"Age", kHttpAge},
|
||||
#line 36 "gethttpheader.gperf"
|
||||
{"From", kHttpFrom},
|
||||
{""}, {""},
|
||||
#line 58 "gethttpheader.gperf"
|
||||
{"Server", kHttpServer},
|
||||
#line 60 "gethttpheader.gperf"
|
||||
{"Warning", kHttpWarning},
|
||||
#line 54 "gethttpheader.gperf"
|
||||
{"Via", kHttpVia},
|
||||
{""},
|
||||
#line 24 "gethttpheader.gperf"
|
||||
{"Connection", kHttpConnection},
|
||||
#line 56 "gethttpheader.gperf"
|
||||
{"Public", kHttpPublic},
|
||||
#line 22 "gethttpheader.gperf"
|
||||
{"Chunked", kHttpChunked},
|
||||
#line 66 "gethttpheader.gperf"
|
||||
{"DNT", kHttpDnt},
|
||||
#line 33 "gethttpheader.gperf"
|
||||
{"Date", kHttpDate},
|
||||
{""}, {""},
|
||||
#line 64 "gethttpheader.gperf"
|
||||
{"Trailer", kHttpTrailer},
|
||||
{""},
|
||||
#line 37 "gethttpheader.gperf"
|
||||
{"Host", kHttpHost},
|
||||
#line 53 "gethttpheader.gperf"
|
||||
{"User-Agent", kHttpUserAgent},
|
||||
#line 57 "gethttpheader.gperf"
|
||||
{"Retry-After", kHttpRetryAfter},
|
||||
#line 51 "gethttpheader.gperf"
|
||||
{"Transfer-Encoding", kHttpTransferEncoding},
|
||||
{""},
|
||||
#line 28 "gethttpheader.gperf"
|
||||
{"Content-Length", kHttpContentLength},
|
||||
#line 19 "gethttpheader.gperf"
|
||||
{"Allow", kHttpAllow},
|
||||
#line 26 "gethttpheader.gperf"
|
||||
{"Content-Encoding", kHttpContentEncoding},
|
||||
#line 25 "gethttpheader.gperf"
|
||||
{"Content-Base", kHttpContentBase},
|
||||
#line 31 "gethttpheader.gperf"
|
||||
{"Content-Range", kHttpContentRange},
|
||||
#line 59 "gethttpheader.gperf"
|
||||
{"Vary", kHttpVary},
|
||||
#line 23 "gethttpheader.gperf"
|
||||
{"Close", kHttpClose},
|
||||
#line 27 "gethttpheader.gperf"
|
||||
{"Content-Language", kHttpContentLanguage},
|
||||
{""},
|
||||
#line 20 "gethttpheader.gperf"
|
||||
{"Authorization", kHttpAuthorization},
|
||||
{""},
|
||||
#line 49 "gethttpheader.gperf"
|
||||
{"Range", kHttpRange},
|
||||
#line 14 "gethttpheader.gperf"
|
||||
{"Accept", kHttpAccept},
|
||||
#line 32 "gethttpheader.gperf"
|
||||
{"Content-Type", kHttpContentType},
|
||||
#line 52 "gethttpheader.gperf"
|
||||
{"Upgrade", kHttpUpgrade},
|
||||
#line 41 "gethttpheader.gperf"
|
||||
{"If-Range", kHttpIfRange},
|
||||
#line 34 "gethttpheader.gperf"
|
||||
{"ETag", kHttpEtag},
|
||||
{""},
|
||||
#line 53 "gethttpheader.gperf"
|
||||
{"User-Agent", kHttpUserAgent},
|
||||
#line 30 "gethttpheader.gperf"
|
||||
{"Content-Md5", kHttpContentMd5},
|
||||
#line 50 "gethttpheader.gperf"
|
||||
{"Referer", kHttpReferer},
|
||||
#line 31 "gethttpheader.gperf"
|
||||
{"Content-Range", kHttpContentRange},
|
||||
#line 33 "gethttpheader.gperf"
|
||||
{"Date", kHttpDate},
|
||||
#line 24 "gethttpheader.gperf"
|
||||
{"Connection", kHttpConnection},
|
||||
#line 57 "gethttpheader.gperf"
|
||||
{"Retry-After", kHttpRetryAfter},
|
||||
#line 22 "gethttpheader.gperf"
|
||||
{"Chunked", kHttpChunked},
|
||||
#line 54 "gethttpheader.gperf"
|
||||
{"Via", kHttpVia},
|
||||
#line 37 "gethttpheader.gperf"
|
||||
{"Host", kHttpHost},
|
||||
#line 17 "gethttpheader.gperf"
|
||||
{"Accept-Language", kHttpAcceptLanguage},
|
||||
#line 56 "gethttpheader.gperf"
|
||||
{"Public", kHttpPublic},
|
||||
#line 39 "gethttpheader.gperf"
|
||||
{"If-Modified-Since", kHttpIfModifiedSince},
|
||||
#line 20 "gethttpheader.gperf"
|
||||
{"Authorization", kHttpAuthorization},
|
||||
#line 42 "gethttpheader.gperf"
|
||||
{"If-Unmodified-Since", kHttpIfUnmodifiedSince},
|
||||
#line 19 "gethttpheader.gperf"
|
||||
{"Allow", kHttpAllow},
|
||||
#line 45 "gethttpheader.gperf"
|
||||
{"Pragma", kHttpPragma},
|
||||
#line 25 "gethttpheader.gperf"
|
||||
{"Content-Base", kHttpContentBase},
|
||||
#line 38 "gethttpheader.gperf"
|
||||
{"If-Match", kHttpIfMatch},
|
||||
#line 59 "gethttpheader.gperf"
|
||||
{"Vary", kHttpVary},
|
||||
#line 50 "gethttpheader.gperf"
|
||||
{"Referer", kHttpReferer},
|
||||
#line 55 "gethttpheader.gperf"
|
||||
{"Location", kHttpLocation},
|
||||
{""},
|
||||
#line 17 "gethttpheader.gperf"
|
||||
{"Accept-Language", kHttpAcceptLanguage},
|
||||
#line 29 "gethttpheader.gperf"
|
||||
{"Content-Location", kHttpContentLocation},
|
||||
#line 32 "gethttpheader.gperf"
|
||||
{"Content-Type", kHttpContentType},
|
||||
#line 40 "gethttpheader.gperf"
|
||||
{"If-None-Match", kHttpIfNoneMatch},
|
||||
#line 15 "gethttpheader.gperf"
|
||||
{"Accept-Charset", kHttpAcceptCharset},
|
||||
{""},
|
||||
#line 67 "gethttpheader.gperf"
|
||||
{"Expect", kHttpExpect},
|
||||
{""},
|
||||
#line 21 "gethttpheader.gperf"
|
||||
{"Cache-Control", kHttpCacheControl},
|
||||
#line 36 "gethttpheader.gperf"
|
||||
{"From", kHttpFrom},
|
||||
#line 43 "gethttpheader.gperf"
|
||||
{"Keep-Alive", kHttpKeepAlive},
|
||||
#line 61 "gethttpheader.gperf"
|
||||
{"Www-Authenticate", kHttpWwwAuthenticate},
|
||||
#line 48 "gethttpheader.gperf"
|
||||
{"Proxy-Connection", kHttpProxyConnection},
|
||||
#line 35 "gethttpheader.gperf"
|
||||
{"Expires", kHttpExpires},
|
||||
#line 46 "gethttpheader.gperf"
|
||||
{"Proxy-Authenticate", kHttpProxyAuthenticate},
|
||||
#line 15 "gethttpheader.gperf"
|
||||
{"Accept-Charset", kHttpAcceptCharset},
|
||||
{""},
|
||||
#line 58 "gethttpheader.gperf"
|
||||
{"Server", kHttpServer},
|
||||
{""},
|
||||
#line 40 "gethttpheader.gperf"
|
||||
{"If-None-Match", kHttpIfNoneMatch},
|
||||
#line 47 "gethttpheader.gperf"
|
||||
{"Proxy-Authorization", kHttpProxyAuthorization},
|
||||
{""},
|
||||
#line 48 "gethttpheader.gperf"
|
||||
{"Proxy-Connection", kHttpProxyConnection},
|
||||
{""},
|
||||
#line 55 "gethttpheader.gperf"
|
||||
{"Location", kHttpLocation},
|
||||
#line 34 "gethttpheader.gperf"
|
||||
{"Etag", kHttpEtag},
|
||||
{""},
|
||||
#line 27 "gethttpheader.gperf"
|
||||
{"Content-Language", kHttpContentLanguage},
|
||||
#line 61 "gethttpheader.gperf"
|
||||
{"WWW-Authenticate", kHttpWwwAuthenticate},
|
||||
#line 44 "gethttpheader.gperf"
|
||||
{"Max-Forwards", kHttpMaxForwards},
|
||||
#line 21 "gethttpheader.gperf"
|
||||
{"Cache-Control", kHttpCacheControl},
|
||||
{""}, {""},
|
||||
#line 29 "gethttpheader.gperf"
|
||||
{"Content-Location", kHttpContentLocation},
|
||||
{""}, {""}, {""}, {""},
|
||||
#line 26 "gethttpheader.gperf"
|
||||
{"Content-Encoding", kHttpContentEncoding},
|
||||
#line 51 "gethttpheader.gperf"
|
||||
{"Transfer-Encoding", kHttpTransferEncoding},
|
||||
{""}, {""}, {""}, {""}, {""},
|
||||
#line 62 "gethttpheader.gperf"
|
||||
{"Last-Modified", kHttpLastModified},
|
||||
#line 28 "gethttpheader.gperf"
|
||||
{"Content-Length", kHttpContentLength},
|
||||
{""}, {""},
|
||||
#line 63 "gethttpheader.gperf"
|
||||
{"Cookie", kHttpCookie},
|
||||
{""},
|
||||
#line 38 "gethttpheader.gperf"
|
||||
{"If-Match", kHttpIfMatch},
|
||||
{""}, {""},
|
||||
#line 30 "gethttpheader.gperf"
|
||||
{"Content-Md5", kHttpContentMd5},
|
||||
{""}, {""}, {""},
|
||||
#line 16 "gethttpheader.gperf"
|
||||
{"Accept-Encoding", kHttpAcceptEncoding},
|
||||
{""},
|
||||
#line 60 "gethttpheader.gperf"
|
||||
{"Warning", kHttpWarning}
|
||||
#line 39 "gethttpheader.gperf"
|
||||
{"If-Modified-Since", kHttpIfModifiedSince},
|
||||
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
|
||||
{""}, {""},
|
||||
#line 42 "gethttpheader.gperf"
|
||||
{"If-Unmodified-Since", kHttpIfUnmodifiedSince}
|
||||
};
|
||||
|
||||
if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
|
||||
|
|
|
@ -91,6 +91,9 @@ static const struct HttpReason {
|
|||
|
||||
/**
|
||||
* Returns string describing HTTP reason phrase.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc2616
|
||||
* @see https://tools.ietf.org/html/rfc6585
|
||||
*/
|
||||
const char *GetHttpReason(int code) {
|
||||
int m, l, r;
|
||||
|
|
|
@ -70,7 +70,12 @@
|
|||
#define kHttpWarning 46
|
||||
#define kHttpWwwAuthenticate 47
|
||||
#define kHttpLastModified 48
|
||||
#define kHttpHeadersMax 49
|
||||
#define kHttpCookie 49
|
||||
#define kHttpTrailer 50
|
||||
#define kHttpTe 51
|
||||
#define kHttpDnt 52
|
||||
#define kHttpExpect 53
|
||||
#define kHttpHeadersMax 54
|
||||
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
|
|
@ -45,7 +45,7 @@ TEST(parsehoststxt, testCorrectlyTokenizesAndSorts) {
|
|||
"203.0.113.2 cat.example. cat\n";
|
||||
struct HostsTxt *ht = calloc(1, sizeof(struct HostsTxt));
|
||||
FILE *f = fmemopen(NULL, BUFSIZ, "r+");
|
||||
fwrite(kInput, 1, strlen(kInput), f);
|
||||
ASSERT_EQ(1, fwrite(kInput, strlen(kInput), 1, f));
|
||||
rewind(f);
|
||||
ASSERT_EQ(0, parsehoststxt(ht, f));
|
||||
sorthoststxt(ht);
|
||||
|
|
|
@ -57,15 +57,21 @@ TEST(fgetwc, testUnicode_oneChar_writtenAsRawUtf8) {
|
|||
fclose(f);
|
||||
}
|
||||
|
||||
TEST(fgetwc, testUnicode_spuriousContChars_synchronizedBeforeRead) {
|
||||
TEST(fgetwc, testUnicode_undecodableSequences_fallsBackToBinary) {
|
||||
FILE *f = fmemopen(NULL, BUFSIZ, "r+");
|
||||
EXPECT_EQ(0x90, fputc(0x90, f));
|
||||
EXPECT_EQ(0x90, fputc(0x90, f));
|
||||
EXPECT_EQ(0200, fputc(0200, f));
|
||||
EXPECT_EQ(0220, fputc(0220, f));
|
||||
EXPECT_EQ(0xF0, fputc(0xF0, f));
|
||||
EXPECT_EQ(0x90, fputc(0x90, f));
|
||||
EXPECT_EQ(0x8C, fputc(0x8C, f));
|
||||
EXPECT_EQ(0xB0, fputc(0xB0, f));
|
||||
EXPECT_EQ(0304, fputc(0304, f));
|
||||
EXPECT_EQ('a', fputc('a', f));
|
||||
rewind(f);
|
||||
EXPECT_EQ(0200, fgetwc(f));
|
||||
EXPECT_EQ(0220, fgetwc(f));
|
||||
EXPECT_EQ(L'𐌰', fgetwc(f));
|
||||
EXPECT_EQ(0304, fgetwc(f));
|
||||
EXPECT_EQ('a', fgetwc(f));
|
||||
fclose(f);
|
||||
}
|
||||
|
|
62
test/libc/stdio/fputc_test.c
Normal file
62
test/libc/stdio/fputc_test.c
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*-*- 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/stdio/stdio.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
|
||||
FILE *f;
|
||||
char buf[512];
|
||||
char testlib_enable_tmp_setup_teardown;
|
||||
|
||||
TEST(fputc, test) {
|
||||
ASSERT_NE(NULL, (f = fopen("hog", "w+")));
|
||||
EXPECT_EQ('h', fputc('h', f));
|
||||
EXPECT_EQ(0xFF, fputc(-1, f));
|
||||
EXPECT_NE(-1, fseek(f, 0, SEEK_SET));
|
||||
EXPECT_EQ('h', fgetc(f));
|
||||
EXPECT_EQ(0, fread(NULL, 0, 0, f));
|
||||
EXPECT_FALSE(feof(f));
|
||||
EXPECT_EQ(0xFF, fgetc(f));
|
||||
EXPECT_EQ(-1, fgetc(f));
|
||||
EXPECT_TRUE(feof(f));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
ASSERT_NE(NULL, (f = fopen("hog", "r")));
|
||||
EXPECT_EQ(2, fread(buf, 1, 512, f));
|
||||
EXPECT_TRUE(!memcmp(buf, "h\377", 2));
|
||||
EXPECT_TRUE(feof(f));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
}
|
||||
|
||||
TEST(fgetc, testUnbuffered) {
|
||||
ASSERT_NE(NULL, (f = fopen("hog", "w+")));
|
||||
EXPECT_NE(-1, setvbuf(f, NULL, _IONBF, 0));
|
||||
EXPECT_EQ('h', fputc('h', f));
|
||||
EXPECT_EQ(0xFF, fputc(-1, f));
|
||||
EXPECT_NE(-1, fseek(f, 0, SEEK_SET));
|
||||
EXPECT_EQ('h', fgetc(f));
|
||||
EXPECT_EQ(0xFF, fgetc(f));
|
||||
EXPECT_EQ(-1, fgetc(f));
|
||||
EXPECT_TRUE(feof(f));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
ASSERT_NE(NULL, (f = fopen("hog", "r")));
|
||||
EXPECT_EQ(2, fread(buf, 1, 512, f));
|
||||
EXPECT_TRUE(!memcmp(buf, "h\377", 2));
|
||||
EXPECT_TRUE(feof(f));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
}
|
|
@ -23,10 +23,35 @@
|
|||
#include "libc/testlib/hyperion.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
|
||||
FILE *f;
|
||||
char buf[512];
|
||||
char testlib_enable_tmp_setup_teardown;
|
||||
|
||||
TEST(fputs, test) {
|
||||
ASSERT_NE(NULL, (f = fopen("hog", "w")));
|
||||
EXPECT_EQ(5, fputs("hello", f));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
ASSERT_NE(NULL, (f = fopen("hog", "r")));
|
||||
EXPECT_EQ(5, fread(buf, 1, 512, f));
|
||||
EXPECT_TRUE(!memcmp(buf, "hello", 5));
|
||||
EXPECT_TRUE(feof(f));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
}
|
||||
|
||||
TEST(puts, test) {
|
||||
ASSERT_NE(NULL, (f = fopen("hog", "w")));
|
||||
EXPECT_EQ(5, fputs("hello", f));
|
||||
EXPECT_EQ('\n', fputc('\n', f));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
ASSERT_NE(NULL, (f = fopen("hog", "r")));
|
||||
EXPECT_EQ(6, fread(buf, 1, 512, f));
|
||||
EXPECT_TRUE(!memcmp(buf, "hello\n", 6));
|
||||
EXPECT_TRUE(feof(f));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
}
|
||||
|
||||
BENCH(fputs, bench) {
|
||||
FILE *f;
|
||||
char *buf;
|
||||
buf = gc(malloc(kHyperionSize));
|
||||
char *buf = gc(malloc(kHyperionSize));
|
||||
EZBENCH2("fputs", f = fmemopen(buf, kHyperionSize, "r+"),
|
||||
fputs(kHyperion, f));
|
||||
}
|
||||
|
|
177
test/libc/stdio/ftell_test.c
Normal file
177
test/libc/stdio/ftell_test.c
Normal file
|
@ -0,0 +1,177 @@
|
|||
/*-*- 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/mem/mem.h"
|
||||
#include "libc/runtime/gc.internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/x/x.h"
|
||||
|
||||
FILE *f;
|
||||
char buf[512];
|
||||
char testlib_enable_tmp_setup_teardown;
|
||||
|
||||
TEST(ftell, test) {
|
||||
ASSERT_NE(NULL, (f = fopen("hog", "w")));
|
||||
EXPECT_EQ(0, ftell(f));
|
||||
EXPECT_EQ(5, fputs("hello", f));
|
||||
EXPECT_EQ(5, ftell(f));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
ASSERT_NE(NULL, (f = fopen("hog", "r")));
|
||||
EXPECT_EQ(0, ftell(f));
|
||||
EXPECT_EQ(1, fread(buf, 1, 1, f));
|
||||
EXPECT_EQ(1, ftell(f));
|
||||
EXPECT_EQ('h', buf[0]);
|
||||
EXPECT_EQ(2, fread(buf, 1, 2, f));
|
||||
EXPECT_EQ(3, ftell(f));
|
||||
EXPECT_EQ('e', buf[0]);
|
||||
EXPECT_EQ('l', buf[1]);
|
||||
EXPECT_FALSE(feof(f));
|
||||
EXPECT_EQ(2, fread(buf, 1, 512, f));
|
||||
EXPECT_EQ(5, ftell(f));
|
||||
EXPECT_TRUE(feof(f));
|
||||
EXPECT_EQ('l', buf[0]);
|
||||
EXPECT_EQ('o', buf[1]);
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
}
|
||||
|
||||
TEST(ftell, testSmallBuffer) {
|
||||
ASSERT_NE(NULL, (f = fopen("hog", "w")));
|
||||
EXPECT_NE(-1, setvbuf(f, gc(xmalloc(1)), _IOFBF, 1));
|
||||
EXPECT_EQ(0, ftell(f));
|
||||
EXPECT_EQ(5, fputs("hello", f));
|
||||
EXPECT_EQ(5, ftell(f));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
ASSERT_NE(NULL, (f = fopen("hog", "r")));
|
||||
EXPECT_NE(-1, setvbuf(f, gc(xmalloc(1)), _IOFBF, 1));
|
||||
EXPECT_EQ(0, ftell(f));
|
||||
EXPECT_EQ(1, fread(buf, 1, 1, f));
|
||||
EXPECT_EQ(1, ftell(f));
|
||||
EXPECT_EQ('h', buf[0]);
|
||||
EXPECT_EQ(2, fread(buf, 1, 2, f));
|
||||
EXPECT_FALSE(feof(f));
|
||||
EXPECT_EQ(3, ftell(f));
|
||||
EXPECT_EQ('e', buf[0]);
|
||||
EXPECT_EQ('l', buf[1]);
|
||||
EXPECT_FALSE(feof(f));
|
||||
EXPECT_EQ(2, fread(buf, 1, 512, f));
|
||||
EXPECT_EQ(5, ftell(f));
|
||||
EXPECT_TRUE(feof(f));
|
||||
EXPECT_EQ('l', buf[0]);
|
||||
EXPECT_EQ('o', buf[1]);
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
}
|
||||
|
||||
TEST(ftell, testNoBuffer) {
|
||||
ASSERT_NE(NULL, (f = fopen("hog", "w")));
|
||||
EXPECT_NE(-1, setvbuf(f, gc(xmalloc(0)), _IONBF, 0));
|
||||
EXPECT_EQ(0, ftell(f));
|
||||
EXPECT_EQ(5, fputs("hello", f));
|
||||
EXPECT_EQ(5, ftell(f));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
ASSERT_NE(NULL, (f = fopen("hog", "r")));
|
||||
EXPECT_NE(-1, setvbuf(f, gc(xmalloc(0)), _IONBF, 0));
|
||||
EXPECT_EQ(0, ftell(f));
|
||||
EXPECT_EQ(1, fread(buf, 1, 1, f));
|
||||
EXPECT_EQ(1, ftell(f));
|
||||
EXPECT_EQ('h', buf[0]);
|
||||
EXPECT_EQ(2, fread(buf, 1, 2, f));
|
||||
EXPECT_FALSE(feof(f));
|
||||
EXPECT_EQ(3, ftell(f));
|
||||
EXPECT_EQ('e', buf[0]);
|
||||
EXPECT_EQ('l', buf[1]);
|
||||
EXPECT_FALSE(feof(f));
|
||||
EXPECT_EQ(2, fread(buf, 1, 512, f));
|
||||
EXPECT_EQ(5, ftell(f));
|
||||
EXPECT_TRUE(feof(f));
|
||||
EXPECT_EQ('l', buf[0]);
|
||||
EXPECT_EQ('o', buf[1]);
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
}
|
||||
|
||||
TEST(fseek, test) {
|
||||
ASSERT_NE(NULL, (f = fopen("hog", "w")));
|
||||
EXPECT_EQ(0, ftell(f));
|
||||
EXPECT_EQ(5, fputs("hello", f));
|
||||
EXPECT_EQ(5, ftell(f));
|
||||
EXPECT_EQ(0, fseek(f, -3, SEEK_CUR));
|
||||
EXPECT_EQ(2, ftell(f));
|
||||
EXPECT_EQ(1, fputs("L", f));
|
||||
EXPECT_EQ(3, ftell(f));
|
||||
EXPECT_EQ(0, fseek(f, -1, SEEK_END));
|
||||
EXPECT_EQ(4, ftell(f));
|
||||
EXPECT_EQ(1, fputs("O", f));
|
||||
EXPECT_EQ(5, ftell(f));
|
||||
EXPECT_EQ(0, fseek(f, 0, SEEK_SET));
|
||||
EXPECT_EQ(0, ftell(f));
|
||||
EXPECT_EQ(1, fputs("H", f));
|
||||
EXPECT_EQ(1, ftell(f));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
ASSERT_NE(NULL, (f = fopen("hog", "r")));
|
||||
EXPECT_EQ(5, fread(buf, 1, 512, f));
|
||||
EXPECT_BINEQ(u"HeLlO", buf);
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
}
|
||||
|
||||
TEST(fseek, testMemory) {
|
||||
ASSERT_NE(NULL, (f = fmemopen(gc(strdup("hello")), 5, "r+")));
|
||||
EXPECT_EQ(0, fseek(f, 0, SEEK_END));
|
||||
EXPECT_EQ(5, ftell(f));
|
||||
EXPECT_EQ(0, fseek(f, -3, SEEK_CUR));
|
||||
EXPECT_EQ(2, ftell(f));
|
||||
EXPECT_EQ(1, fputs("L", f));
|
||||
EXPECT_EQ(3, ftell(f));
|
||||
EXPECT_EQ(0, fseek(f, -1, SEEK_END));
|
||||
EXPECT_EQ(4, ftell(f));
|
||||
EXPECT_EQ(1, fputs("O", f));
|
||||
EXPECT_EQ(5, ftell(f));
|
||||
EXPECT_EQ(0, fseek(f, 0, SEEK_SET));
|
||||
EXPECT_EQ(0, ftell(f));
|
||||
EXPECT_EQ(1, fputs("H", f));
|
||||
EXPECT_EQ(1, ftell(f));
|
||||
rewind(f);
|
||||
EXPECT_EQ(5, fread(buf, 1, 512, f));
|
||||
EXPECT_BINEQ(u"HeLlO", buf);
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
}
|
||||
|
||||
TEST(fseek, testSmallBuffer) {
|
||||
ASSERT_NE(NULL, (f = fopen("hog", "w")));
|
||||
EXPECT_NE(-1, setvbuf(f, gc(xmalloc(1)), _IOFBF, 1));
|
||||
EXPECT_EQ(0, ftell(f));
|
||||
EXPECT_EQ(5, fputs("hello", f));
|
||||
EXPECT_EQ(5, ftell(f));
|
||||
EXPECT_EQ(0, fseek(f, -3, SEEK_CUR));
|
||||
EXPECT_EQ(2, ftell(f));
|
||||
EXPECT_EQ(1, fputs("L", f));
|
||||
EXPECT_EQ(3, ftell(f));
|
||||
EXPECT_EQ(0, fseek(f, -1, SEEK_END));
|
||||
EXPECT_EQ(4, ftell(f));
|
||||
EXPECT_EQ(1, fputs("O", f));
|
||||
EXPECT_EQ(5, ftell(f));
|
||||
EXPECT_EQ(0, fseek(f, 0, SEEK_SET));
|
||||
EXPECT_EQ(0, ftell(f));
|
||||
EXPECT_EQ(1, fputs("H", f));
|
||||
EXPECT_EQ(1, ftell(f));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
ASSERT_NE(NULL, (f = fopen("hog", "r")));
|
||||
EXPECT_EQ(5, fread(buf, 1, 512, f));
|
||||
EXPECT_BINEQ(u"HeLlO", buf);
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
}
|
|
@ -17,37 +17,164 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/rand/rand.h"
|
||||
#include "libc/runtime/gc.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/time/time.h"
|
||||
|
||||
/* TODO(jart): O_APPEND on Windows */
|
||||
|
||||
#define PATH "hog"
|
||||
|
||||
FILE *f;
|
||||
char buf[512];
|
||||
char testlib_enable_tmp_setup_teardown;
|
||||
|
||||
TEST(fwrite, test) {
|
||||
FILE *f;
|
||||
char buf[512];
|
||||
|
||||
ASSERT_NE(NULL, (f = fopen("hog", "wb")));
|
||||
ASSERT_NE(NULL, (f = fopen(PATH, "wb")));
|
||||
EXPECT_EQ(-1, fgetc(f));
|
||||
EXPECT_EQ(5, fwrite("hello", 1, 5, f));
|
||||
EXPECT_EQ(5, ftell(f));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
|
||||
ASSERT_NE(NULL, (f = fopen("hog", "r")));
|
||||
EXPECT_EQ(-1, fwrite("hello", 1, 5, f));
|
||||
ASSERT_NE(NULL, (f = fopen(PATH, "r")));
|
||||
EXPECT_EQ(0, fwrite("hello", 1, 5, f));
|
||||
EXPECT_EQ(EBADF, ferror(f));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
|
||||
ASSERT_NE(NULL, (f = fopen("hog", "a+b")));
|
||||
ASSERT_NE(NULL, (f = fopen(PATH, "a+b")));
|
||||
EXPECT_EQ(5, fwrite("hello", 1, 5, f));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
|
||||
/* TODO(jart): O_APPEND on Windows */
|
||||
if (!IsWindows()) {
|
||||
ASSERT_NE(NULL, (f = fopen("hog", "r")));
|
||||
EXPECT_EQ(10, fread(buf, 1, 10, f));
|
||||
EXPECT_TRUE(!memcmp(buf, "hellohello", 10));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
}
|
||||
if (IsWindows()) return;
|
||||
ASSERT_NE(NULL, (f = fopen(PATH, "r")));
|
||||
EXPECT_EQ(10, fread(buf, 1, 10, f));
|
||||
EXPECT_TRUE(!memcmp(buf, "hellohello", 10));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
}
|
||||
|
||||
TEST(fwrite, testSmallBuffer) {
|
||||
ASSERT_NE(NULL, (f = fopen(PATH, "wb")));
|
||||
setbuffer(f, gc(malloc(1)), 1);
|
||||
EXPECT_EQ(-1, fgetc(f));
|
||||
EXPECT_EQ(5, fwrite("hello", 1, 5, f));
|
||||
EXPECT_EQ(5, ftell(f));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
ASSERT_NE(NULL, (f = fopen(PATH, "r")));
|
||||
setbuffer(f, gc(malloc(1)), 1);
|
||||
EXPECT_EQ(0, fwrite("hello", 1, 5, f));
|
||||
EXPECT_EQ(EBADF, ferror(f));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
ASSERT_NE(NULL, (f = fopen(PATH, "a")));
|
||||
setbuffer(f, gc(malloc(1)), 1);
|
||||
EXPECT_EQ(5, fwrite("hello", 1, 5, f));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
if (IsWindows()) return;
|
||||
ASSERT_NE(NULL, (f = fopen(PATH, "r")));
|
||||
setbuffer(f, gc(malloc(1)), 1);
|
||||
EXPECT_EQ(10, fread(buf, 1, 10, f));
|
||||
EXPECT_TRUE(!memcmp(buf, "hellohello", 10));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
}
|
||||
|
||||
TEST(fwrite, testLineBuffer) {
|
||||
ASSERT_NE(NULL, (f = fopen(PATH, "wb")));
|
||||
setvbuf(f, NULL, _IOLBF, 64);
|
||||
EXPECT_EQ(-1, fgetc(f));
|
||||
EXPECT_EQ(5, fwrite("heyy\n", 1, 5, f));
|
||||
EXPECT_EQ(0, fread(buf, 0, 0, f));
|
||||
EXPECT_FALSE(feof(f));
|
||||
EXPECT_EQ(0, fread(buf, 1, 10, f));
|
||||
EXPECT_EQ(EBADF, ferror(f));
|
||||
EXPECT_EQ(5, ftell(f));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
ASSERT_NE(NULL, (f = fopen(PATH, "r")));
|
||||
setvbuf(f, NULL, _IOLBF, 64);
|
||||
EXPECT_EQ(0, fwrite("heyy\n", 1, 5, f));
|
||||
EXPECT_EQ(EBADF, ferror(f));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
ASSERT_NE(NULL, (f = fopen(PATH, "a")));
|
||||
setvbuf(f, NULL, _IOLBF, 64);
|
||||
EXPECT_EQ(5, fwrite("heyy\n", 1, 5, f));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
if (IsWindows()) return;
|
||||
ASSERT_NE(NULL, (f = fopen(PATH, "r")));
|
||||
setvbuf(f, NULL, _IOLBF, 64);
|
||||
EXPECT_EQ(10, fread(buf, 1, 10, f));
|
||||
EXPECT_TRUE(!memcmp(buf, "heyy\nheyy\n", 10));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
}
|
||||
|
||||
TEST(fwrite, testNoBuffer) {
|
||||
ASSERT_NE(NULL, (f = fopen(PATH, "wb")));
|
||||
setvbuf(f, NULL, _IONBF, 64);
|
||||
EXPECT_EQ(-1, fgetc(f));
|
||||
EXPECT_EQ(5, fwrite("heyy\n", 1, 5, f));
|
||||
EXPECT_EQ(5, ftell(f));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
ASSERT_NE(NULL, (f = fopen(PATH, "r")));
|
||||
setvbuf(f, NULL, _IONBF, 64);
|
||||
EXPECT_EQ(0, fwrite("heyy\n", 1, 5, f));
|
||||
EXPECT_EQ(EBADF, ferror(f));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
ASSERT_NE(NULL, (f = fopen(PATH, "a")));
|
||||
setvbuf(f, NULL, _IONBF, 64);
|
||||
EXPECT_EQ(5, fwrite("heyy\n", 1, 5, f));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
if (IsWindows()) return;
|
||||
ASSERT_NE(NULL, (f = fopen(PATH, "r")));
|
||||
setvbuf(f, NULL, _IONBF, 64);
|
||||
EXPECT_EQ(10, fread(buf, 1, 10, f));
|
||||
EXPECT_TRUE(!memcmp(buf, "heyy\nheyy\n", 10));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
}
|
||||
|
||||
void MeatyReadWriteTest(void) {
|
||||
size_t n;
|
||||
char *mem, *buf;
|
||||
n = 8 * 1024 * 1024;
|
||||
buf = gc(malloc(n));
|
||||
mem = rngset(gc(malloc(n)), n, rand64, -1);
|
||||
ASSERT_NE(NULL, (f = fopen(PATH, "wb")));
|
||||
setbuffer(f, gc(malloc(4 * 1000 * 1000)), 4 * 1000 * 1000);
|
||||
EXPECT_EQ(n, fwrite(mem, 1, n, f));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
ASSERT_NE(NULL, (f = fopen(PATH, "r")));
|
||||
setbuffer(f, gc(malloc(4 * 1000 * 1000)), 4 * 1000 * 1000);
|
||||
EXPECT_EQ(n, fread(buf, 1, n, f));
|
||||
EXPECT_EQ(0, memcmp(buf, mem, n));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
}
|
||||
|
||||
volatile bool gotsigint;
|
||||
void OnSigInt(int sig) {
|
||||
gotsigint = true;
|
||||
}
|
||||
|
||||
TEST(fwrite, signalStorm) {
|
||||
if (IsWindows()) return;
|
||||
int pid;
|
||||
struct sigaction oldchld, oldint;
|
||||
struct sigaction sachld = {.sa_handler = SIG_IGN};
|
||||
struct sigaction saint = {.sa_handler = OnSigInt};
|
||||
EXPECT_NE(-1, sigaction(SIGINT, &saint, &oldint));
|
||||
EXPECT_NE(-1, sigaction(SIGCHLD, &sachld, &oldchld));
|
||||
ASSERT_NE(-1, (pid = fork()));
|
||||
if (!pid) {
|
||||
do {
|
||||
ASSERT_NE(-1, kill(getppid(), SIGINT));
|
||||
usleep(1);
|
||||
} while (!gotsigint);
|
||||
_exit(0);
|
||||
}
|
||||
pause();
|
||||
MeatyReadWriteTest();
|
||||
EXPECT_NE(-1, kill(pid, SIGINT));
|
||||
while (wait(0) == -1 && errno == EINTR) donothing;
|
||||
EXPECT_NE(-1, sigaction(SIGCHLD, &oldchld, NULL));
|
||||
EXPECT_NE(-1, sigaction(SIGINT, &oldint, NULL));
|
||||
}
|
||||
|
|
|
@ -20,7 +20,12 @@
|
|||
#include "libc/mem/mem.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/testlib/ezbench.h"
|
||||
#include "libc/testlib/hyperion.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/x/x.h"
|
||||
|
||||
char testlib_enable_tmp_setup_teardown;
|
||||
|
||||
TEST(getline, testEmpty) {
|
||||
FILE *f = fmemopen("", 0, "r+");
|
||||
|
@ -72,3 +77,41 @@ TEST(getline, testBinaryLine_countExcludesOnlyTheBonusNul) {
|
|||
fclose(f);
|
||||
free(line);
|
||||
}
|
||||
|
||||
void WriteHyperionFile(void) {
|
||||
FILE *f;
|
||||
ASSERT_NE(NULL, (f = fopen("hyperion.txt", "w")));
|
||||
EXPECT_EQ(1, fwrite(kHyperion, kHyperionSize, 1, f));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
}
|
||||
|
||||
void ReadHyperionLines(void) {
|
||||
FILE *f;
|
||||
ssize_t rc;
|
||||
char *data = NULL;
|
||||
size_t size = 0;
|
||||
char *line = NULL;
|
||||
size_t linesize = 0;
|
||||
ASSERT_NE(NULL, (f = fopen("hyperion.txt", "r")));
|
||||
while ((rc = getline(&line, &linesize, f)) != -1) {
|
||||
data = xrealloc(data, size + rc);
|
||||
memcpy(data + size, line, rc);
|
||||
size += rc;
|
||||
}
|
||||
EXPECT_TRUE(feof(f));
|
||||
ASSERT_EQ(kHyperionSize, size);
|
||||
EXPECT_EQ(0, memcmp(kHyperion, data, size));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
free(line);
|
||||
free(data);
|
||||
}
|
||||
|
||||
TEST(getline, lotsOfLines_roundTrips) {
|
||||
WriteHyperionFile();
|
||||
ReadHyperionLines();
|
||||
}
|
||||
|
||||
BENCH(getline, bench) {
|
||||
WriteHyperionFile();
|
||||
EZBENCH2("getline", donothing, ReadHyperionLines());
|
||||
}
|
||||
|
|
69
test/libc/stdio/ungetc_test.c
Normal file
69
test/libc/stdio/ungetc_test.c
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*-*- 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/stdio/stdio.h"
|
||||
#include "libc/testlib/hyperion.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
|
||||
FILE *f;
|
||||
char buf[512];
|
||||
char testlib_enable_tmp_setup_teardown;
|
||||
|
||||
TEST(ungetc, testGetChar_canBeUndoneWithinReason) {
|
||||
ASSERT_NE(NULL, (f = fopen("hog", "wb")));
|
||||
EXPECT_EQ(12, fputs("hello world\n", f));
|
||||
EXPECT_NE(-1, fputs(kHyperion, f));
|
||||
EXPECT_EQ(0, fclose(f));
|
||||
ASSERT_NE(NULL, (f = fopen("hog", "r")));
|
||||
EXPECT_EQ('h', fgetc(f));
|
||||
EXPECT_EQ('e', fgetc(f));
|
||||
EXPECT_EQ('e', ungetc('e', f));
|
||||
EXPECT_EQ('h', ungetc('h', f));
|
||||
EXPECT_EQ(5, fread(buf, 1, 5, f));
|
||||
EXPECT_BINEQ(u"hello", buf);
|
||||
EXPECT_EQ(0, fclose(f));
|
||||
}
|
||||
|
||||
TEST(ungetc, testRead_canBeUndoneWithinReason) {
|
||||
ASSERT_NE(NULL, (f = fopen("hog", "wb")));
|
||||
EXPECT_EQ(12, fputs("hello world\n", f));
|
||||
EXPECT_EQ(0, fclose(f));
|
||||
ASSERT_NE(NULL, (f = fopen("hog", "r")));
|
||||
EXPECT_EQ(3, fread(buf, 1, 3, f));
|
||||
EXPECT_BINEQ(u"hel", buf);
|
||||
EXPECT_EQ('l', ungetc(buf[2], f));
|
||||
EXPECT_EQ('e', ungetc(buf[1], f));
|
||||
EXPECT_EQ(4, fread(buf, 1, 4, f));
|
||||
EXPECT_BINEQ(u"ello", buf);
|
||||
EXPECT_EQ(0, fclose(f));
|
||||
}
|
||||
|
||||
TEST(ungetwc, testGetWideChar_canBeUndoneWithinReason) {
|
||||
memset(buf, 0, sizeof(buf));
|
||||
ASSERT_NE(NULL, (f = fopen("hog", "wb")));
|
||||
EXPECT_NE(-1, fputs("𐌰𐌱\n", f));
|
||||
EXPECT_EQ(0, fclose(f));
|
||||
ASSERT_NE(NULL, (f = fopen("hog", "r")));
|
||||
EXPECT_EQ(L'𐌰', fgetwc(f));
|
||||
EXPECT_EQ(L'𐌱', fgetwc(f));
|
||||
EXPECT_EQ(L'𐌱', ungetwc(L'𐌱', f));
|
||||
EXPECT_EQ(L'𐌰', ungetwc(L'𐌰', f));
|
||||
EXPECT_NE(0, fread(buf, 1, sizeof(buf), f));
|
||||
EXPECT_STREQ("𐌰𐌱\n", buf);
|
||||
EXPECT_EQ(0, fclose(f));
|
||||
}
|
|
@ -16,7 +16,10 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/gc.internal.h"
|
||||
#include "libc/testlib/ezbench.h"
|
||||
#include "libc/testlib/hyperion.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "net/http/escape.h"
|
||||
|
||||
|
@ -39,3 +42,12 @@ TEST(escapehtml, testLargeGrowth) {
|
|||
TEST(escapehtml, testEmpty) {
|
||||
EXPECT_STREQ("", gc(escapehtml("")));
|
||||
}
|
||||
|
||||
TEST(escapehtml, testAstralPlanes_doesNothing) {
|
||||
EXPECT_STREQ("𐌰", escapehtml("𐌰"));
|
||||
}
|
||||
|
||||
BENCH(escapehtml, bench) {
|
||||
EZBENCH2("escapehtml", donothing,
|
||||
free(EscapeHtml(kHyperion, kHyperionSize).data));
|
||||
}
|
||||
|
|
79
test/net/http/escapejsstringliteral_test.c
Normal file
79
test/net/http/escapejsstringliteral_test.c
Normal file
|
@ -0,0 +1,79 @@
|
|||
/*-*- 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/stdio/stdio.h"
|
||||
#include "libc/testlib/ezbench.h"
|
||||
#include "libc/testlib/hyperion.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "net/http/escape.h"
|
||||
|
||||
char *escapejs(const char *s) {
|
||||
struct EscapeResult r;
|
||||
r = EscapeJsStringLiteral(s, strlen(s));
|
||||
ASSERT_EQ(strlen(r.data), r.size);
|
||||
return r.data;
|
||||
}
|
||||
|
||||
TEST(EscapeJsStringLiteral, test) {
|
||||
EXPECT_STREQ("", escapejs(""));
|
||||
EXPECT_STREQ("\\u00ff", escapejs("\377"));
|
||||
EXPECT_STREQ("\\u00ff\\u0080\\u0080\\u0080\\u0080",
|
||||
escapejs("\377\200\200\200\200"));
|
||||
EXPECT_STREQ("\\u0001\\u0002\\u0003 \\u0026\\u003d\\u003c\\u003e\\/",
|
||||
escapejs("\1\2\3 &=<>/"));
|
||||
}
|
||||
|
||||
TEST(EscapeJsStringLiteral, testUcs2) {
|
||||
EXPECT_STREQ("\\u00d0\\u263b", escapejs("Ð☻"));
|
||||
}
|
||||
|
||||
TEST(EscapeJsStringLiteral, testAstralPlanes) {
|
||||
EXPECT_STREQ("\\ud800\\udf30\\ud800\\udf30", escapejs("𐌰𐌰"));
|
||||
}
|
||||
|
||||
TEST(EscapeJsStringLiteral, testBrokenUnicode_sparesInnocentCharacters) {
|
||||
EXPECT_STREQ("\\u00e1YO", escapejs("\xE1YO"));
|
||||
}
|
||||
|
||||
void makefile1(void) {
|
||||
FILE *f;
|
||||
struct EscapeResult r;
|
||||
r = EscapeJsStringLiteral(kHyperion, kHyperionSize);
|
||||
f = fopen("/tmp/a", "wb");
|
||||
fwrite(r.data, r.size, 1, f);
|
||||
fclose(f);
|
||||
free(r.data);
|
||||
}
|
||||
|
||||
void makefile2(void) {
|
||||
int fd;
|
||||
struct EscapeResult r;
|
||||
r = EscapeJsStringLiteral(kHyperion, kHyperionSize);
|
||||
fd = creat("/tmp/a", 0644);
|
||||
write(fd, r.data, r.size);
|
||||
close(fd);
|
||||
free(r.data);
|
||||
}
|
||||
|
||||
BENCH(EscapeJsStringLiteral, bench) {
|
||||
EZBENCH2("escapejs", donothing,
|
||||
free(EscapeJsStringLiteral(kHyperion, kHyperionSize).data));
|
||||
EZBENCH2("makefile1", donothing, makefile1());
|
||||
EZBENCH2("makefile2", donothing, makefile2());
|
||||
}
|
|
@ -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 │
|
||||
|
@ -18,18 +18,35 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/gc.internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/testlib/ezbench.h"
|
||||
#include "libc/testlib/hyperion.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "net/http/escape.h"
|
||||
|
||||
BENCH(fputs, bench) {
|
||||
FILE *f;
|
||||
char *buf = gc(malloc(kHyperionSize));
|
||||
char *buf2 = gc(malloc(kHyperionSize));
|
||||
buf2 = gc(malloc(kHyperionSize));
|
||||
f = fmemopen(buf, kHyperionSize, "r+");
|
||||
ASSERT_EQ(kHyperionSize, fread(buf2, 1, kHyperionSize, f));
|
||||
EZBENCH2("fread", f = fmemopen(buf, kHyperionSize, "r+"),
|
||||
fread(buf2, 1, kHyperionSize, f));
|
||||
char *escapeparam(const char *s) {
|
||||
struct EscapeResult r;
|
||||
r = EscapeUrlParam(s, strlen(s));
|
||||
ASSERT_EQ(strlen(r.data), r.size);
|
||||
return r.data;
|
||||
}
|
||||
|
||||
TEST(escapeparam, test) {
|
||||
EXPECT_STREQ("abc+%26%3C%3E%22%27%01%02", gc(escapeparam("abc &<>\"'\1\2")));
|
||||
}
|
||||
|
||||
TEST(escapeparam, testLargeGrowth) {
|
||||
EXPECT_STREQ("%22%22%22", gc(escapeparam("\"\"\"")));
|
||||
}
|
||||
|
||||
TEST(escapeparam, testEmpty) {
|
||||
EXPECT_STREQ("", gc(escapeparam("")));
|
||||
}
|
||||
|
||||
TEST(escapeparam, testAstralPlanes_usesUtf8HexEncoding) {
|
||||
EXPECT_STREQ("%F0%90%8C%B0", escapeparam("𐌰"));
|
||||
}
|
||||
|
||||
BENCH(escapeparam, bench) {
|
||||
EZBENCH2("escapeparam", donothing,
|
||||
free(EscapeUrlParam(kHyperion, kHyperionSize).data));
|
||||
}
|
2
third_party/lua/lbaselib.c
vendored
2
third_party/lua/lbaselib.c
vendored
|
@ -479,7 +479,7 @@ static int luaB_tostring (lua_State *L) {
|
|||
static const luaL_Reg base_funcs[] = {
|
||||
{"assert", luaB_assert},
|
||||
{"collectgarbage", luaB_collectgarbage},
|
||||
/* {"dofile", luaB_dofile}, */
|
||||
{"dofile", luaB_dofile},
|
||||
{"error", luaB_error},
|
||||
{"getmetatable", luaB_getmetatable},
|
||||
{"ipairs", luaB_ipairs},
|
||||
|
|
1
tool/net/.init.lua
Normal file
1
tool/net/.init.lua
Normal file
|
@ -0,0 +1 @@
|
|||
-- special script called by main redbean process at startup
|
1
tool/net/.reload.lua
Normal file
1
tool/net/.reload.lua
Normal file
|
@ -0,0 +1 @@
|
|||
-- special script called by main redbean process on invalidate
|
|
@ -16,7 +16,6 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/alg/arraylist2.internal.h"
|
||||
#include "libc/bits/bits.h"
|
||||
#include "libc/bits/bswap.h"
|
||||
#include "libc/bits/safemacros.internal.h"
|
||||
|
@ -137,16 +136,8 @@ USAGE\n\
|
|||
You can have redbean run as a daemon by doing the following:\n\
|
||||
\n\
|
||||
redbean.com -vv -d -L redbean.log -P redbean.pid\n\
|
||||
kill -HUP $(cat redbean.pid)\n\
|
||||
kill -TERM $(cat redbean.pid) # 1x: graceful shutdown\n\
|
||||
kill -TERM $(cat redbean.pid) # 2x: forceful shutdown\n\
|
||||
\n\
|
||||
Each connection uses a point in time snapshot of your ZIP file.\n\
|
||||
If your ZIP is deleted then serving continues. If it's replaced\n\
|
||||
then issuing SIGUSR1 (or SIGHUP if daemon) will reindex the zip\n\
|
||||
for subsequent connections without interrupting active ones. If\n\
|
||||
Ctrl-C or SIGTERM is issued then a graceful shutdown is started\n\
|
||||
but if it's issued a second time, active connections are reset.\n\
|
||||
\n\
|
||||
redbean imposes a 32kb limit on requests to limit the memory of\n\
|
||||
connection processes, which grow to whatever number your system\n\
|
||||
|
@ -328,7 +319,6 @@ static int64_t programtime;
|
|||
static size_t contentlength;
|
||||
static int64_t cacheseconds;
|
||||
static uint8_t gzip_footer[8];
|
||||
static const char *programfile;
|
||||
static const char *serverheader;
|
||||
|
||||
static struct Buffer body;
|
||||
|
@ -479,11 +469,6 @@ static const char *GetContentType(const char *path, size_t n) {
|
|||
return "application/octet-stream";
|
||||
}
|
||||
|
||||
static wontreturn void PrintUsage(FILE *f, int rc) {
|
||||
fprintf(f, "SYNOPSIS\n\n %s%s", program_invocation_name, USAGE);
|
||||
exit(rc);
|
||||
}
|
||||
|
||||
static void DescribeAddress(char buf[32], const struct sockaddr_in *addr) {
|
||||
char *p = buf;
|
||||
const uint8_t *ip4 = (const uint8_t *)&addr->sin_addr.s_addr;
|
||||
|
@ -497,7 +482,7 @@ static void DescribeAddress(char buf[32], const struct sockaddr_in *addr) {
|
|||
|
||||
static void SetDefaults(void) {
|
||||
cacheseconds = -1;
|
||||
serverheader = "redbean/0.1";
|
||||
serverheader = "redbean/0.2";
|
||||
serveraddr.sin_family = AF_INET;
|
||||
serveraddr.sin_port = htons(DEFAULT_PORT);
|
||||
serveraddr.sin_addr.s_addr = INADDR_ANY;
|
||||
|
@ -506,6 +491,11 @@ static void SetDefaults(void) {
|
|||
AddRedirect("/favicon.ico=/tool/net/redbean.ico");
|
||||
}
|
||||
|
||||
static wontreturn void PrintUsage(FILE *f, int rc) {
|
||||
fprintf(f, "SYNOPSIS\n\n %s%s", program_invocation_name, USAGE);
|
||||
exit(rc);
|
||||
}
|
||||
|
||||
static void GetOpts(int argc, char *argv[]) {
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "zhduvml:p:w:r:c:L:P:U:G:B:")) != -1) {
|
||||
|
@ -550,7 +540,7 @@ static void GetOpts(int argc, char *argv[]) {
|
|||
daemonuid = atoi(optarg);
|
||||
break;
|
||||
case 'G':
|
||||
daemonuid = atoi(optarg);
|
||||
daemongid = atoi(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
PrintUsage(stdout, EXIT_SUCCESS);
|
||||
|
@ -580,6 +570,8 @@ static void Daemonize(void) {
|
|||
freopen("/dev/null", "r", stdin);
|
||||
freopen(logpath, "a", stdout);
|
||||
freopen(logpath, "a", stderr);
|
||||
LOGIFNEG1(setuid(daemonuid));
|
||||
LOGIFNEG1(setgid(daemongid));
|
||||
}
|
||||
|
||||
static void OnWorkerExit(int pid, int ws) {
|
||||
|
@ -1551,13 +1543,19 @@ static int LuaEscapeLiteral(lua_State *L) {
|
|||
return LuaEscaper(L, EscapeJsStringLiteral);
|
||||
}
|
||||
|
||||
static const luaL_Reg kLuaLibs[] = {
|
||||
{"_G", luaopen_base}, //
|
||||
{"table", luaopen_table}, //
|
||||
{"string", luaopen_string}, //
|
||||
{"math", luaopen_math}, //
|
||||
{"utf8", luaopen_utf8}, //
|
||||
};
|
||||
static void LuaRun(const char *path) {
|
||||
struct Asset *a;
|
||||
const char *code;
|
||||
if ((a = LocateAsset(path, strlen(path)))) {
|
||||
code = LoadAsset(a, NULL);
|
||||
if (luaL_dostring(L, code) != LUA_OK) {
|
||||
WARNF("%s %s", path, lua_tostring(L, -1));
|
||||
}
|
||||
free(code);
|
||||
} else {
|
||||
DEBUGF("%s not found", path);
|
||||
}
|
||||
}
|
||||
|
||||
static const luaL_Reg kLuaFuncs[] = {
|
||||
{"EscapeFragment", LuaEscapeFragment}, //
|
||||
|
@ -1591,14 +1589,16 @@ static const luaL_Reg kLuaFuncs[] = {
|
|||
static void LuaInit(void) {
|
||||
size_t i;
|
||||
L = luaL_newstate();
|
||||
for (i = 0; i < ARRAYLEN(kLuaLibs); ++i) {
|
||||
luaL_requiref(L, kLuaLibs[i].name, kLuaLibs[i].func, 1);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
luaL_openlibs(L);
|
||||
for (i = 0; i < ARRAYLEN(kLuaFuncs); ++i) {
|
||||
lua_pushcfunction(L, kLuaFuncs[i].func);
|
||||
lua_setglobal(L, kLuaFuncs[i].name);
|
||||
}
|
||||
LuaRun("/tool/net/.init.lua");
|
||||
}
|
||||
|
||||
static void LuaReload(void) {
|
||||
LuaRun("/tool/net/.reload.lua");
|
||||
}
|
||||
|
||||
static char *ServeLua(struct Asset *a) {
|
||||
|
@ -1672,12 +1672,35 @@ static char *HandleRedirect(struct Redirect *r) {
|
|||
return p;
|
||||
}
|
||||
|
||||
static const char *DescribeClose(void) {
|
||||
if (killed) return "killed";
|
||||
if (meltdown) return "meltdown";
|
||||
if (terminated) return "terminated";
|
||||
if (connectionclose) return "connectionclose";
|
||||
return "destroyed";
|
||||
static ssize_t SendString(const char *s) {
|
||||
ssize_t rc;
|
||||
for (;;) {
|
||||
if ((rc = write(client, s, strlen(s))) != -1 || errno != EINTR) {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t SendContinue(void) {
|
||||
return SendString("\
|
||||
HTTP/1.1 100 Continue\r\n\
|
||||
\r\n");
|
||||
}
|
||||
|
||||
static ssize_t SendTimeout(void) {
|
||||
return SendString("\
|
||||
HTTP/1.1 408 Request Timeout\r\n\
|
||||
Connection: close\r\n\
|
||||
Content-Length: 0\r\n\
|
||||
\r\n");
|
||||
}
|
||||
|
||||
static ssize_t SendServiceUnavailable(void) {
|
||||
return SendString("\
|
||||
HTTP/1.1 503 Service Unavailable\r\n\
|
||||
Connection: close\r\n\
|
||||
Content-Length: 0\r\n\
|
||||
\r\n");
|
||||
}
|
||||
|
||||
static void LogClose(const char *reason) {
|
||||
|
@ -1688,6 +1711,14 @@ static void LogClose(const char *reason) {
|
|||
}
|
||||
}
|
||||
|
||||
static const char *DescribeClose(void) {
|
||||
if (killed) return "killed";
|
||||
if (meltdown) return "meltdown";
|
||||
if (terminated) return "terminated";
|
||||
if (connectionclose) return "connectionclose";
|
||||
return "destroyed";
|
||||
}
|
||||
|
||||
static char *HandleMessage(size_t hdrlen) {
|
||||
long r;
|
||||
ssize_t cl, rc;
|
||||
|
@ -1703,6 +1734,10 @@ static char *HandleMessage(size_t hdrlen) {
|
|||
CompareHeaderValue(kHttpTransferEncoding, "identity"))) {
|
||||
return ServeError(501, "Not Implemented");
|
||||
}
|
||||
if (HasHeader(kHttpExpect) &&
|
||||
CompareHeaderValue(kHttpExpect, "100-continue")) {
|
||||
return ServeError(417, "Expectation Failed");
|
||||
}
|
||||
if ((cl = ParseContentLength(inbuf.p + req.headers[kHttpContentLength].a,
|
||||
req.headers[kHttpContentLength].b -
|
||||
req.headers[kHttpContentLength].a)) == -1) {
|
||||
|
@ -1719,6 +1754,9 @@ static char *HandleMessage(size_t hdrlen) {
|
|||
if (need > inbuf.n) {
|
||||
return ServeError(413, "Payload Too Large");
|
||||
}
|
||||
if (!CompareHeaderValue(kHttpExpect, "100-continue") && httpversion >= 101) {
|
||||
SendContinue();
|
||||
}
|
||||
while (amtread < need) {
|
||||
if (++frags == 64) {
|
||||
LogClose("payload fragged");
|
||||
|
@ -1838,26 +1876,6 @@ static bool HandleRequest(void) {
|
|||
return true;
|
||||
}
|
||||
|
||||
static void SendString(const char *s) {
|
||||
write(client, s, strlen(s));
|
||||
}
|
||||
|
||||
static void SendTimeout(void) {
|
||||
SendString("\
|
||||
HTTP/1.1 408 Request Timeout\r\n\
|
||||
Connection: close\r\n\
|
||||
Content-Length: 0\r\n\
|
||||
\r\n");
|
||||
}
|
||||
|
||||
static void SendServiceUnavailable(void) {
|
||||
SendString("\
|
||||
HTTP/1.1 503 Service Unavailable\r\n\
|
||||
Connection: close\r\n\
|
||||
Content-Length: 0\r\n\
|
||||
\r\n");
|
||||
}
|
||||
|
||||
static void InitRequest(void) {
|
||||
frags = 0;
|
||||
content = NULL;
|
||||
|
@ -1968,8 +1986,7 @@ void RedBean(void) {
|
|||
if (IsWindows()) uniprocess = true;
|
||||
if (daemonize) Daemonize();
|
||||
gmtoff = GetGmtOffset();
|
||||
programfile = (const char *)getauxval(AT_EXECFN);
|
||||
CHECK(OpenZip(programfile));
|
||||
CHECK(OpenZip((const char *)getauxval(AT_EXECFN)));
|
||||
xsigaction(SIGINT, OnInt, 0, 0, 0);
|
||||
xsigaction(SIGHUP, OnHup, 0, 0, 0);
|
||||
xsigaction(SIGTERM, OnTerm, 0, 0, 0);
|
||||
|
@ -2001,21 +2018,17 @@ void RedBean(void) {
|
|||
printf("%d\n", ntohs(serveraddr.sin_port));
|
||||
fflush(stdout);
|
||||
}
|
||||
LuaInit();
|
||||
UpdateCurrentDate(nowl());
|
||||
inbuf.n = 64 * 1024;
|
||||
inbuf.p = xvalloc(inbuf.n);
|
||||
hdrbuf.n = 4 * 1024;
|
||||
hdrbuf.p = xvalloc(hdrbuf.n);
|
||||
LuaInit();
|
||||
while (!terminated) {
|
||||
if (zombied) {
|
||||
ReapZombies();
|
||||
} else if (invalidated) {
|
||||
if (OpenZip(programfile)) {
|
||||
LOGF("%s reindexed zip", serveraddrstr);
|
||||
} else {
|
||||
WARNF("%s reindexing failed", serveraddrstr);
|
||||
}
|
||||
LuaReload();
|
||||
invalidated = false;
|
||||
} else if (heartbeat) {
|
||||
UpdateCurrentDate(nowl());
|
||||
|
|
Loading…
Add table
Reference in a new issue