
#include "ptytty.h"
#include <errno.h>
#include <sys/file.h>
#include <sys/ioctl.h>

// -----------  Modifier functions

void PtyTty::Open ()
{
  Close();
  have_pair = get_pair();
}
void PtyTty::Open (String& device_id_chars)
{
  Close();
  
  String ptyS = "/dev/pty" + device_id_chars;
  String ttyS = "/dev/tty" + device_id_chars;

  if(ptyS.length() > length_of_dev_name) return;
  strcpy(ptydev,(char*) ptyS);
  strcpy(ttydev,(char*) ttyS);

  if(!OK_devs()) return;
  have_pair = try_to_open_dev_pair();
}

void PtyTty::Close ()
{
  if(!have_pair) return;
  have_pair =0;
  close(master_pty);
  close(slave_tty);
}

PtyTty::operator= (PtyTty& pair)
{
  Close();
  have_pair = pair.Has_pair();
  the_pty   = pair.master_pty();
  the_tty   = pair.slave_tty();
  strcpy( ttydev, (char*) pair.pty_dev() );
  strcpy( ptydev, (char*) pair.tty_dev() );
}

// -----------  Observer functions

int PtyTty::slave_tty()
{
  if(have_pair) return the_tty;
  else          return -1;
}

String PtyTty::pty_dev ()
{
  if(have_pair) return ptydev;
  else          return "";
}
String PtyTty::tty_dev ()
{
  if(have_pair) return ttydev;
  else          return "";
}

// -------------  changing the controlling tty

void  PtyTty::make_slave_tty_the_controlling_tty()
{
  if(!have_pair) { perror("PtyTty: misuse"); return; }

  // Close and reopen slave_tty to make it the controlling tty
  //  of the (child) process.

  ::close(the_tty);
  int tty;
  if ((tty = open("/dev/tty", O_RDWR) == -1) ||
      (ioctl(0, TIOCNOTTY, 0)         == -1) ||
      (::close(tty)                   == -1))
    { perror("Couldnt close tty."); exit(1); }
  
  if ((the_tty = open(ttydev, O_RDWR)) == -1)
    { perror("Couldnt open tty.");  exit(1); }
}

// =============  Internal Functions ===

// -------------  pair aquisition

bool PtyTty::try_to_open_dev_pair()
{
  if ((the_pty = open(ptydev, O_RDWR)) >= 0)
    if ((the_tty = open(ttydev, O_RDWR)) >= 0)
      {
	if(errno==EIO) errno=0;
	return 1;
      }
    else ::close(the_pty);

  return 0;
}

bool PtyTty::get_pair()
{
  int devindex;
  int letter;
  
  strcpy(ptydev,"/dev/ptyxx");
  strcpy(ttydev,"/dev/ttyxx");

  char *ptychar1 = "pqrstuvwxyz";
  char *ptychar2 = "0123456789abcdef";
  
  letter = 0;
  while (letter < 11) {
    ttydev[strlen(ttydev) - 2] = ptychar1[letter];
    ptydev[strlen(ptydev) - 2] = ptychar1[letter];
    letter++;
    
    devindex = 0;
    while (devindex < 16) {
      ttydev[strlen(ttydev) - 1] = ptychar2[devindex];
      ptydev[strlen(ptydev) - 1] = ptychar2[devindex];
      devindex++;
      
      if(try_to_open_dev_pair()) return 1;
    }
  }
  return 0;
}

// ---------------  Representational invariants

// Are the dev names of an acceptable form.
bool PtyTty::OK_devs ()
{
  int ptylen = strlen(ptydev);
  int ttylen = strlen(ptydev);

  // name lengths ok...
  if( ptylen != ttylen || ptylen != length_of_dev_name) return 0;

  // device ids specify a pair...
  if(0!=strcmp( &ptydev[ptylen-2], &ttydev[ttylen-2] ))
    return 0;

  // Files are named "/dev/?ty??"...
  if(length_of_dev_name!=10) exit(1); // programmer rep violation.
  if(0!=strncmp( ptydev, "/dev/pty", 8)) return 0;
  if(0!=strncmp( ttydev, "/dev/tty", 8)) return 0;

  return 1;
}

