#define _XOPEN_SOURCE 700
#include "io.h"
#include "scheduler.h"
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
extern ssize_t write(int fd, const void *buf, size_t count);
extern ssize_t read(int fd, void *buf, size_t count);
#define STDIN_FILENO 0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
static bool io_initialized = false;
static void ensure_nonblocking_io(void) {
if (io_initialized)
return;
int flags = fcntl(STDOUT_FILENO, F_GETFL, 0);
if (flags == -1 || fcntl(STDOUT_FILENO, F_SETFL, flags | O_NONBLOCK) == -1) {
runtime_error("ensure_nonblocking_io: failed to set stdout non-blocking");
}
flags = fcntl(STDIN_FILENO, F_GETFL, 0);
if (flags == -1 || fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK) == -1) {
runtime_error("ensure_nonblocking_io: failed to set stdin non-blocking");
}
io_initialized = true;
}
StackCell *write_line(StackCell *stack) {
ensure_nonblocking_io();
if (!stack || stack->tag != TAG_STRING) {
runtime_error("write_line: expected string on stack");
}
const char *str = stack->value.s;
StackCell *rest = stack->next;
size_t str_len = strlen(str);
size_t total_len = str_len + 1;
char *buffer = malloc(total_len);
if (!buffer) {
runtime_error("write_line: out of memory");
}
memcpy(buffer, str, str_len);
buffer[str_len] = '\n';
strand_push_cleanup(free, buffer);
free_cell(stack);
size_t written = 0;
while (written < total_len) {
ssize_t n = write(STDOUT_FILENO, buffer + written, total_len - written);
if (n > 0) {
written += n;
} else if (n == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
strand_block_on_write(STDOUT_FILENO);
} else {
strand_pop_cleanup(); free(buffer);
runtime_error("write_line: write() failed");
}
} else {
strand_pop_cleanup(); free(buffer);
runtime_error("write_line: unexpected write() return 0");
}
}
strand_pop_cleanup();
free(buffer);
return rest;
}
StackCell *read_line(StackCell *stack) {
ensure_nonblocking_io();
size_t capacity = 128;
size_t length = 0;
char *buffer = malloc(capacity);
if (!buffer) {
runtime_error("read_line: out of memory");
}
strand_push_cleanup(free, buffer);
bool done = false;
while (!done) {
char c;
ssize_t n = read(STDIN_FILENO, &c, 1);
if (n > 0) {
if (c == '\n') {
done = true;
} else {
if (length >= capacity) {
capacity *= 2;
char *new_buffer = realloc(buffer, capacity);
if (!new_buffer) {
strand_pop_cleanup(); free(buffer);
runtime_error("read_line: out of memory");
}
buffer = new_buffer;
strand_update_cleanup_arg(buffer);
}
buffer[length++] = c;
}
} else if (n == 0) {
done = true;
} else {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
strand_block_on_read(STDIN_FILENO);
} else {
strand_pop_cleanup(); free(buffer);
runtime_error("read_line: read() failed");
}
}
}
if (length >= capacity) {
capacity++;
char *new_buffer = realloc(buffer, capacity);
if (!new_buffer) {
strand_pop_cleanup(); free(buffer);
runtime_error("read_line: out of memory");
}
buffer = new_buffer;
strand_update_cleanup_arg(buffer);
}
buffer[length] = '\0';
StackCell *result = alloc_cell();
result->tag = TAG_STRING;
result->value.s = buffer; result->next = stack;
strand_pop_cleanup();
return result;
}