diff --git a/Makefile b/Makefile
index 9ffbd33..ff578a4 100644
--- a/Makefile
+++ b/Makefile
@@ -128,7 +128,8 @@ kal_kern_obj= $(KOBJDIR)/kernel/cpuid.o $(KOBJDIR)/kernel/init.o \
$(KOBJDIR)/kernel/table.o $(KOBJDIR)/kernel/cursor.o \
$(KOBJDIR)/kernel/term.o $(KOBJDIR)/kernel/vga.o \
$(KOBJDIR)/kernel/panic.o $(KOBJDIR)/kernel/map.o \
- $(KOBJDIR)/kernel/heap.o $(KOBJDIR)/kernel/malloc.o
+ $(KOBJDIR)/kernel/heap.o $(KOBJDIR)/kernel/malloc.o \
+ $(KOBJDIR)/kernel/buf.o
$(KOBJDIR)/kernel/cpuid.o: $(KERNELDIR)/kernel/cpu/cpuid.c $(KERNELDIR)/include/*/*.h
@$(KCC) $< -o $@
@@ -160,6 +161,9 @@ $(KOBJDIR)/kernel/heap.o: $(KERNELDIR)/kernel/mm/heap.c $(KERNELDIR)/include/*/*
$(KOBJDIR)/kernel/malloc.o: $(KERNELDIR)/kernel/mm/malloc.c $(KERNELDIR)/include/*/*.h
@$(KCC) $< -o $@
@echo ${CL2}[$@] ${CL}Compiled.${CL3}
+$(KOBJDIR)/kernel/buf.o: $(KERNELDIR)/kernel/buf/buf.c $(KERNELDIR)/include/*/*.h
+ @$(KCC) $< -o $@
+ @echo ${CL2}[$@] ${CL}Compiled.${CL3}
## MISC MAKEFILE ------------------------------------------------------------- #
./ProjectTree: ./.stylehlp_sh
diff --git a/kaleid/crtlib/mem.c b/kaleid/crtlib/mem.c
index 7d03682..208cffc 100644
--- a/kaleid/crtlib/mem.c
+++ b/kaleid/crtlib/mem.c
@@ -38,6 +38,7 @@ void *malloc(size_t n)
rc = KalAllocMemory(&ptr, n, 0, 0);
__set_errno(rc);
+ (void)rc;
return ptr;
}
diff --git a/kaleid/include/base/crtlib.h b/kaleid/include/base/crtlib.h
index 9aa697d..a62aa39 100644
--- a/kaleid/include/base/crtlib.h
+++ b/kaleid/include/base/crtlib.h
@@ -183,6 +183,7 @@ unsigned long strtoul(const char *restrict, char **restrict, int);
//------------------------------------------//
+void *calloc(size_t, size_t) __attribute__((__malloc__));
void *malloc(size_t) __attribute__((__malloc__));
void free(void *);
diff --git a/kaleid/include/base/errno.h b/kaleid/include/base/errno.h
index d063504..a6e2d33 100644
--- a/kaleid/include/base/errno.h
+++ b/kaleid/include/base/errno.h
@@ -54,7 +54,7 @@
// Not an executable format
#define ENOEXEC 8
-// Bad file number
+// Bad file
#define EBADF 9
// Try again
diff --git a/kaleid/include/kernel/buf.h b/kaleid/include/kernel/buf.h
new file mode 100644
index 0000000..13520d5
--- /dev/null
+++ b/kaleid/include/kernel/buf.h
@@ -0,0 +1,113 @@
+//----------------------------------------------------------------------------//
+// GNU GPL OS/K //
+// //
+// Desc: Buffer library //
+// //
+// //
+// Copyright © 2018-2019 The OS/K Team //
+// //
+// This file is part of OS/K. //
+// //
+// OS/K 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 //
+// any later version. //
+// //
+// OS/K 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 OS/K. If not, see . //
+//----------------------------------------------------------------------------//
+
+#ifndef _KALKERN_BASE_H
+#include
+#endif
+
+#ifndef _KALKERN_BUF_H
+#define _KALKERN_BUF_H
+
+typedef struct Buffer_t Buffer_t;
+
+//
+// Buffer flags
+//
+enum
+{
+ BF_LINE = 1, // Line-buffered
+ BF_FILE = 2, // Linked to a file
+ BF_ALLOC = 4, // Buffer was malloc'd
+ BF_FONCLOSE = 8, // Don't flush on close
+};
+
+//
+// Buffer states
+//
+enum
+{
+ BS_CLOSED = 0, // Buffer closed (need allocation)
+ BS_EOF, // Buffer closed, or end of file reached
+ BS_ERR, // Buffer not ready, an error happened
+
+ // Buffer ready for...
+ BS_RDWR, // Both reading and writing
+ BS_RDONLY, // Only for reading
+ BS_WRONLY, // Only for writing
+};
+
+typedef error_t (*BFlusher_t)(Buffer_t *);
+
+struct Buffer_t
+{
+ uint initDone;
+ Lock_t lock;
+
+ int flags; // Buffer flags
+ int state; // Buffer state
+
+ size_t size; // Current size
+
+ char *buf; // Beginning of buffer
+ char *rp; // Read pointer
+ char *wp; // Write pointer
+
+ BFlusher_t flusher; // Called for flushing
+
+ // The following parameters are only used in line buf mode
+
+ int lastLF; // Characters since beginning of line
+ int lineLen; // Line length (for line buffers)
+ int nLines; // Number of lines in "true" buffer
+ // size/(lineLen*nLines) - 1 playback buffers
+};
+
+extern Buffer_t BStdOut, BStdErr;
+
+error_t BCloseBuf(Buffer_t *buf);
+void BFlushOnClose(Buffer_t *buf);
+
+Buffer_t *BOpenPureBuf(char *source, size_t size);
+
+// Creates a lined buffer of pbCount*nLines lines each
+// of lineLen length (pb = playback buffer, for scrolling up)
+// If source==NULL, malloc the buffer
+Buffer_t *BOpenLineBuf(char *source, int mode, int lineLen,
+ int nLines, int pbCount, BFlusher_t flusher);
+
+int BGetFlags(Buffer_t *buf);
+int BGetState(Buffer_t *buf);
+int BGetLineLen(Buffer_t *buf);
+void BSetLineLen(Buffer_t *buf, int len);
+
+void BLockBuf(Buffer_t *buf);
+void BUnlockBuf(Buffer_t *buf);
+bool BTrylockBuf(Buffer_t *buf);
+
+error_t BFlushBuf(Buffer_t *buf);
+error_t BPutOnBuf(Buffer_t *buf, uchar ch);
+error_t BPrintOnBuf(Buffer_t *buf, size_t n, const char *fmt, ...);
+error_t BPrintOnBufV(Buffer_t *buf, size_t n, const char *fmt, va_list ap);
+
+#endif
diff --git a/kaleid/kernel/buf/buf.c b/kaleid/kernel/buf/buf.c
new file mode 100644
index 0000000..3491ffd
--- /dev/null
+++ b/kaleid/kernel/buf/buf.c
@@ -0,0 +1,263 @@
+//----------------------------------------------------------------------------//
+// GNU GPL OS/K //
+// //
+// Desc: Buffer library //
+// //
+// //
+// Copyright © 2018-2019 The OS/K Team //
+// //
+// This file is part of OS/K. //
+// //
+// OS/K 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 //
+// any later version. //
+// //
+// OS/K 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 OS/K. If not, see . //
+//----------------------------------------------------------------------------//
+
+#include
+#include
+
+// Straightforward functions
+int BGetFlags(Buffer_t *buf) { return buf->flags; }
+int BGetState(Buffer_t *buf) { return buf->state; }
+int BGetLineLen(Buffer_t *buf) { return buf->lineLen; }
+void BSetLineLen(Buffer_t *buf, int len) { buf->lineLen = len; }
+
+void BLockBuf(Buffer_t *buf)
+{
+ assert(buf && buf->initDone == INITOK);
+ ExAcquireLock(&buf->lock);
+}
+
+void BUnlockBuf(Buffer_t *buf) { ExReleaseLock(&buf->lock); }
+bool BTrylockBuf(Buffer_t *buf) { return ExAttemptLock(&buf->lock); }
+
+void BFlushOnClose(Buffer_t *buf) { buf->flags |= BF_FONCLOSE; }
+
+//
+// Closes a buffer, not flushing unless the proper flag is set
+//
+error_t BCloseBuf(Buffer_t *buf)
+{
+ if (!buf) return EINVAL;
+
+ assert(buf->initDone == INITOK);
+ ExAcquireLock(&buf->lock);
+
+ if (buf->flags & BF_FONCLOSE) {
+ BFlushBuf(buf);
+ }
+
+ if (buf->flags & BF_ALLOC) {
+ free(buf->buf);
+ }
+
+ buf->buf = buf->rp = buf->wp = NULL;
+ buf->initDone = buf->flags = buf->state = buf->size = 0;
+
+ ExReleaseLock(&buf->lock);
+ KalFreeMemory(buf);
+
+ return EOK;
+}
+
+//
+// Opens a pure text buffer (for WRITING only)
+// If source==NULL, malloc's the internal buffer
+//
+#if 0
+Buffer_t *BOpenPureBuf(char *source, size_t size)
+{
+ /*Buffer_t *buf = malloc(sizeof *buf);
+ buf->flags = 0;
+ ExInitLock(&buf->lock);*/
+
+ return NULL;
+}
+#endif
+
+extern BFlusher_t _VGA_BFlusher;
+
+//
+// Creates a lined buffer of (nLines + pbCount*nLines) lines each
+// of lineLen length (pb = playback buffer, for scrolling up)
+//
+Buffer_t *BOpenLineBuf(char *source, int mode,
+ int lineLen, int nLines, int pbCount, BFlusher_t flusher)
+{
+ assert(lineLen > 0 && nLines > 0 && pbCount >= 0);
+ assert(mode == BS_RDWR || mode == BS_RDONLY || mode == BS_WRONLY);
+
+ Buffer_t *buf = malloc(sizeof *buf);
+
+ ExInitLock(&buf->lock, KLOCK_MUTEX);
+ ExAcquireLock(&buf->lock);
+
+ buf->lastLF = 0;
+ buf->state = mode;
+ buf->flags = BF_LINE;
+ buf->nLines = nLines;
+ buf->lineLen = lineLen;
+ buf->size = lineLen * nLines * (pbCount + 1);
+
+ if (source == NULL) {
+ buf->buf = malloc(buf->size);
+ } else {
+ buf->buf = source;
+ }
+
+ buf->wp = buf->rp = buf->buf;
+ buf->flusher = flusher;
+ buf->initDone = INITOK;
+
+ ExReleaseLock(&buf->lock);
+ return buf;
+}
+
+//
+// Flushes a buffer, returns EBADF when not possible
+//
+error_t BFlushBuf(Buffer_t *buf)
+{
+ error_t rc;
+
+ assert(buf && buf->initDone == INITOK);
+
+ if (!buf) return EINVAL;
+ if (!buf->flusher || buf->state == BS_EOF || buf->state == BS_ERR) {
+ return EBADF;
+ }
+
+ ExAcquireLock(&buf->lock);
+ rc = buf->flusher(buf);
+ ExReleaseLock(&buf->lock);
+
+ return rc;
+}
+
+static error_t bputc(Buffer_t *buf, uchar ch);
+
+//
+// Writes a character on a buffer
+//
+error_t BPutOnBuf(Buffer_t *buf, uchar ch)
+{
+ error_t rc;
+ assert(buf && buf->initDone == INITOK);
+
+ if (!buf) return EINVAL;
+ if (buf->state != BS_RDWR && buf->state != BS_WRONLY) {
+ return EBADF;
+ }
+
+ ExAcquireLock(&buf->lock);
+ rc = bputc(buf, ch);
+ ExReleaseLock(&buf->lock);
+ return rc;
+}
+
+//
+// Internal, unlocked version of BPutOnBuf
+//
+static error_t bputc(Buffer_t *buf, uchar ch)
+{
+ error_t rc;
+ size_t bufSize, pbCount;
+
+ // Implements playback / scrolling up when line buffering
+ // Note that '\n', '\r' and '\t' can never occur in the buffer
+ // That should change; there should be a flag for it
+ if (buf->flags & BF_LINE) {
+
+ // Deal with carriage returns
+ if (ch == '\r') {
+ buf->wp -= buf->lastLF;
+ buf->lastLF = 0;
+ assert(buf->wp >= buf->buf);
+ }
+
+ // Deal with tabs... we need a tabSize field
+ // We assume tabs are 4 characters long for now
+ else if (ch == '\t') {
+ rc = bputc(buf, ' ');
+ if (rc > 0) return rc;
+
+ while (buf->lastLF % 4 > 0) {
+ rc = bputc(buf, ' ');
+ if (rc > 0) return rc;
+ }
+ }
+
+ // Deal with line feeds by filling the rest of the line with spaces
+ // We need a field for choosing a different filler, e.g. '\0'
+ else if (ch == '\n') {
+ assert(buf->lastLF < buf->lineLen);
+ while (buf->lastLF > 0) {
+ rc = bputc(buf, ' ');
+ if (rc > 0) return rc;
+ }
+ }
+
+ // Just a regular character
+ else {
+ // Do we have to scroll up?
+ if (buf->wp == buf->buf + buf->size) {
+ // Yes, so we give up a whole playback buffer worth
+ // of lines so we don't have to do this too often
+ // (basically we make the current buffer a pb one)
+ bufSize = buf->nLines * buf->lineLen;
+ pbCount = (buf->size / bufSize) - 1;
+
+ // Paranoia check
+ assert(buf->size >= (size_t)(buf->nLines * buf->lineLen));
+
+ // If we only have one playback buffer we just give up a line
+ if (pbCount == 0) {
+ // Use of memcpy() is safe because the source occur
+ // after the destination
+ memcpy(buf->buf, buf->buf + buf->lineLen,
+ buf->size - buf->lineLen);
+ buf->wp -= buf->lineLen;
+ }
+
+ // We do have a playback buffer worth of lines to give up
+ else {
+ memcpy(buf->buf, buf->buf + bufSize,
+ buf->size - bufSize);
+ buf->wp -= buf->lineLen;
+ }
+ }
+
+ // Write the damn thing
+ *buf->wp++ = (char)ch;
+
+ // Did we reach the end of line?
+ if (++buf->lastLF == buf->lineLen) {
+ buf->lastLF = 0;
+ }
+ }
+
+ return EOK;
+ }
+
+ // No scrolling up when not line-buffering
+ else {
+ if (buf->wp == buf->buf + buf->size) {
+ buf->state = BS_EOF;
+ }
+
+ return ENOSYS;
+ }
+}
+
+error_t BPrintOnBuf(Buffer_t *buf, size_t n, const char *fmt, ...);
+error_t BPrintOnBufV(Buffer_t *buf, size_t n, const char *fmt, va_list ap);
+