/* 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); }