#include <iostream>
#include <string>
#include <unistd.h>
#include <wait.h>
#include <errno.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/termios.h>

using namespace std;

// Run the command cmd using system after double fork,
// Return a file descriptor for the input end of a pipe
// The command is run with output end of the pipe as output,
// and nothing as input (stdin is closed)
int run_get_pipe(string cmd) {
  int pipes[2];
  pipe(pipes);			// Pipes for communication
  if (pid_t p = fork()) {	// Parent
    close(pipes[1]);		// Never write to the pipe
    waitpid(p, 0, 0);
    return pipes[0];		// Return the pipe that input can be fetched
  } else {			// Child
    close(pipes[0]);		// Never read from the pipe
    close(0);			// Actually, no input at all
    dup2(pipes[1], 1);		// Output of pipe used as stdout
    if (pid_t p2 = fork())
      exit(0);
    else
      system(cmd.c_str());
    exit(0);
  }
}

// The main command loop, never returns
// Repeatedly read a command and execute it
// Trap all lines, adding an identifier before all output
void cmd_loop() {
  int cmd_count = 1;
  string str;
  cout << cmd_count << "> ";
  for (;;) {
    char ch;
    cin.get(ch);
    if (ch != '\n')
      str += ch;
    else {
      int fd = run_get_pipe(str);
      string fdstr;
      while (read(fd, &ch, 1) != 0)
	if (ch != '\n')
	  fdstr += ch;
	else {
	  cout << cmd_count << ": " << fdstr << endl;
	  fdstr = "";
	}
      close(fd);
      str = "";
      ++cmd_count;
      cout << cmd_count << " > ";
    }
  }
}

// Prepare to reset the interrupts
struct termios tios, oldtios;
void reset(int) {
  ioctl(0, TCSETS, &oldtios);
  exit(0);
}

// Main program
// Setup stdin 
int main() {
  // Set the stdin into non-canonical mode
  if (ioctl(0, TCGETS, &oldtios) == -1) {// 0 means stdin
    cerr << "ioctl error: " << strerror(errno) << endl;
    exit(1);
  }
  tios = oldtios;
  tios.c_lflag &= ~ICANON;	// Don't wait for complete line before output
  struct sigaction sa;
  sa.sa_handler = reset;
  sigemptyset(&sa.sa_mask);
  sa.sa_flags = 0;
  sigaction(SIGINT, &sa, 0);
  sigaction(SIGQUIT, &sa, 0);
  sigaction(SIGHUP, &sa, 0);
  if (ioctl(0, TCSETS, &tios) == -1) {
    cerr << "ioctl error: " << strerror(errno) << endl;
    exit(1);
  }
  // Run the command loop
  cmd_loop();
}

