#include #include #include #include #include #include FILE* stderr; FILE* stdout; FILE* stdin; void file_read_buf(FILE* stream) { if (!stream->f_buf) { stream->f_buf = (char*)malloc(BUFSIZ); // FIXME: Handle errors. stream->f_bufrsize = BUFSIZ; } stream->f_bufoff = 0; ssize_t nread = read(stream->f_fd, stream->f_buf, stream->f_bufrsize); if (nread < 0) { stream->f_err = 1; return; } if (nread == 0) { stream->f_eof = 1; return; } stream->f_bufsize = nread; } extern "C" { int fclose(FILE* stream) { if (stream->f_buf) free(stream->f_buf); int status = close(stream->f_fd); if (status < 0) { int savederr = errno; free(stream); // We do not want to leak memory. man fclose(3) says that whether fclose() fails or not, any // further operation on the stream results in undefined behavior. So we are free to free the // stream. errno = savederr; // free might reset errno. We don't want that. } else { free(stream); } return status; } int fflush(FILE*) { return 0; // FIXME: Implement buffered IO. } FILE* fopen(const char* pathname, const char* mode) { int fd = open(pathname, O_RDWR); // FIXME: Use the mode string. if (fd < 0) { return 0; } return fdopen(fd, mode); } FILE* fdopen(int fd, const char*) { if (fd < 0) // FIXME: Also check if the mode string is compatible with how fd was opened. { errno = EBADF; return 0; } FILE* stream = (FILE*)malloc(sizeof(FILE)); if (!stream) { return 0; } stream->f_fd = fd; clearerr(stream); stream->f_buf = 0; stream->f_bufoff = 0; stream->f_bufsize = 0; return stream; } FILE* freopen(const char* pathname, const char*, FILE* stream) // FIXME: If pathname is NULL, open the original file with the new mode. { int fd = open(pathname, O_RDWR); // FIXME: Use the mode string. if (fd < 0) { return 0; } fflush(stream); // To make it future-proof. fclose(stream); stream->f_fd = fd; clearerr(stream); return stream; } int fileno(FILE* stream) { return stream->f_fd; } size_t fread(void* buf, size_t size, size_t nmemb, FILE* stream) { ssize_t status = read(stream->f_fd, buf, size * nmemb); // FIXME: This function should use file_read_buf() to not conflict with fgets(). if (status < 0) { stream->f_err = 1; return 0; } if (status == 0) stream->f_eof = 1; return (size_t)status; } char* fgets(char* buf, int size, FILE* stream) { char* s = buf; int original_size = size; while (size > 1) { if (stream->f_bufoff < stream->f_bufsize) { *buf = *(stream->f_buf + stream->f_bufoff); stream->f_bufoff++; if (*buf == '\n') { buf++; break; } buf++; size--; } else { file_read_buf(stream); if (ferror(stream)) return NULL; if (feof(stream)) break; } } if (size == original_size && feof(stream)) return NULL; // EOF while reading the first character *buf = 0; return s; } int fgetc(FILE* stream) { char result; read: if (stream->f_bufoff < stream->f_bufsize) { result = *(stream->f_buf + stream->f_bufoff); stream->f_bufoff++; } else { file_read_buf(stream); if (ferror(stream)) return EOF; if (feof(stream)) return EOF; goto read; } return (int)result; } int getc(FILE* stream) { return fgetc(stream); } int getchar() { return fgetc(stdin); } int ungetc(int c, FILE* stream) { if (stream->f_bufoff > 0) { stream->f_bufoff--; stream->f_buf[stream->f_bufoff] = (char)c; return c; } else { return EOF; // FIXME: Handle this case properly. } } int ferror(FILE* stream) { return stream->f_err; } int feof(FILE* stream) { return stream->f_eof; } void clearerr(FILE* stream) { stream->f_err = stream->f_eof = 0; } int fseek(FILE* stream, long offset, int whence) { long result = lseek(stream->f_fd, offset, whence); if (result < 0) { return -1; } return 0; } int fseeko(FILE* stream, off_t offset, int whence) { return fseek(stream, offset, whence); } int fsetpos(FILE* stream, const fpos_t* pos) { return fseek(stream, *pos, SEEK_SET); } long ftell(FILE* stream) { return lseek(stream->f_fd, 0, SEEK_CUR); // FIXME: Store the last seeked position in the file struct to avoid redundant syscalls // maybe? We'd have to update this value in fread() and fwrite() as well... } off_t ftello(FILE* stream) { return ftell(stream); } int fgetpos(FILE* stream, fpos_t* pos) { long result = ftell(stream); if (result < 0) { return -1; } *pos = result; return 0; } void rewind(FILE* stream) { lseek(stream->f_fd, 0, SEEK_SET); clearerr(stream); } size_t fwrite(const void* buf, size_t size, size_t nmemb, FILE* stream) { ssize_t status = write(stream->f_fd, buf, size * nmemb); if (status < 0) { stream->f_err = 1; return 0; } if (status == 0) stream->f_eof = 1; return (size_t)status; } void setbuf(FILE*, char*) { NOT_IMPLEMENTED("setbuf"); } int setvbuf(FILE*, char*, int, size_t) { NOT_IMPLEMENTED("setvbuf"); } }