void console_enable_vga(int enabled);
void console_write(const char* s);
+void console_put_char(char c);
int kvsnprintf(char* out, size_t out_size, const char* fmt, va_list ap);
int ksnprintf(char* out, size_t out_size, const char* fmt, ...);
TTY_ISIG = 0x0001,
};
+/* c_oflag bits (POSIX) */
+enum {
+ TTY_OPOST = 0x0001,
+ TTY_ONLCR = 0x0004,
+};
+
void tty_init(void);
int tty_read(void* user_buf, uint32_t len);
#include <stdint.h>
void vga_init(void);
+void vga_put_char(char c);
void vga_print(const char* str);
void vga_set_color(uint8_t fg, uint8_t bg);
#include "vmm.h"
#include "process.h"
+#include "heap.h"
#include "hal/cpu.h"
#include "hal/usermode.h"
current_process->heap_break = heap_brk;
vmm_as_activate(user_as);
+ /* Open /dev/console as fd 0, 1, 2 — mirrors Linux kernel_init:
+ * sys_open("/dev/console", O_RDWR, 0);
+ * sys_dup(0); sys_dup(0); */
+ {
+ fs_node_t* con = vfs_lookup("/dev/console");
+ if (con) {
+ struct file* f = (struct file*)kmalloc(sizeof(*f));
+ if (f) {
+ f->node = con;
+ f->offset = 0;
+ f->flags = 2; /* O_RDWR */
+ f->refcount = 3;
+ current_process->files[0] = f;
+ current_process->files[1] = f;
+ current_process->files[2] = f;
+ kprintf("[INIT] opened /dev/console as fd 0/1/2\n");
+ }
+ } else {
+ kprintf("[INIT] WARNING: /dev/console not found\n");
+ }
+ }
+
kprintf("[ELF] starting /bin/init.elf\n");
kprintf("[ELF] user_range_ok(entry)=%c user_range_ok(stack)=%c\n",
#include "spinlock.h"
#include "uart_console.h"
+#include "hal/uart.h"
#include "vga_console.h"
#include "keyboard.h"
spin_unlock_irqrestore(&g_console_lock, flags);
}
+void console_put_char(char c) {
+ uintptr_t flags = spin_lock_irqsave(&g_console_lock);
+
+ if (g_console_uart_enabled) {
+ hal_uart_putc(c);
+ }
+ if (g_console_vga_enabled) {
+ vga_put_char(c);
+ }
+
+ spin_unlock_irqrestore(&g_console_lock, flags);
+}
+
static void out_char(char** dst, size_t* rem, int* total, char c) {
(*total)++;
if (*rem == 0) return;
f->offset = f->node->length;
}
- uint8_t kbuf[256];
+ uint8_t kbuf[1024];
uint32_t total = 0;
while (total < len) {
uint32_t chunk = len - total;
static waitqueue_t tty_wq;
static uint32_t tty_lflag = TTY_ICANON | TTY_ECHO | TTY_ISIG;
+static uint32_t tty_oflag = TTY_OPOST | TTY_ONLCR;
static uint8_t tty_cc[NCCS] = {0, 0, 0, 0, 1, 0, 0, 0};
static struct winsize tty_winsize = { 24, 80, 0, 0 };
static uint32_t canon_count(void);
+/* Output a single character with OPOST processing to all console backends. */
+static void tty_output_char(char c) {
+ if ((tty_oflag & TTY_OPOST) && (tty_oflag & TTY_ONLCR) && c == '\n') {
+ console_put_char('\r');
+ }
+ console_put_char(c);
+}
+
int tty_write_kbuf(const void* kbuf, uint32_t len) {
if (!kbuf) return -EFAULT;
if (len > 1024 * 1024) return -EINVAL;
const char* p = (const char*)kbuf;
for (uint32_t i = 0; i < len; i++) {
- uart_put_char(p[i]);
+ tty_output_char(p[i]);
}
return (int)len;
}
memset(&t, 0, sizeof(t));
uintptr_t flags = spin_lock_irqsave(&tty_lock);
t.c_lflag = tty_lflag;
+ t.c_oflag = tty_oflag;
for (int i = 0; i < NCCS; i++) t.c_cc[i] = tty_cc[i];
spin_unlock_irqrestore(&tty_lock, flags);
if (copy_to_user(user_arg, &t, sizeof(t)) < 0) return -EFAULT;
if (copy_from_user(&t, user_arg, sizeof(t)) < 0) return -EFAULT;
uintptr_t flags = spin_lock_irqsave(&tty_lock);
tty_lflag = t.c_lflag & (TTY_ICANON | TTY_ECHO | TTY_ISIG);
+ tty_oflag = t.c_oflag & (TTY_OPOST | TTY_ONLCR);
for (int i = 0; i < NCCS; i++) tty_cc[i] = t.c_cc[i];
spin_unlock_irqrestore(&tty_lock, flags);
return 0;
canon_push(c);
wq_wake_one(&tty_wq);
if (lflag & TTY_ECHO) {
- uart_put_char(c);
+ tty_output_char(c);
}
spin_unlock_irqrestore(&tty_lock, flags);
return;
if (c == '\n') {
if (lflag & TTY_ECHO) {
- uart_put_char('\n');
+ tty_output_char('\n');
}
for (uint32_t i = 0; i < line_len; i++) {
if (line_len + 1 < sizeof(line_buf)) {
line_buf[line_len++] = c;
if (lflag & TTY_ECHO) {
- uart_put_char(c);
+ tty_output_char(c);
}
}
}
if (copy_from_user(kbuf, (const void*)up, (size_t)chunk) < 0) return -EFAULT;
for (uint32_t i = 0; i < chunk; i++) {
- uart_put_char(kbuf[i]);
+ tty_output_char(kbuf[i]);
}
up += chunk;
#define _STDIO_WRITE 0x02
#define _STDIO_EOF 0x04
#define _STDIO_ERR 0x08
+#define _STDIO_LBUF 0x10 /* line-buffered (flush on \n) */
+#define _STDIO_UNBUF 0x20 /* unbuffered (flush every write) */
typedef struct _FILE {
int fd;
int remove(const char* path);
int rename(const char* oldpath, const char* newpath);
+/* Buffering modes for setvbuf (POSIX values) */
+#define _IOFBF 0 /* fully buffered */
+#define _IOLBF 1 /* line buffered */
+#define _IONBF 2 /* unbuffered */
+int setvbuf(FILE* fp, char* buf, int mode, size_t size);
+void setbuf(FILE* fp, char* buf);
+
#endif
#define LOCK_UN 8
#define LOCK_NB 4
int flock(int fd, int operation);
+int isatty(int fd);
void* brk(void* addr);
void _exit(int status) __attribute__((noreturn));
#include <stdarg.h>
static FILE _stdin_file = { .fd = 0, .flags = _STDIO_READ };
-static FILE _stdout_file = { .fd = 1, .flags = _STDIO_WRITE };
-static FILE _stderr_file = { .fd = 2, .flags = _STDIO_WRITE };
+static FILE _stdout_file = { .fd = 1, .flags = _STDIO_WRITE | _STDIO_LBUF };
+static FILE _stderr_file = { .fd = 2, .flags = _STDIO_WRITE | _STDIO_UNBUF };
FILE* stdin = &_stdin_file;
FILE* stdout = &_stdout_file;
size_t total = size * nmemb;
const char* src = (const char*)ptr;
size_t done = 0;
+
+ /* Unbuffered: write directly, bypass buffer */
+ if (fp->flags & _STDIO_UNBUF) {
+ while (done < total) {
+ int w = write(fp->fd, src + done, total - done);
+ if (w <= 0) break;
+ done += (size_t)w;
+ }
+ return done / size;
+ }
+
while (done < total) {
int space = BUFSIZ - fp->buf_pos;
int want = (int)(total - done);
done += (size_t)chunk;
if (fp->buf_pos >= BUFSIZ) fflush(fp);
}
+
+ /* Line-buffered: flush if the written data contains a newline */
+ if ((fp->flags & _STDIO_LBUF) && fp->buf_pos > 0) {
+ for (size_t i = 0; i < total; i++) {
+ if (src[i] == '\n') {
+ fflush(fp);
+ break;
+ }
+ }
+ }
return done / size;
}
return -1;
}
+int setvbuf(FILE* fp, char* buf, int mode, size_t size) {
+ (void)buf; (void)size; /* we use internal buffer only */
+ if (!fp) return -1;
+ fp->flags &= ~(_STDIO_LBUF | _STDIO_UNBUF);
+ if (mode == 1 /* _IOLBF */) fp->flags |= _STDIO_LBUF;
+ else if (mode == 2 /* _IONBF */) fp->flags |= _STDIO_UNBUF;
+ /* _IOFBF (0): fully buffered — no extra flag needed */
+ return 0;
+}
+
+void setbuf(FILE* fp, char* buf) {
+ if (!buf) setvbuf(fp, (char*)0, 2 /* _IONBF */, 0);
+ else setvbuf(fp, buf, 0 /* _IOFBF */, BUFSIZ);
+}
+
int vfprintf(FILE* fp, const char* fmt, va_list ap) {
char buf[1024];
int n = vsnprintf(buf, sizeof(buf), fmt, ap);
}
int putchar(int c) {
- char ch = (char)c;
- write(STDOUT_FILENO, &ch, 1);
+ unsigned char ch = (unsigned char)c;
+ fwrite(&ch, 1, 1, stdout);
return c;
}
int puts(const char* s) {
int len = (int)strlen(s);
- write(STDOUT_FILENO, s, (size_t)len);
- write(STDOUT_FILENO, "\n", 1);
+ fwrite(s, 1, (size_t)len, stdout);
+ fwrite("\n", 1, 1, stdout);
return len + 1;
}
}
int vprintf(const char* fmt, va_list ap) {
- char buf[1024];
- int n = vsnprintf(buf, sizeof(buf), fmt, ap);
- if (n > 0) {
- int w = n;
- if (w > (int)(sizeof(buf) - 1)) w = (int)(sizeof(buf) - 1);
- write(STDOUT_FILENO, buf, (size_t)w);
- }
- return n;
+ return vfprintf(stdout, fmt, ap);
}
int printf(const char* fmt, ...) {
return (void*)_syscall1(SYS_BRK, (int)addr);
}
+int isatty(int fd) {
+ /* POSIX: isatty() returns 1 if fd refers to a terminal, 0 otherwise.
+ * Implementation: try TCGETS ioctl; if it succeeds, fd is a tty. */
+ struct { uint32_t a,b,c,d; uint8_t e[8]; } t;
+ int rc = _syscall3(SYS_IOCTL, fd, 0x5401 /* TCGETS */, (int)&t);
+ return (rc == 0) ? 1 : 0;
+}
+
void _exit(int status) {
_syscall1(SYS_EXIT, status);
/* If exit syscall somehow returns, loop forever.