From f90bb804889db7f425550a91a50df28be4003e33 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Thu, 14 Feb 2013 17:29:59 -0800 Subject: [klibc] fopencookie: Add an implementation of fopencookie() fopencookie() is a glibc extension which allows the stdio framework to be used with a user-defined backend. The entire implementation is 172 bytes long on x86-64. Signed-off-by: H. Peter Anvin --- usr/include/stdio.h | 19 +++++++++++++------ usr/klibc/stdio/fclose.c | 2 +- usr/klibc/stdio/fdopen.c | 32 +++++++++++++++++++++++++++++--- usr/klibc/stdio/fflush.c | 2 +- usr/klibc/stdio/fileno.c | 11 ++++++++--- usr/klibc/stdio/fread.c | 2 +- usr/klibc/stdio/fseek.c | 2 +- usr/klibc/stdio/ftell.c | 2 +- usr/klibc/stdio/fwrite.c | 2 +- usr/klibc/stdio/stdioint.h | 3 +++ 10 files changed, 59 insertions(+), 18 deletions(-) diff --git a/usr/include/stdio.h b/usr/include/stdio.h index 21243cc703c65..fc70cf967532f 100644 --- a/usr/include/stdio.h +++ b/usr/include/stdio.h @@ -12,8 +12,19 @@ #include #include +typedef ssize_t cookie_read_function_t(void *, char *, size_t); +typedef ssize_t cookie_write_function_t(void *, const char *, size_t); +typedef int cookie_seek_function_t(void *, off_t, int); +typedef int cookie_close_function_t(void *); +struct cookie_io_functions_t { + cookie_read_function_t *read; + cookie_write_function_t *write; + cookie_seek_function_t *seek; + cookie_close_function_t *close; +}; +typedef struct cookie_io_functions_t cookie_io_functions_t; + struct _IO_file { - int _IO_fileno; /* Underlying file descriptor */ _Bool _IO_eof; /* End of file flag */ _Bool _IO_error; /* Error flag */ }; @@ -41,6 +52,7 @@ enum _IO_bufmode { */ __extern FILE *stdin, *stdout, *stderr; +__extern FILE *fopencookie(void *, const char *, cookie_io_functions_t); __extern FILE *fopen(const char *, const char *); __extern FILE *fdopen(int, const char *); __extern int fclose(FILE *); @@ -110,11 +122,6 @@ fwrite(const void *__p, size_t __s, size_t __n, FILE * __f) return _fwrite(__p, __s * __n, __f) / __s; } -__extern_inline int fileno(FILE *__f) -{ - return __f->_IO_fileno; -} - __extern_inline int ferror(FILE *__f) { return __f->_IO_error; diff --git a/usr/klibc/stdio/fclose.c b/usr/klibc/stdio/fclose.c index 756de4329d777..e4b5c6aeb150c 100644 --- a/usr/klibc/stdio/fclose.c +++ b/usr/klibc/stdio/fclose.c @@ -11,7 +11,7 @@ int fclose(FILE *file) fflush(file); - rv = close(f->pub._IO_fileno); + rv = f->funcs.close(f->cookie); /* Remove from linked list */ f->next->prev = f->prev; diff --git a/usr/klibc/stdio/fdopen.c b/usr/klibc/stdio/fdopen.c index 51285baeeb123..bbfbd1f9914bb 100644 --- a/usr/klibc/stdio/fdopen.c +++ b/usr/klibc/stdio/fdopen.c @@ -15,7 +15,7 @@ struct _IO_file_pvt __stdio_headnode = .next = &__stdio_headnode, }; -FILE *fdopen(int fd, const char *mode) +FILE *fopencookie(void *cookie, const char *mode, cookie_io_functions_t funcs) { struct _IO_file_pvt *f; const size_t bufoffs = @@ -29,9 +29,10 @@ FILE *fdopen(int fd, const char *mode) goto err; f->data = f->buf = (char *)f + bufoffs; - f->pub._IO_fileno = fd; + f->funcs = funcs; + f->cookie = cookie; f->bufsiz = BUFSIZ; - f->bufmode = isatty(fd) ? _IOLBF : _IOFBF; + f->bufmode = _IOFBF; /* Insert into linked list */ f->prev = &__stdio_headnode; @@ -48,6 +49,31 @@ err: return NULL; } +FILE *fdopen(int fd, const char *mode) +{ + FILE *file; + + /* + * Cookie operations for ordinary files. This is only safe because + * all Linux architectures pass integers <= sizeof(pointer) and + * pointers the same way, at least for non-structure arguments. + */ + const struct cookie_io_functions_t file_funcs = { + .read = (cookie_read_function_t *)read, + .write = (cookie_write_function_t *)write, + .seek = (cookie_seek_function_t *)lseek, + .close = (cookie_close_function_t *)close + }; + + file = fopencookie((void *)(intptr_t)fd, mode, file_funcs); + if (file) { + struct _IO_file_pvt *f = stdio_pvt(file); + f->isfile = true; + f->bufmode = isatty(fd) ? _IOLBF : _IOFBF; + } + return file; +} + void __init_stdio(void) { stdin = fdopen(0, NULL); diff --git a/usr/klibc/stdio/fflush.c b/usr/klibc/stdio/fflush.c index dfccd24d4f8d1..2764437feae02 100644 --- a/usr/klibc/stdio/fflush.c +++ b/usr/klibc/stdio/fflush.c @@ -18,7 +18,7 @@ int __fflush(struct _IO_file_pvt *f) p = f->buf; while (f->obytes) { - rv = write(f->pub._IO_fileno, p, f->obytes); + rv = f->funcs.write(f->cookie, p, f->obytes); if (rv == -1) { if (errno == EINTR || errno == EAGAIN) continue; diff --git a/usr/klibc/stdio/fileno.c b/usr/klibc/stdio/fileno.c index b5a101618684e..d6e4e9b527ab9 100644 --- a/usr/klibc/stdio/fileno.c +++ b/usr/klibc/stdio/fileno.c @@ -1,7 +1,12 @@ -#define __NO_STDIO_INLINES #include "stdioint.h" -int fileno(FILE *__f) +int fileno(FILE *file) { - return __f->_IO_fileno; + struct _IO_file_pvt *f = stdio_pvt(file); + + if (f->isfile) + return (int)(intptr_t)f->cookie; + + errno = EBADF; + return -1; } diff --git a/usr/klibc/stdio/fread.c b/usr/klibc/stdio/fread.c index b0994268e7b8e..6d206b73e0fea 100644 --- a/usr/klibc/stdio/fread.c +++ b/usr/klibc/stdio/fread.c @@ -37,7 +37,7 @@ size_t _fread(void *buf, size_t count, FILE *file) nb = f->bufsiz; } - rv = read(f->pub._IO_fileno, rdptr, nb); + rv = f->funcs.read(f->cookie, rdptr, nb); if (rv == -1) { if (errno == EINTR || errno == EAGAIN) continue; diff --git a/usr/klibc/stdio/fseek.c b/usr/klibc/stdio/fseek.c index e35f85e64a632..b250efcd5532e 100644 --- a/usr/klibc/stdio/fseek.c +++ b/usr/klibc/stdio/fseek.c @@ -16,7 +16,7 @@ __extern int fseek(FILE *file, off_t where, int whence) if (whence == SEEK_CUR) where -= f->ibytes; - rv = lseek(f->pub._IO_fileno, where, whence); + rv = f->funcs.seek(f->cookie, where, whence); if (__likely(rv >= 0)) { f->pub._IO_eof = false; f->ibytes = 0; diff --git a/usr/klibc/stdio/ftell.c b/usr/klibc/stdio/ftell.c index cb1202da0bc1f..4db13dd9064cb 100644 --- a/usr/klibc/stdio/ftell.c +++ b/usr/klibc/stdio/ftell.c @@ -3,7 +3,7 @@ off_t ftell(FILE *file) { struct _IO_file_pvt *f = stdio_pvt(file); - off_t pos = lseek(f->pub._IO_fileno, 0, SEEK_CUR); + off_t pos = f->funcs.seek(f->cookie, 0, SEEK_CUR); if (pos >= 0) pos += (int)f->obytes - (int)f->ibytes; diff --git a/usr/klibc/stdio/fwrite.c b/usr/klibc/stdio/fwrite.c index 5d2c3f01343eb..46cb04fa7ddc7 100644 --- a/usr/klibc/stdio/fwrite.c +++ b/usr/klibc/stdio/fwrite.c @@ -23,7 +23,7 @@ static size_t fwrite_noflush(const void *buf, size_t count, * The buffer is empty and the write is large, * so bypass the buffering entirely. */ - rv = write(f->pub._IO_fileno, p, count); + rv = f->funcs.write(f->cookie, p, count); if (rv == -1) { if (errno == EINTR || errno == EAGAIN) continue; diff --git a/usr/klibc/stdio/stdioint.h b/usr/klibc/stdio/stdioint.h index 526c25a8f7118..aeac7a0b575b8 100644 --- a/usr/klibc/stdio/stdioint.h +++ b/usr/klibc/stdio/stdioint.h @@ -18,12 +18,15 @@ struct _IO_file_pvt { struct _IO_file pub; /* Data exported to inlines */ struct _IO_file_pvt *prev, *next; + cookie_io_functions_t funcs; + void *cookie; /* Cookie or file number */ char *buf; /* Buffer */ char *data; /* Location of input data in buffer */ unsigned int ibytes; /* Input data bytes in buffer */ unsigned int obytes; /* Output data bytes in buffer */ unsigned int bufsiz; /* Total size of buffer */ enum _IO_bufmode bufmode; /* Type of buffering */ + bool isfile; /* fileno() is valid on this file */ }; #define stdio_pvt(x) container_of(x, struct _IO_file_pvt, pub) -- cgit 1.2.3-korg