mirror of
https://git.planet-casio.com/Vhex-Kernel-Core/fxlibc.git
synced 2024-12-28 04:23:38 +01:00
stdio: opening primitives for FILE (WIP)
This commit is contained in:
parent
51528170bb
commit
a12b84f1ef
11 changed files with 280 additions and 30 deletions
|
@ -120,9 +120,13 @@ set(SOURCES
|
|||
# stdio
|
||||
src/libc/stdio/asprintf.c
|
||||
src/libc/stdio/dprintf.c
|
||||
src/libc/stdio/fclose.c
|
||||
src/libc/stdio/fdopen.c
|
||||
src/libc/stdio/fflush.c
|
||||
src/libc/stdio/fileutil.c
|
||||
src/libc/stdio/fopen.c
|
||||
src/libc/stdio/fprintf.c
|
||||
src/libc/stdio/freopen.c
|
||||
src/libc/stdio/printf.c
|
||||
src/libc/stdio/printf/format_fixed.c
|
||||
src/libc/stdio/printf/format_fp.c
|
||||
|
|
47
STATUS
47
STATUS
|
@ -84,18 +84,21 @@ TEST: Function/symbol/macro needs to be tested
|
|||
7.18 <stdint.h> => GCC
|
||||
|
||||
7.19 <stdio.h>
|
||||
7.19.1 Introduction TEST
|
||||
No wide-oriented streams (see notes)
|
||||
7.19.1 Introduction TEST (no wide-oriented streams *)
|
||||
|
||||
7.19.4.1 remove TEST
|
||||
7.19.4.2 rename TODO
|
||||
7.19.4.3 tmpfile TODO
|
||||
7.19.4.4 tmpnam TODO
|
||||
7.19.5.1 fclose TODO
|
||||
7.19.5.2 fflush TODO
|
||||
7.19.5.3 fopen TODO
|
||||
|
||||
7.19.5.1 fclose TEST
|
||||
7.19.5.2 fflush TEST
|
||||
7.19.5.3 fopen TEST
|
||||
(EXT) fdopen TEST
|
||||
7.19.5.4 freopen TODO
|
||||
7.19.5.5 setbuf TODO
|
||||
7.19.5.6 setvbuf TODO
|
||||
7.19.5.5 setbuf TEST
|
||||
7.19.5.6 setvbuf TEST
|
||||
|
||||
7.19.6.1 fprintf LDEPS(fwrite)
|
||||
7.19.6.2 fscanf TODO
|
||||
7.19.6.3 printf LDEPS(fwrite, stdout)
|
||||
|
@ -114,10 +117,32 @@ TEST: Function/symbol/macro needs to be tested
|
|||
(EXT) vasprintf -
|
||||
(EXT) dprintf TEST
|
||||
(EXT) vdprintf TEST
|
||||
7.19.7 Character input/output TODO
|
||||
7.19.8 Direct input/output TODO
|
||||
7.19.9 File positioning TODO
|
||||
7.19.10 Error-handling TODO
|
||||
|
||||
7.19.7.1 fgetc TODO
|
||||
7.19.7.2 fgets TODO
|
||||
7.19.7.3 fputc TODO
|
||||
7.19.7.4 fputs TODO
|
||||
7.19.7.5 getc TODO
|
||||
7.19.7.6 getchar TODO
|
||||
7.19.7.7 gets TODO
|
||||
7.19.7.8 putc TODO
|
||||
7.19.7.9 putchar TODO
|
||||
7.19.7.10 puts TODO
|
||||
7.19.7.11 ungetc TODO
|
||||
|
||||
7.19.8.1 fread TODO
|
||||
7.19.8.2 fwrite TODO
|
||||
|
||||
7.19.9.1 fgetpos TODO
|
||||
7.19.9.2 fseek TODO
|
||||
7.19.9.3 fsetpos TODO
|
||||
7.19.9.4 ftell TODO
|
||||
7.19.9.5 rewind TODO
|
||||
|
||||
7.19.10.1 clearerr TODO
|
||||
7.19.10.2 feof TODO
|
||||
7.19.10.3 ferror TODO
|
||||
7.19.10.4 perror TODO
|
||||
|
||||
7.20 <stdlib.h>
|
||||
7.20 MB_CUR_MAX TODO
|
||||
|
|
|
@ -66,9 +66,26 @@ extern int remove(char const *__filename);
|
|||
** File access functions.
|
||||
*/
|
||||
|
||||
/* Flush the stream and disassociate it from the underlying file. */
|
||||
extern int fclose(FILE *__fp);
|
||||
|
||||
/* Flush any written data in the FILE's internal buffer. */
|
||||
extern int fflush(FILE *__fp);
|
||||
|
||||
/* Open a file and associate a stream with it. */
|
||||
extern FILE *fopen(
|
||||
char const * __restrict__ __filename,
|
||||
char const * __restrict__ __mode);
|
||||
|
||||
/* Open a file descriptor and associate a stream with it. */
|
||||
extern FILE *fdopen(int __fd, char const *__mode);
|
||||
|
||||
/* Reopen a stream with another file, or change its mode. */
|
||||
extern FILE *freopen(
|
||||
char const * __restrict__ __filename,
|
||||
char const * __restrict__ __mode,
|
||||
FILE * __restrict__ __fp);
|
||||
|
||||
/* Use __buf as a buffer (of size BUFSIZ) for access to __fp. */
|
||||
extern void setbuf(FILE * __restrict__ __fp, char * __restrict__ __buf);
|
||||
|
||||
|
|
|
@ -39,7 +39,20 @@
|
|||
|
||||
In writing mode, the region [0..bufpos) contains data received through API
|
||||
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.
|
||||
|
||||
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
|
||||
fileutil.c which essentially wrap kernel I/O (plus clearerr() obviously).
|
||||
2. [buf], [bufsize], [bufmode] and [bufowned] are handled by setbuf(),
|
||||
setvbuf() and fclose().
|
||||
3. [bufpos], [bufread] and [bufdir] are set by primitive read/write functions
|
||||
like fgets() or fwrite(), and cleared by fflush().
|
||||
4. [readable], [writable], [append] and [text] are set by fopen() and
|
||||
freopen(), and used as read-only by the primitive functions of 3.
|
||||
|
||||
TODO: EOF indicator */
|
||||
typedef struct {
|
||||
/* File descriptor */
|
||||
int fd;
|
||||
|
|
10
src/libc/stdio/fclose.c
Normal file
10
src/libc/stdio/fclose.c
Normal file
|
@ -0,0 +1,10 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include "fileutil.h"
|
||||
|
||||
int fclose(FILE *fp)
|
||||
{
|
||||
__fp_close(fp, true);
|
||||
return 0;
|
||||
}
|
24
src/libc/stdio/fdopen.c
Normal file
24
src/libc/stdio/fdopen.c
Normal file
|
@ -0,0 +1,24 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "fileutil.h"
|
||||
|
||||
FILE *fdopen(int fd, char const *mode)
|
||||
{
|
||||
FILE *fp = calloc(1, sizeof *fp);
|
||||
if(!fp) goto err;
|
||||
|
||||
int flags = __fp_parse_mode(mode, fp);
|
||||
if(flags < 0) goto err;
|
||||
|
||||
__fp_open(fp, fd, true);
|
||||
|
||||
/* TODO: fdopen(): Seek to the current file offset of the fd */
|
||||
|
||||
return fp;
|
||||
|
||||
err:
|
||||
if(fp && fp->bufowned)
|
||||
free(fp->buf);
|
||||
free(fp);
|
||||
return NULL;
|
||||
}
|
|
@ -13,21 +13,21 @@ int fflush(FILE *fp)
|
|||
if(!fp->buf)
|
||||
return 0;
|
||||
|
||||
int rc = 0;
|
||||
|
||||
/* In reading mode, reset the file offset */
|
||||
if(fp->bufmode == __FILE_BUF_READ && fp->bufpos < fp->bufread) {
|
||||
fp->fdpos = fp->fdpos - fp->bufread + fp->bufpos;
|
||||
lseek(fp->fd, fp->fdpos, SEEK_SET);
|
||||
fp->bufpos = 0;
|
||||
fp->bufread = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* In writing mode, write pending data */
|
||||
else if(fp->bufmode == __FILE_BUF_WRITE && fp->bufpos > 0) {
|
||||
rc = __fp_write(fp, fp->buf, fp->bufpos);
|
||||
if(fp->bufmode == __FILE_BUF_WRITE && fp->bufpos > 0) {
|
||||
ssize_t written = __fp_write(fp, fp->buf, fp->bufpos);
|
||||
fp->bufpos = 0;
|
||||
return (written == (ssize_t)fp->bufpos ? 0 : EOF);
|
||||
}
|
||||
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,19 +1,107 @@
|
|||
#include "fileutil.h"
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
int __fp_write(FILE *fp, void const *data, size_t size)
|
||||
int __fp_open(FILE *fp, int fd, bool use_buffering)
|
||||
{
|
||||
if(!fp)
|
||||
return EOF;
|
||||
/* We initialize fdpos to 0 even in append mode (7.19.3§1) */
|
||||
fp->fd = fd;
|
||||
fp->fdpos = 0;
|
||||
|
||||
ssize_t rc = write(fp->fd, data, size);
|
||||
/* We assume all files in the filesystem are non-interactive in order
|
||||
to conform to (7.19.5.3§7)
|
||||
TODO: Vhex might want something more elaborate here */
|
||||
if(use_buffering && setvbuf(fp, NULL, _IOFBF, BUFSIZ))
|
||||
return -1;
|
||||
|
||||
if(rc < 0) {
|
||||
fp->error = 1;
|
||||
return EOF;
|
||||
}
|
||||
else {
|
||||
fp->fdpos += rc;
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __fp_close(FILE *fp, bool free_fp)
|
||||
{
|
||||
fflush(fp);
|
||||
close(fp->fd);
|
||||
if(fp->bufowned)
|
||||
free(fp->buf);
|
||||
if(free_fp)
|
||||
free(fp);
|
||||
}
|
||||
|
||||
ssize_t __fp_read(FILE *fp, void *data, size_t size)
|
||||
{
|
||||
size_t read_ = 0;
|
||||
|
||||
while(read_ < size) {
|
||||
ssize_t rc = read(fp->fd, data + read_, size - read_);
|
||||
|
||||
if(rc < 0) {
|
||||
fp->error = 1;
|
||||
return EOF;
|
||||
}
|
||||
if(rc == 0)
|
||||
break;
|
||||
|
||||
fp->fdpos += rc;
|
||||
}
|
||||
|
||||
return read_;
|
||||
}
|
||||
|
||||
ssize_t __fp_write(FILE *fp, void const *data, size_t size)
|
||||
{
|
||||
size_t written = 0;
|
||||
|
||||
while(written < size) {
|
||||
ssize_t rc = write(fp->fd, data + written, size - written);
|
||||
|
||||
if(rc < 0) {
|
||||
fp->error = 1;
|
||||
return EOF;
|
||||
}
|
||||
if(rc == 0)
|
||||
break;
|
||||
|
||||
fp->fdpos += rc;
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
int __fp_parse_mode(char const *mode, FILE *fp)
|
||||
{
|
||||
int base = 0;
|
||||
bool binary = false;
|
||||
bool update = false;
|
||||
|
||||
for(int i = 0; mode[i]; i++) {
|
||||
if(mode[i] == 'r' || mode[i] == 'w' || mode[i] == 'a') {
|
||||
if(base) goto err;
|
||||
base = mode[i];
|
||||
}
|
||||
else if(mode[i] == 'b')
|
||||
binary = true;
|
||||
else if(mode[i] == '+')
|
||||
update = true;
|
||||
}
|
||||
if(!base) goto err;
|
||||
|
||||
if(fp) {
|
||||
fp->readable = (base == 'r' || update);
|
||||
fp->writable = (base == 'w' || base == 'a' || update);
|
||||
fp->append = (base == 'a');
|
||||
fp->text = !binary;
|
||||
}
|
||||
|
||||
if(base == 'r')
|
||||
return (update ? O_RDWR : O_RDONLY);
|
||||
if(base == 'w')
|
||||
return (update ? O_RDWR : O_WRONLY) | O_CREAT | O_TRUNC;
|
||||
if(base == 'a')
|
||||
return (update ? O_RDWR : O_WRONLY) | O_CREAT;
|
||||
/* Fallthrough */
|
||||
|
||||
err:
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -6,10 +6,27 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
/* Open a file descriptor in a pre-allocated FILE. */
|
||||
int __fp_open(FILE *fp, int fd, bool use_buffering);
|
||||
|
||||
/* Close fp and free all of its resources. */
|
||||
void __fp_close(FILE *fp, bool free_fp);
|
||||
|
||||
/* Reads data from a file descriptor; updates the fdpos and sets the error
|
||||
indicator. Returns 0 on success, EOF on error. */
|
||||
ssize_t __fp_read(FILE *fp, void *data, size_t size);
|
||||
|
||||
/* Write data to a file descriptor; updates the fdpos and sets the error
|
||||
indicator. Returns 0 on success, EOF on error. */
|
||||
int __fp_write(FILE *fp, void const *data, size_t size);
|
||||
ssize_t __fp_write(FILE *fp, void const *data, size_t size);
|
||||
|
||||
/* Parse mode bits. Returns corresponding open() flags if successful, -1 if the
|
||||
mode is invalid. If [fp != NULL], also sets the fields of [fp]. Sets
|
||||
errno = EINVAL in case of error. */
|
||||
int __fp_parse_mode(char const *mode, FILE *fp);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
25
src/libc/stdio/fopen.c
Normal file
25
src/libc/stdio/fopen.c
Normal file
|
@ -0,0 +1,25 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include "fileutil.h"
|
||||
|
||||
FILE *fopen(char const * restrict filename, char const * restrict mode)
|
||||
{
|
||||
FILE *fp = calloc(1, sizeof *fp);
|
||||
if(!fp) goto err;
|
||||
|
||||
int flags = __fp_parse_mode(mode, fp);
|
||||
if(flags < 0) goto err;
|
||||
|
||||
int fd = open(filename, flags, 0755);
|
||||
if(fd < 0) goto err;
|
||||
|
||||
__fp_open(fp, fd, true);
|
||||
return fp;
|
||||
|
||||
err:
|
||||
if(fp && fp->bufowned)
|
||||
free(fp->buf);
|
||||
free(fp);
|
||||
return NULL;
|
||||
}
|
27
src/libc/stdio/freopen.c
Normal file
27
src/libc/stdio/freopen.c
Normal file
|
@ -0,0 +1,27 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include "fileutil.h"
|
||||
|
||||
FILE *freopen(char const * restrict filename, char const * restrict mode,
|
||||
FILE * restrict fp)
|
||||
{
|
||||
__fp_close(fp, false);
|
||||
memset(fp, 0, sizeof *fp);
|
||||
|
||||
int flags = __fp_parse_mode(mode, fp);
|
||||
if(flags < 0) goto err;
|
||||
|
||||
int fd = open(filename, flags, 0755);
|
||||
if(fd < 0) goto err;
|
||||
|
||||
__fp_open(fp, fd, true);
|
||||
return fp;
|
||||
|
||||
err:
|
||||
if(fp && fp->bufowned)
|
||||
free(fp->buf);
|
||||
free(fp);
|
||||
return NULL;
|
||||
}
|
Loading…
Reference in a new issue