diff --git a/iobridge.c b/iobridge.c new file mode 100644 index 0000000..1eece9d --- /dev/null +++ b/iobridge.c @@ -0,0 +1,560 @@ + +#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 + diff --git a/iobridge_test.c b/iobridge_test.c new file mode 100644 index 0000000..5d963a0 --- /dev/null +++ b/iobridge_test.c @@ -0,0 +1,41 @@ + +#include +#include +#include + +uint32_t read_be32(void) { + uint32_t x = getchar(); + x = (x << 8) | getchar(); + x = (x << 8) | getchar(); + x = (x << 8) | getchar(); + return x; +} + +int main(void) { + // Test 0x02: + putchar(0x02); putchar(0); putchar(0); putchar(0); putchar(46); + printf("Hello World! Type the following string: 12345\n"); + // Test 0x01: + putchar(0x01); if(getchar() != 0x1) exit(1); + uint32_t x = read_be32(); + if(x != 0x0000005) exit(1); + char buf[5]; fread(buf, 1, 5, stdin); + if(buf[0] != '1' || buf[1] != '2' || buf[2] != '3' || buf[3] != '4' || buf[4] != '5') exit(1); + // Test 0x03: + putchar(0x03); putchar(0); putchar(0); putchar(0); putchar(7); + printf("stderr\n"); + // Test 0x04, 0x05, 0x06: + putchar(0x04); if (getchar() != 0x4) exit(1); + int c = getchar(); + putchar(0x05); putchar(c); putchar(0x06); putchar(c); + // Test 0x07: + putchar(0x07); putchar(0); putchar(0); putchar(0); putchar(10); if (getchar() != 0x7) exit(1); + x = read_be32(); + if(x != 0x000000a) exit(1); + // Parrot it: + char b2[10]; fread(b2, 1, 10, stdin); + putchar(0x02); putchar(0); putchar(0); putchar(0); putchar(10); + fwrite(b2, 1, 10, stdout); + // Test 0x08: +} + diff --git a/iobridge_unix.h b/iobridge_unix.h new file mode 100644 index 0000000..738dc16 --- /dev/null +++ b/iobridge_unix.h @@ -0,0 +1,418 @@ + +#ifdef NEEDS_TYPES + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #define FILE_HANDLE_TYPE FILE * + char host[] = "UNIX"; +#else + +struct files_t { + FILE *in; + FILE *out; +}; + +struct files_t *popen2(char * program, char * args[]); +int pclose2(struct files_t *fp); + +#define CLEANUP_PIPE(pipe) close((pipe)[0]); close((pipe)[1]) + +typedef struct files_t files_t; + +struct files_chain_t { + files_t files; + pid_t pid; + struct files_chain_t *next; +}; +typedef struct files_chain_t files_chain_t; + +static files_chain_t *files_chain; +static files_t * root_handle; + +static int _do_popen2(files_chain_t *link, char * program, char * args[]) { + int child_in[2]; + int child_out[2]; + if (0 != pipe(child_in)) { + return -1; + } + if (0 != pipe(child_out)) { + CLEANUP_PIPE(child_in); + return -1; + } + + pid_t cpid = link->pid = fork(); + if (0 > cpid) { + CLEANUP_PIPE(child_in); + CLEANUP_PIPE(child_out); + return -1; + } + if (0 == cpid) { + if (0 > dup2(child_in[0], 0) || 0 > dup2(child_out[1], 1)) { + exit(1); + } + CLEANUP_PIPE(child_in); + CLEANUP_PIPE(child_out); + + for (files_chain_t *p = files_chain; p; p = p->next) { + int fd_in = fileno(p->files.in); + if (fd_in != 0) { + close(fd_in); + } + int fd_out = fileno(p->files.out); + if (fd_out != 1) { + close(fd_out); + } + } + + execv(program, args); + exit(1); + } + + close(child_in[0]); + close(child_out[1]); + link->files.in = fdopen(child_in[1], "w"); + link->files.out = fdopen(child_out[0], "r"); + return 0; +} + +files_t *popen2(char * program, char * args[]) { + files_chain_t *link = (files_chain_t *) malloc(sizeof (files_chain_t)); + if (NULL == link) { + return NULL; + } + + if (0 > _do_popen2(link, program, args)) { + free(link); + return NULL; + } + + link->next = files_chain; + files_chain = link; + return (files_t *) link; +} + +int pclose2(files_t *fp) { + files_chain_t **p = &files_chain; + int found = 0; + while (*p) { + if (*p == (files_chain_t *) fp) { + *p = (*p)->next; + found = 1; + break; + } + p = &(*p)->next; + } + + if (!found) { + return -1; + } + if (0 > fclose(fp->in) || 0 > fclose(fp->out)) { + free((files_chain_t *) fp); + return -1; + } + + int status = -1; + pid_t wait_pid; + do { + wait_pid = waitpid(((files_chain_t *) fp)->pid, &status, 0); + } while (-1 == wait_pid && EINTR == errno); + + free((files_chain_t *) fp); + + if (wait_pid == -1) { + return -1; + } + return status; +} + +void write_be32(uint32_t value) { + fputc(value >> 24, root_handle->in); + fputc(value >> 16, root_handle->in); + fputc(value >> 8, root_handle->in); + fputc(value, root_handle->in); +} + +uint32_t read_be32() { + uint32_t value = fgetc(root_handle->out) << 24; + value |= fgetc(root_handle->out) << 16; + value |= fgetc(root_handle->out) << 8; + value |= fgetc(root_handle->out); + return value; +} + +int main(int argc, char * argv[]) { + if (argc < 2) { + fprintf(stderr, "Usage: %s [args...]\n", argv[0]); + exit(1); + } + + if (!(root_handle = popen2(argv[1], argv + 2))) { + fprintf(stderr, "Failed to run %s.\n", argv[1]); + exit(1); + } + + while(!feof(root_handle->out)) { + int packet_code = fgetc(root_handle->out); + switch(packet_code) { + case 0x01: packet_0x01(); break; + case 0x02: packet_0x02(); break; + case 0x03: packet_0x03(); break; + case 0x04: packet_0x04(); break; + case 0x05: packet_0x05(); break; + case 0x06: packet_0x06(); break; + case 0x07: packet_0x07(); break; + case 0x08: packet_0x08(); break; + case 0x09: packet_0x09(); break; + case 0x0A: packet_0x0A(); break; + case 0x0B: packet_0x0B(); break; + case 0x0C: packet_0x0C(); break; + case 0x0D: packet_0x0D(); break; + case 0x0E: packet_0x0E(); break; + case 0x0F: packet_0x0F(); break; + case 0x10: packet_0x10(); break; + case 0x11: packet_0x11(); break; + case 0x12: packet_0x12(); break; + case 0x13: packet_0x13(); break; + case 0x14: packet_0x14(); break; + case 0x15: packet_0x15(); break; + case 0x16: packet_0x16(); break; + case 0x17: packet_0x17(); break; + case 0x18: packet_0x18(); break; + case 0x19: packet_0x19(); break; + case 0x1A: packet_0x1A(); break; + case 0x1B: packet_0x1B(); break; + case 0x1C: packet_0x1C(); break; + case 0x1D: packet_0x1D(); break; + case 0x1E: packet_0x1E(); break; + case 0x1F: packet_0x1F(); break; + case 0x20: packet_0x20(); break; + case 0x21: packet_0x21(); break; + case 0x22: packet_0x22(); break; + case 0x23: packet_0x23(); break; + case 0x24: packet_0x24(); break; + case 0x25: packet_0x25(); break; + case 0x26: packet_0x26(); break; + case 0x27: packet_0x27(); break; + default: fprintf(stderr, "[IOBridge] Unknown packet code: 0x%02X\n", packet_code); exit(1); + } + } + + pclose2(root_handle); +} + +void child_send(int c) { + fputc(c, root_handle->in); +} + +int child_recv() { + int c = fgetc(root_handle->out); + return c == EOF ? -1 : c; +} + +void child_read(char ** buffer, uint32_t * size) { + *buffer = (char *) malloc(*size); + if (!*buffer) { + fprintf(stderr, "Failed to allocate %u bytes.\n", *size); + exit(1); + } + *size = fread(buffer, 1, *size, root_handle->out); +} + +void stdin_read(char ** buffer, uint32_t * size) { + *buffer = (char *) malloc(*size); + if (!*buffer) { + fprintf(stderr, "Failed to allocate %u bytes.\n", *size); + exit(1); + } + *size = fread(buffer, 1, *size, stdin); +} + +void child_write(uint8_t * buffer, uint32_t size) { + fwrite(buffer, 1, size, root_handle->in); +} + +ssize_t read_line(char ** buffer, size_t * size) { + return getline(buffer, size, root_handle->out) != -1; +} + +void write_output(uint8_t * buffer, size_t size) { + fwrite(buffer, 1, size, stdout); +} + +void write_error(uint8_t * buffer, size_t size) { + fwrite(buffer, 1, size, stderr); +} + +int get_key() { + int x = fgetc(stdin); + if (x == EOF) return -1; + else return x; +} + +void * io_malloc(size_t size) { + return malloc(size); +} +void io_free(void * ptr) { + free(ptr); +} +void * io_realloc(void * ptr, size_t size) { + return realloc(ptr, size); +} +FILE *io_open(char *f, char *m) { + return fopen(f, m); +} +void io_close(FILE *f) { + fclose(f); +} +void io_read_file(FILE * f, uint8_t ** buf, uint32_t * len) { + *buf = (uint8_t *) malloc(*len); + *len = fread(*buf, 1, *len, f); +} +void io_write_file(FILE * f, uint8_t * buf, uint32_t len) { + fwrite(buf, 1, len, f); +} +uint32_t io_get_file_size(FILE * f) { + long pos = ftell(f); + fseek(f, 0, SEEK_END); + uint32_t size = ftell(f); + fseek(f, pos, SEEK_SET); + return size; +} +void io_seek_file(FILE_HANDLE_TYPE f, uint32_t off, uint8_t whence) { + switch (whence) { + case 0: fseek(f, off, SEEK_SET); break; + case 1: fseek(f, off, SEEK_CUR); break; + case 2: fseek(f, off, SEEK_END); break; + } +} +void io_rename(char * src, char * dst) { + rename(src, dst); +} +void io_delete(char * f) { + unlink(f); +} +void io_mkdir(char * src) { + mkdir(src, 0777); +} +void io_rmdir(char * src) { + rmdir(src); +} +void io_chdir(char * src) { + chdir(src); +} +void io_getcwd(char ** buffer) { + uint32_t size = PATH_MAX; + *buffer = (char *) malloc(size); + if (!getcwd(*buffer, size)) { + free(*buffer); + *buffer = NULL; + } +} + +unsigned long io_time() { + return time(NULL); +} +void io_localtime(uint32_t * year, uint8_t * month, uint8_t * day, uint8_t * hour, uint8_t * minute, uint8_t * second) { + struct tm * timeinfo; + time_t rawtime; + time(&rawtime); + timeinfo = localtime(&rawtime); + *year = timeinfo->tm_year + 1900; + *month = timeinfo->tm_mon + 1; + *day = timeinfo->tm_mday; + *hour = timeinfo->tm_hour; + *minute = timeinfo->tm_min; + *second = timeinfo->tm_sec; +} +void io_gmtime(uint32_t * year, uint8_t * month, uint8_t * day, uint8_t * hour, uint8_t * minute, uint8_t * second) { + struct tm * timeinfo; + time_t rawtime; + time(&rawtime); + timeinfo = gmtime(&rawtime); + *year = timeinfo->tm_year + 1900; + *month = timeinfo->tm_mon + 1; + *day = timeinfo->tm_mday; + *hour = timeinfo->tm_hour; + *minute = timeinfo->tm_min; + *second = timeinfo->tm_sec; +} +void io_sleep(uint32_t time) { + usleep(time * 1000); +} + +void term_cooked() { + struct termios cooked; + tcgetattr(STDIN_FILENO, &cooked); + cooked.c_lflag |= (ICANON | ECHO); + tcsetattr(STDIN_FILENO, TCSANOW, &cooked); +} +void term_raw() { + struct termios raw; + tcgetattr(STDIN_FILENO, &raw); + raw.c_lflag &= ~(ICANON | ECHO); + tcsetattr(STDIN_FILENO, TCSANOW, &raw); +} +void term_bounds(uint32_t * width, uint32_t * height) { + struct winsize w; + ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); + *width = w.ws_col; + *height = w.ws_row; +} + +void term_flush() { fflush(stdout); } +void io_flush(FILE_HANDLE_TYPE f) { fflush(f); } +uint8_t get_errno() { return errno; } + +uint8_t file_exists(char * path) { + struct stat st; + return stat(path, &st) == 0; +} + +void io_handle_packet_0x25() { + uint32_t len = read_be32(); + uint8_t * data; child_read((char **) &data, &len); + if(data[len - 1] == 0) data[len - 1] = 0x00; + DIR * dir = opendir((char *) data); + if(dir == NULL) { + child_send(0x25); + write_be32(0); + io_free(data); + return; + } + + struct dirent * ent; + vector(uint8_t) buffer = NULL; + while((ent = readdir(dir)) != NULL) { + uint32_t len = strlen(ent->d_name) + 1; + vector_push_back(buffer, (len >> 8) & 0xff); + vector_push_back(buffer, len & 0xff); + for (int i = 0; i < len; i++) { + vector_push_back(buffer, ent->d_name[i]); + } + vector_push_back(buffer, ent->d_type); + } + closedir(dir); + + child_send(0x25); + write_be32(vector_size(buffer)); + child_write(buffer, vector_size(buffer)); + vector_free(data); +} + +#endif \ No newline at end of file diff --git a/v0.1.md b/v0.1.md new file mode 100644 index 0000000..6f13567 --- /dev/null +++ b/v0.1.md @@ -0,0 +1,80 @@ +Numbers: Big Endian. +Input strings not null terminated unless explicitly stated. + + +Outgoing API: + 0x00 - reserved + 0x01 - read line from stdin; returns a 0x01 packet. + 0x02 [len: u32, data: u8[]] - write to stdout + 0x03 [len: u32, data: u8[]] - write to stderr + 0x04 - read a byte from stdin; returns a 0x04 packet. + 0x05 [byte: u8] - write a byte to stdout + 0x06 [byte: u8] - write a byte to stderr + 0x07 [len: u32] - read from stdin. returns a 0x07 packet. + + 0x08 [len: u32] - allocate a buffer of len bytes. returns a 0x08 packet. + 0x09 [ptr: u32] - free a buffer allocated at ptr. + 0x0A [ptr: u32, off: u32, len: u32] - read len bytes from ptr + off. returns a 0x0A packet. + 0x0B [ptr: u32, off: u32, len: u32, data: u8[]] - write len bytes to ptr + off. + 0x0C [ptr: u32] - query the size of the buffer allocated at ptr. returns a 0x0C packet. + 0x0D [ptr: u32, new_len: u32] - resize the buffer allocated at ptr to new_len bytes. + + Expect null-terminated data: + * 0x0E [len: u32, mode: u32, data: u8[]] - open a file (fopen). returns a 0x0E packet. + + 0x0F [ptr: u32] - close a file (fclose). + 0x10 [ptr: u32, len: u32] - read len bytes from file. returns a 0x10 packet. + 0x11 [ptr: u32, len: u32] - write len bytes to file. + 0x12 [ptr: u32] - query the size of the file. returns a 0x12 packet. + 0x13 [ptr: u32, off: u32, whence: u8]: seek to off bytes from whence + (0-backwards,1-forwards,2-from beginning,3-from end). + + Expect null-terminated data: + * 0x14 [len: u32, len2: u32, data1: u8[], data2: u8[]] - rename. + * 0x15 [len: u32, data: u8[]] - unlink. + * 0x16 [len: u32, data: u8[]] - mkdir. + * 0x17 [len: u32, data: u8[]] - rmdir. + * 0x18 [len: u32, data: u8[]] - chdir. + + 0x19 - getcwd, returns a 0x19 packet. + + 0x1A - UNIX time, returns a 0x1A packet. + 0x1B - local time, returns a 0x1B packet. + 0x1C - GMT, returns a 0x1C packet. + + 0x1D [time: u32] - sleep for n ms. + 0x1E - put terminal in raw mode (if platform supports that). + 0x1F - put terminal in cooked mode (if platform supports that). + 0x20 - get terminal size, returns a 0x20 packet. + + 0x21 [ptr: u32] - flush file. + 0x22 - flush stdout. + + 0x23 - get errno state. returns a 0x23 packet. + + Expect null-terminated data: + * 0x24 [len: u32, data: u8[]] - file exists? returns a 0x24 packet. + * 0x25 [len: u32, data: u8[]] - list directory entries. returns a 0x25 packet. + + 0x26 - API version. returns a 0x26 packet. + 0x27 - Host environment. returns a 0x27 packet. +Incoming API: + 0x01 [len: u32] [data: u8[]] - line read from stdin. + 0x04 [byte: u16] - byte read from stdin. + 0x07 [len: u32] [data: u8[]] - data read from stdin. + 0x08 [ptr: u32] - buffer allocated at ptr (ptr = 0 if unable to allocate). + 0x0A [len: u32] [data: u8[]] - data read from ptr + off. + 0x0C [len: u32] - size of the buffer allocated at ptr. + 0x0E [ptr: u32] - file opened at ptr (ptr = 0 if unable to open). + 0x10 [len: u32] [data: u8[]] - data read from file. + 0x12 [len: u32] - size of the file. + 0x19 [len: u32] [data: u8[]] - current working directory (len = 0 if unable to determine). + 0x1A [time: u64] - UNIX time. + 0x1B [year: u32, month: u8, day: u8, hour: u8, minute: u8, second: u8] - local time. + 0x1C [year: u32, month: u8, day: u8, hour: u8, minute: u8, second: u8] - GMT. + 0x20 [width: u16, height: u16] - terminal size (or 0 if unknown). + 0x23 [error: u8] - error state. + 0x24 [exists: u8] - exists? + 0x25 [no: u32, (len: u32, path: u8[], size: u32, type: u8)[]] - list directory entries. + 0x26 [major: u8, minor: u8] - API version (0, 1). + 0x27 [len: u32, data: u8[]] - Host environment. \ No newline at end of file