Implement vectored I/O.
[monolithium.git] / crt / src / io.c
index 0c64c6b8871a02722b01918c2c2bda7027c050ad..2b461679844b9cc1e7e7bb44f433fe36c5e42c98 100644 (file)
  */
 
 #include <stdio.h>
-#include <monolithium.h>
-
-#define FILE_READ        (1 << 0)
-#define FILE_WRITE       (1 << 1)
-#define FILE_APPEND      (1 << 2)
-#define FILE_BUFFER_DIR  (1 << 3)
-#define FILE_BUFFER_FULL (1 << 31)
-
-struct __crt_file
-{
-    list_entry_t link;
-    handle_t mutex;
-    uint32_t flags;
-
-    int fd;
-    long position;
-    size_t size;
-
-    uint8_t *buffer;
-    size_t buffer_size;
-    size_t buffer_start, buffer_end;
-};
+#include <unistd.h>
+#include "io_priv.h"
 
 static list_entry_t open_files;
 
@@ -63,6 +43,8 @@ int fileno(FILE *stream)
 
 static int __crt_fflush_stream(FILE *stream)
 {
+    if (!stream->buffer) return EOF;
+
     if (!(stream->flags & FILE_BUFFER_DIR))
     {
         stream->buffer_start = stream->buffer_end = 0;
@@ -70,11 +52,38 @@ static int __crt_fflush_stream(FILE *stream)
         return 0;
     }
 
-    if (stream->fd != -1)
+    if (stream->fd == -1) return 0;
+
+    if (stream->buffer_start < stream->buffer_end)
     {
-        // TODO: Not implemented
-        errno = ENOSYS;
-        return EOF;
+        ssize_t written = write(stream->fd, &stream->buffer[stream->buffer_start], stream->buffer_end - stream->buffer_start);
+        if (written < 0) return EOF;
+
+        stream->buffer_start += written;
+        stream->buffer_start %= stream->buffer_size;
+        if (written) stream->flags &= ~FILE_BUFFER_FULL;
+    }
+    else
+    {
+        if (stream->buffer_start < stream->buffer_size)
+        {
+            ssize_t written = write(stream->fd, &stream->buffer[stream->buffer_start], stream->buffer_size - stream->buffer_start);
+            if (written < 0) return EOF;
+
+            stream->buffer_start += written;
+            stream->buffer_start %= stream->buffer_size;
+            if (written) stream->flags &= ~FILE_BUFFER_FULL;
+        }
+
+        if (stream->buffer_end)
+        {
+            ssize_t written = write(stream->fd, stream->buffer, stream->buffer_end);
+            if (written < 0) return EOF;
+
+            stream->buffer_start += written;
+            stream->buffer_start %= stream->buffer_size;
+            if (written) stream->flags &= ~FILE_BUFFER_FULL;
+        }
     }
 
     return 0;
@@ -106,37 +115,124 @@ int fflush(FILE *stream)
     return ret;
 }
 
+int setvbuf(FILE *stream, char *buf, int mode, size_t size)
+{
+    int ret = EOF;
+    syscall_wait_mutex(stream->mutex, NO_TIMEOUT);
+
+    switch (mode)
+    {
+    case _IONBF:
+        if (fflush_unlocked(stream) == 0)
+        {
+            stream->buffer = NULL;
+            stream->buffer_size = 0;
+            ret = 0;
+        }
+        break;
+
+    case _IOLBF:
+        if (!buf || fflush_unlocked(stream) == 0)
+        {
+            if (buf)
+            {
+                stream->buffer = buf;
+                stream->buffer_size = size;
+            }
+
+            stream->flags |= FILE_BUFFER_LINE;
+            ret = 0;
+        }
+        break;
+
+    case _IOFBF:
+        if (!buf || fflush_unlocked(stream) == 0)
+        {
+            if (buf)
+            {
+                stream->buffer = buf;
+                stream->buffer_size = size;
+            }
+
+            stream->flags &= ~FILE_BUFFER_LINE;
+            ret = 0;
+        }
+        break;
+    }
+
+    syscall_release_mutex(stream->mutex);
+    return ret;
+}
+
+void setbuf(FILE *stream, char *buf)
+{
+    setvbuf(stream, buf, buf ? _IOFBF : _IONBF, BUFSIZ);
+}
+
+void setbuffer(FILE *stream, char *buf, size_t size)
+{
+    setvbuf(stream, buf, buf ? _IOFBF : _IONBF, size);
+}
+
+void setlinebuf(FILE *stream)
+{
+    setvbuf(stream, NULL, _IOLBF, 0);
+}
+
 inline int fgetc_unlocked(FILE *stream)
 {
+    char c;
+
     if (!(stream->flags & FILE_READ))
     {
         errno = EPERM;
         return EOF;
     }
 
-    if (stream->position == stream->size) return EOF;
-
-    if (stream->flags & FILE_BUFFER_DIR)
+    if (stream->buffer)
     {
-        fflush_unlocked(stream);
-        stream->flags &= ~FILE_BUFFER_DIR;
-    }
+        if (stream->flags & FILE_BUFFER_DIR)
+        {
+            fflush_unlocked(stream);
+            stream->flags &= ~FILE_BUFFER_DIR;
+        }
+
+        if (stream->buffer_start == stream->buffer_end && !(stream->flags & FILE_BUFFER_FULL))
+        {
+            if (stream->fd == -1) return EOF;
+
+            if (stream->buffer_end < stream->buffer_size)
+            {
+                ssize_t amount = read(stream->fd, &stream->buffer[stream->buffer_end], stream->buffer_size - stream->buffer_end);
+                if (amount < 0) return EOF;
+
+                stream->buffer_end += amount;
+                stream->buffer_end %= stream->buffer_size;
+                if (amount && (stream->buffer_start == stream->buffer_end)) stream->flags |= FILE_BUFFER_FULL;
+            }
+
+            if (stream->buffer_end < stream->buffer_start)
+            {
+                ssize_t amount = read(stream->fd, &stream->buffer[stream->buffer_end], stream->buffer_start - stream->buffer_end);
+                if (amount < 0) return EOF;
+
+                stream->buffer_end += amount;
+                stream->buffer_end %= stream->buffer_size;
+                if (amount && (stream->buffer_start == stream->buffer_end)) stream->flags |= FILE_BUFFER_FULL;
+            }
+        }
 
-    if (stream->fd != -1
-        && stream->buffer_start == stream->buffer_end
-        && !(stream->flags & FILE_BUFFER_FULL))
+        c = stream->buffer[stream->buffer_start];
+        stream->buffer_start++;
+        stream->buffer_end %= stream->buffer_size;
+
+        stream->flags &= ~FILE_BUFFER_FULL;
+    }
+    else
     {
-        // TODO: Not implemented
-        errno = ENOSYS;
-        return EOF;
+        if (read(stream->fd, &c, 1) != 1) c = EOF;
     }
 
-    uint8_t c = stream->buffer[stream->buffer_start];
-    stream->buffer_start++;
-    stream->buffer_end %= stream->buffer_size;
-    stream->position++;
-
-    stream->flags &= ~FILE_BUFFER_FULL;
     return (int)((unsigned int)c);
 }
 
@@ -156,35 +252,37 @@ inline int fputc_unlocked(int c, FILE *stream)
         return EOF;
     }
 
-    if (!(stream->flags & FILE_BUFFER_DIR))
-    {
-        fflush_unlocked(stream);
-        stream->flags |= FILE_BUFFER_DIR;
-    }
-
-    if ((stream->flags & FILE_APPEND) && (stream->position < stream->size))
+    if (stream->buffer)
     {
-        fflush_unlocked(stream);
-        stream->position = stream->size;
-    }
+        if (!(stream->flags & FILE_BUFFER_DIR))
+        {
+            fflush_unlocked(stream);
+            stream->flags |= FILE_BUFFER_DIR;
+        }
 
-    if (stream->flags & FILE_BUFFER_FULL)
-    {
-        errno = ENOSPC;
-        return EOF;
-    }
+        if (stream->flags & FILE_BUFFER_FULL)
+        {
+            errno = ENOSPC;
+            return EOF;
+        }
 
-    stream->buffer[stream->buffer_end] = (uint8_t)c;
-    stream->buffer_end++;
-    stream->buffer_end %= stream->buffer_size;
+        stream->buffer[stream->buffer_end] = (uint8_t)c;
+        stream->buffer_end++;
+        stream->buffer_end %= stream->buffer_size;
 
-    stream->position++;
-    if (stream->position > stream->size) stream->size = stream->position;
+        if (stream->buffer_start == stream->buffer_end)
+        {
+            stream->flags |= FILE_BUFFER_FULL;
+        }
 
-    if (stream->fd != -1 && ((char)c == '\n' || (stream->buffer_start == stream->buffer_end)))
+        if (stream->fd != -1 && ((stream->flags & FILE_BUFFER_FULL) || ((stream->flags & FILE_BUFFER_LINE) && (char)c == '\n')))
+        {
+            fflush_unlocked(stream);
+        }
+    }
+    else
     {
-        stream->flags |= FILE_BUFFER_FULL;
-        fflush_unlocked(stream);
+        if (write(stream->fd, &c, 1) != 1) return EOF;
     }
 
     return 0;
@@ -249,7 +347,6 @@ int ungetc(int c, FILE *stream)
     stream->buffer_start--;
 
     stream->buffer[stream->buffer_start] = (uint8_t)c;
-    stream->position--;
 
     syscall_release_mutex(stream->mutex);
     return c;
@@ -270,9 +367,61 @@ int puts(const char *s)
     return 0;
 }
 
+FILE *fdopen(int fd, const char *mode)
+{
+    FILE *stream = (FILE*)malloc(sizeof(FILE) + BUFSIZ);
+
+    if (stream == NULL)
+    {
+        errno = ENOMEM;
+        return NULL;
+    }
+
+    sysret_t status = syscall_create_mutex(NULL, TRUE, &stream->mutex);
+    if (status != ERR_SUCCESS)
+    {
+        free(stream);
+        errno = __crt_translate_error(status);
+        return NULL;
+    }
+
+    stream->flags = 0;
+
+    const char *ptr;
+    for (ptr = mode; *ptr; ptr++)
+    {
+        switch (*ptr)
+        {
+        case 'r':
+            stream->flags |= FILE_READ;
+            break;
+        case 'w':
+            stream->flags |= FILE_WRITE;
+            break;
+        case 'a':
+            stream->flags |= FILE_APPEND;
+            break;
+        case '+':
+            stream->flags |= FILE_READ | FILE_WRITE;
+            break;
+        }
+    }
+
+    stream->fd = fd;
+
+    char *buffer = (char*)((uintptr_t)stream + sizeof(FILE));
+    setbuf(stream, buffer);
+    return stream;
+}
+
 FILE *fmemopen(void *buf, size_t size, const char *mode)
 {
     FILE *stream = (FILE*)malloc(sizeof(FILE));
+    if (stream == NULL)
+    {
+        errno = ENOMEM;
+        return NULL;
+    }
 
     sysret_t status = syscall_create_mutex(NULL, TRUE, &stream->mutex);
     if (status != ERR_SUCCESS)
@@ -283,9 +432,28 @@ FILE *fmemopen(void *buf, size_t size, const char *mode)
     }
 
     stream->flags = 0;
+
+    const char *ptr;
+    for (ptr = mode; *ptr; ptr++)
+    {
+        switch (*ptr)
+        {
+        case 'r':
+            stream->flags |= FILE_READ;
+            break;
+        case 'w':
+            stream->flags |= FILE_WRITE;
+            break;
+        case 'a':
+            stream->flags |= FILE_APPEND;
+            break;
+        case '+':
+            stream->flags |= FILE_READ | FILE_WRITE;
+            break;
+        }
+    }
+
     stream->fd = -1;
-    stream->position = 0;
-    stream->size = size;
     stream->buffer = buf;
     stream->buffer_size = size;
     stream->buffer_start = stream->buffer_end = 0;
@@ -294,9 +462,47 @@ FILE *fmemopen(void *buf, size_t size, const char *mode)
     return stream;
 }
 
+FILE *fopen(const char *pathname, const char *mode)
+{
+    int open_flags = 0;
+
+    const char *ptr;
+    for (ptr = mode; *ptr; ptr++)
+    {
+        switch (*ptr)
+        {
+        case 'r':
+            open_flags = (open_flags & ~3) | O_RDONLY;
+            break;
+        case 'w':
+            open_flags = (open_flags & ~3) | O_WRONLY;
+            break;
+        case 'a':
+            open_flags |= O_APPEND;
+            break;
+        case '+':
+            open_flags = (open_flags & ~3) | O_RDWR;
+            break;
+        }
+    }
+
+    int fd = open(pathname, open_flags);
+    if (fd < 0) return NULL;
+
+    FILE *stream = fdopen(fd, mode);
+    if (!stream) close(fd);
+    return NULL;
+}
+
 int fclose(FILE *stream)
 {
+    fflush(stream);
     list_remove(&stream->link);
+    syscall_wait_mutex(stream->mutex, NO_TIMEOUT);
+
+    if (stream->fd) close(stream->fd);
+
+    syscall_close_object(stream->mutex);
     free(stream);
     return 0;
 }