Refactor FILE creation and destruction.
This commit is contained in:
parent
a90e6d5d16
commit
476b27c301
|
@ -35,6 +35,7 @@ errno.o \
|
|||
fabs.o \
|
||||
fbufsize.o \
|
||||
fclose.o \
|
||||
fdeletefile.o \
|
||||
feof.o \
|
||||
ferror.o \
|
||||
fflush.o \
|
||||
|
@ -54,12 +55,14 @@ freadable.o \
|
|||
freading.o \
|
||||
fread.o \
|
||||
fregister.o \
|
||||
fresetfile.o \
|
||||
fscanf.o \
|
||||
fseek.o \
|
||||
fseeko.o \
|
||||
fsetdefaultbuf.o \
|
||||
fseterr.o \
|
||||
fsetlocking.o \
|
||||
fshutdown.o \
|
||||
ftell.o \
|
||||
ftello.o \
|
||||
fwritable.o \
|
||||
|
|
|
@ -18,6 +18,8 @@ typedef struct _FILE
|
|||
size_t buffersize;
|
||||
unsigned char* buffer;
|
||||
void* user;
|
||||
void* free_user;
|
||||
int (*reopen_func)(void* user, const char* mode);
|
||||
size_t (*read_func)(void* ptr, size_t size, size_t nmemb, void* user);
|
||||
size_t (*write_func)(const void* ptr, size_t size, size_t nmemb, void* user);
|
||||
int (*seek_func)(void* user, off_t offset, int whence);
|
||||
|
@ -28,7 +30,7 @@ typedef struct _FILE
|
|||
int (*error_func)(void* user);
|
||||
int (*fileno_func)(void* user);
|
||||
int (*close_func)(void* user);
|
||||
void (*free_func)(struct _FILE* fp);
|
||||
void (*free_func)(void* free_user, struct _FILE* fp);
|
||||
/* Application writers shouldn't use anything beyond this point. */
|
||||
struct _FILE* prev;
|
||||
struct _FILE* next;
|
||||
|
|
|
@ -26,14 +26,7 @@
|
|||
|
||||
extern "C" int fclose(FILE* fp)
|
||||
{
|
||||
if ( fflush(fp) )
|
||||
{
|
||||
/* TODO: How to report errors here? fclose may need us to return its
|
||||
exact error value, for instance, as with popen/pclose. */;
|
||||
}
|
||||
int result = fp->close_func ? fp->close_func(fp->user) : 0;
|
||||
funregister(fp);
|
||||
if ( fp->free_func )
|
||||
fp->free_func(fp);
|
||||
return result;
|
||||
int ret = fshutdown(fp);
|
||||
fdeletefile(fp);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||
|
||||
This file is part of the Sortix C Library.
|
||||
|
||||
The Sortix C Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
The Sortix C Library is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with the Sortix C Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
fdeletefile.cpp
|
||||
Deallocator for things returned by fnewfile after being shut down.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
extern "C" void fdeletefile(FILE* fp)
|
||||
{
|
||||
funregister(fp);
|
||||
if ( fp->free_func )
|
||||
fp->free_func(fp->free_user, fp);
|
||||
}
|
102
libc/fdio.c
102
libc/fdio.c
|
@ -45,6 +45,17 @@ typedef struct fdio_struct
|
|||
int fd;
|
||||
} fdio_t;
|
||||
|
||||
static int fdio_reopen(void* user, const char* mode)
|
||||
{
|
||||
(void) user;
|
||||
(void) mode;
|
||||
// TODO: Unfortunately, we don't support this yet. Note that we don't really
|
||||
// have to support this according to POSIX - but it'd be nicer to push this
|
||||
// restriction into the kernel and argue it's a security problem "What? No
|
||||
// you can't make this read-only descriptor readable!".
|
||||
return errno = ENOTSUP, -1;
|
||||
}
|
||||
|
||||
static size_t fdio_read(void* ptr, size_t size, size_t nmemb, void* user)
|
||||
{
|
||||
uint8_t* buf = (uint8_t*) ptr;
|
||||
|
@ -57,6 +68,8 @@ static size_t fdio_read(void* ptr, size_t size, size_t nmemb, void* user)
|
|||
ssize_t numbytes = read(fdio->fd, buf + sofar, total - sofar);
|
||||
if ( numbytes < 0 ) { fdio->flags |= FDIO_ERROR; break; }
|
||||
if ( numbytes == 0 ) { fdio->flags |= FDIO_EOF; break; }
|
||||
// TODO: Is this a bug? Looks like one, but perhaps this is needed when
|
||||
// reading from line-buffered terminals.
|
||||
return numbytes / size;
|
||||
sofar += numbytes;
|
||||
}
|
||||
|
@ -130,7 +143,33 @@ static int fdio_close(void* user)
|
|||
return result;
|
||||
}
|
||||
|
||||
int fdio_install(FILE* fp, const char* mode, int fd)
|
||||
int fdio_open_descriptor(const char* path, const char* mode)
|
||||
{
|
||||
int omode = 0;
|
||||
int oflags = 0;
|
||||
char c;
|
||||
// TODO: This is too hacky and a little buggy.
|
||||
const char* origmode = mode;
|
||||
while ( (c = *mode++) )
|
||||
switch ( c )
|
||||
{
|
||||
case 'r': omode = O_RDONLY; break;
|
||||
case 'a': oflags |= O_APPEND; /* fall-through */
|
||||
case 'w': omode = O_WRONLY; oflags |= O_CREAT | O_TRUNC; break;
|
||||
case '+':
|
||||
omode = O_RDWR;
|
||||
break;
|
||||
case 'b': break;
|
||||
case 't': break;
|
||||
default:
|
||||
fprintf(stderr, "Unsupported fopen mode: '%s'\n", origmode);
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
return open(path, omode | oflags, 0666);
|
||||
}
|
||||
|
||||
int fdio_install_fd(FILE* fp, int fd, const char* mode)
|
||||
{
|
||||
fdio_t* fdio = (fdio_t*) calloc(1, sizeof(fdio_t));
|
||||
if ( !fdio )
|
||||
|
@ -155,6 +194,7 @@ int fdio_install(FILE* fp, const char* mode, int fd)
|
|||
if ( !fstat(fd, &st) && fdio->flags & FDIO_WRITING && S_ISDIR(st.st_mode) )
|
||||
return free(fdio), errno = EISDIR, 0;
|
||||
fp->user = fdio;
|
||||
fp->reopen_func = fdio_reopen;
|
||||
fp->read_func = fdio_read;
|
||||
fp->write_func = fdio_write;
|
||||
fp->seek_func = fdio_seek;
|
||||
|
@ -170,48 +210,42 @@ int fdio_install(FILE* fp, const char* mode, int fd)
|
|||
return 1;
|
||||
}
|
||||
|
||||
FILE* fdio_newfile(int fd, const char* mode)
|
||||
int fdio_install_path(FILE* fp, const char* path, const char* mode)
|
||||
{
|
||||
int fd = fdio_open_descriptor(path, mode);
|
||||
if ( fd < 0 )
|
||||
return 0;
|
||||
if ( !fdio_install_fd(fp, fd, mode) )
|
||||
return close(fd), 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
FILE* fdio_new_fd(int fd, const char* mode)
|
||||
{
|
||||
FILE* fp = fnewfile();
|
||||
if ( !fp ) { return NULL; }
|
||||
if ( !fdio_install(fp, mode, fd) ) { fclose(fp); return NULL; }
|
||||
if ( !fp )
|
||||
return NULL;
|
||||
if ( !fdio_install_fd(fp, fd, mode) )
|
||||
return fclose(fp), (FILE*) NULL;
|
||||
return fp;
|
||||
}
|
||||
|
||||
FILE* fdio_new_path(const char* path, const char* mode)
|
||||
{
|
||||
FILE* fp = fnewfile();
|
||||
if ( !fp )
|
||||
return NULL;
|
||||
if ( !fdio_install_path(fp, path, mode) )
|
||||
return fclose(fp), (FILE*) NULL;
|
||||
return fp;
|
||||
}
|
||||
|
||||
FILE* fdopen(int fd, const char* mode)
|
||||
{
|
||||
return fdio_newfile(fd, mode);
|
||||
return fdio_new_fd(fd, mode);
|
||||
}
|
||||
|
||||
FILE* fopen(const char* path, const char* mode)
|
||||
{
|
||||
int omode = 0;
|
||||
int oflags = 0;
|
||||
char c;
|
||||
// TODO: This is too hacky and a little buggy.
|
||||
const char* origmode = mode;
|
||||
while ( ( c = *mode++ ) )
|
||||
{
|
||||
switch ( c )
|
||||
{
|
||||
case 'r': omode = O_RDONLY; break;
|
||||
case 'a': oflags |= O_APPEND; /* fall-through */
|
||||
case 'w': omode = O_WRONLY; oflags |= O_CREAT | O_TRUNC; break;
|
||||
case '+':
|
||||
omode = O_RDWR;
|
||||
break;
|
||||
case 'b': break;
|
||||
case 't': break;
|
||||
default:
|
||||
fprintf(stderr, "Unsupported fopen mode: '%s'\n", origmode);
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
mode = origmode;
|
||||
int fd = open(path, omode | oflags, 0666);
|
||||
if ( fd < 0 ) { return NULL; }
|
||||
FILE* fp = fdopen(fd, mode);
|
||||
if ( !fp ) { close(fd); return NULL; }
|
||||
return fp;
|
||||
return fdio_new_path(path, mode);
|
||||
}
|
||||
|
|
|
@ -29,8 +29,11 @@
|
|||
|
||||
__BEGIN_DECLS
|
||||
|
||||
int fdio_install(FILE* fp, const char* mode, int fd);
|
||||
FILE* fdio_newfile(int fd, const char* mode);
|
||||
int fdio_install_fd(FILE* fp, int fd, const char* mode);
|
||||
int fdio_install_path(FILE* fp, const char* path, const char* mode);
|
||||
FILE* fdio_new_fd(int fd, const char* mode);
|
||||
FILE* fdio_new_path(const char* path, const char* mode);
|
||||
int fdio_open_descriptor(const char* path, const char* mode);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
|
|
|
@ -40,6 +40,8 @@ extern "C" char* fgets(char* dest, int size, FILE* fp)
|
|||
}
|
||||
if ( !i && (ferror(fp) || feof(fp)) )
|
||||
return NULL;
|
||||
// TODO: The end-of-file state is lost here if feof(fp) and we are reading
|
||||
// from a terminal that encountered a soft EOF.
|
||||
dest[i] = '\0';
|
||||
return dest;
|
||||
}
|
||||
|
|
|
@ -25,25 +25,19 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static void ffreefile(FILE* fp)
|
||||
static void fnewfile_destroyer(void* /*user*/, FILE* fp)
|
||||
{
|
||||
if ( fp->flags & _FILE_BUFFER_OWNED )
|
||||
free(fp->buffer);
|
||||
free(fp);
|
||||
}
|
||||
|
||||
extern "C" FILE* fnewfile(void)
|
||||
{
|
||||
FILE* fp = (FILE*) calloc(sizeof(FILE), 1);
|
||||
if ( !fp ) { return NULL; }
|
||||
fp->buffersize = 0;
|
||||
fp->buffer = NULL;
|
||||
fp->flags = _FILE_AUTO_LOCK;
|
||||
fp->buffer_mode = 0;
|
||||
fp->offset_input_buffer = 0;
|
||||
fp->amount_input_buffered = 0;
|
||||
fp->amount_output_buffered = 0;
|
||||
fp->free_func = ffreefile;
|
||||
if ( !fp )
|
||||
return NULL;
|
||||
fp->free_user = NULL;
|
||||
fp->free_func = fnewfile_destroyer;
|
||||
fresetfile(fp);
|
||||
fregister(fp);
|
||||
return fp;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013.
|
||||
|
||||
This file is part of the Sortix C Library.
|
||||
|
||||
The Sortix C Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
The Sortix C Library is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with the Sortix C Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
fresetfile.cpp
|
||||
After a FILE has been shut down, returns all fields to their default state.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// Note: This function preserves a few parts of the fields - this means that if
|
||||
// you are using this to reset a fresh FILE object, you should memset it
|
||||
// to zeroes first to avoid problems.
|
||||
extern "C" void fresetfile(FILE* fp)
|
||||
{
|
||||
FILE* prev = fp->prev;
|
||||
FILE* next = fp->next;
|
||||
void* free_user = fp->free_user;
|
||||
void (*free_func)(void*, FILE*) = fp->free_func;
|
||||
int kept_flags = fp->flags & (_FILE_REGISTERED | 0);
|
||||
memset(fp, 0, sizeof(*fp));
|
||||
fp->flags = kept_flags | _FILE_AUTO_LOCK;
|
||||
fp->buffer_mode = -1;
|
||||
fp->free_user = free_user;
|
||||
fp->free_func = free_func;
|
||||
fp->prev = prev;
|
||||
fp->next = next;
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||
|
||||
This file is part of the Sortix C Library.
|
||||
|
||||
The Sortix C Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
The Sortix C Library is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with the Sortix C Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
fshutdown.cpp
|
||||
Uninstalls the backend from a FILE so another can be reinstalled.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
extern "C" int fshutdown(FILE* fp)
|
||||
{
|
||||
int ret = fflush(fp);
|
||||
if ( ret )
|
||||
{
|
||||
/* TODO: How to report errors here? fclose may need us to return its
|
||||
exact error value, for instance, as with popen/pclose. */;
|
||||
}
|
||||
ret = fp->close_func ? fp->close_func(fp->user) : ret;
|
||||
if ( fp->flags & _FILE_BUFFER_OWNED )
|
||||
free(fp->buffer);
|
||||
// Resetting the FILE here isn't needed in the case where fclose calls us,
|
||||
// but it's nice to zero it out anyway (avoiding state) data, and it's a
|
||||
// feature when called by freopen that wishes to reuse the FILE. It also
|
||||
// means that the file is always in a consistent state.
|
||||
fresetfile(fp);
|
||||
return ret;
|
||||
}
|
|
@ -164,12 +164,15 @@ extern char* tempnam(const char* dir, const char* pfx);
|
|||
#define fsetlocking __fsetlocking
|
||||
int fflush_stop_reading(FILE* fp);
|
||||
int fflush_stop_writing(FILE* fp);
|
||||
void fdeletefile(FILE* fp);
|
||||
void fseterr(FILE* fp);
|
||||
void fregister(FILE* fp);
|
||||
void fresetfile(FILE* fp);
|
||||
void funregister(FILE* fp);
|
||||
FILE* fnewfile(void);
|
||||
int fsetdefaultbuf(FILE* fp);
|
||||
int fcloseall(void);
|
||||
int fshutdown(FILE* fp);
|
||||
int fpipe(FILE* pipes[2]);
|
||||
/* Internally used by standard library. */
|
||||
#if defined(LIBC_LIBRARY)
|
||||
|
|
|
@ -35,9 +35,12 @@ FILE* stderr;
|
|||
|
||||
int init_stdio()
|
||||
{
|
||||
stdin = fdio_newfile(0, "r");
|
||||
stdout = fdio_newfile(1, "w");
|
||||
stderr = fdio_newfile(2, "w");
|
||||
// TODO: These calls require memory allocation and can fail - which we don't
|
||||
// currently handle. How about declaring these as global objects and
|
||||
// using fdio_install_fd instead?
|
||||
stdin = fdio_new_fd(0, "r");
|
||||
stdout = fdio_new_fd(1, "w");
|
||||
stderr = fdio_new_fd(2, "w");
|
||||
setvbuf(stderr, NULL, _IONBF, 0);
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue