lib: c: io: implement standard FILE and DIR interfaces
This commit is contained in:
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#include "file.h"
|
||||
|
||||
int feof(struct __opaque_file *stream)
|
||||
{
|
||||
return (stream->f_flags & FILE_EOF) != 0;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#include "file.h"
|
||||
|
||||
int ferr(struct __opaque_file *stream)
|
||||
{
|
||||
return (stream->f_flags & FILE_ERR) != 0;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user