iobridge/iobridge.c

561 lines
15 KiB
C

#define NEEDS_TYPES
#ifdef IOBRIDGE_UNIX
#include "iobridge_unix.h"
#endif
#undef NEEDS_TYPES
void write_be32(uint32_t value);
uint32_t read_be32();
void startup(char * program, char * args[]);
void child_send(int c);
int child_recv();
ssize_t read_line(char ** buffer, size_t * size);
void child_read(char ** buffer, uint32_t * size);
void child_write(uint8_t * buffer, uint32_t size);
void write_output(uint8_t * buffer, size_t size);
void write_error(uint8_t * buffer, size_t size);
int get_key();
void stdin_read(char ** buffer, uint32_t * size);
void * io_malloc(size_t size);
void io_free(void * ptr);
void * io_realloc(void * ptr, size_t size);
FILE_HANDLE_TYPE io_open(char *f, char *m);
void io_close(FILE_HANDLE_TYPE f);
void io_read_file(FILE_HANDLE_TYPE f, uint8_t ** buf, uint32_t * len);
void io_write_file(FILE_HANDLE_TYPE f, uint8_t * buf, uint32_t len);
uint32_t io_get_file_size(FILE_HANDLE_TYPE f);
enum { IO_SEEK_SET, IO_SEEK_CUR, IO_SEEK_END };
void io_seek_file(FILE_HANDLE_TYPE f, uint32_t off, uint8_t whence);
void io_rename(char * src, char * dst);
void io_delete(char * src);
void io_mkdir(char * src);
void io_rmdir(char * src);
void io_chdir(char * src);
void io_getcwd(char ** buffer);
unsigned long io_time();
void io_localtime(uint32_t * year, uint8_t * month, uint8_t * day, uint8_t * hour, uint8_t * minute, uint8_t * second);
void io_gmtime(uint32_t * year, uint8_t * month, uint8_t * day, uint8_t * hour, uint8_t * minute, uint8_t * second);
void io_sleep(uint32_t time);
void term_cooked();
void term_raw();
void term_flush();
void io_flush(FILE_HANDLE_TYPE f);
uint8_t get_errno();
uint8_t file_exists(char * path);
void term_bounds(uint32_t * width, uint32_t * height);
void io_handle_packet_0x25();
#define vector(type) type *
#define _internal_veccap(vec, size) \
do { \
if (vec) { \
((size_t *)(vec))[-1] = (size); \
} \
} while (0)
#define _internal_vecsiz(vec, size) \
do { \
if (vec) { \
((size_t *)(vec))[-2] = (size); \
} \
} while (0)
#define vector_capacity(vec) \
((vec) ? ((size_t *)(vec))[-1] : (size_t)0)
#define vector_size(vec) \
((vec) ? ((size_t *)(vec))[-2] : (size_t)0)
#define vector_empty(vec) \
(vector_size(vec) == 0)
#define vector_grow(vec, count) \
do { \
const size_t cv_sz = (count) * sizeof(*(vec)) + (sizeof(size_t) * 2); \
if (!(vec)) { \
size_t *cv_p = io_malloc(cv_sz); \
assert(cv_p); \
(vec) = (void *)(&cv_p[2]); \
_internal_veccap((vec), (count)); \
_internal_vecsiz((vec), 0); \
} else { \
size_t *cv_p1 = &((size_t *)(vec))[-2]; \
size_t *cv_p2 = io_realloc(cv_p1, (cv_sz)); \
assert(cv_p2); \
(vec) = (void *)(&cv_p2[2]); \
_internal_veccap((vec), (count)); \
} \
} while (0)
#define vector_pop_back(vec) \
do { \
_internal_vecsiz((vec), vector_size(vec) - 1); \
} while (0)
#define vector_erase(vec, i) \
do { \
if (vec) { \
const size_t cv_sz = vector_size(vec); \
if ((i) < cv_sz) { \
_internal_vecsiz((vec), cv_sz - 1); \
size_t cv_x; \
for (cv_x = (i); cv_x < (cv_sz - 1); ++cv_x) { \
(vec)[cv_x] = (vec)[cv_x + 1]; \
} \
} \
} \
} while (0)
#define vector_free(vec) \
do { \
if (vec) { \
size_t *p1 = &((size_t *)(vec))[-2]; \
io_free(p1); \
} \
} while (0)
#define vector_begin(vec) \
(vec)
#define vector_end(vec) \
((vec) ? &((vec)[vector_size(vec)]) : NULL)
#define vector_push_back(vec, value) \
do { \
size_t cv_cap = vector_capacity(vec); \
if (cv_cap <= vector_size(vec)) { \
vector_grow((vec), cv_cap + 16); \
} \
(vec)[vector_size(vec)] = (value); \
_internal_vecsiz((vec), vector_size(vec) + 1); \
} while (0)
vector(FILE_HANDLE_TYPE) files;
vector(uint8_t *) buffers;
vector(uint32_t) buffer_bounds;
// Packet implementations
void packet_0x01() {
// Read a line of stdin of arbitary size.
char * line = NULL; size_t size;
if (!read_line(&line, &size)) {
child_send(0x01);
write_be32(0);
return;
} else {
child_send(0x01);
write_be32(strlen(line) + 1);
child_write(line, strlen(line) + 1);
}
}
void packet_0x02() {
uint32_t len = read_be32();
uint8_t * data;
child_read((char **) &data, &len);
write_output(data, len);
io_free(data);
}
void packet_0x03() {
uint32_t len = read_be32();
uint8_t * data;
child_read((char **) &data, &len);
write_error(data, len);
io_free(data);
}
void packet_0x04() {
int16_t byte = get_key();
if (byte == -1) {
uint8_t b[] = { 0x04, 0xff, 0xff };
child_write(b, 3);
} else {
uint8_t b[] = { 0x04, (uint8_t) byte, (uint8_t) (byte >> 8) };
child_write(b, 3);
}
}
void packet_0x05() {
uint8_t byte = child_recv();
write_output((char *) &byte, 1);
}
void packet_0x06() {
uint8_t byte = child_recv();
write_error((char *) &byte, 1);
}
void packet_0x07() {
uint32_t len = read_be32();
uint8_t * data;
stdin_read((char **) &data, &len);
write_be32(len);
child_write((char *) data, len);
io_free(data);
}
void packet_0x08() {
uint32_t len = read_be32();
uint8_t * data = (uint8_t *) io_malloc(len);
if (data == NULL) {
child_send(0x08);
write_be32(0);
return;
}
// linearly search buffers to see if there's a tombstone anywhere.
for (uint32_t i = 0; i < vector_size(buffers); i++) {
if (buffers[i] == NULL) {
buffers[i] = data;
buffer_bounds[i] = len;
child_send(0x08);
write_be32(i);
return;
}
}
vector_push_back(buffer_bounds, len);
vector_push_back(buffers, data);
child_send(0x08);
write_be32(vector_size(buffers) - 1);
}
void packet_0x09() {
uint32_t ptr = read_be32();
if (ptr > vector_size(buffers))
return;
io_free(buffers[ptr]);
buffers[ptr] = NULL;
buffer_bounds[ptr] = 0;
}
void packet_0x0A() {
uint32_t ptr = read_be32();
uint32_t off = read_be32();
uint32_t len = read_be32();
if (ptr > vector_size(buffers) || off > buffer_bounds[ptr] || buffers[ptr] == NULL) {
child_send(0x0A);
write_be32(0);
return;
}
len = len < buffer_bounds[ptr] - off ? len : buffer_bounds[ptr] - off;
child_send(0x0A);
write_be32(len);
child_write((char *) buffers[ptr] + off, len);
}
void packet_0x0B() {
uint32_t ptr = read_be32();
uint32_t off = read_be32();
uint32_t len = read_be32();
uint8_t * data;
child_read((char **) &data, &len);
if (ptr > vector_size(buffers))
return;
if (off > buffer_bounds[ptr])
return;
if (buffers[ptr] == NULL)
return;
len = len < buffer_bounds[ptr] - off ? len : buffer_bounds[ptr] - off;
memcpy(buffers[ptr] + off, data, len);
io_free(data);
}
void packet_0x0C() {
uint32_t ptr = read_be32();
if (ptr > vector_size(buffers)|| buffers[ptr] == NULL) {
child_send(0x0C);
write_be32(0);
return;
}
child_send(0x0C);
write_be32(buffer_bounds[ptr]);
}
void packet_0x0D() {
uint32_t ptr = read_be32();
uint32_t new_len = read_be32();
if (ptr > vector_size(buffers) || buffers[ptr] == NULL)
return;
uint8_t * new_buffer = (uint8_t *) io_realloc(buffers[ptr], new_len);
buffers[ptr] = new_buffer;
buffer_bounds[ptr] = new_len;
}
void packet_0x0E() {
uint32_t len = read_be32();
uint32_t mode = read_be32();
uint8_t * data;
child_read((char **) &data, &len);
char mode_str[6]; int mode_ptr = 0;
while (mode != 0) {
if ((mode & 0xFF) != 0)
mode_str[mode_ptr++] = mode & 0xFF;
mode >>= 8;
}
mode_str[mode_ptr] = 0;
if(data[len - 1] == 0) data[len - 1] = 0x00;
FILE_HANDLE_TYPE file = io_open((char *) data, mode_str);
if (file == NULL) {
child_send(0x0E);
write_be32(0);
io_free(data);
return;
}
// linearly search files to see if there's a tombstone anywhere.
for (uint32_t i = 0; i < vector_size(buffers); i++) {
if (files[i] == NULL) {
files[i] = file;
child_send(0x0E);
write_be32(i);
io_free(data);
return;
}
}
vector_push_back(files, file);
child_send(0x0E);
write_be32(vector_size(files) - 1);
io_free(data);
}
void packet_0x0F() {
uint32_t ptr = read_be32();
if (ptr > vector_size(files) || files[ptr] == NULL)
return;
io_close(files[ptr]);
files[ptr] = NULL;
}
void packet_0x10() {
uint32_t ptr = read_be32();
uint32_t len = read_be32();
if (ptr > vector_size(files) || files[ptr] == NULL) {
child_send(0x10);
write_be32(0);
return;
}
uint8_t * data;
io_read_file(files[ptr], &data, &len);
child_send(0x10);
write_be32(len);
child_write((char *) data, len);
io_free(data);
}
void packet_0x11() {
uint32_t ptr = read_be32();
uint32_t len = read_be32();
uint8_t * data;
child_read((char **) &data, &len);
if (ptr > vector_size(files) || files[ptr] == NULL)
return;
io_write_file(files[ptr], data, len);
io_free(data);
}
void packet_0x12() {
uint32_t ptr = read_be32();
if (ptr > vector_size(files) || files[ptr] == NULL) {
child_send(0x12);
write_be32(0);
return;
}
child_send(0x12);
write_be32(io_get_file_size(files[ptr]));
}
void packet_0x13() {
uint32_t ptr = read_be32();
uint32_t off = read_be32();
uint8_t whence = child_recv();
if (ptr > vector_size(files) || files[ptr] == NULL)
return;
switch(whence) {
case 0: io_seek_file(files[ptr], (long)off, IO_SEEK_CUR); break;
case 1: io_seek_file(files[ptr], -(long)off, IO_SEEK_CUR); break;
case 2: io_seek_file(files[ptr], off, IO_SEEK_SET); break;
case 3: io_seek_file(files[ptr], -off, IO_SEEK_END); break;
}
}
void packet_0x14() {
uint32_t len1 = read_be32();
uint32_t len2 = read_be32();
uint8_t * data1; child_read((char **) &data1, &len1);
uint8_t * data2; child_read((char **) &data2, &len2);
if(data1[len1 - 1] == 0) data1[len1 - 1] = 0x00;
if(data2[len2 - 1] == 0) data2[len2 - 1] = 0x00;
io_rename((char *) data1, (char *) data2);
io_free(data1);
io_free(data2);
}
void packet_0x15() {
uint32_t len = read_be32();
uint8_t * data; child_read((char **) &data, &len);
if(data[len - 1] == 0) data[len - 1] = 0x00;
io_delete((char *) data);
io_free(data);
}
void packet_0x16() {
uint32_t len = read_be32();
uint8_t * data; child_read((char **) &data, &len);
if(data[len - 1] == 0) data[len - 1] = 0x00;
io_mkdir((char *) data);
io_free(data);
}
void packet_0x17() {
uint32_t len = read_be32();
uint8_t * data; child_read((char **) &data, &len);
if(data[len - 1] == 0) data[len - 1] = 0x00;
io_rmdir((char *) data);
io_free(data);
}
void packet_0x18() {
uint32_t len = read_be32();
uint8_t * data; child_read((char **) &data, &len);
if(data[len - 1] == 0) data[len - 1] = 0x00;
io_chdir((char *) data);
io_free(data);
}
void packet_0x19() {
char * cwd; io_getcwd(&cwd);
if (cwd == NULL) {
child_send(0x19);
write_be32(0);
return;
}
child_send(0x19);
write_be32(strlen(cwd) + 1);
child_write(cwd, strlen(cwd) + 1);
io_free(cwd);
}
void packet_0x1A() {
child_send(0x1A);
unsigned long t = io_time();
write_be32(t >> 32);
write_be32(t & 0xFFFFFFFF);
}
void packet_0x1B() {
child_send(0x1B);
uint32_t year; uint8_t month, day, hour, minute, second;
io_localtime(&year, &month, &day, &hour, &minute, &second);
write_be32(year);
child_send(month);
child_send(day);
child_send(hour);
child_send(minute);
child_send(second);
}
void packet_0x1C() {
child_send(0x1C);
uint32_t year; uint8_t month, day, hour, minute, second;
io_gmtime(&year, &month, &day, &hour, &minute, &second);
write_be32(year);
child_send(month);
child_send(day);
child_send(hour);
child_send(minute);
child_send(second);
}
void packet_0x1D() {
uint32_t time = read_be32();
io_sleep(time);
}
void packet_0x1E() {
term_raw();
}
void packet_0x1F() {
term_cooked();
}
void packet_0x20() {
uint32_t width, height;
term_bounds(&width, &height);
write_be32(width);
write_be32(height);
}
void packet_0x21() {
uint32_t ptr = read_be32();
if (ptr > vector_size(files) || files[ptr] == NULL)
return;
io_flush(files[ptr]);
}
void packet_0x22() {
term_flush();
}
void packet_0x23() {
child_send(0x23);
child_send(get_errno());
}
void packet_0x24() {
uint32_t len = read_be32();
uint8_t * data; child_read((char **) &data, &len);
if(data[len - 1] == 0) data[len - 1] = 0x00;
if(file_exists((char *) data) == 0) {
child_send(0x24);
child_send(1);
} else {
child_send(0x24);
child_send(0);
}
io_free(data);
}
void packet_0x25() {
io_handle_packet_0x25();
}
void packet_0x26() {
child_send(0x26);
child_send(0); // Version major.
child_send(1); // Version minor.
}
void packet_0x27() {
child_send(0x27);
write_be32(strlen(host) + 1);
child_write(host, strlen(host) + 1);
}
#ifdef IOBRIDGE_UNIX
#include "iobridge_unix.h"
#endif