lib: c: io: implement standard FILE and DIR interfaces

This commit is contained in:
2026-03-25 20:21:12 +00:00
parent b975256cb9
commit 96784f611f
21 changed files with 599 additions and 0 deletions
+20
View File
@@ -1,6 +1,8 @@
#ifndef DIRENT_H_
#define DIRENT_H_
#include <sys/types.h>
#define DT_UNKNOWN 0
#define DT_BLK 1
#define DT_CHR 2
@@ -10,4 +12,22 @@
#define DT_REG 6
#define DT_SOCK 7
struct dirent {
ino_t d_ino;
off_t d_off;
unsigned short d_reclen;
unsigned char d_type;
char d_name[256];
};
struct __opaque_dir;
typedef struct __opaque_dir DIR;
extern DIR *opendir(const char *name);
extern DIR *fdopendir(int fd);
extern int closedir(DIR *dirp);
extern struct dirent *readdir(DIR *dirp);
#endif
+29
View File
@@ -4,10 +4,15 @@
#include <stdarg.h>
#include <stddef.h>
#define EOF -1
#ifdef __cplusplus
extern "C" {
#endif
struct __opaque_file;
typedef struct __opaque_file FILE;
extern int snprintf(char *buffer, size_t count, const char *format, ...);
extern int vsnprintf(
char *buffer,
@@ -15,6 +20,30 @@ extern int vsnprintf(
const char *format,
va_list va);
extern FILE *fopen(const char *path, const char *mode);
extern int fclose(FILE *stream);
extern int feof(FILE *stream);
extern int ferr(FILE *stream);
extern size_t fread(void *buf, size_t size, size_t count, FILE *stream);
extern size_t fwrite(const void *buf, size_t size, size_t count, FILE *stream);
extern int fgetc(FILE *stream);
static inline int getc(FILE *stream)
{
return fgetc(stream);
}
extern int fputc(int c, FILE *stream);
static inline int putc(int c, FILE *stream)
{
return fputc(c, stream);
}
extern char *fgets(char *restrict str, int count, FILE *restrict stream);
extern int fputs(const char *restrict str, FILE *restrict stream);
#ifdef __cplusplus
}
#endif
+2
View File
@@ -7,6 +7,8 @@
#define SEEK_CUR 1
#define SEEK_END 2
typedef size_t ino_t;
struct dentry {
unsigned long d_ino;
unsigned short d_reclen;
+19
View File
@@ -0,0 +1,19 @@
#include "dir.h"
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
int closedir(struct __opaque_dir *dirp)
{
close(dirp->d_fd);
if (dirp->d_buf) {
free(dirp->d_buf);
}
free(dirp);
return SUCCESS;
}
+81
View File
@@ -0,0 +1,81 @@
#include "dir.h"
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define DEFAULT_BUFFER_SIZE 4096
static void dirent_convert(const struct dentry *in, struct dirent *out)
{
out->d_ino = in->d_ino;
out->d_type = in->d_type;
out->d_reclen = in->d_reclen;
strncpy(out->d_name, in->d_name, sizeof out->d_name - 1);
out->d_name[sizeof out->d_name - 1] = 0;
}
int __libc_dir_refill(struct __opaque_dir *d)
{
if (d->d_fd < 0) {
return EBADF;
}
if (d->d_flags & DIR_ERR) {
return EIO;
}
if (d->d_flags & DIR_EOF) {
return SUCCESS;
}
if (!d->d_buf) {
d->d_buf_max = DEFAULT_BUFFER_SIZE;
d->d_buf = malloc(d->d_buf_max);
if (!d->d_buf) {
d->d_buf_max = 0;
return ENOMEM;
}
}
long r = getdents(d->d_fd, (struct dentry *)d->d_buf, d->d_buf_max);
if (r < 0) {
return -r;
}
d->d_buf_datalen = r;
if (r > 0) {
struct dentry *dent
= (struct dentry *)(d->d_buf + d->d_buf_readptr);
dirent_convert(dent, &d->d_current);
}
return SUCCESS;
}
int __libc_dir_move_next(struct __opaque_dir *d)
{
if (d->d_fd < 0) {
return -EBADF;
}
if (d->d_flags & DIR_ERR) {
return -EIO;
}
if (d->d_flags & DIR_EOF) {
return -SUCCESS;
}
d->d_buf_readptr += d->d_current.d_reclen;
if (d->d_buf_readptr >= d->d_buf_datalen) {
return 0;
}
struct dentry *dent = (struct dentry *)(d->d_buf + d->d_buf_readptr);
dirent_convert(dent, &d->d_current);
return 1;
}
+24
View File
@@ -0,0 +1,24 @@
#ifndef _LIBC_IO_DIR_H_
#define _LIBC_IO_DIR_H_
#include <dirent.h>
#include <stddef.h>
enum dir_flags {
DIR_EOF = 0x01u,
DIR_ERR = 0x02u,
};
struct __opaque_dir {
enum dir_flags d_flags;
int d_fd;
char *d_buf;
struct dirent d_current;
size_t d_buf_datalen, d_buf_max;
size_t d_buf_readptr;
};
extern int __libc_dir_refill(struct __opaque_dir *d);
extern int __libc_dir_move_next(struct __opaque_dir *d);
#endif
+24
View File
@@ -0,0 +1,24 @@
#include "file.h"
#include <errno.h>
#include <fcntl.h>
#include <mango/log.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int fclose(struct __opaque_file *stream)
{
if (stream->f_fd < 0) {
return __set_errno(EBADF);
}
close(stream->f_fd);
if (stream->f_buf) {
free(stream->f_buf);
}
free(stream);
return SUCCESS;
}
View File
+6
View File
@@ -0,0 +1,6 @@
#include "file.h"
int feof(struct __opaque_file *stream)
{
return (stream->f_flags & FILE_EOF) != 0;
}
+6
View File
@@ -0,0 +1,6 @@
#include "file.h"
int ferr(struct __opaque_file *stream)
{
return (stream->f_flags & FILE_ERR) != 0;
}
+31
View File
@@ -0,0 +1,31 @@
#include "file.h"
#include <errno.h>
#include <stdio.h>
int fgetc(struct __opaque_file *stream)
{
if (stream->f_flags & (FILE_EOF | FILE_ERR)) {
return EOF;
}
size_t available = __libc_file_available(stream);
if (available == 0) {
int err = __libc_file_refill(stream);
if (err != SUCCESS) {
__set_errno(err);
stream->f_flags |= FILE_ERR;
return EOF;
}
available = __libc_file_available(stream);
}
if (available == 0) {
stream->f_flags |= FILE_EOF;
return EOF;
}
char c = stream->f_buf[stream->f_buf_readptr++];
return c;
}
+40
View File
@@ -0,0 +1,40 @@
#include "file.h"
#include <stdio.h>
char *fgets(
char *restrict str,
int count,
struct __opaque_file *restrict stream)
{
if (stream->f_flags & (FILE_EOF | FILE_ERR)) {
return NULL;
}
size_t i = 0;
while (1) {
int c = fgetc(stream);
if (c == EOF) {
str[i] = 0;
break;
}
str[i++] = c;
if (c == '\n') {
str[i] = 0;
break;
}
}
if (ferr(stream)) {
return NULL;
}
if (feof(stream) && i == 0) {
return NULL;
}
return str;
}
+76
View File
@@ -0,0 +1,76 @@
#include "file.h"
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define DEFAULT_BUFFER_SIZE 4096
#define FILE_UNUSABLE(f) (((f)->f_flags & (FILE_EOF | FILE_ERR)) != 0)
int __libc_file_refill(struct __opaque_file *f)
{
f->f_buf_datalen = 0;
f->f_buf_readptr = 0;
if (f->f_fd < 0) {
return EBADF;
}
if (f->f_flags & FILE_ERR) {
return EIO;
}
if (f->f_flags & FILE_EOF) {
return SUCCESS;
}
if (!f->f_buf) {
f->f_buf_max = DEFAULT_BUFFER_SIZE;
f->f_buf = malloc(f->f_buf_max);
if (!f->f_buf) {
f->f_buf_max = 0;
return ENOMEM;
}
}
long r = read(f->f_fd, f->f_buf, f->f_buf_max);
if (r < 0) {
return -r;
}
f->f_buf_datalen = r;
return SUCCESS;
}
int __libc_file_read(struct __opaque_file *f, void *out, size_t count)
{
if (f->f_fd < 0) {
return EBADF;
}
if (f->f_flags & FILE_ERR) {
return EIO;
}
if (f->f_flags & FILE_EOF) {
return SUCCESS;
}
size_t available = f->f_buf_datalen - f->f_buf_readptr;
if (count > available) {
count = available;
}
memcpy(out, f->f_buf + f->f_buf_readptr, count);
f->f_buf_readptr += count;
return count;
}
size_t __libc_file_available(struct __opaque_file *f)
{
return f->f_buf_datalen - f->f_buf_readptr;
}
+25
View File
@@ -0,0 +1,25 @@
#ifndef _LIBC_IO_FILE_H_
#define _LIBC_IO_FILE_H_
#include <stddef.h>
enum file_flags {
FILE_EOF = 0x01u,
FILE_ERR = 0x02u,
FILE_BIN = 0x04u,
};
struct __opaque_file {
enum file_flags f_flags;
int f_fd;
char f_unget;
char *f_buf;
size_t f_buf_datalen, f_buf_max;
size_t f_buf_readptr;
};
extern int __libc_file_refill(struct __opaque_file *f);
extern int __libc_file_read(struct __opaque_file *f, void *out, size_t count);
extern size_t __libc_file_available(struct __opaque_file *f);
#endif
+151
View File
@@ -0,0 +1,151 @@
#include "file.h"
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
enum mode {
MODE_R = 0x01u,
MODE_W = 0x02u,
MODE_A = 0x04u,
MODE_B = 0x08u,
MODE_X = 0x10u,
MODE_PLUS = 0x20u,
};
static int flags_from_mode(
const char *mode_string,
int *out_sysflags,
enum file_flags *out_fflags)
{
enum mode mode = 0;
int sysflags = 0;
enum file_flags fflags = 0;
for (int i = 0; mode_string[i]; i++) {
switch (mode_string[i]) {
case 'r':
if (mode & (MODE_R | MODE_W | MODE_A | MODE_X)) {
return -1;
}
mode |= MODE_R;
break;
case 'w':
if (mode & (MODE_R | MODE_W | MODE_A)) {
return -1;
}
mode |= MODE_W;
break;
case 'a':
if (mode & (MODE_R | MODE_W | MODE_A | MODE_X)) {
return -1;
}
break;
case '+':
if (mode & MODE_PLUS) {
return -1;
}
mode |= MODE_PLUS;
break;
case 'b':
if (mode & MODE_B) {
return -1;
}
mode |= MODE_B;
break;
case 'x':
if (mode & (MODE_R | MODE_A | MODE_X)) {
return -1;
}
mode |= MODE_X;
break;
default:
return -1;
}
}
switch ((int)mode) {
case MODE_R | MODE_B:
fflags = FILE_BIN;
case MODE_R:
sysflags = O_RDONLY;
break;
case MODE_W | MODE_B:
fflags = FILE_BIN;
case MODE_W:
sysflags = O_WRONLY | O_TRUNC | O_CREAT;
break;
case MODE_A | MODE_B:
fflags = FILE_BIN;
case MODE_A:
sysflags = O_WRONLY | O_APPEND | O_CREAT;
break;
case MODE_R | MODE_PLUS | MODE_B:
fflags = FILE_BIN;
case MODE_R | MODE_PLUS:
sysflags = O_RDWR;
break;
case MODE_W | MODE_PLUS | MODE_B:
fflags = FILE_BIN;
case MODE_W | MODE_PLUS:
sysflags = O_RDWR | O_TRUNC | O_CREAT;
break;
case MODE_W | MODE_PLUS | MODE_X | MODE_B:
fflags = FILE_BIN;
case MODE_W | MODE_PLUS | MODE_X:
sysflags = O_RDWR | O_TRUNC | O_CREAT | O_EXCL;
break;
case MODE_A | MODE_PLUS | MODE_B:
fflags = FILE_BIN;
case MODE_A | MODE_PLUS:
sysflags = O_RDWR | O_APPEND | O_CREAT;
break;
default:
return -1;
}
return 0;
}
struct __opaque_file *fopen(const char *path, const char *mode)
{
int sysflags = 0;
enum file_flags fflags = 0;
if (flags_from_mode(mode, &sysflags, &fflags) != 0) {
__set_errno(EINVAL);
return NULL;
}
struct __opaque_file *out = malloc(sizeof *out);
if (!out) {
__set_errno(ENOMEM);
return NULL;
}
memset(out, 0x0, sizeof *out);
int fd = open(path, sysflags);
if (fd < 0) {
free(out);
__set_errno(-fd);
return NULL;
}
out->f_fd = fd;
out->f_flags = fflags;
__set_errno(SUCCESS);
return out;
}
View File
View File
View File
View File
+30
View File
@@ -0,0 +1,30 @@
#include "dir.h"
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
extern struct __opaque_dir *opendir(const char *name)
{
struct __opaque_dir *out = malloc(sizeof *out);
if (!out) {
__set_errno(ENOMEM);
return NULL;
}
memset(out, 0x0, sizeof *out);
int fd = open(name, O_RDONLY | O_DIRECTORY);
if (fd < 0) {
free(out);
return NULL;
}
out->d_flags = 0;
out->d_fd = fd;
return out;
}
+35
View File
@@ -0,0 +1,35 @@
#include "dir.h"
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
struct dirent *readdir(struct __opaque_dir *dirp)
{
int result = __libc_dir_move_next(dirp);
if (result < 0) {
dirp->d_flags |= DIR_ERR;
__set_errno(-result);
return NULL;
}
if (result > 0) {
return &dirp->d_current;
}
result = __libc_dir_refill(dirp);
if (result < 0) {
dirp->d_flags |= DIR_ERR;
__set_errno(-result);
return NULL;
}
if (!dirp->d_buf_datalen) {
return NULL;
}
return &dirp->d_current;
}