Implement stdio line buffering semantics.
This commit is contained in:
parent
15c48f4efc
commit
a90e6d5d16
|
@ -57,6 +57,7 @@ fregister.o \
|
||||||
fscanf.o \
|
fscanf.o \
|
||||||
fseek.o \
|
fseek.o \
|
||||||
fseeko.o \
|
fseeko.o \
|
||||||
|
fsetdefaultbuf.o \
|
||||||
fseterr.o \
|
fseterr.o \
|
||||||
fsetlocking.o \
|
fsetlocking.o \
|
||||||
ftell.o \
|
ftell.o \
|
||||||
|
@ -84,6 +85,8 @@ memmove.o \
|
||||||
memset.o \
|
memset.o \
|
||||||
op-new.o \
|
op-new.o \
|
||||||
rewind.o \
|
rewind.o \
|
||||||
|
setbuf.o \
|
||||||
|
setvbuf.o \
|
||||||
sort.o \
|
sort.o \
|
||||||
sprint.o \
|
sprint.o \
|
||||||
sscanf.o \
|
sscanf.o \
|
||||||
|
|
|
@ -2,11 +2,12 @@
|
||||||
#define _FILE_DECL
|
#define _FILE_DECL
|
||||||
#define BUFSIZ 8192UL
|
#define BUFSIZ 8192UL
|
||||||
#define _FILE_REGISTERED (1<<0)
|
#define _FILE_REGISTERED (1<<0)
|
||||||
#define _FILE_NO_BUFFER (1<<1)
|
#define _FILE_BUFFER_MODE_SET (1<<1)
|
||||||
#define _FILE_LAST_WRITE (1<<2)
|
#define _FILE_LAST_WRITE (1<<2)
|
||||||
#define _FILE_LAST_READ (1<<3)
|
#define _FILE_LAST_READ (1<<3)
|
||||||
#define _FILE_AUTO_LOCK (1<<4)
|
#define _FILE_AUTO_LOCK (1<<4)
|
||||||
#define _FILE_STREAM (1<<5)
|
#define _FILE_STREAM (1<<5)
|
||||||
|
#define _FILE_BUFFER_OWNED (1<<6)
|
||||||
#define _FILE_MAX_PUSHBACK 8
|
#define _FILE_MAX_PUSHBACK 8
|
||||||
typedef struct _FILE
|
typedef struct _FILE
|
||||||
{
|
{
|
||||||
|
@ -32,6 +33,7 @@ typedef struct _FILE
|
||||||
struct _FILE* prev;
|
struct _FILE* prev;
|
||||||
struct _FILE* next;
|
struct _FILE* next;
|
||||||
int flags;
|
int flags;
|
||||||
|
int buffer_mode;
|
||||||
size_t offset_input_buffer;
|
size_t offset_input_buffer;
|
||||||
size_t amount_input_buffered;
|
size_t amount_input_buffered;
|
||||||
size_t amount_output_buffered;
|
size_t amount_output_buffered;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
|
||||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013.
|
||||||
|
|
||||||
This file is part of the Sortix C Library.
|
This file is part of the Sortix C Library.
|
||||||
|
|
||||||
|
@ -32,7 +32,11 @@
|
||||||
|
|
||||||
extern "C" int fgetc(FILE* fp)
|
extern "C" int fgetc(FILE* fp)
|
||||||
{
|
{
|
||||||
if ( fp->flags & _FILE_NO_BUFFER )
|
if ( !(fp->flags & _FILE_BUFFER_MODE_SET) )
|
||||||
|
if ( fsetdefaultbuf(fp) != 0 )
|
||||||
|
return EOF; // TODO: ferror doesn't report error!
|
||||||
|
|
||||||
|
if ( fp->buffer_mode == _IONBF )
|
||||||
{
|
{
|
||||||
unsigned char c;
|
unsigned char c;
|
||||||
if ( fread(&c, sizeof(c), 1, fp) != 1 )
|
if ( fread(&c, sizeof(c), 1, fp) != 1 )
|
||||||
|
|
|
@ -26,5 +26,5 @@
|
||||||
|
|
||||||
extern "C" int flbf(FILE* fp)
|
extern "C" int flbf(FILE* fp)
|
||||||
{
|
{
|
||||||
return !(fp->flags & _FILE_NO_BUFFER);
|
return fp->buffer_mode == _IOLBF;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
|
||||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013.
|
||||||
|
|
||||||
This file is part of the Sortix C Library.
|
This file is part of the Sortix C Library.
|
||||||
|
|
||||||
|
@ -27,7 +27,8 @@
|
||||||
|
|
||||||
static void ffreefile(FILE* fp)
|
static void ffreefile(FILE* fp)
|
||||||
{
|
{
|
||||||
free(fp->buffer);
|
if ( fp->flags & _FILE_BUFFER_OWNED )
|
||||||
|
free(fp->buffer);
|
||||||
free(fp);
|
free(fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,10 +36,10 @@ extern "C" FILE* fnewfile(void)
|
||||||
{
|
{
|
||||||
FILE* fp = (FILE*) calloc(sizeof(FILE), 1);
|
FILE* fp = (FILE*) calloc(sizeof(FILE), 1);
|
||||||
if ( !fp ) { return NULL; }
|
if ( !fp ) { return NULL; }
|
||||||
fp->buffersize = BUFSIZ;
|
fp->buffersize = 0;
|
||||||
fp->buffer = (unsigned char*) malloc(fp->buffersize);
|
fp->buffer = NULL;
|
||||||
if ( !fp->buffer ) { free(fp); return NULL; }
|
|
||||||
fp->flags = _FILE_AUTO_LOCK;
|
fp->flags = _FILE_AUTO_LOCK;
|
||||||
|
fp->buffer_mode = 0;
|
||||||
fp->offset_input_buffer = 0;
|
fp->offset_input_buffer = 0;
|
||||||
fp->amount_input_buffered = 0;
|
fp->amount_input_buffered = 0;
|
||||||
fp->amount_output_buffered = 0;
|
fp->amount_output_buffered = 0;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
|
||||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013.
|
||||||
|
|
||||||
This file is part of the Sortix C Library.
|
This file is part of the Sortix C Library.
|
||||||
|
|
||||||
|
@ -26,7 +26,11 @@
|
||||||
|
|
||||||
extern "C" int fputc(int c, FILE* fp)
|
extern "C" int fputc(int c, FILE* fp)
|
||||||
{
|
{
|
||||||
if ( fp->flags & _FILE_NO_BUFFER )
|
if ( !(fp->flags & _FILE_BUFFER_MODE_SET) )
|
||||||
|
if ( fsetdefaultbuf(fp) != 0 )
|
||||||
|
return EOF; // TODO: ferror doesn't report error!
|
||||||
|
|
||||||
|
if ( fp->buffer_mode == _IONBF )
|
||||||
{
|
{
|
||||||
unsigned char c_char = c;
|
unsigned char c_char = c;
|
||||||
if ( fwrite(&c_char, sizeof(c_char), 1, fp) != 1 )
|
if ( fwrite(&c_char, sizeof(c_char), 1, fp) != 1 )
|
||||||
|
@ -45,7 +49,7 @@ extern "C" int fputc(int c, FILE* fp)
|
||||||
return EOF;
|
return EOF;
|
||||||
|
|
||||||
fp->buffer[fp->amount_output_buffered++] = c;
|
fp->buffer[fp->amount_output_buffered++] = c;
|
||||||
if ( c == '\n' && fflush(fp) != 0 )
|
if ( fp->buffer_mode == _IOLBF && c == '\n' && fflush(fp) != 0 )
|
||||||
return EOF;
|
return EOF;
|
||||||
|
|
||||||
return c;
|
return c;
|
||||||
|
|
|
@ -26,8 +26,11 @@
|
||||||
|
|
||||||
extern "C" size_t fread(void* ptr, size_t size, size_t nmemb, FILE* fp)
|
extern "C" size_t fread(void* ptr, size_t size, size_t nmemb, FILE* fp)
|
||||||
{
|
{
|
||||||
if ( fp->flags & _FILE_NO_BUFFER )
|
if ( fp->buffer_mode == _IONBF )
|
||||||
{
|
{
|
||||||
|
if ( !(fp->flags & _FILE_BUFFER_MODE_SET) )
|
||||||
|
if ( fsetdefaultbuf(fp) != 0 )
|
||||||
|
return EOF; // TODO: ferror doesn't report error!
|
||||||
if ( !fp->read_func )
|
if ( !fp->read_func )
|
||||||
return 0; // TODO: ferror doesn't report error!
|
return 0; // TODO: ferror doesn't report error!
|
||||||
if ( fp->flags & _FILE_LAST_WRITE )
|
if ( fp->flags & _FILE_LAST_WRITE )
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
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/>.
|
||||||
|
|
||||||
|
fsetdefaultbuf.cpp
|
||||||
|
Sets up default buffering semantics for a FILE.
|
||||||
|
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
extern "C" int fsetdefaultbuf(FILE* fp)
|
||||||
|
{
|
||||||
|
char* buf = (char*) malloc(sizeof(char) * BUFSIZ);
|
||||||
|
if ( !buf )
|
||||||
|
{
|
||||||
|
// TODO: Determine whether this is truly what we would want and whether
|
||||||
|
// a buffer should be pre-allocated when the FILE is created such that
|
||||||
|
// this situation _cannot_ occur.
|
||||||
|
|
||||||
|
// Alright, we're in a bit of a situation here. Normally, we'd go
|
||||||
|
// buffered but we are out of memory. We could either fail, but that
|
||||||
|
// would mean subsequent calls such as fgetc and fputc would also fail -
|
||||||
|
// however that we are out of memory doesn't mean that IO would also
|
||||||
|
// fail. Therefore we'll revert to unbuffered semantics and hope that's
|
||||||
|
// good enough.
|
||||||
|
|
||||||
|
return setvbuf(fp, NULL, _IONBF, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the buffering semantics depending on whether the destination is
|
||||||
|
// an interactive device or not.
|
||||||
|
#ifdef SORTIX_KERNEL
|
||||||
|
int mode = _IOLBF; // TODO: Detect this?
|
||||||
|
#else
|
||||||
|
int mode = fp->buffer_mode != -1 ? fp->buffer_mode
|
||||||
|
: isatty(fileno(fp)) ? _IOLBF : _IOFBF;
|
||||||
|
#endif
|
||||||
|
int ret = setvbuf(fp, buf, mode, BUFSIZ);
|
||||||
|
if ( ret )
|
||||||
|
{
|
||||||
|
free(buf);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The buffer now belongs to the FILE.
|
||||||
|
fp->flags |= _FILE_BUFFER_OWNED;
|
||||||
|
return ret;
|
||||||
|
}
|
|
@ -26,8 +26,12 @@
|
||||||
|
|
||||||
extern "C" size_t fwrite(const void* ptr, size_t size, size_t nmemb, FILE* fp)
|
extern "C" size_t fwrite(const void* ptr, size_t size, size_t nmemb, FILE* fp)
|
||||||
{
|
{
|
||||||
if ( fp->flags & _FILE_NO_BUFFER )
|
if ( fp->buffer_mode == _IONBF )
|
||||||
{
|
{
|
||||||
|
if ( !(fp->flags & _FILE_BUFFER_MODE_SET) )
|
||||||
|
if ( fsetdefaultbuf(fp) != 0 )
|
||||||
|
return EOF; // TODO: ferror doesn't report error!
|
||||||
|
|
||||||
if ( !fp->write_func )
|
if ( !fp->write_func )
|
||||||
return 0; // TODO: ferror doesn't report error!
|
return 0; // TODO: ferror doesn't report error!
|
||||||
if ( fp->flags & _FILE_LAST_READ )
|
if ( fp->flags & _FILE_LAST_READ )
|
||||||
|
|
|
@ -111,6 +111,7 @@ extern int rename(const char* oldname, const char* newname);
|
||||||
extern void rewind(FILE* stream);
|
extern void rewind(FILE* stream);
|
||||||
extern int snprintf(char* restrict s, size_t n, const char* restrict format, ...);
|
extern int snprintf(char* restrict s, size_t n, const char* restrict format, ...);
|
||||||
extern void setbuf(FILE* restrict stream, char* restrict buf);
|
extern void setbuf(FILE* restrict stream, char* restrict buf);
|
||||||
|
extern int setvbuf(FILE* restrict stream, char* restrict buf, int type, size_t size);
|
||||||
extern char* sortix_gets(void);
|
extern char* sortix_gets(void);
|
||||||
extern int sortix_puts(const char* str);
|
extern int sortix_puts(const char* str);
|
||||||
extern int sprintf(char* restrict s, const char* restrict format, ...);
|
extern int sprintf(char* restrict s, const char* restrict format, ...);
|
||||||
|
@ -139,7 +140,6 @@ extern int getchar_unlocked(void);
|
||||||
extern int getc_unlocked(FILE* stream);
|
extern int getc_unlocked(FILE* stream);
|
||||||
extern int putchar_unlocked(int c);
|
extern int putchar_unlocked(int c);
|
||||||
extern int putc_unlocked(int c, FILE* steam);
|
extern int putc_unlocked(int c, FILE* steam);
|
||||||
extern int setvbuf(FILE* restrict stream, char* restrict buf, int type, size_t size);
|
|
||||||
extern int vdprintf(int fildes, const char* restrict format, __gnuc_va_list ap);
|
extern int vdprintf(int fildes, const char* restrict format, __gnuc_va_list ap);
|
||||||
extern void flockfile(FILE* file);
|
extern void flockfile(FILE* file);
|
||||||
extern void funlockfile(FILE* file);
|
extern void funlockfile(FILE* file);
|
||||||
|
@ -168,6 +168,7 @@ void fseterr(FILE* fp);
|
||||||
void fregister(FILE* fp);
|
void fregister(FILE* fp);
|
||||||
void funregister(FILE* fp);
|
void funregister(FILE* fp);
|
||||||
FILE* fnewfile(void);
|
FILE* fnewfile(void);
|
||||||
|
int fsetdefaultbuf(FILE* fp);
|
||||||
int fcloseall(void);
|
int fcloseall(void);
|
||||||
int fpipe(FILE* pipes[2]);
|
int fpipe(FILE* pipes[2]);
|
||||||
/* Internally used by standard library. */
|
/* Internally used by standard library. */
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
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/>.
|
||||||
|
|
||||||
|
setbuf.cpp
|
||||||
|
Sets up buffering semantics for a FILE.
|
||||||
|
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
// TODO: This function just be removed as setvbuf provides a superior interface,
|
||||||
|
// however it's currently defined in C89, C99 and C11 - so removing it
|
||||||
|
// is non-standard - and this little file doesn't hurt anyone. :-)
|
||||||
|
extern "C" void setbuf(FILE* fp, char* buf)
|
||||||
|
{
|
||||||
|
setvbuf(fp, buf, buf ? _IOFBF : _IONBF, BUFSIZ);
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
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/>.
|
||||||
|
|
||||||
|
setvbuf.cpp
|
||||||
|
Sets up buffering semantics for a FILE.
|
||||||
|
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
extern "C" int setvbuf(FILE* fp, char* buf, int mode, size_t size)
|
||||||
|
{
|
||||||
|
if ( fp->flags & _FILE_BUFFER_MODE_SET )
|
||||||
|
return errno = EINVAL, -1;
|
||||||
|
fp->buffer_mode = mode;
|
||||||
|
if ( buf )
|
||||||
|
{
|
||||||
|
fp->buffer = (unsigned char*) buf;
|
||||||
|
fp->buffersize = size;
|
||||||
|
fp->flags |= _FILE_BUFFER_MODE_SET;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -38,6 +38,7 @@ int init_stdio()
|
||||||
stdin = fdio_newfile(0, "r");
|
stdin = fdio_newfile(0, "r");
|
||||||
stdout = fdio_newfile(1, "w");
|
stdout = fdio_newfile(1, "w");
|
||||||
stderr = fdio_newfile(2, "w");
|
stderr = fdio_newfile(2, "w");
|
||||||
|
setvbuf(stderr, NULL, _IONBF, 0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,11 @@
|
||||||
|
|
||||||
extern "C" int ungetc(int c, FILE* fp)
|
extern "C" int ungetc(int c, FILE* fp)
|
||||||
{
|
{
|
||||||
if ( !fp->read_func || (fp->flags & _FILE_NO_BUFFER) )
|
if ( !(fp->flags & _FILE_BUFFER_MODE_SET) )
|
||||||
|
if ( fsetdefaultbuf(fp) != 0 )
|
||||||
|
return EOF; // TODO: ferror doesn't report error!
|
||||||
|
|
||||||
|
if ( !fp->read_func || fp->buffer_mode == _IONBF )
|
||||||
return EOF;
|
return EOF;
|
||||||
|
|
||||||
if ( fp->flags & _FILE_LAST_WRITE )
|
if ( fp->flags & _FILE_LAST_WRITE )
|
||||||
|
|
Loading…
Reference in New Issue