[crt] Properly implement I/O buffers
authorcoderain <coderain@sdf.org>
Mon, 30 Oct 2017 23:44:57 +0000 (00:44 +0100)
committercoderain <coderain@sdf.org>
Mon, 30 Oct 2017 23:44:57 +0000 (00:44 +0100)
crt/include/stdio.h
crt/src/io.c

index 53d31a0e2c99095496f09a5e49e6d157dd23d066..aa09f56549cb10b30c2d8fefb7a41825b15483c0 100644 (file)
@@ -58,4 +58,14 @@ int fputs_unlocked(const char *s, FILE *stream);
 FILE *fmemopen(void *buf, size_t size, const char *mode);
 int fclose(FILE *stream);
 
+#define BUFSIZ 4096
+#define _IOFBF 0
+#define _IOLBF 1
+#define _IONBF 2
+
+int setvbuf(FILE *stream, char *buf, int mode, size_t size);
+void setbuf(FILE *stream, char *buf);
+void setbuffer(FILE *stream, char *buf, size_t size);
+void setlinebuf(FILE *stream);
+
 #endif
index 0c64c6b8871a02722b01918c2c2bda7027c050ad..f4338b9ad68507f9df49c7d5e66baf3b90e65c1e 100644 (file)
  */
 
 #include <stdio.h>
+#include <unistd.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_LINE (1 << 3)
+#define FILE_BUFFER_DIR  (1 << 4)
 #define FILE_BUFFER_FULL (1 << 31)
 
 struct __crt_file
@@ -33,10 +35,9 @@ struct __crt_file
     uint32_t flags;
 
     int fd;
-    long position;
     size_t size;
 
-    uint8_t *buffer;
+    char *buffer;
     size_t buffer_size;
     size_t buffer_start, buffer_end;
 };
@@ -63,6 +64,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 +73,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 +136,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;
+            }
+        }
+
+        c = stream->buffer[stream->buffer_start];
+        stream->buffer_start++;
+        stream->buffer_end %= stream->buffer_size;
 
-    if (stream->fd != -1
-        && stream->buffer_start == stream->buffer_end
-        && !(stream->flags & FILE_BUFFER_FULL))
+        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 +273,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 +368,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;
@@ -283,8 +401,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;