libc: Basic write buffers
All checks were successful
continuous-integration/drone/pr Build is passing
All checks were successful
continuous-integration/drone/pr Build is passing
This commit is contained in:
parent
cfb0ead2d9
commit
a3ed950be8
@ -41,3 +41,4 @@ luna_app(pivot_root.cpp pivot_root)
|
|||||||
luna_app(cp.cpp cp)
|
luna_app(cp.cpp cp)
|
||||||
luna_app(kill.cpp kill)
|
luna_app(kill.cpp kill)
|
||||||
luna_app(gol.cpp gol)
|
luna_app(gol.cpp gol)
|
||||||
|
luna_app(buffer-test.cpp buffer-test)
|
||||||
|
27
apps/buffer-test.cpp
Normal file
27
apps/buffer-test.cpp
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Writing incomplete line to stdout (_IOLBF=%d)...\n", stdout->_mode);
|
||||||
|
fputs("hi!", stdout);
|
||||||
|
sleep(3);
|
||||||
|
putchar('\n');
|
||||||
|
fprintf(stderr, "Incomplete line should have been written.\n");
|
||||||
|
|
||||||
|
FILE* f = fopen("/dev/console", "w+");
|
||||||
|
assert(f);
|
||||||
|
assert(setvbuf(f, NULL, _IOFBF, 0) == 0);
|
||||||
|
|
||||||
|
fprintf(stderr, "Writing long text to file (_IOFBF=%d)...\n", f->_mode);
|
||||||
|
|
||||||
|
fputs("Hello world!\nHow are you doing!\nThis is a test for many lines of buffering.\n", f);
|
||||||
|
|
||||||
|
sleep(3);
|
||||||
|
fflush(f);
|
||||||
|
|
||||||
|
fprintf(stderr, "Long text should have been written.\n");
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
}
|
@ -31,6 +31,7 @@ char* getpass()
|
|||||||
tcsetpgrp(STDIN_FILENO, getpgid(0));
|
tcsetpgrp(STDIN_FILENO, getpgid(0));
|
||||||
|
|
||||||
fputs("Password: ", stdout);
|
fputs("Password: ", stdout);
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
if (tcgetattr(STDIN_FILENO, &orig) < 0)
|
if (tcgetattr(STDIN_FILENO, &orig) < 0)
|
||||||
{
|
{
|
||||||
|
@ -10,11 +10,20 @@
|
|||||||
#define __need_NULL
|
#define __need_NULL
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#define FOPEN_MAX 64 // Make sure this value matches FD_MAX in the kernel source.
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
int _fd;
|
int _fd;
|
||||||
int _err;
|
int _err;
|
||||||
int _eof;
|
int _eof;
|
||||||
|
size_t _bufcap;
|
||||||
|
size_t _bufsize;
|
||||||
|
size_t _bufindex;
|
||||||
|
char* _buffer;
|
||||||
|
int _flags;
|
||||||
|
int _pending;
|
||||||
|
int _mode;
|
||||||
} FILE;
|
} FILE;
|
||||||
|
|
||||||
#define EOF -1
|
#define EOF -1
|
||||||
@ -26,11 +35,15 @@ extern FILE* stderr;
|
|||||||
#define stdout stdout
|
#define stdout stdout
|
||||||
#define stderr stderr
|
#define stderr stderr
|
||||||
|
|
||||||
#define BUFSIZ 1024
|
#define BUFSIZ 4096
|
||||||
#define FILENAME_MAX \
|
#define FILENAME_MAX \
|
||||||
1024 // As Luna does not impose a limit on this, this is the recommended size for character arrays holding a file
|
1024 // As Luna does not impose a limit on this, this is the recommended size for character arrays holding a file
|
||||||
// name.
|
// name.
|
||||||
|
|
||||||
|
#define _IONBF 0
|
||||||
|
#define _IOLBF 1
|
||||||
|
#define _IOFBF 2
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
@ -172,6 +185,9 @@ extern "C"
|
|||||||
/* Create a unique temporary file. */
|
/* Create a unique temporary file. */
|
||||||
FILE* tmpfile(void);
|
FILE* tmpfile(void);
|
||||||
|
|
||||||
|
/* Change a file's buffering mode and internal buffer. */
|
||||||
|
int setvbuf(FILE* stream, char* buf, int mode, size_t size);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -4,6 +4,9 @@
|
|||||||
|
|
||||||
extern char** environ;
|
extern char** environ;
|
||||||
|
|
||||||
|
extern "C" FILE* _fdopen_impl(int, const char*, int);
|
||||||
|
extern "C" void _init_stdio();
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
void libc_init(int argc, char** argv, int envc, char** envp)
|
void libc_init(int argc, char** argv, int envc, char** envp)
|
||||||
@ -11,8 +14,10 @@ extern "C"
|
|||||||
ignore(argc, argv, envc);
|
ignore(argc, argv, envc);
|
||||||
environ = envp;
|
environ = envp;
|
||||||
|
|
||||||
stdin = fdopen(STDIN_FILENO, "r");
|
_init_stdio();
|
||||||
stdout = fdopen(STDOUT_FILENO, "w");
|
|
||||||
stderr = fdopen(STDERR_FILENO, "w");
|
stdin = _fdopen_impl(STDIN_FILENO, "r", _IOLBF);
|
||||||
|
stdout = _fdopen_impl(STDOUT_FILENO, "w", _IOLBF);
|
||||||
|
stderr = _fdopen_impl(STDERR_FILENO, "w", _IONBF);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,10 @@ FILE* stdin = nullptr;
|
|||||||
FILE* stderr = nullptr;
|
FILE* stderr = nullptr;
|
||||||
FILE* stdout = nullptr;
|
FILE* stdout = nullptr;
|
||||||
|
|
||||||
|
FILE* s_open_files[FOPEN_MAX];
|
||||||
|
|
||||||
|
#define FFLAG_FREE_AFTER_BUFFER_USE (1 << 0)
|
||||||
|
|
||||||
static const char* read_tmpdir()
|
static const char* read_tmpdir()
|
||||||
{
|
{
|
||||||
const char* tmpdir = getenv("TMPDIR");
|
const char* tmpdir = getenv("TMPDIR");
|
||||||
@ -51,11 +55,71 @@ static int fdopen_check_compatible_mode(int fd, int new_flags)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int flush_write_buffer(FILE* stream)
|
||||||
|
{
|
||||||
|
if (stream->_mode == _IONBF) return 0;
|
||||||
|
|
||||||
|
ssize_t result = write(stream->_fd, stream->_buffer, stream->_bufsize);
|
||||||
|
|
||||||
|
stream->_bufindex = 0;
|
||||||
|
stream->_bufsize = 0;
|
||||||
|
|
||||||
|
return result < 0 ? EOF : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t write_into_buffer(FILE* stream, const u8* data, ssize_t size)
|
||||||
|
{
|
||||||
|
ssize_t total_written = 0;
|
||||||
|
|
||||||
|
while (size > 0)
|
||||||
|
{
|
||||||
|
ssize_t nwritten;
|
||||||
|
if (stream->_mode != _IONBF)
|
||||||
|
{
|
||||||
|
if ((stream->_bufsize + size) > stream->_bufcap)
|
||||||
|
{
|
||||||
|
if (flush_write_buffer(stream) < 0) return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t size_remaining = stream->_bufcap - stream->_bufsize;
|
||||||
|
nwritten = size > size_remaining ? size_remaining : size;
|
||||||
|
memcpy(stream->_buffer + stream->_bufsize, data, nwritten);
|
||||||
|
|
||||||
|
stream->_bufsize += nwritten;
|
||||||
|
|
||||||
|
if (stream->_mode == _IOLBF && memchr(data, '\n', nwritten))
|
||||||
|
{
|
||||||
|
if (flush_write_buffer(stream) < 0) return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
nwritten = write(stream->_fd, data, size > BUFSIZ ? BUFSIZ : size);
|
||||||
|
if (nwritten < 0) return nwritten;
|
||||||
|
size -= nwritten;
|
||||||
|
data += nwritten;
|
||||||
|
total_written += nwritten;
|
||||||
|
}
|
||||||
|
|
||||||
|
return total_written;
|
||||||
|
}
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
int fflush(FILE*)
|
void _init_stdio()
|
||||||
{
|
{
|
||||||
// FIXME: Files are not buffered right now.
|
memset(&s_open_files, 0, sizeof(s_open_files));
|
||||||
|
}
|
||||||
|
|
||||||
|
int fflush(FILE* stream)
|
||||||
|
{
|
||||||
|
if (stream && stream->_mode != _IONBF) flush_write_buffer(stream);
|
||||||
|
else if (!stream)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < FOPEN_MAX; i++)
|
||||||
|
{
|
||||||
|
if (s_open_files[i]) fflush(s_open_files[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,10 +142,19 @@ extern "C"
|
|||||||
f->_fd = fd;
|
f->_fd = fd;
|
||||||
clearerr(f);
|
clearerr(f);
|
||||||
|
|
||||||
|
f->_pending = 0;
|
||||||
|
f->_flags = flags;
|
||||||
|
f->_mode = isatty(fd) ? _IOLBF : _IOFBF;
|
||||||
|
f->_bufsize = f->_bufindex = 0;
|
||||||
|
f->_buffer = nullptr;
|
||||||
|
setvbuf(f, NULL, f->_mode, 0);
|
||||||
|
|
||||||
|
s_open_files[fd] = f;
|
||||||
|
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE* fdopen(int fd, const char* mode)
|
FILE* _fdopen_impl(int fd, const char* mode, int buffering_mode)
|
||||||
{
|
{
|
||||||
int flags;
|
int flags;
|
||||||
|
|
||||||
@ -95,9 +168,23 @@ extern "C"
|
|||||||
f->_fd = fd;
|
f->_fd = fd;
|
||||||
clearerr(f);
|
clearerr(f);
|
||||||
|
|
||||||
|
f->_pending = 0;
|
||||||
|
f->_flags = flags;
|
||||||
|
f->_mode = buffering_mode < 0 ? (isatty(fd) ? _IOLBF : _IOFBF) : buffering_mode;
|
||||||
|
f->_bufsize = f->_bufindex = 0;
|
||||||
|
f->_buffer = nullptr;
|
||||||
|
setvbuf(f, NULL, f->_mode, 0);
|
||||||
|
|
||||||
|
s_open_files[fd] = f;
|
||||||
|
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FILE* fdopen(int fd, const char* mode)
|
||||||
|
{
|
||||||
|
return _fdopen_impl(fd, mode, -1);
|
||||||
|
}
|
||||||
|
|
||||||
FILE* freopen(const char* path, const char* mode, FILE* stream)
|
FILE* freopen(const char* path, const char* mode, FILE* stream)
|
||||||
{
|
{
|
||||||
int flags;
|
int flags;
|
||||||
@ -106,6 +193,10 @@ extern "C"
|
|||||||
|
|
||||||
close(stream->_fd);
|
close(stream->_fd);
|
||||||
|
|
||||||
|
s_open_files[stream->_fd] = nullptr;
|
||||||
|
|
||||||
|
if (stream->_buffer && (stream->_pending & FFLAG_FREE_AFTER_BUFFER_USE)) free(stream->_buffer);
|
||||||
|
|
||||||
if (!path) { fail("FIXME: freopen() called with path=nullptr"); }
|
if (!path) { fail("FIXME: freopen() called with path=nullptr"); }
|
||||||
|
|
||||||
int fd = open(path, flags, 0666);
|
int fd = open(path, flags, 0666);
|
||||||
@ -114,13 +205,28 @@ extern "C"
|
|||||||
stream->_fd = fd;
|
stream->_fd = fd;
|
||||||
clearerr(stream);
|
clearerr(stream);
|
||||||
|
|
||||||
|
stream->_pending = 0;
|
||||||
|
stream->_flags = flags;
|
||||||
|
stream->_mode = isatty(fd) ? _IOLBF : _IOFBF;
|
||||||
|
stream->_bufsize = stream->_bufindex = 0;
|
||||||
|
stream->_buffer = nullptr;
|
||||||
|
setvbuf(stream, NULL, stream->_mode, 0);
|
||||||
|
|
||||||
|
s_open_files[fd] = stream;
|
||||||
|
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
int fclose(FILE* stream)
|
int fclose(FILE* stream)
|
||||||
{
|
{
|
||||||
|
if (fflush(stream) < 0) return EOF;
|
||||||
|
|
||||||
if (close(stream->_fd) < 0) return EOF;
|
if (close(stream->_fd) < 0) return EOF;
|
||||||
|
|
||||||
|
if (stream->_buffer && (stream->_pending & FFLAG_FREE_AFTER_BUFFER_USE)) free(stream->_buffer);
|
||||||
|
|
||||||
|
s_open_files[stream->_fd] = nullptr;
|
||||||
|
|
||||||
free(stream);
|
free(stream);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -155,7 +261,12 @@ extern "C"
|
|||||||
{
|
{
|
||||||
if (size * nmemb == 0) return 0;
|
if (size * nmemb == 0) return 0;
|
||||||
|
|
||||||
ssize_t nwrite = write(stream->_fd, buf, size * nmemb);
|
ssize_t nwrite = write_into_buffer(stream, (const u8*)buf, size * nmemb);
|
||||||
|
if (nwrite < 0)
|
||||||
|
{
|
||||||
|
stream->_err = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
return (size_t)nwrite / size;
|
return (size_t)nwrite / size;
|
||||||
}
|
}
|
||||||
@ -211,7 +322,7 @@ extern "C"
|
|||||||
int fputc(int c, FILE* stream)
|
int fputc(int c, FILE* stream)
|
||||||
{
|
{
|
||||||
u8 value = (u8)c;
|
u8 value = (u8)c;
|
||||||
ssize_t rc = write(stream->_fd, &value, 1);
|
ssize_t rc = write_into_buffer(stream, &value, 1);
|
||||||
if (rc <= 0) return EOF;
|
if (rc <= 0) return EOF;
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
@ -228,7 +339,7 @@ extern "C"
|
|||||||
|
|
||||||
int fputs(const char* str, FILE* stream)
|
int fputs(const char* str, FILE* stream)
|
||||||
{
|
{
|
||||||
ssize_t rc = write(stream->_fd, str, strlen(str));
|
ssize_t rc = write_into_buffer(stream, (const u8*)str, strlen(str));
|
||||||
return (rc < 0) ? -1 : 0;
|
return (rc < 0) ? -1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -496,4 +607,32 @@ extern "C"
|
|||||||
{
|
{
|
||||||
fail("FIXME: ungetc: not implemented");
|
fail("FIXME: ungetc: not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int setvbuf(FILE* stream, char* buf, int mode, size_t size)
|
||||||
|
{
|
||||||
|
int pending = 0;
|
||||||
|
if (mode < 0 || mode > _IOFBF) return errno = EINVAL, -1;
|
||||||
|
if (stream->_bufsize != 0 || stream->_bufindex != 0) return -1; // Buffer is already in use.
|
||||||
|
if (mode != _IONBF && buf == NULL)
|
||||||
|
{
|
||||||
|
size = BUFSIZ;
|
||||||
|
buf = (char*)calloc(size, 1);
|
||||||
|
if (!buf) return -1;
|
||||||
|
pending = FFLAG_FREE_AFTER_BUFFER_USE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buf = NULL;
|
||||||
|
size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stream->_buffer && (stream->_pending & FFLAG_FREE_AFTER_BUFFER_USE)) free(stream->_buffer);
|
||||||
|
|
||||||
|
stream->_buffer = buf;
|
||||||
|
stream->_bufcap = size;
|
||||||
|
stream->_mode = mode;
|
||||||
|
stream->_pending = pending;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user