View of xos/usr/lib/libc/stdio.c


XOS | Parent Directory | View | Download

/* 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 <http://www.gnu.org/licenses/>. */
 
#include "stdio.h"
 
#include "fcntl.h"
#include "unistd.h"
 
#include <string.h>
#include <stdlib.h>
#include <errno.h>
 
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);
}