diff --git a/etc/console-unix.c b/etc/console-unix.c new file mode 100644 index 0000000..95e8771 --- /dev/null +++ b/etc/console-unix.c @@ -0,0 +1,226 @@ +#undef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 200112L + +#include +#include +#include +#include +#include +#include + +#ifdef __linux +#include +#endif + +#ifdef __NetBSD__ +#include +#include +#endif + +#include "../uxn.h" +#include "console.h" + +/* +Copyright (c) 2022-2023 Devine Lu Linvega, Andrew Alderwick + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE. +*/ + +/* process */ +static char *fork_args[32]; +static int child_mode; +static int pty_fd; +static int to_child_fd[2]; +static int from_child_fd[2]; +static int saved_in; +static int saved_out; +static pid_t child_pid; +struct winsize ws = {24, 80, 8, 12}; + +static void +parse_args(Uint8 *d) +{ + Uint8 *port_addr = d + 0x3; + int addr = PEEK2(port_addr); + char *pos = (char *)&uxn.ram[addr]; + int i = 0; + do { + fork_args[i++] = pos; + while(*pos != 0) pos++; + pos++; + } while(*pos != '\0'); + fork_args[i] = NULL; +} + +/* call after we're sure the process has exited */ +static void +clean_after_child(void) +{ + child_pid = 0; + if(child_mode >= 0x80) { + close(pty_fd); + dup2(saved_in, 0); + dup2(saved_out, 1); + } else { + if(child_mode & 0x01) { + close(to_child_fd[1]); + dup2(saved_out, 1); + } + if(child_mode & 0x06) { + close(from_child_fd[0]); + dup2(saved_in, 0); + } + } + child_mode = 0; + saved_in = -1; + saved_out = -1; +} + +int +console_input(Uint8 c, int type) +{ + uxn.dev[0x12] = c; + uxn.dev[0x17] = type; + return uxn_eval(PEEK2(&uxn.dev[0x10])); +} + +void +console_listen(int i, int argc, char **argv) +{ + for(; i < argc; i++) { + char *p = argv[i]; + while(*p) console_input(*p++, CONSOLE_ARG); + console_input('\n', i == argc - 1 ? CONSOLE_END : CONSOLE_EOA); + } +} + +static void +start_fork_pty(Uint8 *d) +{ + int fd = -1; + pid_t pid = forkpty(&fd, NULL, NULL, NULL); + if(pid < 0) { /* failure */ + d[0x6] = 0xff; + fprintf(stderr, "fork failure\n"); + } else if(pid == 0) { /* child */ + setenv("TERM", "ansi", 1); + execvp(fork_args[0], fork_args); + d[0x6] = 0xff; + fprintf(stderr, "exec failure\n"); + } else { /*parent*/ + child_pid = pid; + pty_fd = fd; + ioctl(fd, TIOCSWINSZ, &ws); + saved_in = dup(0); + saved_out = dup(1); + dup2(fd, 0); + dup2(fd, 1); + } +} + +static void +start_fork_pipe(Uint8 *d) +{ + pid_t pid; + if(child_mode & 0x01) { + /* parent writes to child's stdin */ + if(pipe(to_child_fd) == -1) { + d[0x6] = 0xff; + fprintf(stderr, "pipe error: to child\n"); + return; + } + } + if(child_mode & 0x06) { + /* parent reads from child's stdout and/or stderr */ + if(pipe(from_child_fd) == -1) { + d[0x6] = 0xff; + fprintf(stderr, "pipe error: from child\n"); + return; + } + } + pid = fork(); + if(pid < 0) { /* failure */ + d[0x6] = 0xff; + fprintf(stderr, "fork failure\n"); + } else if(pid == 0) { /* child */ + if(child_mode & 0x01) { + dup2(to_child_fd[0], 0); + close(to_child_fd[1]); + } + if(child_mode & 0x06) { + if(child_mode & 0x02) dup2(from_child_fd[1], 1); + if(child_mode & 0x04) dup2(from_child_fd[1], 2); + close(from_child_fd[0]); + } + execvp(fork_args[0], fork_args); + d[0x6] = 0xff; + fprintf(stderr, "exec failure\n"); + } else { /*parent*/ + child_pid = pid; + if(child_mode & 0x01) { + saved_out = dup(1); + dup2(to_child_fd[1], 1); + close(to_child_fd[0]); + } + if(child_mode & 0x06) { + saved_in = dup(0); + dup2(from_child_fd[0], 0); + close(from_child_fd[1]); + } + } +} + +static void +kill_child(Uint8 *d, int options) +{ + int wstatus; + if(child_pid) { + kill(child_pid, 9); + if(waitpid(child_pid, &wstatus, options)) { + d[0x6] = 1; + d[0x7] = WEXITSTATUS(wstatus); + clean_after_child(); + } + } +} + +static void +start_fork(Uint8 *d) +{ + fflush(stderr); + kill_child(d, 0); + child_mode = d[0x5]; + parse_args(d); + if(child_mode >= 0x80) + start_fork_pty(d); + else + start_fork_pipe(d); +} + +Uint8 +console_dei(Uint8 addr) +{ + Uint8 port = addr & 0x0f, *d = &uxn.dev[addr & 0xf0]; + switch(port) { + case 0x6: + case 0x7: kill_child(d, WNOHANG); + } + return d[port]; +} + +void +console_deo(Uint8 addr) +{ + FILE *fd; + switch(addr) { + case 0x15: /* Console/dead */ start_fork(&uxn.dev[0x10]); break; + case 0x16: /* Console/exit*/ kill_child(&uxn.dev[0x10], 0); break; + case 0x18: fd = stdout, fputc(uxn.dev[0x18], fd), fflush(fd); break; + case 0x19: fd = stderr, fputc(uxn.dev[0x19], fd), fflush(fd); break; + } +} diff --git a/etc/console-unix.h b/etc/console-unix.h new file mode 100644 index 0000000..be8abc2 --- /dev/null +++ b/etc/console-unix.h @@ -0,0 +1,20 @@ +/* +Copyright (c) 2021 Devine Lu Linvega, Andrew Alderwick + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE. +*/ + +#define CONSOLE_STD 0x1 +#define CONSOLE_ARG 0x2 +#define CONSOLE_EOA 0x3 +#define CONSOLE_END 0x4 + +int console_input(Uint8 c, int type); +void console_listen(int i, int argc, char **argv); +Uint8 console_dei(Uint8 addr); +void console_deo(Uint8 addr); diff --git a/src/devices/console.c b/src/devices/console.c index 95e8771..000f23b 100644 --- a/src/devices/console.c +++ b/src/devices/console.c @@ -1,27 +1,11 @@ -#undef _POSIX_C_SOURCE -#define _POSIX_C_SOURCE 200112L - -#include #include #include -#include -#include -#include - -#ifdef __linux -#include -#endif - -#ifdef __NetBSD__ -#include -#include -#endif #include "../uxn.h" #include "console.h" /* -Copyright (c) 2022-2023 Devine Lu Linvega, Andrew Alderwick +Copyright (c) 2022-2024 Devine Lu Linvega, Andrew Alderwick Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -31,56 +15,6 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE. */ -/* process */ -static char *fork_args[32]; -static int child_mode; -static int pty_fd; -static int to_child_fd[2]; -static int from_child_fd[2]; -static int saved_in; -static int saved_out; -static pid_t child_pid; -struct winsize ws = {24, 80, 8, 12}; - -static void -parse_args(Uint8 *d) -{ - Uint8 *port_addr = d + 0x3; - int addr = PEEK2(port_addr); - char *pos = (char *)&uxn.ram[addr]; - int i = 0; - do { - fork_args[i++] = pos; - while(*pos != 0) pos++; - pos++; - } while(*pos != '\0'); - fork_args[i] = NULL; -} - -/* call after we're sure the process has exited */ -static void -clean_after_child(void) -{ - child_pid = 0; - if(child_mode >= 0x80) { - close(pty_fd); - dup2(saved_in, 0); - dup2(saved_out, 1); - } else { - if(child_mode & 0x01) { - close(to_child_fd[1]); - dup2(saved_out, 1); - } - if(child_mode & 0x06) { - close(from_child_fd[0]); - dup2(saved_in, 0); - } - } - child_mode = 0; - saved_in = -1; - saved_out = -1; -} - int console_input(Uint8 c, int type) { @@ -99,127 +33,11 @@ console_listen(int i, int argc, char **argv) } } -static void -start_fork_pty(Uint8 *d) -{ - int fd = -1; - pid_t pid = forkpty(&fd, NULL, NULL, NULL); - if(pid < 0) { /* failure */ - d[0x6] = 0xff; - fprintf(stderr, "fork failure\n"); - } else if(pid == 0) { /* child */ - setenv("TERM", "ansi", 1); - execvp(fork_args[0], fork_args); - d[0x6] = 0xff; - fprintf(stderr, "exec failure\n"); - } else { /*parent*/ - child_pid = pid; - pty_fd = fd; - ioctl(fd, TIOCSWINSZ, &ws); - saved_in = dup(0); - saved_out = dup(1); - dup2(fd, 0); - dup2(fd, 1); - } -} - -static void -start_fork_pipe(Uint8 *d) -{ - pid_t pid; - if(child_mode & 0x01) { - /* parent writes to child's stdin */ - if(pipe(to_child_fd) == -1) { - d[0x6] = 0xff; - fprintf(stderr, "pipe error: to child\n"); - return; - } - } - if(child_mode & 0x06) { - /* parent reads from child's stdout and/or stderr */ - if(pipe(from_child_fd) == -1) { - d[0x6] = 0xff; - fprintf(stderr, "pipe error: from child\n"); - return; - } - } - pid = fork(); - if(pid < 0) { /* failure */ - d[0x6] = 0xff; - fprintf(stderr, "fork failure\n"); - } else if(pid == 0) { /* child */ - if(child_mode & 0x01) { - dup2(to_child_fd[0], 0); - close(to_child_fd[1]); - } - if(child_mode & 0x06) { - if(child_mode & 0x02) dup2(from_child_fd[1], 1); - if(child_mode & 0x04) dup2(from_child_fd[1], 2); - close(from_child_fd[0]); - } - execvp(fork_args[0], fork_args); - d[0x6] = 0xff; - fprintf(stderr, "exec failure\n"); - } else { /*parent*/ - child_pid = pid; - if(child_mode & 0x01) { - saved_out = dup(1); - dup2(to_child_fd[1], 1); - close(to_child_fd[0]); - } - if(child_mode & 0x06) { - saved_in = dup(0); - dup2(from_child_fd[0], 0); - close(from_child_fd[1]); - } - } -} - -static void -kill_child(Uint8 *d, int options) -{ - int wstatus; - if(child_pid) { - kill(child_pid, 9); - if(waitpid(child_pid, &wstatus, options)) { - d[0x6] = 1; - d[0x7] = WEXITSTATUS(wstatus); - clean_after_child(); - } - } -} - -static void -start_fork(Uint8 *d) -{ - fflush(stderr); - kill_child(d, 0); - child_mode = d[0x5]; - parse_args(d); - if(child_mode >= 0x80) - start_fork_pty(d); - else - start_fork_pipe(d); -} - -Uint8 -console_dei(Uint8 addr) -{ - Uint8 port = addr & 0x0f, *d = &uxn.dev[addr & 0xf0]; - switch(port) { - case 0x6: - case 0x7: kill_child(d, WNOHANG); - } - return d[port]; -} - void console_deo(Uint8 addr) { FILE *fd; switch(addr) { - case 0x15: /* Console/dead */ start_fork(&uxn.dev[0x10]); break; - case 0x16: /* Console/exit*/ kill_child(&uxn.dev[0x10], 0); break; case 0x18: fd = stdout, fputc(uxn.dev[0x18], fd), fflush(fd); break; case 0x19: fd = stderr, fputc(uxn.dev[0x19], fd), fflush(fd); break; } diff --git a/src/uxn11.c b/src/uxn11.c index 781869d..db4da0e 100644 --- a/src/uxn11.c +++ b/src/uxn11.c @@ -49,7 +49,6 @@ emu_dei(Uint8 addr) { switch(addr & 0xf0) { case 0x00: return system_dei(addr); - case 0x10: return console_dei(addr); case 0x20: return screen_dei(addr); case 0xc0: return datetime_dei(addr); } diff --git a/src/uxncli.c b/src/uxncli.c index 9ca2e06..c1a337d 100644 --- a/src/uxncli.c +++ b/src/uxncli.c @@ -25,7 +25,6 @@ emu_dei(Uint8 addr) { switch(addr & 0xf0) { case 0x00: return system_dei(addr); - case 0x10: return console_dei(addr); case 0xc0: return datetime_dei(addr); } return uxn.dev[addr];