#include "haspsx.h"
#ifdef HAS_POSIX_PTMX
#define _XOPEN_SOURCE 600
#endif

#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <fcntl.h>
#include "env_write.h"
#include "fmt.h"
#include "str.h"
#include "strerr.h"
#include "fd.h"
#include <termios.h>
#include <unistd.h>

#include "hasptmx.h"
#include "hasptc.h"
#include "conf-secure.h"

#ifndef O_NONBLOCK
#define O_NONBLOCK O_NDELAY
#endif


#ifdef HAS_POSIX_PTMX
#ifndef PTYGET_OK
#define PTYGET_OK

#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <fcntl.h>

static char *pty45(void)
{
  int fd;
  char * fnslave;

  fd = posix_openpt(O_RDWR | O_NONBLOCK);
  if (fd == -1) return 0;
  if (fd_move(4,fd) == -1) return 0;

#ifdef PTYGET_SECURE
  grantpt(4);
#endif
  unlockpt(4);

  fnslave = ptsname(4);
  if (!fnslave) return 0;

  fd = open(fnslave,O_RDWR | O_NOCTTY);
  if (fd == -1) return 0;
  if (fd_move(5,fd) == -1) return 0;

  if (tcflush(5,TCIOFLUSH) == -1) return 0;

  return fnslave;
}

#endif
#endif


#ifdef HASPTMX
#ifndef PTYGET_OK
#define PTYGET_OK

#include <sys/sysmacros.h>
#include <sys/stropts.h>
#include <sys/stream.h>
#include <sys/ptms.h>

char fnslave[10 + FMT_ULONG]; /* 10 for /dev/pts/ */

static char *pty45(void)
{
  struct strioctl si;
  struct stat st;
  int fd;
  char *s;

  fd = open("/dev/ptmx",O_RDWR | O_NONBLOCK);
  if (fd == -1) return 0;
  if (fd_move(4,fd) == -1) return 0;

  if (fstat(4,&st) == -1) return 0;
  s = fnslave;
  s += fmt_str(s,"/dev/pts/");
  s += fmt_ulong(s,(unsigned long) minor(st.st_rdev));
  *s = 0;

#ifdef PTYGET_SECURE

  if (chown(fnslave,getuid(),getegid()) == -1)
    return 0;
  if (chmod(fnslave,0600) == -1)
    return 0;
#endif

  si.ic_cmd = UNLKPT;
  si.ic_timout = 0;
  si.ic_len = 0;
  si.ic_dp = 0;
  if (ioctl(4,I_STR,&si) == -1) return 0;

  fd = open(fnslave,O_RDWR | O_NOCTTY);
  if (fd == -1) return 0;
  if (fd_move(5,fd) == -1) return 0;

  if (ioctl(5,I_PUSH,"ptem") == -1) return 0;
  if (ioctl(5,I_PUSH,"ldterm") == -1) return 0;
  if (tcflush(5,TCIOFLUSH) == -1) return 0;

  return fnslave;
}

#endif
#endif


#ifdef HASPTC
#ifndef PTYGET_OK
#define PTYGET_OK

#ifdef PTYGET_SECURE

  ERROR! ptc-based systems have no pty security.

#endif

#include <sys/sysmacros.h>

static char fnslave[10 + FMT_ULONG]; /* 10 for /dev/pts/ */

static char *pty45(void)
{
  struct stat st;
  int fd;

  fd = open("/dev/ptc",O_RDWR | O_NONBLOCK);
  if (fd == -1) return 0;
  if (fd_move(4,fd) == -1) return 0;

  if (fstat(4,&st) == -1) return 0;
  s = fnslave;
  s += fmt_str(s,"/dev/pts/");
  s += fmt_ulong(s,(unsigned long) minor(st.st_rdev));
  *s = 0;

  fd = open(fnslave,O_RDWR | O_NOCTTY);
  if (fd == -1) return 0;
  if (fd_move(5,fd) == -1) return 0;

  return fnslave;
}

#endif
#endif


#ifndef PTYGET_OK

static char fnmaster[] = "/dev/pty??";
static char fnslave[] = "/dev/tty??";

/* Do not change the sizes of pty1 and pty2! */
static char pty1[16] = "pqrstuvwxyzPQRST";
static char pty2[16] = "0123456789abcdef";

static char *pty45(void)
{
  int fd;
  struct stat st;
  int pos;
  int increment;
  int loop;

  pos = 255 & getpid();
  increment = 1 + (254 & time((time_t *)0));

  for (loop = 0;loop < 256;++loop) {
    pos = 255 & (pos + increment);

    fnslave[8] = fnmaster[8] = pty1[15 & pos];
    fnslave[9] = fnmaster[9] = pty2[15 & (pos >> 4)];

    if (fnmaster[8] == '-') continue;
    if (stat(fnmaster,&st) == -1) { pty1[15 & pos] = '-'; continue; }

    fd = open(fnmaster,O_RDWR | O_NONBLOCK);
    if (fd != -1) {
      if (fd_move(4,fd) == -1) return 0;

#ifdef PTYGET_SECURE
      if (chown(fnslave,getuid(),getegid()) == -1)
        return 0;
      if (chmod(fnslave,0600) == -1)
        return 0;
      revoke(fnslave);
#endif

      fd = open(fnslave,O_RDWR | O_NOCTTY);
      if (fd != -1) {
	if (fd_move(5,fd) == -1) return 0;
	return fnslave;
      }
      close(4);
    }
  }
  return 0;
}

#endif


int main(int argc,char **argv)
{
  char *tty;
  (void)argc;	/* Silence a compiler warning. */

  tty = pty45();

  if (setgid(getgid()) == -1) _exit(1);
  if (setuid(getuid()) == -1) _exit(1);

  if (!tty)
    strerr_die2sys(111,"ptyget: fatal: ","unable to allocate pty: ");

#ifndef PTYGET_SECURE
  strerr_warn1("ptyget: warning: unable to allocate secure pty",0);
#endif

  if (!env_put2("TTY",tty))
    strerr_die2x(111,"ptyget: fatal: ","out of memory");

  if (!argv[1])
    strerr_die1x(1,"ptyget: usage: ptyget subprogram [ args ... ]");

  execvp(argv[1],argv + 1);
  strerr_die4sys(111,"ptyget: fatal: ","unable to run ",argv[1],": ");
}
