In pty code, fix file handle leaks, add session stuff and termios configuration
authorNick Downing <nick@ndcode.org>
Thu, 26 May 2022 01:01:32 +0000 (11:01 +1000)
committerNick Downing <nick@ndcode.org>
Thu, 26 May 2022 01:30:01 +0000 (11:30 +1000)
hrcg/Makefile
hrcg/emu_65c02.c
hrcg/stty_sane.c [new file with mode: 0644]
hrcg/stty_sane.h [new file with mode: 0644]

index b3ac99f..644f810 100644 (file)
@@ -18,7 +18,7 @@ frogs1.set \
 frogs2.set \
 lily_pad.pic
 
-emu_65c02: emu_65c02.o vrEmu6502/src/vrEmu6502.o
+emu_65c02: emu_65c02.o stty_sane.o vrEmu6502/src/vrEmu6502.o
        ${CC} ${LDFLAGS} -o $@ $^ -lSDL2
 
 hrcg.obj: \
index 742ef38..ed2aecc 100644 (file)
@@ -6,10 +6,12 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/ioctl.h>
 #include <sys/wait.h>
 #include <termios.h>
 #include <unistd.h>
 #include <SDL2/SDL.h>
+#include "stty_sane.h"
 #include "vrEmu6502/src/vrEmu6502.h"
 
 #define APPLE_WIDTH 560
@@ -392,22 +394,93 @@ int main(int argc, char **argv) {
     }
 
     // Open the slave side ot the PTY
-    int fd_slave;
-    {
-      char *p = ptsname(fd_master);
-      fd_slave = open(p, O_RDWR);
-      if (fd_slave == -1) {
-        perror(p);
-        exit(EXIT_FAILURE);
-      }
+    char *slave_name = ptsname(fd_master);
+    int fd_slave = open(slave_name, O_RDWR);
+    if (fd_slave == -1) {
+      perror(slave_name);
+      exit(EXIT_FAILURE);
     }
 
     signal(SIGCHLD, sigchld_handler);
     child_pid = fork();
     if (child_pid == 0) {
+      close(fd_master);
+
+      // make the current process a new session leader
+      // see http://www.rkoucha.fr/tech_corner/pty_pdip.html
+      if (setsid() == -1) {
+        perror("setsid()");
+        exit(EXIT_FAILURE);
+      }
+
+      // as the child is a session leader, set the controlling terminal to be
+      // the slave side of the pty (mandatory for programs like the shell to
+      // make them manage correctly their outputs)
+      // see http://www.rkoucha.fr/tech_corner/pty_pdip.html
+      if (ioctl(fd_slave, TIOCSCTTY, 1) == -1) {
+        perror("TIOCSCTTY");
+        exit(EXIT_FAILURE);
+      }
+
+      // inform applications like /bin/ls to provide 40-column output
+      struct winsize winsize = {.ws_row = 24, .ws_col = 40};
+      if (ioctl(fd_slave, TIOCSWINSZ, &winsize) == -1) {
+        perror("TIOCSWINSZ");
+        exit(EXIT_FAILURE);
+      }
+
+      // for some reason, the termios configuration MUST be done after fork()
+      struct termios attr;
+      if (tcgetattr(fd_slave, &attr) == -1) {
+        perror("tcgetattr()");
+        exit(EXIT_FAILURE);
+      }
+
+#if 1
+      stty_sane(&attr);
+      attr.c_cc[VERASE] = 8; // ctrl-h
+#elif 1 // see cfmakeraw() in termios man page
+      // with minimal cooked mode (+ICRNL, +OPOST, +ECHO, +ICANON, +ISIG)
+      attr.c_iflag &= ~(
+        IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | IXON
+      );
+      attr.c_iflag |= ICRNL;
+      attr.c_oflag |= OPOST;
+      attr.c_lflag &= ~(ECHONL | IEXTEN);
+      attr.c_lflag |= ECHO | ICANON | ISIG;
+      attr.c_cflag &= ~(CSIZE | PARENB);
+      attr.c_cflag |= CS8;
+#else // see https://github.com/python/cpython/blob/3.10/Lib/tty.py
+      // with minimal cooked mode (+ICRNL, +OPOST, +ECHO, +ICANON, +ISIG)
+      attr.c_iflag &= ~(BRKINT | INPCK | ISTRIP | IXON);
+      attr.c_iflag |= ICRNL;
+      attr.c_oflag |= OPOST;
+      attr.c_cflag &= ~(CSIZE | PARENB);
+      attr.c_cflag |= CS8;
+      attr.c_lflag &= ~IEXTEN;
+      attr.c_lflag |= ECHO | ICANON | ISIG;
+      attr.c_cc[VMIN] = 1;
+      attr.c_cc[VTIME] = 0;
+#endif
+      if (tcsetattr(fd_slave, TCSAFLUSH, &attr) == -1) {
+        perror("tcsetattr()");
+        exit(EXIT_FAILURE);
+      }
+
+      int fd_err = dup(STDERR_FILENO);
+      if (fd_err == -1) {
+        perror("dup()");
+        exit(EXIT_FAILURE);
+      }
+      if (fcntl(fd_err, F_SETFD, FD_CLOEXEC) == -1) {
+        perror("fcntl()");
+        exit(EXIT_FAILURE);
+      }
+
       dup2(fd_slave, STDIN_FILENO);
       dup2(fd_slave, STDOUT_FILENO);
       dup2(fd_slave, STDERR_FILENO);
+      close(fd_slave);
 
       int n = argc - argn;
       char **p = malloc((n + 1) * sizeof(char *));
@@ -415,13 +488,14 @@ int main(int argc, char **argv) {
       p[n] = NULL;
       execve(argv[argn], p, environ);
 
+      dup2(fd_err, STDERR_FILENO);
       perror(argv[argn]);
       exit(EXIT_FAILURE);
     }
+    close(fd_slave);
 
     fd_in = fd_master;
     fd_out = fd_master;
-    close(fd_slave);
   }
 
   // do this before creating the CPU
@@ -435,8 +509,16 @@ int main(int argc, char **argv) {
     }
     atexit(termios_atexit);
 
-    // see https://github.com/python/cpython/blob/3.10/Lib/tty.py
     struct termios attr = termios_attr;
+#if 1 // see cfmakeraw() in termios man page
+    attr.c_iflag &= ~(
+      IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON
+    );
+    attr.c_oflag &= ~OPOST;
+    attr.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
+    attr.c_cflag &= ~(CSIZE | PARENB);
+    attr.c_cflag |= CS8;
+#else // see https://github.com/python/cpython/blob/3.10/Lib/tty.py
     attr.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
     attr.c_oflag &= ~OPOST;
     attr.c_cflag &= ~(CSIZE | PARENB);
@@ -444,6 +526,7 @@ int main(int argc, char **argv) {
     attr.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
     attr.c_cc[VMIN] = 1;
     attr.c_cc[VTIME] = 0;
+#endif
     if (tcsetattr(fd_in, TCSAFLUSH, &attr) == -1) {
       perror("tcsetattr()");
       exit(EXIT_FAILURE);
@@ -526,10 +609,8 @@ int main(int argc, char **argv) {
           if (i < 0x80) {
             //switch (i) {
             //default:
-              if (i >= 'a' && i <= 'z')
-                i -= 0x20;
-              if (i >= 0x40 && i < 0x60 && (e->keysym.mod & KMOD_CTRL))
-                i -= 0x40;
+              if ((i >= 0x40 && i < 0x80) && (e->keysym.mod & KMOD_CTRL))
+                i &= 0x1f;
               key_waiting = i | 0x80;
             //  break;
             //}
diff --git a/hrcg/stty_sane.c b/hrcg/stty_sane.c
new file mode 100644 (file)
index 0000000..00c5175
--- /dev/null
@@ -0,0 +1,243 @@
+// see https://github.com/wertarbyte/coreutils/blob/master/src/stty.c
+// this is a cut-down version which changes the data tables into inline code
+
+#include "stty_sane.h"
+
+#ifndef _POSIX_VDISABLE
+# define _POSIX_VDISABLE 0
+#endif
+
+#define Control(c) ((c) & 0x1f)
+/* Canonical values for control characters. */
+#ifndef CINTR
+# define CINTR Control ('c')
+#endif
+#ifndef CQUIT
+# define CQUIT 28
+#endif
+#ifndef CERASE
+# define CERASE 127
+#endif
+#ifndef CKILL
+# define CKILL Control ('u')
+#endif
+#ifndef CEOF
+# define CEOF Control ('d')
+#endif
+#ifndef CEOL
+# define CEOL _POSIX_VDISABLE
+#endif
+#ifndef CSTART
+# define CSTART Control ('q')
+#endif
+#ifndef CSTOP
+# define CSTOP Control ('s')
+#endif
+#ifndef CSUSP
+# define CSUSP Control ('z')
+#endif
+#if defined VEOL2 && !defined CEOL2
+# define CEOL2 _POSIX_VDISABLE
+#endif
+/* Some platforms have VSWTC, others VSWTCH.  In both cases, this control
+   character is initialized by CSWTCH, if present.  */
+#if defined VSWTC && !defined VSWTCH
+# define VSWTCH VSWTC
+#endif
+/* ISC renamed swtch to susp for termios, but we'll accept either name.  */
+#if defined VSUSP && !defined VSWTCH
+# define VSWTCH VSUSP
+# if defined CSUSP && !defined CSWTCH
+#  define CSWTCH CSUSP
+# endif
+#endif
+#if defined VSWTCH && !defined CSWTCH
+# define CSWTCH _POSIX_VDISABLE
+#endif
+
+/* SunOS 5.3 loses (^Z doesn't work) if `swtch' is the same as `susp'.
+   So the default is to disable `swtch.'  */
+#if defined __sparc__ && defined __svr4__
+# undef CSWTCH
+# define CSWTCH _POSIX_VDISABLE
+#endif
+
+#if defined VWERSE && !defined VWERASE /* AIX-3.2.5 */
+# define VWERASE VWERSE
+#endif
+#if defined VDSUSP && !defined CDSUSP
+# define CDSUSP Control ('y')
+#endif
+#if !defined VREPRINT && defined VRPRNT /* Irix 4.0.5 */
+# define VREPRINT VRPRNT
+#endif
+#if defined VREPRINT && !defined CRPRNT
+# define CRPRNT Control ('r')
+#endif
+#if defined CREPRINT && !defined CRPRNT
+# define CRPRNT Control ('r')
+#endif
+#if defined VWERASE && !defined CWERASE
+# define CWERASE Control ('w')
+#endif
+#if defined VLNEXT && !defined CLNEXT
+# define CLNEXT Control ('v')
+#endif
+#if defined VDISCARD && !defined VFLUSHO
+# define VFLUSHO VDISCARD
+#endif
+#if defined VFLUSH && !defined VFLUSHO /* Ultrix 4.2 */
+# define VFLUSHO VFLUSH
+#endif
+#if defined CTLECH && !defined ECHOCTL /* Ultrix 4.3 */
+# define ECHOCTL CTLECH
+#endif
+#if defined TCTLECH && !defined ECHOCTL        /* Ultrix 4.2 */
+# define ECHOCTL TCTLECH
+#endif
+#if defined CRTKIL && !defined ECHOKE  /* Ultrix 4.2 and 4.3 */
+# define ECHOKE CRTKIL
+#endif
+#if defined VFLUSHO && !defined CFLUSHO
+# define CFLUSHO Control ('o')
+#endif
+#if defined VSTATUS && !defined CSTATUS
+# define CSTATUS Control ('t')
+#endif
+
+void stty_sane(struct termios *attr) {
+  attr->c_cflag |= CREAD;
+
+  attr->c_iflag &= ~IGNBRK;
+  attr->c_iflag |= BRKINT;
+  attr->c_iflag &= ~INLCR;
+  attr->c_iflag &= ~IGNCR;
+  attr->c_iflag |= ICRNL;
+  attr->c_iflag &= ~IXOFF;
+#ifdef IUCLC
+  attr->c_iflag &= ~IUCLC;
+#endif
+#ifdef IXANY
+  attr->c_iflag &= ~IXANY;
+#endif
+#ifdef IMAXBEL
+  attr->c_iflag |= IMAXBEL;
+#endif
+#ifdef IUTF8
+  attr->c_iflag &= ~IUTF8;
+#endif
+
+  attr->c_oflag |= OPOST;
+#ifdef OLCUC
+  attr->c_oflag &= ~OLCUC;
+#endif
+#ifdef OCRNL
+  attr->c_oflag &= ~OCRNL;
+#endif
+#ifdef ONLCR
+  attr->c_oflag |= ONLCR;
+#endif
+#ifdef ONOCR
+  attr->c_oflag &= ~ONOCR;
+#endif
+#ifdef ONLRET
+  attr->c_oflag &= ~ONLRET;
+#endif
+#ifdef OFILL
+  attr->c_oflag &= ~OFILL;
+#endif
+#ifdef OFDEL
+  attr->c_oflag &= ~OFDEL;
+#endif
+#ifdef NLDLY
+  attr->c_oflag = (attr->c_oflag & ~NLDLY) | NL0;
+#endif
+#ifdef CRDLY
+  attr->c_oflag = (attr->c_oflag & ~CRDLY) | CR0;
+#endif
+#ifdef TABDLY
+# ifdef TAB0
+  attr->c_oflag = (attr->c_oflag & ~TABDLY) | TAB0;
+# endif
+#else
+# ifdef OXTABS
+  attr->c_oflag &= ~OXTABS;
+# endif
+#endif
+#ifdef BSDLY
+  attr->c_oflag = (attr->c_oflag & ~BSDLY) | BS0;
+#endif
+#ifdef VTDLY
+  attr->c_oflag = (attr->c_oflag & ~VTDLY) | VT0;
+#endif
+#ifdef FFDLY
+  attr->c_oflag = (attr->c_oflag & ~FFDLY) | FF0;
+#endif
+
+  attr->c_lflag |= ISIG;
+  attr->c_lflag |= ICANON;
+#ifdef IEXTEN
+  attr->c_lflag |= IEXTEN;
+#endif
+  attr->c_lflag |= ECHO;
+  attr->c_lflag |= ECHOE;
+  attr->c_lflag |= ECHOK;
+  attr->c_lflag &= ~ECHONL;
+  attr->c_lflag &= ~NOFLSH;
+#ifdef XCASE
+  attr->c_lflag &= ~XCASE;
+#endif
+#ifdef TOSTOP
+  attr->c_lflag &= ~TOSTOP;
+#endif
+#ifdef ECHOPRT
+  attr->c_lflag &= ~ECHOPRT;
+#endif
+#ifdef ECHOCTL
+  attr->c_lflag |= ECHOCTL;
+#endif
+#ifdef ECHOKE
+  attr->c_lflag |= ECHOKE;
+#endif
+
+  attr->c_cc[VINTR] = CINTR;
+  attr->c_cc[VQUIT] = CQUIT;
+  attr->c_cc[VERASE] = CERASE;
+  attr->c_cc[VKILL] = CKILL;
+  attr->c_cc[VEOF] = CEOF;
+  attr->c_cc[VEOL] = CEOL;
+#ifdef VEOL2
+  attr->c_cc[VEOL2] = CEOL2;
+#endif
+#ifdef VSWTCH
+  attr->c_cc[VSWTCH] = CSWTCH;
+#endif
+  attr->c_cc[VSTART] = CSTART;
+  attr->c_cc[VSTOP] = CSTOP;
+  attr->c_cc[VSUSP] = CSUSP;
+#ifdef VDSUSP
+  attr->c_cc[VDSUSP] = CDSUSP;
+#endif
+#ifdef VREPRINT
+  attr->c_cc[VREPRINT] = CRPRNT;
+#else
+# ifdef CREPRINT /* HPUX 10.20 needs this */
+  attr->c_cc[CREPRINT] = CRPRNT;
+# endif
+#endif
+#ifdef VWERASE
+  attr->c_cc[VWERASE] = CWERASE;
+#endif
+#ifdef VLNEXT
+  attr->c_cc[VLNEXT] = CLNEXT;
+#endif
+#ifdef VFLUSHO
+  attr->c_cc[VFLUSHO] = CFLUSHO;
+#endif
+#ifdef VSTATUS
+  attr->c_cc[VSTATUS] = CSTATUS;
+#endif
+
+  attr->c_cc[VMIN] = 1;
+  attr->c_cc[VTIME] = 0;
+}
diff --git a/hrcg/stty_sane.h b/hrcg/stty_sane.h
new file mode 100644 (file)
index 0000000..767e481
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef _STTY_SANE_H
+#define _STTY_SANE_H
+
+#include <termios.h>
+
+void stty_sane(struct termios *attr);
+
+#endif