266 lines
6.3 KiB
C++
266 lines
6.3 KiB
C++
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <luna.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
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");
|
|
}
|
|
} |