mirror of
https://git.planet-casio.com/Vhex-Kernel-Core/fxlibc.git
synced 2024-12-29 13:03:38 +01:00
stdio: ungetc(), update (+) and append (a) modes (DONE)
This commit is contained in:
parent
06b66252c9
commit
6ec0c24e2d
11 changed files with 213 additions and 77 deletions
|
@ -154,6 +154,7 @@ set(SOURCES
|
||||||
src/libc/stdio/setvbuf.c
|
src/libc/stdio/setvbuf.c
|
||||||
src/libc/stdio/snprintf.c
|
src/libc/stdio/snprintf.c
|
||||||
src/libc/stdio/sprintf.c
|
src/libc/stdio/sprintf.c
|
||||||
|
src/libc/stdio/ungetc.c
|
||||||
src/libc/stdio/vasprintf.c
|
src/libc/stdio/vasprintf.c
|
||||||
src/libc/stdio/vdprintf.c
|
src/libc/stdio/vdprintf.c
|
||||||
src/libc/stdio/vfprintf.c
|
src/libc/stdio/vfprintf.c
|
||||||
|
|
21
STATUS
21
STATUS
|
@ -85,6 +85,7 @@ TEST: Function/symbol/macro needs to be tested
|
||||||
|
|
||||||
7.19 <stdio.h>
|
7.19 <stdio.h>
|
||||||
7.19.1 Introduction TEST (no wide-oriented streams *)
|
7.19.1 Introduction TEST (no wide-oriented streams *)
|
||||||
|
7.19.1 stdin, stdout, stderr TODO
|
||||||
|
|
||||||
7.19.4.1 remove TEST
|
7.19.4.1 remove TEST
|
||||||
7.19.4.2 rename TODO
|
7.19.4.2 rename TODO
|
||||||
|
@ -93,22 +94,22 @@ TEST: Function/symbol/macro needs to be tested
|
||||||
|
|
||||||
7.19.5.1 fclose -
|
7.19.5.1 fclose -
|
||||||
7.19.5.2 fflush -
|
7.19.5.2 fflush -
|
||||||
7.19.5.3 fopen TEST ("a" and "+" modes remain)
|
7.19.5.3 fopen -
|
||||||
(EXT) fdopen TEST
|
(EXT) fdopen TEST
|
||||||
7.19.5.4 freopen TEST
|
7.19.5.4 freopen TEST (mode changes untested)
|
||||||
7.19.5.5 setbuf -
|
7.19.5.5 setbuf -
|
||||||
7.19.5.6 setvbuf -
|
7.19.5.6 setvbuf -
|
||||||
|
|
||||||
7.19.6.1 fprintf LDEPS(fwrite)
|
7.19.6.1 fprintf TEST
|
||||||
7.19.6.2 fscanf TODO
|
7.19.6.2 fscanf TODO
|
||||||
7.19.6.3 printf LDEPS(fwrite, stdout)
|
7.19.6.3 printf LDEPS(stdout)
|
||||||
7.19.6.4 scanf TODO
|
7.19.6.4 scanf TODO
|
||||||
7.19.6.5 snprintf -
|
7.19.6.5 snprintf -
|
||||||
7.19.6.6 sprintf -
|
7.19.6.6 sprintf -
|
||||||
7.19.6.7 sscanf TODO
|
7.19.6.7 sscanf TODO
|
||||||
7.19.6.8 vfprintf LDEPS(fwrite)
|
7.19.6.8 vfprintf TEST
|
||||||
7.19.6.9 vfscanf TODO
|
7.19.6.9 vfscanf TODO
|
||||||
7.19.6.10 vprintf LDEPS(fwrite, stdout)
|
7.19.6.10 vprintf LDEPS(stdout)
|
||||||
7.19.6.11 vscanf TODO
|
7.19.6.11 vscanf TODO
|
||||||
7.19.6.12 vsnprintf -
|
7.19.6.12 vsnprintf -
|
||||||
7.19.6.13 vsprintf -
|
7.19.6.13 vsprintf -
|
||||||
|
@ -128,10 +129,10 @@ TEST: Function/symbol/macro needs to be tested
|
||||||
7.19.7.8 putc LDPES(fputc)
|
7.19.7.8 putc LDPES(fputc)
|
||||||
7.19.7.9 putchar LDEPS(fputc)
|
7.19.7.9 putchar LDEPS(fputc)
|
||||||
7.19.7.10 puts LDEPS(fputs)
|
7.19.7.10 puts LDEPS(fputs)
|
||||||
7.19.7.11 ungetc TODO
|
7.19.7.11 ungetc -
|
||||||
|
|
||||||
7.19.8.1 fread TEST ("a" and "+" modes remain)
|
7.19.8.1 fread -
|
||||||
7.19.8.2 fwrite TEST ("a" and "+" modes remain)
|
7.19.8.2 fwrite -
|
||||||
|
|
||||||
7.19.9.1 fgetpos -
|
7.19.9.1 fgetpos -
|
||||||
7.19.9.2 fseek -
|
7.19.9.2 fseek -
|
||||||
|
@ -200,7 +201,7 @@ TEST: Function/symbol/macro needs to be tested
|
||||||
(EXT) strncasecmp -
|
(EXT) strncasecmp -
|
||||||
(EXT) strdup -
|
(EXT) strdup -
|
||||||
(EXT) strndup -
|
(EXT) strndup -
|
||||||
(EXT) memrchr -
|
(EXT) memrchr - (Unoptimized: byte-by-byte)
|
||||||
|
|
||||||
7.22 <tgmath.h> => GCC
|
7.22 <tgmath.h> => GCC
|
||||||
|
|
||||||
|
|
|
@ -259,6 +259,9 @@ extern int feof(FILE *__fp);
|
||||||
/* Test the error flag. */
|
/* Test the error flag. */
|
||||||
extern int ferror(FILE *__fp);
|
extern int ferror(FILE *__fp);
|
||||||
|
|
||||||
|
/* Print a message followed by strerror(errno) to stdout. */
|
||||||
|
extern void perror(char const *__s);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -41,6 +41,12 @@
|
||||||
but not yet written to the file descriptor. ftell() reports fdpos + bufpos.
|
but not yet written to the file descriptor. ftell() reports fdpos + bufpos.
|
||||||
The rest of the buffer is undefined.
|
The rest of the buffer is undefined.
|
||||||
|
|
||||||
|
The ungetc() function pushes back characters into the buffer; if the FILE is
|
||||||
|
unbuffered, then it's made buffered temporarily to hold the characters and
|
||||||
|
cleared at the next fflush() or read. The buffer is put in reading mode. For
|
||||||
|
this reason, reading functions should test [fp->buf] to check whether there
|
||||||
|
is a buffer instead of [fp->bufmode != _IONBF].
|
||||||
|
|
||||||
Many fields in the FILE structure are abstracted away by API calls in layers:
|
Many fields in the FILE structure are abstracted away by API calls in layers:
|
||||||
|
|
||||||
1. [fd], [fdpos] and [error] are updated by the primitive functions of
|
1. [fd], [fdpos] and [error] are updated by the primitive functions of
|
||||||
|
@ -67,6 +73,8 @@ typedef struct {
|
||||||
size_t bufpos;
|
size_t bufpos;
|
||||||
size_t bufread;
|
size_t bufread;
|
||||||
size_t bufsize;
|
size_t bufsize;
|
||||||
|
/* Number of ungetc()'d characters at the start of buffer data */
|
||||||
|
int bufungetc;
|
||||||
/* Buffering mode; one of _IOFBF, _IOLBF, or _IONBF */
|
/* Buffering mode; one of _IOFBF, _IOLBF, or _IONBF */
|
||||||
uint8_t bufmode :2;
|
uint8_t bufmode :2;
|
||||||
/* We own the buffer and it needs to be freed */
|
/* We own the buffer and it needs to be freed */
|
||||||
|
|
|
@ -5,30 +5,38 @@
|
||||||
|
|
||||||
int fflush(FILE *fp)
|
int fflush(FILE *fp)
|
||||||
{
|
{
|
||||||
// TODO: fflush(NULL) should flush "all" files (do we track them?)
|
/* TODO: fflush(NULL) should flush "all" files (do we track them?) */
|
||||||
if(!fp) {
|
if(!fp) {
|
||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
return EOF;
|
return EOF;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!fp->buf)
|
if(!fp->buf)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
/* In reading mode, reset the file offset */
|
/* In reading mode, reset the file offset */
|
||||||
if(fp->bufdir == __FILE_BUF_READ && fp->bufpos < fp->bufread) {
|
if(__fp_hasbuf_read(fp)) {
|
||||||
fp->fdpos = fp->fdpos - fp->bufread + fp->bufpos;
|
fp->fdpos = fp->fdpos - fp->bufread + fp->bufpos;
|
||||||
lseek(fp->fd, fp->fdpos, SEEK_SET);
|
lseek(fp->fd, fp->fdpos, SEEK_SET);
|
||||||
fp->bufpos = 0;
|
|
||||||
fp->bufread = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* In writing mode, write pending data */
|
/* In writing mode, write pending data */
|
||||||
if(fp->bufdir == __FILE_BUF_WRITE && fp->bufpos > 0) {
|
else if(__fp_hasbuf_write(fp)) {
|
||||||
ssize_t written = __fp_write(fp, fp->buf, fp->bufpos);
|
ssize_t written = __fp_write(fp, fp->buf, fp->bufpos);
|
||||||
int rc = (written == (ssize_t)fp->bufpos ? 0 : EOF);
|
rc = (written == (ssize_t)fp->bufpos ? 0 : EOF);
|
||||||
fp->bufpos = 0;
|
|
||||||
return rc;
|
/* TODO: fflush(): Keep data that couldn't be written */
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
fp->bufpos = 0;
|
||||||
|
fp->bufread = 0;
|
||||||
|
fp->bufungetc = 0;
|
||||||
|
|
||||||
|
/* Clear buffering for unbuffered streams that used ungetc() */
|
||||||
|
if(fp->bufmode == _IONBF)
|
||||||
|
__fp_remove_buffer(fp);
|
||||||
|
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "fileutil.h"
|
#include "fileutil.h"
|
||||||
|
#include <gint/defs/util.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -34,22 +35,76 @@ void __fp_close(FILE *fp, bool free_fp)
|
||||||
free(fp);
|
free(fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void __fp_remove_buffer(FILE *fp)
|
||||||
|
{
|
||||||
|
if(fp->bufowned)
|
||||||
|
free(fp->buf);
|
||||||
|
|
||||||
|
fp->buf = NULL;
|
||||||
|
fp->bufowned = false;
|
||||||
|
fp->bufsize = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool __fp_set_buffer(FILE *fp, void *buf, size_t size)
|
||||||
|
{
|
||||||
|
bool owned = false;
|
||||||
|
|
||||||
|
if(!buf) {
|
||||||
|
owned = true;
|
||||||
|
buf = malloc(size);
|
||||||
|
if(!buf)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fp->buf = buf;
|
||||||
|
fp->bufowned = owned;
|
||||||
|
fp->bufsize = size;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void __fp_buffer_mode_read(FILE *fp)
|
void __fp_buffer_mode_read(FILE *fp)
|
||||||
{
|
{
|
||||||
if(fp->bufmode != _IONBF && fp->bufdir == __FILE_BUF_WRITE &&
|
if(__fp_hasbuf_write(fp)) {
|
||||||
fp->bufpos > 0)
|
|
||||||
fflush(fp);
|
fflush(fp);
|
||||||
|
}
|
||||||
|
if(fp->buf)
|
||||||
fp->bufdir = __FILE_BUF_READ;
|
fp->bufdir = __FILE_BUF_READ;
|
||||||
}
|
}
|
||||||
|
|
||||||
void __fp_buffer_mode_write(FILE *fp)
|
void __fp_buffer_mode_write(FILE *fp)
|
||||||
{
|
{
|
||||||
if(fp->bufmode != _IONBF && fp->bufdir == __FILE_BUF_READ &&
|
if(__fp_hasbuf_read(fp))
|
||||||
fp->bufpos < fp->bufread)
|
|
||||||
fflush(fp);
|
fflush(fp);
|
||||||
|
if(fp->buf)
|
||||||
fp->bufdir = __FILE_BUF_WRITE;
|
fp->bufdir = __FILE_BUF_WRITE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ssize_t __fp_buffered_read(FILE *fp, void *data, size_t request_size)
|
||||||
|
{
|
||||||
|
if(!fp->buf || __fp_hasbuf_write(fp))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
int read_size = min((int)request_size, fp->bufread - fp->bufpos);
|
||||||
|
if(read_size <= 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
memcpy(data, fp->buf + fp->bufpos, read_size);
|
||||||
|
fp->bufpos += read_size;
|
||||||
|
fp->bufungetc = max(fp->bufungetc - read_size, 0);
|
||||||
|
|
||||||
|
/* Rewind the buffer if we read it fully */
|
||||||
|
if(fp->bufpos >= fp->bufread) {
|
||||||
|
fp->bufread = 0;
|
||||||
|
fp->bufpos = 0;
|
||||||
|
|
||||||
|
/* Clear temporary ungetc() buffers of _IONBF streams */
|
||||||
|
if(fp->bufmode == _IONBF)
|
||||||
|
__fp_remove_buffer(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return read_size;
|
||||||
|
}
|
||||||
|
|
||||||
ssize_t __fp_read(FILE *fp, void *data, size_t size)
|
ssize_t __fp_read(FILE *fp, void *data, size_t size)
|
||||||
{
|
{
|
||||||
size_t read_ = 0;
|
size_t read_ = 0;
|
||||||
|
@ -77,12 +132,6 @@ ssize_t __fp_write(FILE *fp, void const *data, size_t size)
|
||||||
{
|
{
|
||||||
size_t written = 0;
|
size_t written = 0;
|
||||||
|
|
||||||
if(fp->append) {
|
|
||||||
int rc = fseek(fp, 0, SEEK_END);
|
|
||||||
if(rc < 0)
|
|
||||||
return EOF;
|
|
||||||
}
|
|
||||||
|
|
||||||
while(written < size) {
|
while(written < size) {
|
||||||
ssize_t rc = write(fp->fd, data + written, size - written);
|
ssize_t rc = write(fp->fd, data + written, size - written);
|
||||||
|
|
||||||
|
|
|
@ -9,18 +9,38 @@ extern "C" {
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
/* Check whether a buffer has written data to flush. */
|
||||||
|
#define __fp_hasbuf_write(fp) \
|
||||||
|
(fp->buf && fp->bufdir == __FILE_BUF_WRITE && fp->bufpos > 0)
|
||||||
|
/* Check whether a buffer has read data to yield. */
|
||||||
|
#define __fp_hasbuf_read(fp) \
|
||||||
|
(fp->buf && fp->bufdir == __FILE_BUF_READ && fp->bufpos < fp->bufread)
|
||||||
|
|
||||||
/* Open a file descriptor in a pre-allocated FILE. */
|
/* Open a file descriptor in a pre-allocated FILE. */
|
||||||
int __fp_open(FILE *fp, int fd, bool use_buffering);
|
int __fp_open(FILE *fp, int fd, bool use_buffering);
|
||||||
|
|
||||||
/* Close fp and free all of its resources. */
|
/* Close fp and free all of its resources. */
|
||||||
void __fp_close(FILE *fp, bool free_fp);
|
void __fp_close(FILE *fp, bool free_fp);
|
||||||
|
|
||||||
|
/* Remove the buffer on fp. */
|
||||||
|
void __fp_remove_buffer(FILE *fp);
|
||||||
|
|
||||||
|
/* Set the specified buffer on fp (can be NULL), in which case malloc().
|
||||||
|
Returns true on success, false on error. */
|
||||||
|
bool __fp_set_buffer(FILE *fp, void *buf, size_t size);
|
||||||
|
|
||||||
/* Set reading mode on the buffer. */
|
/* Set reading mode on the buffer. */
|
||||||
void __fp_buffer_mode_read(FILE *fp);
|
void __fp_buffer_mode_read(FILE *fp);
|
||||||
|
|
||||||
/* Set writing mode on the buffer. */
|
/* Set writing mode on the buffer. */
|
||||||
void __fp_buffer_mode_write(FILE *fp);
|
void __fp_buffer_mode_write(FILE *fp);
|
||||||
|
|
||||||
|
/* Reads data from the buffer. Always reads as much as possible, up to size.
|
||||||
|
Returns amount of data read; if >= 0 but < size, the buffer should be
|
||||||
|
refilled. Returns -1 to indicate that unbuffered access should be used.
|
||||||
|
Allows reading from temporary ungetc() buffers and cleans them. */
|
||||||
|
ssize_t __fp_buffered_read(FILE *fp, void *data, size_t size);
|
||||||
|
|
||||||
/* Reads data from a file descriptor; updates the fdpos and sets the error
|
/* Reads data from a file descriptor; updates the fdpos and sets the error
|
||||||
indicator. Returns 0 on success, EOF on error. */
|
indicator. Returns 0 on success, EOF on error. */
|
||||||
ssize_t __fp_read(FILE *fp, void *data, size_t size);
|
ssize_t __fp_read(FILE *fp, void *data, size_t size);
|
||||||
|
|
|
@ -9,43 +9,44 @@ size_t fread(void *data, size_t membsize, size_t nmemb, FILE *fp)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t size;
|
size_t request_size;
|
||||||
if(__builtin_umul_overflow(membsize, nmemb, &size)) {
|
if(__builtin_umul_overflow(membsize, nmemb, &request_size)) {
|
||||||
fp->error = 1;
|
fp->error = 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!fp->buf) {
|
/* If the stream if unbuffered, we might have no buffer for the reads.
|
||||||
ssize_t rc = __fp_read(fp, data, size);
|
If it's buffered, we always have one. It's also possible that fp is
|
||||||
return (rc == EOF) ? 0 : rc;
|
unbuffered (_IONBF) but has a buffer temporarily because ungetc()
|
||||||
}
|
has been used, in which case we have to transition from buffered
|
||||||
|
reads into direct reads midway. We use __fp_buffered_read() to
|
||||||
|
handle this. */
|
||||||
|
|
||||||
|
size_t read_size = 0;
|
||||||
__fp_buffer_mode_read(fp);
|
__fp_buffer_mode_read(fp);
|
||||||
|
|
||||||
size_t size_read = 0;
|
while(read_size < request_size) {
|
||||||
while(size_read < size) {
|
int remaining = request_size - read_size;
|
||||||
/* Take what's available in the buffer (may be 0, but only
|
int chunk = __fp_buffered_read(fp, data+read_size, remaining);
|
||||||
during the first iteration) */
|
|
||||||
size_t size_frag = fp->bufread - fp->bufpos;
|
|
||||||
if(size_frag > size - size_read)
|
|
||||||
size_frag = size - size_read;
|
|
||||||
memcpy(data + size_read, fp->buf + fp->bufpos, size_frag);
|
|
||||||
size_read += size_frag;
|
|
||||||
fp->bufpos += size_frag;
|
|
||||||
|
|
||||||
if(fp->bufpos >= fp->bufread) {
|
/* Stream is not/no longer buffered, finish unbuffered */
|
||||||
fp->bufread = 0;
|
if(chunk < 0) {
|
||||||
fp->bufpos = 0;
|
ssize_t rc = __fp_read(fp, data+read_size, remaining);
|
||||||
|
return read_size + (rc == EOF ? 0 : rc);
|
||||||
}
|
}
|
||||||
if(size_read >= size)
|
|
||||||
|
read_size += chunk;
|
||||||
|
if(read_size >= request_size)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* Get more data */
|
/* Get more data from the file descriptor into the buffer */
|
||||||
|
if(fp->buf) {
|
||||||
ssize_t rc = __fp_read(fp, fp->buf, fp->bufsize);
|
ssize_t rc = __fp_read(fp, fp->buf, fp->bufsize);
|
||||||
if(rc <= 0) /* EOF or error */
|
if(rc <= 0) /* EOF or error */
|
||||||
break;
|
break;
|
||||||
fp->bufread = rc;
|
fp->bufread = rc;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return size_read;
|
return read_size;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,11 @@ size_t fwrite(void const *data, size_t membsize, size_t nmemb, FILE *fp)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(fp->append && fseek(fp, 0, SEEK_END) == EOF) {
|
||||||
|
fp->error = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if(!fp->buf) {
|
if(!fp->buf) {
|
||||||
ssize_t rc = __fp_write(fp, data, size);
|
ssize_t rc = __fp_write(fp, data, size);
|
||||||
return (rc == EOF) ? 0 : rc;
|
return (rc == EOF) ? 0 : rc;
|
||||||
|
|
|
@ -1,36 +1,23 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include "fileutil.h"
|
||||||
|
|
||||||
int setvbuf(FILE * restrict fp, char * restrict buf, int mode, size_t size)
|
int setvbuf(FILE * restrict fp, char * restrict buf, int mode, size_t size)
|
||||||
{
|
{
|
||||||
if(fp->bufmode != _IONBF) {
|
if(fp->bufmode != _IONBF || fp->buf) {
|
||||||
fflush(fp);
|
fflush(fp);
|
||||||
if(fp->bufowned)
|
|
||||||
free(fp->buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fp->buf = NULL;
|
__fp_remove_buffer(fp);
|
||||||
fp->bufowned = false;
|
|
||||||
fp->bufmode = _IONBF;
|
fp->bufmode = _IONBF;
|
||||||
fp->bufsize = 0;
|
|
||||||
|
|
||||||
if(mode == _IONBF)
|
if(mode == _IONBF)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if(buf) {
|
if(!__fp_set_buffer(fp, buf, size))
|
||||||
fp->buf = buf;
|
|
||||||
fp->bufsize = size;
|
|
||||||
fp->bufmode = mode;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
fp->buf = malloc(size);
|
|
||||||
if(!fp->buf)
|
|
||||||
return -1;
|
return -1;
|
||||||
fp->bufsize = size;
|
|
||||||
fp->bufowned = true;
|
|
||||||
fp->bufmode = mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
fp->bufmode = mode;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
53
src/libc/stdio/ungetc.c
Normal file
53
src/libc/stdio/ungetc.c
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "fileutil.h"
|
||||||
|
|
||||||
|
int ungetc(int c, FILE *fp)
|
||||||
|
{
|
||||||
|
if(c == EOF)
|
||||||
|
return c;
|
||||||
|
if(!fp->readable) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return EOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If there is no buffer, create a temporary one */
|
||||||
|
if(!fp->buf)
|
||||||
|
__fp_set_buffer(fp, NULL, 16);
|
||||||
|
|
||||||
|
__fp_buffer_mode_read(fp);
|
||||||
|
|
||||||
|
/* If there is a spot available in the buffer, use it */
|
||||||
|
if(fp->bufpos > 0) {
|
||||||
|
fp->buf[--fp->bufpos] = c;
|
||||||
|
fp->bufungetc++;
|
||||||
|
fp->eof = 0;
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Otherwise, make some room. If the buffer is full of ungetc()
|
||||||
|
characters then we can't preserve the semantics, so we fail. */
|
||||||
|
if(fp->bufungetc >= (int)fp->bufsize)
|
||||||
|
return EOF;
|
||||||
|
|
||||||
|
/* If the buffer is full, discard read data that isn't ungetc()'d. */
|
||||||
|
if(fp->bufread >= fp->bufsize) {
|
||||||
|
int discarded = fp->bufread - fp->bufungetc;
|
||||||
|
if(fseek(fp, -discarded, SEEK_CUR))
|
||||||
|
return EOF;
|
||||||
|
fp->bufread -= discarded;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Then move whichever type of read data remains to the very end of the
|
||||||
|
buffer, so we have as much space as possible for more ungetc(). */
|
||||||
|
memmove(fp->buf + fp->bufsize - fp->bufread, fp->buf, fp->bufread);
|
||||||
|
fp->bufpos = fp->bufsize - fp->bufread;
|
||||||
|
fp->bufread = fp->bufsize;
|
||||||
|
|
||||||
|
/* Because we've made space, conclude like the first case */
|
||||||
|
fp->buf[--fp->bufpos] = c;
|
||||||
|
fp->bufungetc++;
|
||||||
|
fp->eof = 0;
|
||||||
|
return c;
|
||||||
|
}
|
Loading…
Reference in a new issue