2023-01-06 16:35:07 +00:00
|
|
|
#include <bits/errno-return.h>
|
2023-03-12 09:23:08 +00:00
|
|
|
#include <fcntl.h>
|
2023-01-06 19:15:43 +00:00
|
|
|
#include <luna/Format.h>
|
2023-01-06 12:31:14 +00:00
|
|
|
#include <stdio.h>
|
2023-03-12 09:23:08 +00:00
|
|
|
#include <stdlib.h>
|
2023-01-07 00:49:26 +00:00
|
|
|
#include <string.h>
|
2023-01-06 12:31:14 +00:00
|
|
|
#include <sys/syscall.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
2023-03-19 18:19:20 +00:00
|
|
|
FILE* stdin = nullptr;
|
2023-01-06 16:35:07 +00:00
|
|
|
FILE* stderr = nullptr;
|
2023-03-18 18:23:18 +00:00
|
|
|
FILE* stdout = nullptr;
|
2023-01-06 16:35:07 +00:00
|
|
|
|
2023-07-21 22:02:12 +00:00
|
|
|
FILE* s_open_files[FOPEN_MAX];
|
|
|
|
|
2023-07-22 08:58:34 +00:00
|
|
|
enum FileStatusFlags
|
|
|
|
{
|
|
|
|
BufferIsMalloced = (1 << 0),
|
|
|
|
LastRead = (1 << 1),
|
|
|
|
LastWrite = (1 << 2),
|
|
|
|
};
|
2023-07-21 22:02:12 +00:00
|
|
|
|
2023-06-19 09:52:38 +00:00
|
|
|
static const char* read_tmpdir()
|
|
|
|
{
|
|
|
|
const char* tmpdir = getenv("TMPDIR");
|
|
|
|
if (!tmpdir) return "/tmp";
|
|
|
|
return tmpdir;
|
|
|
|
}
|
|
|
|
|
2023-03-12 16:36:04 +00:00
|
|
|
static int fopen_parse_mode(const char* mode)
|
|
|
|
{
|
|
|
|
int result = 0;
|
|
|
|
switch (*mode)
|
|
|
|
{
|
|
|
|
case 'r': result |= O_RDONLY; break;
|
|
|
|
case 'w': result |= (O_WRONLY | O_CREAT | O_TRUNC); break;
|
|
|
|
case 'a': result |= (O_WRONLY | O_CREAT | O_APPEND); break;
|
|
|
|
|
|
|
|
default: errno = EINVAL; return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strchr(mode, '+')) result |= O_RDWR;
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2023-03-28 18:56:00 +00:00
|
|
|
static int fdopen_check_compatible_mode(int fd, int new_flags)
|
|
|
|
{
|
|
|
|
int old_flags = fcntl(fd, F_GETFL);
|
|
|
|
if (old_flags < 0) return -1;
|
|
|
|
|
|
|
|
int old_mode = old_flags & O_ACCMODE;
|
|
|
|
int new_mode = new_flags & O_ACCMODE;
|
|
|
|
if ((old_mode & new_mode) != new_mode)
|
|
|
|
{
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-07-21 22:02:12 +00:00
|
|
|
static int flush_write_buffer(FILE* stream)
|
|
|
|
{
|
2023-07-22 08:58:34 +00:00
|
|
|
if (stream->_buf.mode == _IONBF) return 0;
|
2023-07-21 22:02:12 +00:00
|
|
|
|
2023-07-22 08:58:34 +00:00
|
|
|
ssize_t result = write(stream->_fd, stream->_buf.buffer, stream->_buf.size);
|
2023-07-21 22:02:12 +00:00
|
|
|
|
2023-07-22 08:58:34 +00:00
|
|
|
stream->_buf.index = 0;
|
|
|
|
stream->_buf.size = 0;
|
2023-07-21 22:02:12 +00:00
|
|
|
|
2023-07-22 09:17:51 +00:00
|
|
|
stream->_buf.status &= ~FileStatusFlags::LastWrite;
|
|
|
|
|
2023-07-21 22:02:12 +00:00
|
|
|
return result < 0 ? EOF : 0;
|
|
|
|
}
|
|
|
|
|
2023-07-22 09:17:51 +00:00
|
|
|
static int flush_read_buffer(FILE* stream)
|
|
|
|
{
|
|
|
|
if (stream->_buf.mode == _IONBF) return 0;
|
|
|
|
|
|
|
|
// Reset the stream to its expected position.
|
|
|
|
ssize_t unread_bytes = stream->_buf.size - stream->_buf.index;
|
|
|
|
lseek(stream->_fd, -unread_bytes, SEEK_CUR);
|
|
|
|
|
|
|
|
stream->_buf.index = 0;
|
|
|
|
stream->_buf.size = 0;
|
|
|
|
|
|
|
|
stream->_buf.status &= ~FileStatusFlags::LastRead;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-07-21 22:02:12 +00:00
|
|
|
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;
|
2023-07-22 08:58:34 +00:00
|
|
|
if (stream->_buf.mode != _IONBF)
|
2023-07-21 22:02:12 +00:00
|
|
|
{
|
2023-07-22 09:17:51 +00:00
|
|
|
if (stream->_buf.status & FileStatusFlags::LastRead) flush_read_buffer(stream);
|
|
|
|
|
2023-07-22 08:58:34 +00:00
|
|
|
if ((stream->_buf.size + size) > stream->_buf.capacity)
|
2023-07-21 22:02:12 +00:00
|
|
|
{
|
|
|
|
if (flush_write_buffer(stream) < 0) return -1;
|
|
|
|
}
|
|
|
|
|
2023-07-22 08:58:34 +00:00
|
|
|
ssize_t size_remaining = stream->_buf.capacity - stream->_buf.size;
|
2023-07-21 22:02:12 +00:00
|
|
|
nwritten = size > size_remaining ? size_remaining : size;
|
2023-07-22 08:58:34 +00:00
|
|
|
memcpy(stream->_buf.buffer + stream->_buf.size, data, nwritten);
|
2023-07-21 22:02:12 +00:00
|
|
|
|
2023-07-22 09:17:51 +00:00
|
|
|
stream->_buf.status |= FileStatusFlags::LastWrite;
|
|
|
|
|
2023-07-22 08:58:34 +00:00
|
|
|
stream->_buf.size += nwritten;
|
2023-07-21 22:02:12 +00:00
|
|
|
|
2023-07-22 08:58:34 +00:00
|
|
|
if (stream->_buf.mode == _IOLBF && memchr(data, '\n', nwritten))
|
2023-07-21 22:02:12 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-07-22 09:17:51 +00:00
|
|
|
static ssize_t read_data_into_buffer(FILE* stream)
|
|
|
|
{
|
|
|
|
stream->_buf.index = 0;
|
|
|
|
ssize_t nread = read(stream->_fd, stream->_buf.buffer, stream->_buf.capacity);
|
|
|
|
if (nread >= 0) stream->_buf.size = nread;
|
|
|
|
stream->_buf.status |= FileStatusFlags::LastRead;
|
|
|
|
return nread;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t read_from_buffer(FILE* stream, u8* data, ssize_t size)
|
|
|
|
{
|
|
|
|
ssize_t total_read = 0;
|
|
|
|
|
|
|
|
while (size > 0)
|
|
|
|
{
|
|
|
|
ssize_t nread;
|
|
|
|
if (stream->_buf.mode != _IONBF)
|
|
|
|
{
|
|
|
|
if (stream->_buf.status & FileStatusFlags::LastWrite) flush_write_buffer(stream);
|
|
|
|
|
|
|
|
if (stream->_buf.size == stream->_buf.index)
|
|
|
|
{
|
|
|
|
ssize_t rc;
|
|
|
|
if ((rc = read_data_into_buffer(stream)) < 0) return -1;
|
|
|
|
if (rc == 0) return total_read;
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t size_remaining = stream->_buf.size - stream->_buf.index;
|
|
|
|
nread = size > size_remaining ? size_remaining : size;
|
|
|
|
memcpy(data, stream->_buf.buffer + stream->_buf.index, nread);
|
|
|
|
|
|
|
|
stream->_buf.index += nread;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
nread = read(stream->_fd, data, size > BUFSIZ ? BUFSIZ : size);
|
|
|
|
if (nread < 0) return nread;
|
|
|
|
if (nread == 0) return total_read;
|
|
|
|
size -= nread;
|
|
|
|
data += nread;
|
|
|
|
total_read += nread;
|
|
|
|
}
|
|
|
|
|
|
|
|
return total_read;
|
|
|
|
}
|
|
|
|
|
2023-01-06 12:31:14 +00:00
|
|
|
extern "C"
|
|
|
|
{
|
2023-07-21 22:02:12 +00:00
|
|
|
void _init_stdio()
|
2023-06-19 08:48:02 +00:00
|
|
|
{
|
2023-07-21 22:02:12 +00:00
|
|
|
memset(&s_open_files, 0, sizeof(s_open_files));
|
|
|
|
}
|
|
|
|
|
|
|
|
int fflush(FILE* stream)
|
|
|
|
{
|
2023-07-22 09:17:51 +00:00
|
|
|
if (stream && stream->_buf.mode != _IONBF)
|
|
|
|
{
|
|
|
|
if (stream->_buf.status & FileStatusFlags::LastWrite) flush_write_buffer(stream);
|
|
|
|
else if (stream->_buf.status & FileStatusFlags::LastRead)
|
|
|
|
flush_read_buffer(stream);
|
|
|
|
}
|
2023-07-21 22:02:12 +00:00
|
|
|
else if (!stream)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < FOPEN_MAX; i++)
|
|
|
|
{
|
|
|
|
if (s_open_files[i]) fflush(s_open_files[i]);
|
|
|
|
}
|
|
|
|
}
|
2023-06-19 08:48:02 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-03-12 16:36:04 +00:00
|
|
|
FILE* fopen(const char* path, const char* mode)
|
2023-03-12 09:23:08 +00:00
|
|
|
{
|
2023-03-12 16:36:04 +00:00
|
|
|
int flags;
|
|
|
|
|
|
|
|
if ((flags = fopen_parse_mode(mode)) < 0) return nullptr;
|
|
|
|
|
2023-03-12 09:23:08 +00:00
|
|
|
FILE* f = (FILE*)malloc(sizeof(FILE));
|
2023-03-18 18:23:18 +00:00
|
|
|
if (!f) { return nullptr; }
|
|
|
|
|
|
|
|
int fd = open(path, flags, 0666);
|
|
|
|
if (fd < 0)
|
2023-03-12 09:23:08 +00:00
|
|
|
{
|
2023-03-18 18:23:18 +00:00
|
|
|
free(f);
|
2023-03-12 09:23:08 +00:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
f->_fd = fd;
|
|
|
|
clearerr(f);
|
|
|
|
|
2023-07-21 22:02:12 +00:00
|
|
|
f->_flags = flags;
|
2023-07-22 08:58:34 +00:00
|
|
|
f->_buf.status = 0;
|
|
|
|
f->_buf.mode = isatty(fd) ? _IOLBF : _IOFBF;
|
|
|
|
f->_buf.size = f->_buf.index = 0;
|
|
|
|
f->_buf.buffer = nullptr;
|
|
|
|
setvbuf(f, NULL, f->_buf.mode, 0);
|
2023-07-21 22:02:12 +00:00
|
|
|
|
|
|
|
s_open_files[fd] = f;
|
|
|
|
|
2023-03-12 09:23:08 +00:00
|
|
|
return f;
|
|
|
|
}
|
|
|
|
|
2023-07-21 22:02:12 +00:00
|
|
|
FILE* _fdopen_impl(int fd, const char* mode, int buffering_mode)
|
2023-03-18 18:23:18 +00:00
|
|
|
{
|
|
|
|
int flags;
|
|
|
|
|
|
|
|
if ((flags = fopen_parse_mode(mode)) < 0) return nullptr;
|
|
|
|
|
2023-03-28 18:56:00 +00:00
|
|
|
if (fdopen_check_compatible_mode(fd, flags) < 0) return nullptr;
|
2023-03-18 18:23:18 +00:00
|
|
|
|
|
|
|
FILE* f = (FILE*)malloc(sizeof(FILE));
|
|
|
|
if (!f) { return nullptr; }
|
|
|
|
|
|
|
|
f->_fd = fd;
|
|
|
|
clearerr(f);
|
|
|
|
|
2023-07-21 22:02:12 +00:00
|
|
|
f->_flags = flags;
|
2023-07-22 08:58:34 +00:00
|
|
|
f->_buf.status = 0;
|
|
|
|
f->_buf.mode = buffering_mode < 0 ? (isatty(fd) ? _IOLBF : _IOFBF) : buffering_mode;
|
|
|
|
f->_buf.size = f->_buf.index = 0;
|
|
|
|
f->_buf.buffer = nullptr;
|
|
|
|
setvbuf(f, NULL, f->_buf.mode, 0);
|
2023-07-21 22:02:12 +00:00
|
|
|
|
|
|
|
s_open_files[fd] = f;
|
|
|
|
|
2023-03-18 18:23:18 +00:00
|
|
|
return f;
|
|
|
|
}
|
|
|
|
|
2023-07-21 22:02:12 +00:00
|
|
|
FILE* fdopen(int fd, const char* mode)
|
|
|
|
{
|
|
|
|
return _fdopen_impl(fd, mode, -1);
|
|
|
|
}
|
|
|
|
|
2023-06-19 08:46:08 +00:00
|
|
|
FILE* freopen(const char* path, const char* mode, FILE* stream)
|
|
|
|
{
|
|
|
|
int flags;
|
|
|
|
|
|
|
|
if ((flags = fopen_parse_mode(mode)) < 0) return nullptr;
|
|
|
|
|
|
|
|
close(stream->_fd);
|
|
|
|
|
2023-07-21 22:02:12 +00:00
|
|
|
s_open_files[stream->_fd] = nullptr;
|
|
|
|
|
2023-07-22 08:58:34 +00:00
|
|
|
if (stream->_buf.buffer && (stream->_buf.status & FileStatusFlags::BufferIsMalloced)) free(stream->_buf.buffer);
|
2023-07-21 22:02:12 +00:00
|
|
|
|
2023-06-19 08:46:08 +00:00
|
|
|
if (!path) { fail("FIXME: freopen() called with path=nullptr"); }
|
|
|
|
|
|
|
|
int fd = open(path, flags, 0666);
|
|
|
|
if (fd < 0) { return nullptr; }
|
|
|
|
|
|
|
|
stream->_fd = fd;
|
|
|
|
clearerr(stream);
|
|
|
|
|
2023-07-21 22:02:12 +00:00
|
|
|
stream->_flags = flags;
|
2023-07-22 08:58:34 +00:00
|
|
|
stream->_buf.status = 0;
|
|
|
|
stream->_buf.mode = isatty(fd) ? _IOLBF : _IOFBF;
|
|
|
|
stream->_buf.size = stream->_buf.index = 0;
|
|
|
|
stream->_buf.buffer = nullptr;
|
|
|
|
setvbuf(stream, NULL, stream->_buf.mode, 0);
|
2023-07-21 22:02:12 +00:00
|
|
|
|
|
|
|
s_open_files[fd] = stream;
|
|
|
|
|
2023-06-19 08:46:08 +00:00
|
|
|
return stream;
|
|
|
|
}
|
|
|
|
|
2023-03-12 09:23:08 +00:00
|
|
|
int fclose(FILE* stream)
|
|
|
|
{
|
2023-07-21 22:02:12 +00:00
|
|
|
if (fflush(stream) < 0) return EOF;
|
|
|
|
|
2023-03-12 09:23:08 +00:00
|
|
|
if (close(stream->_fd) < 0) return EOF;
|
|
|
|
|
2023-07-22 08:58:34 +00:00
|
|
|
if (stream->_buf.buffer && (stream->_buf.status & FileStatusFlags::BufferIsMalloced)) free(stream->_buf.buffer);
|
2023-07-21 22:02:12 +00:00
|
|
|
|
|
|
|
s_open_files[stream->_fd] = nullptr;
|
|
|
|
|
2023-03-12 09:23:08 +00:00
|
|
|
free(stream);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-03-12 16:38:35 +00:00
|
|
|
int fileno(FILE* stream)
|
|
|
|
{
|
|
|
|
return stream->_fd;
|
|
|
|
}
|
|
|
|
|
2023-03-12 09:23:08 +00:00
|
|
|
size_t fread(void* buf, size_t size, size_t nmemb, FILE* stream)
|
|
|
|
{
|
|
|
|
if (size * nmemb == 0) return 0;
|
|
|
|
|
2023-07-22 09:17:51 +00:00
|
|
|
ssize_t nread = read_from_buffer(stream, (u8*)buf, size * nmemb);
|
2023-03-12 09:23:08 +00:00
|
|
|
|
|
|
|
if (nread < 0)
|
|
|
|
{
|
|
|
|
stream->_err = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else if (nread == 0)
|
|
|
|
{
|
|
|
|
stream->_eof = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return (size_t)nread / size;
|
|
|
|
}
|
|
|
|
|
2023-03-12 10:37:41 +00:00
|
|
|
size_t fwrite(const void* buf, size_t size, size_t nmemb, FILE* stream)
|
|
|
|
{
|
|
|
|
if (size * nmemb == 0) return 0;
|
|
|
|
|
2023-07-21 22:02:12 +00:00
|
|
|
ssize_t nwrite = write_into_buffer(stream, (const u8*)buf, size * nmemb);
|
|
|
|
if (nwrite < 0)
|
|
|
|
{
|
|
|
|
stream->_err = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
2023-03-12 10:37:41 +00:00
|
|
|
|
|
|
|
return (size_t)nwrite / size;
|
|
|
|
}
|
|
|
|
|
2023-03-12 12:15:24 +00:00
|
|
|
int fseek(FILE* stream, long offset, int whence)
|
|
|
|
{
|
2023-07-22 09:19:48 +00:00
|
|
|
fflush(stream);
|
|
|
|
|
2023-03-12 12:15:24 +00:00
|
|
|
long result = lseek(stream->_fd, offset, whence);
|
|
|
|
if (result < 0) return -1;
|
|
|
|
|
|
|
|
// man fseek(3): A successful call to the fseek() function clears the end-of-file indicator for the stream.
|
|
|
|
stream->_eof = 0;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
long ftell(FILE* stream)
|
|
|
|
{
|
2023-07-22 09:19:48 +00:00
|
|
|
fflush(stream);
|
|
|
|
|
2023-03-12 12:15:24 +00:00
|
|
|
return lseek(stream->_fd, 0, SEEK_CUR);
|
|
|
|
}
|
|
|
|
|
|
|
|
void rewind(FILE* stream)
|
|
|
|
{
|
2023-07-22 09:19:48 +00:00
|
|
|
fflush(stream);
|
|
|
|
|
2023-03-12 12:15:24 +00:00
|
|
|
lseek(stream->_fd, 0, SEEK_SET);
|
|
|
|
|
|
|
|
clearerr(stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
int fgetpos(FILE* stream, fpos_t* pos)
|
|
|
|
{
|
|
|
|
long offset = ftell(stream);
|
|
|
|
if (offset < 0) return -1;
|
|
|
|
|
|
|
|
*pos = offset;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int fsetpos(FILE* stream, const fpos_t* pos)
|
|
|
|
{
|
2023-07-22 09:19:48 +00:00
|
|
|
fflush(stream);
|
|
|
|
|
2023-03-12 12:15:24 +00:00
|
|
|
return fseek(stream, *pos, SEEK_SET);
|
|
|
|
}
|
|
|
|
|
2023-03-12 09:23:08 +00:00
|
|
|
int ferror(FILE* stream)
|
|
|
|
{
|
|
|
|
return stream->_err;
|
|
|
|
}
|
|
|
|
|
|
|
|
int feof(FILE* stream)
|
|
|
|
{
|
|
|
|
return stream->_eof;
|
|
|
|
}
|
|
|
|
|
2023-03-18 18:23:18 +00:00
|
|
|
int fputc(int c, FILE* stream)
|
|
|
|
{
|
|
|
|
u8 value = (u8)c;
|
2023-07-21 22:02:12 +00:00
|
|
|
ssize_t rc = write_into_buffer(stream, &value, 1);
|
2023-03-18 18:23:18 +00:00
|
|
|
if (rc <= 0) return EOF;
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
int putc(int c, FILE* stream)
|
|
|
|
{
|
|
|
|
return fputc(c, stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
int putchar(int c)
|
|
|
|
{
|
|
|
|
return fputc(c, stdout);
|
|
|
|
}
|
|
|
|
|
|
|
|
int fputs(const char* str, FILE* stream)
|
|
|
|
{
|
2023-07-21 22:02:12 +00:00
|
|
|
ssize_t rc = write_into_buffer(stream, (const u8*)str, strlen(str));
|
2023-03-18 18:23:18 +00:00
|
|
|
return (rc < 0) ? -1 : 0;
|
|
|
|
}
|
|
|
|
|
2023-03-23 20:35:09 +00:00
|
|
|
int fgetc(FILE* stream)
|
|
|
|
{
|
|
|
|
u8 value;
|
2023-07-22 09:17:51 +00:00
|
|
|
ssize_t rc = read_from_buffer(stream, &value, 1);
|
2023-03-23 20:35:09 +00:00
|
|
|
if (rc <= 0) return EOF;
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
int getc(FILE* stream)
|
|
|
|
{
|
|
|
|
return fgetc(stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
int getchar()
|
|
|
|
{
|
|
|
|
return fgetc(stdin);
|
|
|
|
}
|
|
|
|
|
|
|
|
char* fgets(char* buf, size_t size, FILE* stream)
|
|
|
|
{
|
|
|
|
size_t i = 0;
|
|
|
|
while (i + 1 < size)
|
|
|
|
{
|
|
|
|
int c = fgetc(stream);
|
|
|
|
if (c == EOF) break;
|
|
|
|
buf[i++] = (char)c;
|
|
|
|
if (c == '\n') break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == 0) return NULL;
|
|
|
|
|
|
|
|
buf[i] = 0;
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
2023-05-20 13:36:30 +00:00
|
|
|
ssize_t getline(char** linep, size_t* n, FILE* stream)
|
|
|
|
{
|
|
|
|
return getdelim(linep, n, '\n', stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t getdelim(char** linep, size_t* n, int delim, FILE* stream)
|
|
|
|
{
|
|
|
|
if (!n || !linep)
|
|
|
|
{
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
char* buf = *linep;
|
|
|
|
size_t size = *n;
|
|
|
|
size_t len = 0;
|
|
|
|
|
|
|
|
if (!buf)
|
|
|
|
{
|
|
|
|
buf = (char*)malloc(BUFSIZ);
|
|
|
|
size = BUFSIZ;
|
|
|
|
if (!buf) return -1;
|
|
|
|
*linep = buf;
|
|
|
|
*n = size;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
int c = fgetc(stream);
|
|
|
|
if (c == EOF) break;
|
|
|
|
|
|
|
|
if (len == size)
|
|
|
|
{
|
|
|
|
buf = (char*)realloc(buf, size + 64);
|
|
|
|
size += 64;
|
|
|
|
if (!buf) return -1;
|
|
|
|
*linep = buf;
|
|
|
|
*n = size;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf[len++] = (char)c;
|
|
|
|
if (c == delim) break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len == 0) return -1;
|
|
|
|
|
|
|
|
if (len == size)
|
|
|
|
{
|
|
|
|
buf = (char*)realloc(buf, size + 16);
|
|
|
|
size += 16;
|
|
|
|
if (!buf) return -1;
|
|
|
|
*linep = buf;
|
|
|
|
*n = size;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf[len] = '\0';
|
|
|
|
|
|
|
|
return (ssize_t)len;
|
|
|
|
}
|
|
|
|
|
2023-03-12 09:23:08 +00:00
|
|
|
void clearerr(FILE* stream)
|
|
|
|
{
|
|
|
|
stream->_eof = stream->_err = 0;
|
|
|
|
}
|
|
|
|
|
2023-01-06 19:15:43 +00:00
|
|
|
int vsnprintf(char* buf, size_t max, const char* format, va_list ap)
|
|
|
|
{
|
|
|
|
return (int)vstring_format(buf, max, format, ap);
|
|
|
|
}
|
|
|
|
|
|
|
|
int snprintf(char* buf, size_t max, const char* format, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, format);
|
|
|
|
|
|
|
|
int rc = vsnprintf(buf, max, format, ap);
|
|
|
|
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
int vsprintf(char* buf, const char* format, va_list ap)
|
|
|
|
{
|
|
|
|
return vsnprintf(buf, (size_t)-1, format, ap);
|
|
|
|
}
|
|
|
|
|
|
|
|
int sprintf(char* buf, const char* format, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, format);
|
|
|
|
|
|
|
|
int rc = vsnprintf(buf, (size_t)-1, format, ap);
|
|
|
|
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
2023-01-07 00:49:26 +00:00
|
|
|
|
2023-03-18 19:11:19 +00:00
|
|
|
int vfprintf(FILE* stream, const char* format, va_list ap)
|
|
|
|
{
|
2023-04-07 10:02:49 +00:00
|
|
|
usize count = cstyle_format(
|
|
|
|
format,
|
|
|
|
[](char c, void* f) -> Result<void> {
|
|
|
|
int rc = fputc(c, (FILE*)f);
|
|
|
|
if (rc == EOF) return err(errno);
|
|
|
|
return {};
|
|
|
|
},
|
|
|
|
stream, ap)
|
|
|
|
.value_or(-1);
|
|
|
|
|
|
|
|
if (count == (usize)-1) return -1;
|
|
|
|
|
|
|
|
return (int)count;
|
2023-03-18 19:11:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int fprintf(FILE* stream, const char* format, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, format);
|
|
|
|
|
|
|
|
int rc = vfprintf(stream, format, ap);
|
|
|
|
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
int vprintf(const char* format, va_list ap)
|
|
|
|
{
|
|
|
|
return vfprintf(stdout, format, ap);
|
|
|
|
}
|
|
|
|
|
2023-01-07 00:49:26 +00:00
|
|
|
int printf(const char* format, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, format);
|
|
|
|
|
2023-03-18 19:11:19 +00:00
|
|
|
int rc = vfprintf(stdout, format, ap);
|
2023-01-07 00:49:26 +00:00
|
|
|
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2023-06-18 21:44:30 +00:00
|
|
|
int sscanf(const char* str, const char* format, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, format);
|
|
|
|
|
|
|
|
int rc = vsscanf(str, format, ap);
|
|
|
|
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
int vfscanf(FILE* stream, const char* format, va_list ap)
|
|
|
|
{
|
|
|
|
char buf[BUFSIZ];
|
|
|
|
if (!fgets(buf, sizeof(buf), stream)) return EOF;
|
|
|
|
return vsscanf(buf, format, ap);
|
|
|
|
}
|
|
|
|
|
|
|
|
int fscanf(FILE* stream, const char* format, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, format);
|
|
|
|
|
|
|
|
int rc = vfscanf(stream, format, ap);
|
|
|
|
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
int vscanf(const char* format, va_list ap)
|
|
|
|
{
|
|
|
|
return vfscanf(stdin, format, ap);
|
|
|
|
}
|
|
|
|
|
|
|
|
int scanf(const char* format, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, format);
|
|
|
|
|
|
|
|
int rc = vfscanf(stdin, format, ap);
|
|
|
|
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2023-01-07 00:49:26 +00:00
|
|
|
int puts(const char* s)
|
|
|
|
{
|
2023-03-18 19:11:19 +00:00
|
|
|
if (fputs(s, stdout) < 0) return -1;
|
|
|
|
if (putchar('\n') == EOF) return -1;
|
2023-01-07 00:49:26 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2023-01-13 20:08:10 +00:00
|
|
|
|
|
|
|
void perror(const char* s)
|
|
|
|
{
|
|
|
|
int err = errno;
|
2023-03-18 19:11:19 +00:00
|
|
|
if (s && *s) fprintf(stderr, "%s: ", s);
|
|
|
|
fprintf(stderr, "%s\n", strerror(err));
|
2023-01-13 20:08:10 +00:00
|
|
|
}
|
2023-04-12 16:11:36 +00:00
|
|
|
|
|
|
|
int remove(const char* path)
|
|
|
|
{
|
|
|
|
// On Luna, unlink() allows removal of directories.
|
|
|
|
return unlink(path);
|
|
|
|
}
|
2023-05-09 20:04:34 +00:00
|
|
|
|
|
|
|
FILE* tmpfile()
|
|
|
|
{
|
2023-06-19 09:52:38 +00:00
|
|
|
int fd = open(read_tmpdir(), O_RDWR | O_TMPFILE, 0600);
|
2023-05-09 20:04:34 +00:00
|
|
|
if (fd < 0) return nullptr;
|
|
|
|
|
|
|
|
FILE* f = fdopen(fd, "w+b");
|
|
|
|
if (!f) close(fd);
|
|
|
|
return f;
|
|
|
|
}
|
2023-06-19 08:48:02 +00:00
|
|
|
|
|
|
|
int ungetc(int, FILE*)
|
|
|
|
{
|
|
|
|
fail("FIXME: ungetc: not implemented");
|
|
|
|
}
|
2023-07-21 22:02:12 +00:00
|
|
|
|
|
|
|
int setvbuf(FILE* stream, char* buf, int mode, size_t size)
|
|
|
|
{
|
2023-07-22 08:58:34 +00:00
|
|
|
int status = 0;
|
2023-07-21 22:02:12 +00:00
|
|
|
if (mode < 0 || mode > _IOFBF) return errno = EINVAL, -1;
|
2023-07-22 08:58:34 +00:00
|
|
|
if (stream->_buf.size != 0 || stream->_buf.index != 0) return -1; // Buffer is already in use.
|
2023-07-21 22:02:12 +00:00
|
|
|
if (mode != _IONBF && buf == NULL)
|
|
|
|
{
|
|
|
|
size = BUFSIZ;
|
|
|
|
buf = (char*)calloc(size, 1);
|
|
|
|
if (!buf) return -1;
|
2023-07-22 08:58:34 +00:00
|
|
|
status = FileStatusFlags::BufferIsMalloced;
|
2023-07-21 22:02:12 +00:00
|
|
|
}
|
2023-07-22 08:58:34 +00:00
|
|
|
else if (mode == _IONBF)
|
2023-07-21 22:02:12 +00:00
|
|
|
{
|
|
|
|
buf = NULL;
|
|
|
|
size = 0;
|
|
|
|
}
|
|
|
|
|
2023-07-22 08:58:34 +00:00
|
|
|
if (stream->_buf.buffer && (stream->_buf.status & FileStatusFlags::BufferIsMalloced)) free(stream->_buf.buffer);
|
2023-07-21 22:02:12 +00:00
|
|
|
|
2023-07-22 08:58:34 +00:00
|
|
|
stream->_buf.buffer = buf;
|
|
|
|
stream->_buf.capacity = size;
|
|
|
|
stream->_buf.mode = mode;
|
|
|
|
stream->_buf.status = status;
|
2023-07-21 22:02:12 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2023-01-06 12:31:14 +00:00
|
|
|
}
|