561 lines
15 KiB
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
|
||
|
|