stdio: opening primitives for FILE (WIP)

This commit is contained in:
Lephenixnoir 2022-01-02 18:51:16 +01:00
parent 51528170bb
commit a12b84f1ef
No known key found for this signature in database
GPG key ID: 1BBA026E13FC0495
11 changed files with 280 additions and 30 deletions

View file

@ -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
View file

@ -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

View file

@ -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);

View file

@ -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
View 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
View 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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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
View 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
View 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;
}