
#define _XOPEN_SOURCE 600

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <sys/select.h>

#define K   1024

// man pty

/**
 * Termios are documented in man 3 tcgetattr
 */
void noecho()
{
  struct termios t;

  tcgetattr(STDIN_FILENO,&t);
  // Turn off:
  // ICANNON    - canonical mode (line buffered/processed input)
  // ECHO       - echoing characters as we type them
  // ISIG       - Send signals when special keys are typed (Ctrl-C, Ctrl-Z, etc)
  // IEXTEN     - Enable line editing
  t.c_lflag &= ~(ICANON|ECHO|ISIG|IEXTEN);
  // Set MIN minimum number of characters to block read on and TIME maximum
  // amount of time to wait for input.
  t.c_cc[VMIN] = 1;
  t.c_cc[VTIME] = 0;
  tcsetattr(STDIN_FILENO,TCSANOW,&t);
}

void echo()
{
  struct termios t;

  tcgetattr(STDIN_FILENO, &t);
  t.c_lflag |= (ICANON|ECHO|ISIG|IEXTEN);
  tcsetattr(STDIN_FILENO, TCSANOW, &t);
}

int main(int argc, char *argv[])
{
  struct termios tio;
  struct winsize ws;
  pid_t pid;
  int pty, tty, r;
  char *pts, buf[K];

  // Add a signal handler to handle things like SIGCHLD, maybe SIGWINCH

  // Get line attributes from our TTY:
  if (tcgetattr(STDIN_FILENO, &tio) < 0) {
    perror("tcgetattr");
    exit(1);
  }
  // Get the window size from our TTY:
  if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) < 0) {
    fprintf(stderr,"Getting winsize\n");
    exit(1);
  }
  // Open master end of the PTY:
  if ((pty = posix_openpt(O_RDWR | O_NOCTTY)) < 0) {
    perror("openpt");
    exit(1);
  }
  // The remaining can probably be done in the child process.
  // Allocate a slave terminal to us:
  if (grantpt(pty) < 0) {
    perror("grantpt");
    exit(1);
  }
  // Unlock and set permissions on slave terminal side:
  if (unlockpt(pty) < 0) {
    perror("unlockpt");
    exit(1);
  }
  // Find out what the device name of the slave side pseudo terminal is:
  if ((pts = ptsname(pty)) == NULL) {
    perror("ptsname");
    exit(1);
  }

  pid = fork();
  if (pid < 0) {
    perror("fork");
    exit(1);
  }
  if (pid == 0) {
    // Open slave side PTY:
    if ((tty = open(pts, O_RDWR)) < 0) {
      perror("slave open");
      exit(1);
    }
    dup2(tty, STDIN_FILENO);
    dup2(tty, STDOUT_FILENO);
    dup2(tty, STDERR_FILENO);
    close(tty);
    // Set line attributes (immediately):
    if (tcsetattr(STDIN_FILENO, TCSANOW, &tio) < 0) {
      perror("tcsetattr");
    }
    // Set the window size:
    if (ioctl(STDIN_FILENO, TIOCSWINSZ, &ws) < 0) {
      fprintf(stderr,"winsize\n");
    }
    execlp("arogue", "arogue", NULL);
    perror("execlp");
    exit(1);
  }
  // In the parent process:

  fd_set rfds;

  noecho();

  // Add support for async I/O on keyboard and from the master pty:
  while (1) {
    FD_ZERO(&rfds);
    FD_SET(STDIN_FILENO, &rfds);
    FD_SET(pty, &rfds);

    select(pty+1, &rfds, NULL, NULL, NULL);
    // If true, then something is available on stdin:
    if (FD_ISSET(STDIN_FILENO, &rfds)) {
      r = read(STDIN_FILENO, buf, K);
      if (r < 0) break;
      write(pty, buf, r);
    }
    // If true, something is available on the pty to read:
    if (FD_ISSET(pty, &rfds)) {
      r = read(pty, buf, K);
      if (r < 0) break;
      write(STDOUT_FILENO, buf, r);
    }
  }

  echo();

  exit(0);
}
