Luna/libc/src/stdio.cpp

494 lines
9.9 KiB
C++
Raw Normal View History

#include <bits/errno-return.h>
#include <fcntl.h>
#include <luna/Format.h>
#include <stdio.h>
#include <stdlib.h>
2023-01-07 00:49:26 +00:00
#include <string.h>
#include <sys/syscall.h>
#include <unistd.h>
2023-03-19 18:19:20 +00:00
FILE* stdin = nullptr;
FILE* stderr = nullptr;
FILE* stdout = nullptr;
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;
}
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;
}
extern "C"
{
int fflush(FILE*)
{
// FIXME: Files are not buffered right now.
return 0;
}
FILE* fopen(const char* path, const char* mode)
{
int flags;
if ((flags = fopen_parse_mode(mode)) < 0) return nullptr;
FILE* f = (FILE*)malloc(sizeof(FILE));
if (!f) { return nullptr; }
int fd = open(path, flags, 0666);
if (fd < 0)
{
free(f);
return nullptr;
}
f->_fd = fd;
clearerr(f);
return f;
}
FILE* fdopen(int fd, const char* mode)
{
int flags;
if ((flags = fopen_parse_mode(mode)) < 0) return nullptr;
if (fdopen_check_compatible_mode(fd, flags) < 0) return nullptr;
FILE* f = (FILE*)malloc(sizeof(FILE));
if (!f) { return nullptr; }
f->_fd = fd;
clearerr(f);
return f;
}
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);
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);
return stream;
}
int fclose(FILE* stream)
{
if (close(stream->_fd) < 0) return EOF;
free(stream);
return 0;
}
2023-03-12 16:38:35 +00:00
int fileno(FILE* stream)
{
return stream->_fd;
}
size_t fread(void* buf, size_t size, size_t nmemb, FILE* stream)
{
if (size * nmemb == 0) return 0;
ssize_t nread = read(stream->_fd, buf, size * nmemb);
if (nread < 0)
{
stream->_err = 1;
return 0;
}
else if (nread == 0)
{
stream->_eof = 1;
return 0;
}
else
return (size_t)nread / size;
}
size_t fwrite(const void* buf, size_t size, size_t nmemb, FILE* stream)
{
if (size * nmemb == 0) return 0;
ssize_t nwrite = write(stream->_fd, buf, size * nmemb);
return (size_t)nwrite / size;
}
int fseek(FILE* stream, long offset, int whence)
{
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)
{
return lseek(stream->_fd, 0, SEEK_CUR);
}
void rewind(FILE* stream)
{
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)
{
return fseek(stream, *pos, SEEK_SET);
}
int ferror(FILE* stream)
{
return stream->_err;
}
int feof(FILE* stream)
{
return stream->_eof;
}
int fputc(int c, FILE* stream)
{
u8 value = (u8)c;
ssize_t rc = write(stream->_fd, &value, 1);
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)
{
ssize_t rc = write(stream->_fd, str, strlen(str));
return (rc < 0) ? -1 : 0;
}
2023-03-23 20:35:09 +00:00
int fgetc(FILE* stream)
{
u8 value;
ssize_t rc = read(stream->_fd, &value, 1);
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;
}
void clearerr(FILE* stream)
{
stream->_eof = stream->_err = 0;
}
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
int vfprintf(FILE* stream, const char* format, va_list ap)
{
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;
}
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);
int rc = vfprintf(stdout, format, ap);
2023-01-07 00:49:26 +00:00
va_end(ap);
return rc;
}
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)
{
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;
if (s && *s) fprintf(stderr, "%s: ", s);
fprintf(stderr, "%s\n", strerror(err));
2023-01-13 20:08:10 +00:00
}
int remove(const char* path)
{
// On Luna, unlink() allows removal of directories.
return unlink(path);
}
FILE* tmpfile()
{
// FIXME: use /tmp as the directory when the tmpfs is mounted only there.
int fd = open("/", O_RDWR | O_TMPFILE, 0600);
if (fd < 0) return nullptr;
FILE* f = fdopen(fd, "w+b");
if (!f) close(fd);
return f;
}
int ungetc(int, FILE*)
{
fail("FIXME: ungetc: not implemented");
}
}