/* Copyright (C) 2008 Emmanuel Varoquaux
This file is part of XOS.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
#include "stdio.h"
#include "fcntl.h"
#include "unistd.h"
#include
#include
#include
struct access_mode_struct {
unsigned read : 1; /* ouvert en lecture */
unsigned write : 1; /* ouvert en ecriture */
unsigned create : 1; /* creer le fichier */
unsigned truncate : 1; /* tronquer le fichier */
unsigned append : 1; /* mode ajout */
};
struct __FILE {
/* chainage */
FILE *previous; /* precedent */
FILE *next; /* suivant */
/* fichier sous-jacent */
int fd; /* descripteur de fichier */
unsigned tty : 1; /* terminal */
dev_t dev; /* peripherique associe au terminal */
struct access_mode_struct access_mode; /* mode d'acces */
/* position et indicateurs */
off_t offset; /* position dans le fichier (-1 si inconnue) */
unsigned seekable : 1; /* positionnable */
unsigned eof : 1; /* indicateur de fin de fichier */
unsigned error : 1; /* indicateur d'erreur */
/* caractere replace */
unsigned pushed_back : 1; /* existence d'un caractere replace */
unsigned char pushed_back_byte; /* caractere replace */
/* tampon */
unsigned auto_buf : 1; /* tampon alloue "automatiquement" */
char *buf; /* tampon (NULL si pas de tampon ou si pas alloue) */
size_t buf_size; /* taille du tampon */
unsigned line_buf : 1; /* tampon de ligne */
char *buf_end; /* fin du tampon */
char *buf_read_end; /* fin des caracteres lus dans le tampon */
char *buf_cur; /* position courante dans le tampon */
/* operations d'entrees-sorties */
int state; /* etat */
};
/* Etats d'un flux avec tampon :
* buf_read_end > buf : lecture en cours
* buf_read_end == buf && buf_cur > buf : ecriture en cours
* buf_read_end == buf && buf_cur == buf : ni lecture ni ecriture en cours. */
#define reading_buf(stream) ((stream)->buf_cur < (stream)->buf_read_end)
#define writing_buf(stream) ((stream)->buf_cur > (stream)->buf_read_end)
#define empty_buf(stream) ((stream)->buf_cur == (stream)->buf_read_end)
#define full_buf(stream) ((stream)->buf_cur == (stream)->buf_end)
static struct __FILE stream_table[FOPEN_MAX];
static FILE *first_free;
static FILE *first_open;
/* Table des flux */
static void init_stream_table()
{
unsigned int i;
for (i = 0; i < FOPEN_MAX - 1; i++)
stream_table[i].next = &stream_table[i + 1];
stream_table[FOPEN_MAX - 1].next = NULL;
first_free = &stream_table[0];
first_open = NULL;
}
static FILE *alloc_stream()
{
FILE *stream;
if (!first_free) {
errno = EMFILE;
return NULL;
}
stream = first_free;
first_free = first_free->next;
return stream;
}
static void free_stream(FILE *stream)
{
stream->next = first_free;
first_free = stream;
}
/* Initialisation et destruction des flux */
static void init_stream(FILE *stream, int fd, const struct access_mode_struct *access_mode, int buffered)
{
off_t offset;
int seekable;
if (!access_mode->read && access_mode->append) { /* mode "a" */
offset = __lseek(fd, 0, SEEK_END); /* inconnu (-1) si erreur */
seekable = offset != -1 || errno != ESPIPE ? 1 : 0;
}
else {
offset = -1; /* inconnu */
seekable = 1; /* le fichier est positionnable... jusqu'a preuve du contraire */
}
stream->previous = NULL;
stream->next = first_open;
stream->fd = fd;
stream->tty = 0; /* par defaut, ne pas considerer comme un terminal */
stream->access_mode = *access_mode;
stream->offset = offset;
stream->seekable = seekable;
stream->eof = 0;
stream->error = 0;
stream->pushed_back = 0;
stream->auto_buf = buffered;
stream->buf = NULL;
if (first_open)
first_open->previous = stream;
first_open = stream;
}
static void destroy_stream(FILE *stream)
{
if (stream->previous)
stream->previous->next = stream->next;
else
first_open = stream->next;
if (stream->next)
stream->next->previous = stream->previous;
if (stream->auto_buf && stream->buf)
free(stream->buf);
}
/* Routines d'entrees-sorties sur les fichiers */
/* Retourne le nombre de caracteres lus. */
static size_t io_read(FILE *stream, void *buf, size_t count)
{
ssize_t n;
if ((n = __read(stream->fd, buf, count)) == -1) {
stream->error = 1;
return 0;
}
if (n == 0) {
stream->eof = 1;
return 0;
}
if (stream->offset != -1)
stream->offset += n;
return n;
}
/* Retourne 0 en cas de succes, EOF en cas d'erreur. */
static int io_write(FILE *stream, const void *buf, size_t count)
{
ssize_t n;
if ((n = __write(stream->fd, buf, count)) == -1) {
stream->error = 1;
return EOF;
}
if (stream->offset != -1) {
if (!stream->access_mode.append)
stream->offset += n;
else
stream->offset = -1;
}
return 0;
}
/* Retourne 0 en cas de succes, -1 en cas d'erreur. */
static int io_seek(FILE *stream, off_t offset, int whence)
{
if ((stream->offset = __lseek(stream->fd, offset, whence)) == -1) {
if (errno == ESPIPE)
stream->seekable = 0;
return -1;
}
return 0;
}
/* Fonctions sur les tampons */
static void init_buf(FILE *stream)
{
stream->buf_end = stream->buf + stream->buf_size;
stream->buf_read_end = stream->buf;
stream->buf_cur = stream->buf;
}
static int fill_buf(FILE *stream)
{
size_t n;
if (!(n = io_read(stream, stream->buf, stream->buf_size)))
return EOF;
stream->buf_read_end = stream->buf + n;
stream->buf_cur = stream->buf;
return 0;
}
static void discard_buf(FILE *stream)
{
stream->buf_read_end = stream->buf;
stream->buf_cur = stream->buf;
}
static int flush_buf(FILE *stream)
{
if (io_write(stream, stream->buf, stream->buf_cur - stream->buf) == EOF)
return EOF;
stream->buf_cur = stream->buf;
return 0;
}
/* Retourne le nombre de caracteres dans le tampon relativement a la position
* dans le fichier. Ce nombre est negatif si une lecture est en cours, positif
* si une ecriture est en cours, nul si le tampon est vide. */
static ssize_t count_buf(FILE *stream)
{
return stream->buf_cur - stream->buf_read_end;
}
/* Fonctions sur les flux */
static void attach_stream(FILE *stream, int fd, const struct access_mode_struct *access_mode, int buffered)
{
init_stream(stream, fd, access_mode, buffered);
}
static int detach_stream(FILE *stream, int *fd)
{
int errnum;
errnum = 0;
if (stream->buf && writing_buf(stream))
if (flush_buf(stream) == EOF)
errnum = errno;
*fd = stream->fd;
destroy_stream(stream);
return errnum ? errno = errnum, EOF : 0;
}
static void push_back_byte(FILE *stream, int c)
{
stream->pushed_back = 1;
stream->pushed_back_byte = c;
}
static int pop_byte(FILE *stream)
{
stream->pushed_back = 0;
return stream->pushed_back_byte;
}
/* Affecte un tampon au flux. Si buf est NULL, le tampon est alloue. */
static int set_buf(FILE *stream, char *buf, size_t size, int line_buffered)
{
struct stat statbuf;
if (buf)
stream->auto_buf = 0;
else {
if (!(buf = malloc(size)))
return -1;
stream->auto_buf = 1;
}
stream->buf = buf;
stream->buf_size = size;
stream->line_buf = line_buffered;
if (__fstat(stream->fd, &statbuf) == 0) {
stream->tty = S_ISCHR(statbuf.st_mode) && __isatty(stream->fd) ? 1 : 0;
if (stream->tty)
stream->dev = statbuf.st_rdev;
}
else
stream->tty = 0;
init_buf(stream);
return 0;
}
/* Alloue automatiquement un tampon pour le flux. */
static int alloc_buf(FILE *stream)
{
struct stat statbuf;
if (__fstat(stream->fd, &statbuf) == 0) {
stream->tty = S_ISCHR(statbuf.st_mode) && __isatty(stream->fd) ? 1 : 0;
if (stream->tty)
stream->dev = statbuf.st_rdev;
}
else
stream->tty = 0;
if (!(stream->buf = malloc(statbuf.st_blksize))) {
stream->error = 1;
return EOF;
}
stream->buf_size = statbuf.st_blksize;
stream->line_buf = stream->tty;
init_buf(stream);
return 0;
}
static void flush_tty(dev_t dev)
{
FILE *stream;
for (stream = first_open; stream; stream = stream->next)
if (stream->tty && stream->dev == dev && stream->buf && writing_buf(stream))
flush_buf(stream);
}
static int flush_all()
{
int errnum;
FILE *stream;
errnum = 0;
for (stream = first_open; stream; stream = stream->next)
if (stream->buf && writing_buf(stream))
if (flush_buf(stream) == EOF)
if (!errnum)
errnum = errno;
return errnum ? errno = errnum, EOF : 0;
}
static int nbuf_get_byte(FILE *stream)
{
unsigned char b;
if (!io_read(stream, &b, 1))
return EOF;
return b;
}
static int buf_get_byte(FILE *stream)
{
unsigned char b;
b = *stream->buf_cur++;
if (empty_buf(stream))
discard_buf(stream);
return b;
}
static size_t nbuf_get_bytes(FILE *stream, void *ptr, size_t count)
{
return io_read(stream, ptr, count);
}
static size_t buf_get_bytes(FILE *stream, void *ptr, size_t count)
{
size_t avail;
avail = -count_buf(stream);
if (count > avail)
count = avail;
memcpy(ptr, stream->buf_cur, count);
stream->buf_cur += count;
if (empty_buf(stream))
discard_buf(stream);
return count;
}
static int nbuf_put_byte(FILE *stream, int c)
{
unsigned char b;
b = c;
if (io_write(stream, &b, 1) == EOF)
return EOF;
return b;
}
static int buf_put_byte(FILE *stream, int c)
{
unsigned char b;
b = c;
*stream->buf_cur++ = b;
if (stream->line_buf && b == '\n')
if (flush_buf(stream) == EOF)
return EOF;
return b;
}
static size_t nbuf_put_bytes(FILE *stream, const void *ptr, size_t count)
{
return io_write(stream, ptr, count) == 0 ? count : 0;
}
static size_t buf_put_bytes(FILE *stream, const void *ptr, size_t count)
{
size_t avail, n;
unsigned char b;
avail = stream->buf_size - count_buf(stream);
if (count > avail)
count = avail;
if (stream->line_buf) {
n = count;
while (n > 0) {
b = *(const unsigned char *)ptr++;
*stream->buf_cur++ = b;
n--;
if (b == '\n') {
if (flush_buf(stream) == EOF)
return 0;
break;
}
}
return count - n;
}
else {
memcpy(stream->buf_cur, ptr, count);
stream->buf_cur += count;
return count;
}
}
static void reset_state(FILE *stream)
{
stream->state = 0;
}
static int init_read(FILE *stream)
{
switch (stream->state) {
case 0:
/* verification du mode d'acces */
if (!stream->access_mode.read) {
errno = EBADF;
stream->error = 1;
return EOF;
}
if (stream->pushed_back) { /* caractere replace par ungetc() */
stream->state = 1;
return 0;
}
case 2:
default:
/* Si le flux est associe a un terminal, on doit vider tous les tampons en
ecriture sur ce terminal. */
if (stream->tty)
flush_tty(stream->dev);
if (stream->eof) /* POSIX */
return EOF;
if (!stream->buf) {
if (!stream->auto_buf) { /* pas de tampon */
stream->state = 3;
return 0;
}
/* allocation "automatique" d'un tampon */
if (alloc_buf(stream) == EOF)
return EOF;
}
else if (writing_buf(stream))
/* Il reste des caracteres a ecrire. */
if (flush_buf(stream) == EOF)
return EOF;
stream->state = 4;
return 0;
}
}
static int get_byte(FILE *stream)
{
switch (stream->state) {
case 0:
case 2:
if (init_read(stream) == EOF)
return EOF;
}
switch (stream->state) {
case 1: /* caractere replace par ungetc() */
stream->state = 2;
return pop_byte(stream);
case 3: /* pas de tampon */
return nbuf_get_byte(stream);
case 4: /* tampon */
default:
if (empty_buf(stream)) /* tampon vide */
if (fill_buf(stream) == EOF)
return EOF;
return buf_get_byte(stream);
}
}
static size_t get_bytes(FILE *stream, void *ptr, size_t count)
{
size_t to_read, n;
to_read = count;
do {
switch (stream->state) {
case 0:
case 2:
if (init_read(stream) == EOF)
return 0;
}
switch (stream->state) {
case 1: /* caractere replace par ungetc() */
*(unsigned char *)ptr++ = pop_byte(stream);
to_read--;
stream->state = 2;
continue;
case 3: /* pas de tampon */
if (!(n = nbuf_get_bytes(stream, ptr, to_read)))
goto end;
ptr += n;
to_read -= n;
continue;
case 4: /* tampon */
if (empty_buf(stream)) /* tampon vide */
if (fill_buf(stream) == EOF)
goto end;
if (!(n = buf_get_bytes(stream, ptr, to_read)))
goto end;
ptr += n;
to_read -= n;
continue;
}
}
while (to_read > 0);
end:
return count - to_read;
}
static int init_write(FILE *stream)
{
/* verification du mode d'acces */
if (!stream->access_mode.write) {
errno = EBADF;
stream->error = 1;
return EOF;
}
if (stream->access_mode.append)
stream->offset = -1;
stream->pushed_back = 0;
if (!stream->buf) {
if (!stream->auto_buf) { /* pas de tampon */
stream->state = 1;
return 0;
}
/* allocation "automatique" d'un tampon */
if (alloc_buf(stream) == EOF)
return EOF;
}
else if (reading_buf(stream)) {
/* Il reste des caracteres non lus. */
if (stream->seekable && !stream->access_mode.append)
if (io_seek(stream, count_buf(stream), SEEK_CUR) == -1 && stream->seekable)
return EOF;
discard_buf(stream);
}
stream->state = 2;
return 0;
}
static int put_byte(FILE *stream, int c)
{
switch (stream->state) {
case 0:
if (init_write(stream) == EOF)
return EOF;
}
switch (stream->state) {
case 1: /* pas de tampon */
return nbuf_put_byte(stream, c);
case 2: /* tampon */
default:
if (full_buf(stream)) /* tampon plein */
if (flush_buf(stream) == EOF)
return EOF;
return buf_put_byte(stream, c);
}
}
static size_t put_bytes(FILE *stream, const void *ptr, size_t count)
{
size_t to_write, n;
to_write = count;
do {
switch (stream->state) {
case 0:
if (init_write(stream) == EOF)
return 0;
}
switch (stream->state) {
case 1: /* pas de tampon */
if (!(n = nbuf_put_bytes(stream, ptr, count)))
goto end;
ptr += n;
to_write -= n;
continue;
case 2: /* tampon */
if (full_buf(stream)) { /* tampon plein */
if (flush_buf(stream) == EOF)
goto end;
}
if (!(n = buf_put_bytes(stream, ptr, to_write)))
goto end;
ptr += n;
to_write -= n;
}
}
while (to_write > 0);
end:
return count - to_write;
}
/* Conversions */
static int get_access_mode(const char *mode, struct access_mode_struct *access_mode)
{
switch (*mode++) {
case 'r':
access_mode->read = 1;
access_mode->write = 0;
access_mode->create = 0;
access_mode->truncate = 0;
access_mode->append = 0;
break;
case 'w':
access_mode->read = 0;
access_mode->write = 1;
access_mode->create = 1;
access_mode->truncate = 1;
access_mode->append = 0;
break;
case 'a':
access_mode->read = 0;
access_mode->write = 1;
access_mode->create = 1;
access_mode->truncate = 0;
access_mode->append = 1;
break;
default:
return 0;
}
if (!*mode)
return 1;
if (*mode != '+')
mode++;
if (*mode == '+')
access_mode->write = access_mode->read = 1;
return 1;
}
static int get_oflag(const struct access_mode_struct *access_mode)
{
int oflag;
if (access_mode->read && access_mode->write)
oflag = O_RDWR;
else
oflag = access_mode->read ? O_RDONLY : O_WRONLY;
if (access_mode->truncate)
oflag |= O_TRUNC;
if (access_mode->append)
oflag |= O_APPEND;
return oflag;
}
/* Flux d'entrees-sorties standards */
FILE *stdin __attribute__ ((weak, visibility ("default")));
FILE *stdout __attribute__ ((weak, visibility ("default")));
FILE *stderr __attribute__ ((weak, visibility ("default")));
/* Code d'initialisation */
void __stdio_init()
{
static const struct access_mode_struct stdin_access_mode = {
.read = 1,
.write = 0,
.create = 0,
.truncate = 0,
.append = 0
};
static const struct access_mode_struct stdout_access_mode = {
.read = 0,
.write = 1,
.create = 0,
.truncate = 0,
.append = 0
};
static const struct access_mode_struct stderr_access_mode = {
.read = 1,
.write = 1,
.create = 0,
.truncate = 0,
.append = 0
};
init_stream_table();
stdin = alloc_stream();
attach_stream(stdin, STDIN_FILENO, &stdin_access_mode, 1);
stdout = alloc_stream();
attach_stream(stdout, STDOUT_FILENO, &stdout_access_mode, 1);
stderr = alloc_stream();
attach_stream(stderr, STDERR_FILENO, &stderr_access_mode, 0);
}
void __stdio_fini()
{
FILE *stream;
int fd;
while (first_open) {
stream = first_open;
detach_stream(stream, &fd);
free_stream(stream);
__close(fd);
}
}
/* Fonctions d'ouverture et de fermeture de flux */
static inline int openm(const char *filename, const struct access_mode_struct *access_mode)
{
int oflag;
oflag = get_oflag(access_mode);
if (access_mode->create)
return __open(filename, oflag | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
else
return __open(filename, oflag);
}
FILE __attribute__ ((weak, visibility ("default"))) *fopen(const char *filename, const char *mode)
{
struct access_mode_struct access_mode;
int fd;
FILE *stream;
int errnum;
if (!get_access_mode(mode, &access_mode)) {
errno = EINVAL;
return NULL;
}
if ((fd = openm(filename, &access_mode)) == -1)
return NULL;
if (!(stream = alloc_stream())) {
errnum = errno;
__close(fd);
errno = errnum;
return NULL;
}
attach_stream(stream, fd, &access_mode, 1);
return stream;
}
typeof (fopen) __fopen __attribute__ ((alias ("fopen")));
FILE __attribute__ ((weak, visibility ("default"))) *fdopen(int fd, const char *mode)
{
struct access_mode_struct access_mode;
FILE *stream;
if (!get_access_mode(mode, &access_mode)) {
errno = EINVAL;
return NULL;
}
if (!(stream = alloc_stream()))
return NULL;
attach_stream(stream, fd, &access_mode, 1);
return stream;
}
typeof (fdopen) __fdopen __attribute__ ((alias ("fdopen")));
FILE __attribute__ ((weak, visibility ("default"))) *freopen(const char *filename, const char *mode, FILE *stream)
{
int fd;
struct access_mode_struct access_mode;
int errnum;
if (!filename) { /* tente de modifier le mode d'ouverture du flux */
errno = EINVAL; /* non implemente */
return NULL;
}
detach_stream(stream, &fd);
__close(fd);
if (!get_access_mode(mode, &access_mode)) {
errnum = EINVAL;
goto error;
}
if ((fd = openm(filename, &access_mode)) == -1) {
errnum = errno;
goto error;
}
init_stream(stream, fd, &access_mode, 1);
return stream;
error:
free_stream(stream);
errno = errnum;
return NULL;
}
typeof (freopen) __freopen __attribute__ ((alias ("freopen")));
int __attribute__ ((weak, visibility ("default"))) fclose(FILE *stream)
{
int errnum;
int fd;
errnum = 0;
if (detach_stream(stream, &fd) == EOF)
errnum = errno;
free_stream(stream);
if (__close(fd) == -1)
if (!errnum)
errnum = errno;
return errnum ? errno = errnum, EOF : 0;
}
typeof (fclose) __fclose __attribute__ ((alias ("fclose")));
/* Operations sur les tampons d'un flux */
/* POSIX reclame que setvbuf() soit utilisee apres que le flux soit associe a
* un fichier mais avant toute autre operation (autre qu'un appel sans succes a
* setvbuf()) sur le flux. */
int __attribute__ ((weak, visibility ("default"))) setvbuf(FILE *stream, char *buf, int mode, size_t size)
{
int line_buf;
switch (mode) {
case _IONBF:
goto no_buf;
case _IOLBF:
line_buf = 1;
goto buf;
case _IOFBF:
line_buf = 0;
goto buf;
default:
errno = EINVAL;
return -1;
}
buf:
if (size == 0) {
if (buf)
goto no_buf;
errno = EINVAL;
return -1;
}
return set_buf(stream, buf, size, line_buf);
no_buf:
stream->auto_buf = 0;
stream->buf = NULL;
return 0;
}
typeof (setvbuf) __setvbuf __attribute__ ((alias ("setvbuf")));
void __attribute__ ((weak, visibility ("default"))) setbuf(FILE *stream, char *buf)
{
__setvbuf(stream, buf, buf ? _IOFBF : _IONBF, BUFSIZ);
}
typeof (setbuf) __setbuf __attribute__ ((alias ("setbuf")));
void __attribute__ ((weak, visibility ("default"))) setbuffer(FILE *stream, char *buf, size_t size)
{
__setvbuf(stream, buf, buf ? _IOFBF : _IONBF, size);
}
typeof (setbuffer) __setbuffer __attribute__ ((alias ("setbuffer")));
void __attribute__ ((weak, visibility ("default"))) setlinebuf(FILE *stream)
{
__setvbuf(stream, NULL, _IOLBF, BUFSIZ);
}
typeof (setlinebuf) __setlinebuf __attribute__ ((alias ("setlinebuf")));
/* Etat d'un flux */
void __attribute__ ((weak, visibility ("default"))) clearerr(FILE *stream)
{
stream->eof = 0;
stream->error = 0;
}
typeof (clearerr) __clearerr __attribute__ ((alias ("clearerr")));
int __attribute__ ((weak, visibility ("default"))) feof(FILE *stream)
{
return stream->eof;
}
typeof (feof) __feof __attribute__ ((alias ("feof")));
int __attribute__ ((weak, visibility ("default"))) ferror(FILE *stream)
{
return stream->error;
}
typeof (ferror) __ferror __attribute__ ((alias ("ferror")));
int __attribute__ ((weak, visibility ("default"))) fileno(FILE *stream)
{
return stream->fd;
}
typeof (fileno) __fileno __attribute__ ((alias ("fileno")));
/* Positionnement d'un flux */
static inline void adjust_offset(FILE *stream, long *offset)
{
if (stream->buf)
*offset += count_buf(stream);
if (stream->pushed_back)
*offset -= 1;
}
/* fseek() vide les tampons associes au flux pointe par stream et, si ce flux
* est positionnable, fixe son indicateur de position. ANSI C recommande
* d'intercaler un appel a une fonction de positionnement entre une lecture et
* une ecriture sur un meme flux, pour l'effet de bord de synchronisation. */
int __attribute__ ((weak, visibility ("default"))) fseek(FILE *stream, long offset, int whence)
{
switch (whence) {
case SEEK_SET:
case SEEK_CUR:
case SEEK_END:
break;
default:
errno = EINVAL;
return -1;
}
if (stream->buf && writing_buf(stream))
if (flush_buf(stream) == EOF)
return -1;
if (whence == SEEK_CUR)
adjust_offset(stream, &offset);
stream->pushed_back = 0;
if (stream->buf && reading_buf(stream))
discard_buf(stream);
if (!stream->seekable) {
errno = ESPIPE;
return -1;
}
if (io_seek(stream, offset, whence) == -1)
return -1;
stream->eof = 0;
return 0;
}
typeof (fseek) __fseek __attribute__ ((alias ("fseek")));
long __attribute__ ((weak, visibility ("default"))) ftell(FILE *stream)
{
long offset;
if (stream->offset == -1) {
if (!stream->seekable) {
errno = ESPIPE;
return -1;
}
if (io_seek(stream, 0, stream->access_mode.append && stream->buf && writing_buf(stream) ? SEEK_END : SEEK_CUR) == -1)
return -1;
}
offset = stream->offset;
adjust_offset(stream, &offset);
return offset;
}
typeof (ftell) __ftell __attribute__ ((alias ("ftell")));
void __attribute__ ((weak, visibility ("default"))) rewind(FILE *stream)
{
__fseek(stream, 0, SEEK_SET);
__clearerr(stream);
}
typeof (rewind) __rewind __attribute__ ((alias ("rewind")));
int __attribute__ ((weak, visibility ("default"))) fgetpos(FILE *stream, fpos_t *pos)
{
return (*pos = __ftell(stream)) == -1 ? -1 : 0;
}
typeof (fgetpos) __fgetpos __attribute__ ((alias ("fgetpos")));
int __attribute__ ((weak, visibility ("default"))) fsetpos(FILE *stream, const fpos_t *pos)
{
return __fseek(stream, *pos, SEEK_SET);
}
typeof (fsetpos) __fsetpos __attribute__ ((alias ("fsetpos")));
/* Saisie de caracteres et de chaines */
int __attribute__ ((weak, visibility ("default"))) fgetc(FILE *stream)
{
reset_state(stream);
return get_byte(stream);
}
typeof (fgetc) __fgetc __attribute__ ((alias ("fgetc")));
char __attribute__ ((weak, visibility ("default"))) *fgets(char *s, int n, FILE *stream)
{
char *ptr;
int c;
if (n == 0)
return NULL;
n--;
ptr = s;
if (n == 0)
goto end;
reset_state(stream);
if ((c = get_byte(stream)) == EOF)
return NULL;
*(unsigned char *)ptr++ = c;
n--;
while (c != '\n' && n > 0) {
if ((c = get_byte(stream)) == EOF) {
if (stream->eof)
break;
return NULL;
}
*(unsigned char *)ptr++ = c;
n--;
}
end:
*ptr = '\0';
return s;
}
typeof (fgets) __fgets __attribute__ ((alias ("fgets")));
int __attribute__ ((weak, visibility ("default"))) getc(FILE *stream)
{
reset_state(stream);
return get_byte(stream);
}
typeof (getc) __getc __attribute__ ((alias ("getc")));
int __attribute__ ((weak, visibility ("default"))) getchar()
{
reset_state(stdin);
return get_byte(stdin);
}
typeof (getchar) __getchar __attribute__ ((alias ("getchar")));
char __attribute__ ((weak, visibility ("default"))) *gets(char *s)
{
char *ptr;
int c;
ptr = s;
reset_state(stdin);
if ((c = get_byte(stdin)) == EOF)
return NULL;
while (c != '\n') {
*(unsigned char *)ptr++ = c;
if ((c = get_byte(stdin)) == EOF) {
if (stdin->eof)
break;
return NULL;
}
}
*ptr = '\0';
return s;
}
typeof (gets) __gets __attribute__ ((alias ("gets")));
int __attribute__ ((weak, visibility ("default"))) ungetc(int c, FILE *stream)
{
if (c == EOF) /* POSIX */
return EOF;
if (stream->pushed_back)
return EOF;
push_back_byte(stream, c);
stream->eof = 0;
return (unsigned char)c;
}
typeof (ungetc) __ungetc __attribute__ ((alias ("ungetc")));
/* Ecriture de caracteres et de chaines */
/* ANSI C necessite qu'une fonction de positionnement dans le fichier soit
* appelee entre une lecture et une ecriture, a moins que l'operation de
* lecture atteigne la fin du fichier.
* Comme extension, notre implementation autorise systematiquement une ecriture
* apres une lecture. */
int __attribute__ ((weak, visibility ("default"))) fputc(int c, FILE *stream)
{
reset_state(stream);
return put_byte(stream, c);
}
typeof (fputc) __fputc __attribute__ ((alias ("fputc")));
int __attribute__ ((weak, visibility ("default"))) fputs(const char *s, FILE *stream)
{
size_t len;
if (!*s)
return 0;
len = strlen(s);
reset_state(stream);
return put_bytes(stream, s, len) == len ? 0 : EOF;
}
typeof (fputs) __fputs __attribute__ ((alias ("fputs")));
int __attribute__ ((weak, visibility ("default"))) putc(int c, FILE *stream)
{
reset_state(stream);
return put_byte(stream, c);
}
typeof (putc) __putc __attribute__ ((alias ("putc")));
int __attribute__ ((weak, visibility ("default"))) putchar(int c)
{
reset_state(stdout);
return put_byte(stdout, c);
}
typeof (putchar) __putchar __attribute__ ((alias ("putchar")));
int __attribute__ ((weak, visibility ("default"))) puts(const char *s)
{
size_t len;
if (!*s)
return 0;
len = strlen(s);
reset_state(stdout);
if (put_bytes(stdout, s, len) != len)
return EOF;
if (put_byte(stdout, '\n') == EOF)
return EOF;
return 0;
}
typeof (puts) __puts __attribute__ ((alias ("puts")));
/* Entrees/sorties binaires sur un flux */
size_t __attribute__ ((weak, visibility ("default"))) fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
{
if (size == 0 || nmemb == 0)
return 0;
reset_state(stream);
return get_bytes(stream, ptr, nmemb * size) / size;
}
typeof (fread) __fread __attribute__ ((alias ("fread")));
size_t __attribute__ ((weak, visibility ("default"))) fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
{
if (size == 0 || nmemb == 0)
return 0;
reset_state(stream);
return put_bytes(stream, ptr, nmemb * size) / size;
}
typeof (fwrite) __fwrite __attribute__ ((alias ("fwrite")));
/* Vidage des tampons d'un flux */
int __attribute__ ((weak, visibility ("default"))) fflush(FILE *stream)
{
if (!stream)
return flush_all();
return stream->buf && writing_buf(stream) ? flush_buf(stream) : 0;
}
typeof (fflush) __fflush __attribute__ ((alias ("fflush")));
/* Noms de fichiers */
int __attribute__ ((weak, visibility ("default"))) remove(const char *path)
{
struct stat statbuf;
if (__lstat(path, &statbuf) == -1)
return -1;
if (S_ISDIR(statbuf.st_mode))
return __rmdir(path);
else
return __unlink(path);
}
typeof (remove) __remove __attribute__ ((alias ("remove")));
/* Affichage d'un message d'erreur systeme */
void __attribute__ ((weak, visibility ("default"))) perror(const char *s)
{
int errnum;
errnum = errno;
if (s && *s)
fprintf(stderr, "%s: ", s);
if (errnum >= 0 && errnum < sys_nerr && sys_errlist[errnum])
fprintf(stderr, "%s\n", sys_errlist[errnum]);
else
fprintf(stderr, "Unknown error %d\n", errnum);
}