[lib][stdio] wrap file io APIs with stdio APIs
Implement stdio file io APIs with LK lib/fs APIs Signed-off-by: Chieh-Min Wang <cmwang@google.com>
This commit is contained in:
committed by
Travis Geiselbrecht
parent
d39f2db58d
commit
b651168fa1
@@ -6,6 +6,7 @@
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
#include <lib/fs.h>
|
||||
#include <lk/err.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <string.h>
|
||||
@@ -57,8 +58,56 @@ static bool test_path_normalize(void) {
|
||||
END_TEST;
|
||||
}
|
||||
|
||||
#define TEST_MNT "/test"
|
||||
#define TEST_FILE TEST_MNT "/stdio_file_tests.txt"
|
||||
|
||||
static inline void test_stdio_fs_teardown(void *ptr) {
|
||||
fs_remove_file(TEST_FILE);
|
||||
fs_unmount(TEST_MNT);
|
||||
}
|
||||
|
||||
static bool test_stdio_fs(void) {
|
||||
__attribute__((cleanup(test_stdio_fs_teardown))) BEGIN_TEST;
|
||||
|
||||
// Setup
|
||||
const char *content = "Hello World\n";
|
||||
const size_t content_len = strlen(content);
|
||||
fs_mount(TEST_MNT, "memfs", NULL);
|
||||
|
||||
// Tests
|
||||
FILE *stream = fopen(TEST_FILE, "w");
|
||||
ASSERT_NE(NULL, stream, "failed to open/create file " TEST_MNT);
|
||||
|
||||
char buf[1024];
|
||||
// test stdout fprintf, fputs
|
||||
EXPECT_EQ(content_len, fprintf(stdout, "%s", content), "");
|
||||
EXPECT_EQ(content_len, fputs(content, stdout), "");
|
||||
|
||||
// test fwrite
|
||||
EXPECT_EQ(content_len, fwrite(content, 1, content_len, stream), "");
|
||||
|
||||
// test fread
|
||||
ASSERT_EQ(NO_ERROR, fseek(stream, 0, SEEK_SET), "fseek failed");
|
||||
EXPECT_EQ(content_len, fread(buf, 1, content_len, stream), "");
|
||||
EXPECT_BYTES_EQ((const uint8_t *)content, (const uint8_t *)buf, content_len,
|
||||
"fread content mismatched");
|
||||
|
||||
// testing fputs
|
||||
ASSERT_EQ(NO_ERROR, fseek(stream, 0, SEEK_SET), "fseek failed");
|
||||
EXPECT_EQ(content_len, fputs(content, stream), "");
|
||||
|
||||
// testing fgets
|
||||
ASSERT_EQ(NO_ERROR, fseek(stream, 0, SEEK_SET), "fseek failed");
|
||||
ASSERT_NE(NULL, fgets(buf, content_len + 1, stream), "");
|
||||
EXPECT_BYTES_EQ((const uint8_t *)content, (const uint8_t *)buf, content_len,
|
||||
"fgets content mismatched");
|
||||
|
||||
END_TEST;
|
||||
}
|
||||
|
||||
BEGIN_TEST_CASE(fs_tests);
|
||||
RUN_TEST(test_path_normalize);
|
||||
RUN_TEST(test_stdio_fs);
|
||||
END_TEST_CASE(fs_tests);
|
||||
|
||||
#endif // WITH_LIB_UNITTEST
|
||||
|
||||
@@ -11,11 +11,29 @@
|
||||
#include <printf.h>
|
||||
#include <sys/types.h>
|
||||
#include <lib/io.h>
|
||||
#if defined(WITH_LIB_FS)
|
||||
#include <lib/fs.h>
|
||||
#endif // WITH_LIB_FS
|
||||
|
||||
__BEGIN_CDECLS
|
||||
|
||||
#if defined(WITH_LIB_FS)
|
||||
struct fs_handle {
|
||||
filehandle *handle;
|
||||
off_t offset;
|
||||
bool readonly;
|
||||
};
|
||||
#endif // WITH_LIB_FS
|
||||
typedef struct FILE {
|
||||
#if defined(WITH_LIB_FS)
|
||||
union {
|
||||
io_handle_t *io;
|
||||
struct fs_handle fs_handle;
|
||||
};
|
||||
bool use_fs;
|
||||
#else
|
||||
io_handle_t *io;
|
||||
#endif // WITH_LIB_FS
|
||||
} FILE;
|
||||
|
||||
extern FILE __stdio_FILEs[];
|
||||
@@ -24,6 +42,8 @@ extern FILE __stdio_FILEs[];
|
||||
#define stdout (&__stdio_FILEs[1])
|
||||
#define stderr (&__stdio_FILEs[2])
|
||||
|
||||
#define EOF (-1)
|
||||
|
||||
FILE *fopen(const char *filename, const char *mode);
|
||||
int fclose(FILE *stream);
|
||||
size_t fread(void *ptr, size_t size, size_t count, FILE *stream);
|
||||
@@ -45,9 +65,12 @@ int putchar(int c);
|
||||
int fputs(const char *s, FILE *fp);
|
||||
int puts(const char *str);
|
||||
|
||||
int getc(FILE *fp);
|
||||
int fgetc(FILE *fp);
|
||||
#define getc(fp) fgetc(fp)
|
||||
int getchar(void);
|
||||
|
||||
char *fgets(char *s, int size, FILE *stream);
|
||||
|
||||
#if !DISABLE_DEBUG_OUTPUT
|
||||
int printf(const char *fmt, ...) __PRINTFLIKE(1, 2);
|
||||
int vprintf(const char *fmt, va_list ap);
|
||||
|
||||
197
lib/libc/stdio.c
197
lib/libc/stdio.c
@@ -6,15 +6,24 @@
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
#include <lk/debug.h>
|
||||
#include <lk/err.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <platform/debug.h>
|
||||
|
||||
#if defined(WITH_LIB_FS)
|
||||
#define STDIO_FIELDS .use_fs = false,
|
||||
#else
|
||||
#define STDIO_FIELDS
|
||||
#endif // WITH_LIB_FS
|
||||
|
||||
#define DEFINE_STDIO_DESC(id) \
|
||||
[(id)] = { \
|
||||
.io = &console_io, \
|
||||
STDIO_FIELDS \
|
||||
}
|
||||
|
||||
FILE __stdio_FILEs[3] = {
|
||||
@@ -24,9 +33,126 @@ FILE __stdio_FILEs[3] = {
|
||||
};
|
||||
#undef DEFINE_STDIO_DESC
|
||||
|
||||
FILE *fopen(const char *filename, const char *mode) {
|
||||
#if defined(WITH_LIB_FS)
|
||||
FILE *stream = (FILE *) malloc(sizeof(FILE));
|
||||
if (stream == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
stream->use_fs = true;
|
||||
stream->fs_handle.offset = 0;
|
||||
stream->fs_handle.readonly = (!strchr(mode, 'w') && !strchr(mode, 'a'));
|
||||
|
||||
status_t ret = fs_open_file(filename, &(stream->fs_handle.handle));
|
||||
|
||||
if (ret == ERR_NOT_FOUND && !stream->fs_handle.readonly) {
|
||||
ret = fs_create_file(filename, &(stream->fs_handle.handle), 0);
|
||||
}
|
||||
|
||||
if (ret != NO_ERROR) {
|
||||
free(stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (strchr(mode, 'a')) {
|
||||
struct file_stat stat;
|
||||
if (NO_ERROR == fs_stat_file(stream->fs_handle.handle, &stat)) {
|
||||
stream->fs_handle.offset = stat.size;
|
||||
}
|
||||
}
|
||||
|
||||
return stream;
|
||||
#endif // WITH_LIB_FS
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int fclose(FILE *stream) {
|
||||
#if defined(WITH_LIB_FS)
|
||||
if (stream && !stream->use_fs) {
|
||||
fs_close_file(stream->fs_handle.handle);
|
||||
free(stream);
|
||||
}
|
||||
#endif // WITH_LIB_FS
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t fread(void *ptr, size_t size, size_t count, FILE *stream) {
|
||||
#if defined(WITH_LIB_FS)
|
||||
if (stream->use_fs) {
|
||||
size_t rsize = fs_read_file(stream->fs_handle.handle, ptr,
|
||||
stream->fs_handle.offset, size * count);
|
||||
stream->fs_handle.offset += rsize;
|
||||
return rsize / size;
|
||||
}
|
||||
#endif // WITH_LIB_FS
|
||||
return io_read(stream->io, ptr, size * count) / size;
|
||||
}
|
||||
|
||||
int fflush(FILE *stream) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int feof(FILE *stream) {
|
||||
#if defined(WITH_LIB_FS)
|
||||
if (!stream->use_fs) {
|
||||
return 0;
|
||||
}
|
||||
struct file_stat stat;
|
||||
if (NO_ERROR != fs_stat_file(stream->fs_handle.handle, &stat)) {
|
||||
return 1;
|
||||
}
|
||||
return (uint64_t)stream->fs_handle.offset >= stat.size;
|
||||
#endif // WITH_LIB_FS
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline off_t clamp(off_t val, off_t min, off_t max) {
|
||||
const off_t t = (val < min) ? min : val;
|
||||
return (t > max) ? max : t;
|
||||
}
|
||||
|
||||
int fseek(FILE *stream, long offset, int whence) {
|
||||
#if defined(WITH_LIB_FS)
|
||||
if (!stream->use_fs) {
|
||||
return 0;
|
||||
}
|
||||
struct file_stat stat;
|
||||
if (NO_ERROR != fs_stat_file(stream->fs_handle.handle, &stat)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (whence) {
|
||||
case SEEK_SET:
|
||||
stream->fs_handle.offset = clamp(offset, 0, stat.size);
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
stream->fs_handle.offset = clamp(stream->fs_handle.offset + offset, 0, stat.size);
|
||||
break;
|
||||
case SEEK_END:
|
||||
stream->fs_handle.offset = clamp(stat.size - offset, 0, stat.size);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
#endif // WITH_LIB_FS
|
||||
return 0;
|
||||
}
|
||||
|
||||
long ftell(FILE *stream) {
|
||||
#if defined(WITH_LIB_FS)
|
||||
if (!stream->use_fs) {
|
||||
return 0;
|
||||
}
|
||||
return stream->fs_handle.offset;
|
||||
#endif // WITH_LIB_FS
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int fputc(int _c, FILE *fp) {
|
||||
unsigned char c = _c;
|
||||
return io_write(fp->io, (char *)&c, 1);
|
||||
return fwrite(&c, /*size=*/1, /*count=*/1, fp);
|
||||
}
|
||||
|
||||
int putchar(int c) {
|
||||
@@ -42,40 +168,75 @@ int puts(const char *str) {
|
||||
|
||||
int fputs(const char *s, FILE *fp) {
|
||||
size_t len = strlen(s);
|
||||
|
||||
return io_write(fp->io, s, len);
|
||||
return fwrite(s, /*size=*/1, /*count=*/len, fp);
|
||||
}
|
||||
|
||||
size_t fwrite(const void *ptr, size_t size, size_t count, FILE *fp) {
|
||||
size_t bytes_written;
|
||||
|
||||
if (size == 0 || count == 0)
|
||||
size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream) {
|
||||
if (stream == stdin) {
|
||||
return 0;
|
||||
|
||||
// fast path for size == 1
|
||||
if (likely(size == 1)) {
|
||||
return io_write(fp->io, ptr, count);
|
||||
}
|
||||
|
||||
bytes_written = io_write(fp->io, ptr, size * count);
|
||||
return bytes_written / size;
|
||||
if (stream == stdout || stream == stderr) {
|
||||
size_t bytes_written;
|
||||
|
||||
if (size == 0 || count == 0)
|
||||
return 0;
|
||||
|
||||
// fast path for size == 1
|
||||
if (likely(size == 1)) {
|
||||
return io_write(stream->io, ptr, count);
|
||||
}
|
||||
|
||||
bytes_written = io_write(stream->io, ptr, size * count);
|
||||
return bytes_written / size;
|
||||
}
|
||||
|
||||
#if defined(WITH_LIB_FS)
|
||||
if (stream->fs_handle.readonly) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t wsize = fs_write_file(stream->fs_handle.handle, ptr,
|
||||
stream->fs_handle.offset, size * count);
|
||||
stream->fs_handle.offset += wsize;
|
||||
|
||||
return wsize / size;
|
||||
#endif // WITH_LIB_FS
|
||||
return 0;
|
||||
}
|
||||
|
||||
int getc(FILE *fp) {
|
||||
int fgetc(FILE *fp) {
|
||||
char c;
|
||||
ssize_t ret = io_read(fp->io, &c, sizeof(c));
|
||||
ssize_t ret = fread(&c, /*size=*/1, /*count=*/1, fp);
|
||||
|
||||
return (ret > 0) ? c : ret;
|
||||
return (ret > 0) ? c : EOF;
|
||||
}
|
||||
|
||||
int getchar(void) {
|
||||
return getc(stdin);
|
||||
}
|
||||
|
||||
char *fgets(char *s, int size, FILE *stream) {
|
||||
int c = -1;
|
||||
char *cs = s;
|
||||
|
||||
if (size < 1) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (--size > 0 && (c = fgetc(stream)) != EOF) {
|
||||
if ((*cs++ = c) == '\n') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*cs = '\0';
|
||||
return (c == EOF && cs == s) ? NULL : s;
|
||||
}
|
||||
|
||||
int _fprintf_output_func(const char *str, size_t len, void *state) {
|
||||
FILE *fp = (FILE *)state;
|
||||
|
||||
return io_write(fp->io, str, len);
|
||||
return fwrite(str, /*size=*/1, /*count=*/len, fp);
|
||||
}
|
||||
|
||||
int vfprintf(FILE *fp, const char *fmt, va_list ap) {
|
||||
|
||||
Reference in New Issue
Block a user