/*
 * This file was generated automatically by ExtUtils::ParseXS version 3.44 from the
 * contents of Tty.xs. Do not edit this file, edit Tty.xs instead.
 *
 *    ANY CHANGES MADE HERE WILL BE LOST!
 *
 */

#line 1 "Tty.xs"
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#define PTY_DEBUG 1

#ifdef PTY_DEBUG
static int print_debug;
#endif

#ifdef PerlIO
typedef int SysRet;
typedef PerlIO * InOutStream;
#else
# define PERLIO_IS_STDIO 1
# define PerlIO_fileno fileno
typedef int SysRet;
typedef FILE * InOutStream;
#endif

#include "patchlevel.h"

#if (PATCHLEVEL < 3) || ((PATCHLEVEL == 3) && (SUBVERSION < 22))
     /* before 5.003_22 */
#    define MY_start_subparse(fmt,flags) start_subparse()
#else
#  if (PATCHLEVEL == 3) && (SUBVERSION == 22)
     /* 5.003_22 */
#    define MY_start_subparse(fmt,flags) start_subparse(flags)
#  else
     /* 5.003_23  onwards */
#    define MY_start_subparse(fmt,flags) start_subparse(fmt,flags)
#  endif
#endif

/*
 * The following pty-allocation code was heavily inspired by its
 * counterparts in openssh 3.0p1 and Xemacs 21.4.5 but is a complete
 * rewrite by me, Roland Giersig <RGiersig@cpan.org>.
 *
 * Nevertheless my references to Tatu Ylonen <ylo@cs.hut.fi>
 * and the Xemacs development team for their inspiring code.
 *
 * mysignal and strlcpy were borrowed from openssh and have their
 * copyright messages attached.
 */

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

#ifdef HAVE_LIBUTIL_H
# include <libutil.h>
#endif /* HAVE_UTIL_H */

#ifdef HAVE_UTIL_H
# ifdef UTIL_H_ABS_PATH
#  include UTIL_H_ABS_PATH
# elif ((PATCHLEVEL < 19) && (SUBVERSION < 4))
#  include <util.h>
# endif
#endif /* HAVE_UTIL_H */

#ifdef HAVE_PTY_H
# include <pty.h>
#endif

#ifdef HAVE_SYS_PTY_H
# include <sys/pty.h>
#endif

#ifdef HAVE_SYS_PTYIO_H
# include <sys/ptyio.h>
#endif

#if defined(HAVE_DEV_PTMX) && defined(HAVE_SYS_STROPTS_H)
# include <sys/stropts.h>
#endif

#ifdef HAVE_TERMIOS_H
#include <termios.h>
#endif

#ifdef HAVE_TERMIO_H
#include <termio.h>
#endif

#ifndef O_NOCTTY
#define O_NOCTTY 0
#endif


/* from  $OpenBSD: misc.c,v 1.12 2001/06/26 17:27:24 markus Exp $        */

/*
 * Copyright (c) 2000 Markus Friedl.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <signal.h>

typedef void (*mysig_t)(int);

static mysig_t
mysignal(int sig, mysig_t act)
{
#ifdef HAVE_SIGACTION
        struct sigaction sa, osa;

        if (sigaction(sig, NULL, &osa) == -1)
                return (mysig_t) -1;
        if (osa.sa_handler != act) {
                memset(&sa, 0, sizeof(sa));
                sigemptyset(&sa.sa_mask);
                sa.sa_flags = 0;
#if defined(SA_INTERRUPT)
                if (sig == SIGALRM)
                        sa.sa_flags |= SA_INTERRUPT;
#endif
                sa.sa_handler = act;
                if (sigaction(sig, &sa, NULL) == -1)
                        return (mysig_t) -1;
        }
        return (osa.sa_handler);
#else
        return (signal(sig, act));
#endif
}

/*  from  $OpenBSD: strlcpy.c,v 1.5 2001/05/13 15:40:16 deraadt Exp $     */

/*
 * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef HAVE_STRLCPY

/*
 * Copy src to string dst of size siz.  At most siz-1 characters
 * will be copied.  Always NUL terminates (unless siz == 0).
 * Returns strlen(src); if retval >= siz, truncation occurred.
 */
static size_t
strlcpy(dst, src, siz)
        char *dst;
        const char *src;
        size_t siz;
{
        register char *d = dst;
        register const char *s = src;
        register size_t n = siz;

        /* Copy as many bytes as will fit */
        if (n != 0 && --n != 0) {
                do {
                        if ((*d++ = *s++) == 0)
                                break;
                } while (--n != 0);
        }

        /* Not enough room in dst, add NUL and traverse rest of src */
        if (n == 0) {
                if (siz != 0)
                        *d = '\0';              /* NUL-terminate dst */
                while (*s++)
                        ;
        }

        return(s - src - 1);    /* count does not include NUL */
}

#endif /* !HAVE_STRLCPY */

/*
 * Move file descriptor so it doesn't collide with stdin/out/err
 */

static void
make_safe_fd(int * fd)
{
  if (*fd < 3) {
    int newfd;
    newfd = fcntl(*fd, F_DUPFD, 3);
    if (newfd < 0) {
      if (PL_dowarn)
	warn("IO::Tty::pty_allocate(nonfatal): tried to move fd %d up but fcntl() said %.100s", *fd, strerror(errno));
    } else {
      close (*fd);
      *fd = newfd;
    }
  }
}

/*
 * After having acquired a master pty, try to find out the slave name,
 * initialize and open the slave.
 */

#if defined (HAVE_PTSNAME)
char * ptsname(int);
#endif

static int
open_slave(int *ptyfd, int *ttyfd, char *namebuf, int namebuflen)
{ 
    /*
     * now do some things that are supposedly healthy for ptys,
     * i.e. changing the access mode.
     */
#if defined(HAVE_GRANTPT) ||  defined(HAVE_UNLOCKPT)
    {
	mysig_t old_signal;
	old_signal = mysignal(SIGCHLD, SIG_DFL);
#if defined(HAVE_GRANTPT)
#if PTY_DEBUG
	if (print_debug)
	  fprintf(stderr, "trying grantpt()...\n");
#endif
	if (grantpt(*ptyfd) < 0) {
	    if (PL_dowarn)
		warn("IO::Tty::pty_allocate(nonfatal): grantpt(): %.100s", strerror(errno));
	}

#endif /* HAVE_GRANTPT */
#if defined(HAVE_UNLOCKPT)
#if PTY_DEBUG
	if (print_debug)
	  fprintf(stderr, "trying unlockpt()...\n");
#endif
	if (unlockpt(*ptyfd) < 0) {
	    if (PL_dowarn)
		warn("IO::Tty::pty_allocate(nonfatal): unlockpt(): %.100s", strerror(errno));
	}
#endif /* HAVE_UNLOCKPT */
	mysignal(SIGCHLD, old_signal);
    } 
#endif /* HAVE_GRANTPT || HAVE_UNLOCKPT */
 

    /*
     * find the slave name, if we don't have it already
     */
    
#if defined (HAVE_PTSNAME_R)
    if (namebuf[0] == 0) {
#if PTY_DEBUG
	if (print_debug)
	  fprintf(stderr, "trying ptsname_r()...\n");
#endif
	if(ptsname_r(*ptyfd, namebuf, namebuflen)) {
	    if (PL_dowarn)
		warn("IO::Tty::open_slave(nonfatal): ptsname_r(): %.100s", strerror(errno));
	}
    }
#endif /* HAVE_PTSNAME_R */

#if defined (HAVE_PTSNAME)
    if (namebuf[0] == 0) {
	char * name;
#if PTY_DEBUG
	if (print_debug)
	  fprintf(stderr, "trying ptsname()...\n");
#endif
	name = ptsname(*ptyfd);
	if (name) {
	    if(strlcpy(namebuf, name, namebuflen) >= namebuflen) {
	      warn("ERROR: IO::Tty::open_slave: ttyname truncated");
	      return 0;
	    }
	} else {
	    if (PL_dowarn)
		warn("IO::Tty::open_slave(nonfatal): ptsname(): %.100s", strerror(errno));
	}
    }
#endif /* HAVE_PTSNAME */

    if (namebuf[0] == 0)
	return 0;		/* we failed to get the slave name */

#if defined (__SVR4) && defined (__sun)
       #include <sys/types.h>
       #include <unistd.h>
       {
           uid_t euid = geteuid();
           uid_t uid  = getuid();

           /* root running as another user
            * grantpt() has done the wrong thing
             */
           if (euid != uid && uid == 0) {
#if PTY_DEBUG
		if (print_debug)
	  	    fprintf(stderr, "trying seteuid() from %d to %d...\n",
			euid, uid);
#endif
		if (setuid(uid)) {
		    warn("ERROR: IO::Tty::open_slave: couldn't seteuid to root: %d", errno);
		    return 0;
		}
		if (chown(namebuf, euid, -1)) {
		    warn("ERROR: IO::Tty::open_slave: couldn't fchown the pty: %d", errno);
		    return 0;
		}
		if (seteuid(euid)) {
		    warn("ERROR: IO::Tty::open_slave: couldn't seteuid back: %d", errno);
		    return 0;
		}
           }
       }
#endif

    if (*ttyfd >= 0) {
      make_safe_fd(ptyfd);
      make_safe_fd(ttyfd);
      return 1;			/* we already have an open slave, so
                                   no more init is needed */
    }

    /*
     * Open the slave side.
     */
#if PTY_DEBUG
    if (print_debug)
      fprintf(stderr, "trying to open %s...\n", namebuf);
#endif

    *ttyfd = open(namebuf, O_RDWR | O_NOCTTY);
    if (*ttyfd < 0) {
      if (PL_dowarn)
	warn("IO::Tty::open_slave(nonfatal): open(%.200s): %.100s",
	     namebuf, strerror(errno));
      close(*ptyfd);
      return 0;		/* too bad, couldn't open slave side */
    }

#if defined (I_PUSH)
    /*
     * Push appropriate streams modules for Solaris pty(7).
     * HP-UX pty(7) doesn't have ttcompat module.
     * We simply try to push all relevant modules but warn only on
     * those platforms we know these are required.
     */
#if PTY_DEBUG
    if (print_debug)
      fprintf(stderr, "trying to I_PUSH ptem...\n");
#endif
    if (ioctl(*ttyfd, I_PUSH, "ptem") < 0)
#if defined (__solaris) || defined(__hpux)
	if (PL_dowarn)
	    warn("IO::Tty::pty_allocate: ioctl I_PUSH ptem: %.100s", strerror(errno))
#endif
	      ;

#if PTY_DEBUG
    if (print_debug)
      fprintf(stderr, "trying to I_PUSH ldterm...\n");
#endif
    if (ioctl(*ttyfd, I_PUSH, "ldterm") < 0)
#if defined (__solaris) || defined(__hpux)
	if (PL_dowarn)
	    warn("IO::Tty::pty_allocate: ioctl I_PUSH ldterm: %.100s", strerror(errno))
#endif
	      ;

#if PTY_DEBUG
    if (print_debug)
      fprintf(stderr, "trying to I_PUSH ttcompat...\n");
#endif
    if (ioctl(*ttyfd, I_PUSH, "ttcompat") < 0)
#if defined (__solaris)
	if (PL_dowarn)
	    warn("IO::Tty::pty_allocate: ioctl I_PUSH ttcompat: %.100s", strerror(errno))
#endif
	      ;
#endif /* I_PUSH */

    /* finally we make sure the filedescriptors are > 2 to avoid
       problems with stdin/out/err.  This can happen if the user
       closes one of them before allocating a pty and leads to nasty
       side-effects, so we take a proactive stance here.  Normally I
       would say "Those who mess with stdin/out/err shall bear the
       consequences to the fullest" but hey, I'm a nice guy... ;O) */

    make_safe_fd(ptyfd);
    make_safe_fd(ttyfd);

    return 1;
}

/*
 * Allocates and opens a pty.  Returns 0 if no pty could be allocated, or
 * nonzero if a pty was successfully allocated.  On success, open file
 * descriptors for the pty and tty sides and the name of the tty side are
 * returned (the buffer must be able to hold at least 64 characters).
 *
 * Instead of trying just one method we go through all available
 * methods until we get a positive result.
 */

static int
allocate_pty(int *ptyfd, int *ttyfd, char *namebuf, int namebuflen)
{
    *ptyfd = -1;
    *ttyfd = -1;
    namebuf[0] = 0;

    /*
     * first we try to get a master device
     */
    do { /* we use do{}while(0) and break instead of goto */

#if defined(HAVE__GETPTY)
	/* _getpty(3) for SGI Irix */
	{
	    char *slave;
	    mysig_t old_signal;

#if PTY_DEBUG
	    if (print_debug)
	      fprintf(stderr, "trying _getpty()...\n");
#endif
	    /* _getpty spawns a suid prog, so don't ignore SIGCHLD */
    	    old_signal = mysignal(SIGCHLD, SIG_DFL);
	    slave = _getpty(ptyfd, O_RDWR, 0622, 0);
	    mysignal(SIGCHLD, old_signal);

	    if (slave != NULL) {
	        if (strlcpy(namebuf, slave, namebuflen) >= namebuflen) {
		  warn("ERROR: pty_allocate: ttyname truncated");
		  return 0;
		}
		if (open_slave(ptyfd, ttyfd, namebuf, namebuflen))
		    break;
		close(*ptyfd);
		*ptyfd = -1;
	    } else {
		if (PL_dowarn)
		    warn("pty_allocate(nonfatal): _getpty(): %.100s", strerror(errno));
		*ptyfd = -1;
	    }
	}
#endif

#if defined(HAVE_PTSNAME) || defined(HAVE_PTSNAME_R)
/* we don't need to try these if we don't have a way to get the pty names */

#if defined(HAVE_POSIX_OPENPT)
#if PTY_DEBUG
	if (print_debug)
	  fprintf(stderr, "trying posix_openpt()...\n");
#endif
	*ptyfd = posix_openpt(O_RDWR|O_NOCTTY);
	if (*ptyfd >= 0 && open_slave(ptyfd, ttyfd, namebuf, namebuflen))
	    break;		/* got one */
	if (PL_dowarn)
	    warn("pty_allocate(nonfatal): posix_openpt(): %.100s", strerror(errno));
#endif /* defined(HAVE_POSIX_OPENPT) */

#if defined(HAVE_GETPT)
	/* glibc defines this */
#if PTY_DEBUG
	if (print_debug)
	  fprintf(stderr, "trying getpt()...\n");
#endif
	*ptyfd = getpt();
	if (*ptyfd >= 0 && open_slave(ptyfd, ttyfd, namebuf, namebuflen))
	    break;		/* got one */
	if (PL_dowarn)
	    warn("pty_allocate(nonfatal): getpt(): %.100s", strerror(errno));
#endif /* defined(HAVE_GETPT) */

#if defined(HAVE_OPENPTY)
	/* openpty(3) exists in a variety of OS'es, but due to it's
	 * broken interface (no maxlen to slavename) we'll only use it
	 * to create the tty/pty pair and rely on ptsname to get the
	 * name.  */
	{
	    mysig_t old_signal;
	    int ret;

#if PTY_DEBUG
	    if (print_debug)
	      fprintf(stderr, "trying openpty()...\n");
#endif
	    old_signal = mysignal(SIGCHLD, SIG_DFL);
	    ret = openpty(ptyfd, ttyfd, NULL, NULL, NULL);
	    mysignal(SIGCHLD, old_signal);
	    if (ret >= 0 && *ptyfd >= 0) {
		if (open_slave(ptyfd, ttyfd, namebuf, namebuflen))
		    break;
	    }
	    *ptyfd = -1;
	    *ttyfd = -1;
	    if (PL_dowarn)
		warn("pty_allocate(nonfatal): openpty(): %.100s", strerror(errno));
	}
#endif /* defined(HAVE_OPENPTY) */

	/*
	 * now try various cloning devices
	 */

#if defined(HAVE_DEV_PTMX)
#if PTY_DEBUG
	if (print_debug)
	  fprintf(stderr, "trying /dev/ptmx...\n");
#endif

	*ptyfd = open("/dev/ptmx", O_RDWR | O_NOCTTY);
	if (*ptyfd >= 0 && open_slave(ptyfd, ttyfd, namebuf, namebuflen))
	    break;
	if (PL_dowarn)
	    warn("pty_allocate(nonfatal): open(/dev/ptmx): %.100s", strerror(errno));
#endif /* HAVE_DEV_PTMX */ 

#if defined(HAVE_DEV_PTYM_CLONE)
#if PTY_DEBUG
	if (print_debug)
	  fprintf(stderr, "trying /dev/ptym/clone...\n");
#endif

	*ptyfd = open("/dev/ptym/clone", O_RDWR | O_NOCTTY);
	if (*ptyfd >= 0 && open_slave(ptyfd, ttyfd, namebuf, namebuflen))
	    break;
	if (PL_dowarn)
	    warn("pty_allocate(nonfatal): open(/dev/ptym/clone): %.100s", strerror(errno));
#endif /* HAVE_DEV_PTYM_CLONE */

#if defined(HAVE_DEV_PTC)
	/* AIX-style pty code. */
#if PTY_DEBUG
	if (print_debug)
	  fprintf(stderr, "trying /dev/ptc...\n");
#endif

	*ptyfd = open("/dev/ptc", O_RDWR | O_NOCTTY);
	if (*ptyfd >= 0 && open_slave(ptyfd, ttyfd, namebuf, namebuflen))
	    break;
	if (PL_dowarn)
	    warn("pty_allocate(nonfatal): open(/dev/ptc): %.100s", strerror(errno));
#endif /* HAVE_DEV_PTC */

#if defined(HAVE_DEV_PTMX_BSD)
#if PTY_DEBUG
	if (print_debug)
	  fprintf(stderr, "trying /dev/ptmx_bsd...\n");
#endif
	*ptyfd = open("/dev/ptmx_bsd", O_RDWR | O_NOCTTY);
	if (*ptyfd >= 0 && open_slave(ptyfd, ttyfd, namebuf, namebuflen))
	    break;
	if (PL_dowarn)
	    warn("pty_allocate(nonfatal): open(/dev/ptmx_bsd): %.100s", strerror(errno));
#endif /* HAVE_DEV_PTMX_BSD */ 

#endif /* !defined(HAVE_PTSNAME) && !defined(HAVE_PTSNAME_R) */

	/*
	 * we still don't have a pty, so try some oldfashioned stuff, 
	 * looking for a pty/tty pair ourself.
	 */

#if defined(_CRAY)
	{
	    char buf[64];
	    int i;
	    int highpty;
	    
#ifdef _SC_CRAY_NPTY
	    highpty = sysconf(_SC_CRAY_NPTY);
	    if (highpty == -1)
		highpty = 128;
#else
	    highpty = 128;
#endif
#if PTY_DEBUG
	    if (print_debug)
	      fprintf(stderr, "trying CRAY /dev/pty/???...\n");
#endif
	    for (i = 0; i < highpty; i++) {
		sprintf(buf, "/dev/pty/%03d", i);
		*ptyfd = open(buf, O_RDWR | O_NOCTTY);
		if (*ptyfd < 0)
		    continue;
		sprintf(buf, "/dev/ttyp%03d", i);
		if (strlcpy(namebuf, buf, namebuflen) >= namebuflen) {
		  warn("ERROR: pty_allocate: ttyname truncated");
		  return 0;
		}
		break;
	    }
	    if (*ptyfd >= 0 && open_slave(ptyfd, ttyfd, namebuf, namebuflen))
		break;
	}
#endif

#if defined(HAVE_DEV_PTYM)
	{
	    /* HPUX */
	    char buf[64];
	    char tbuf[64];
	    int i;
	    struct stat sb;
	    const char *ptymajors = "abcefghijklmnopqrstuvwxyz";
	    const char *ptyminors = "0123456789abcdef";
	    int num_minors = strlen(ptyminors);
	    int num_ptys = strlen(ptymajors) * num_minors;
	    
#if PTY_DEBUG
	    if (print_debug)
	      fprintf(stderr, "trying HPUX /dev/ptym/pty[a-ce-z][0-9a-f]...\n");
#endif
	    /* try /dev/ptym/pty[a-ce-z][0-9a-f] */
	    for (i = 0; i < num_ptys; i++) {
		sprintf(buf, "/dev/ptym/pty%c%c",
			 ptymajors[i / num_minors],
			 ptyminors[i % num_minors]);
		sprintf(tbuf, "/dev/pty/tty%c%c",
			 ptymajors[i / num_minors],
			 ptyminors[i % num_minors]);
		if (strlcpy(namebuf, tbuf, namebuflen) >= namebuflen) {
		  warn("ERROR: pty_allocate: ttyname truncated");
		  return 0;
		}
		if(stat(buf, &sb))
		    break;	/* file does not exist, skip rest */
		*ptyfd = open(buf, O_RDWR | O_NOCTTY);
		if (*ptyfd >= 0 && open_slave(ptyfd, ttyfd, namebuf, namebuflen))
		    break;
		namebuf[0] = 0;
	    }
	    if (*ptyfd >= 0)
		break;

#if PTY_DEBUG
	    if (print_debug)
	      fprintf(stderr, "trying HPUX /dev/ptym/pty[a-ce-z][0-9][0-9]...\n");
#endif
	    /* now try /dev/ptym/pty[a-ce-z][0-9][0-9] */
	    num_minors = 100;
	    num_ptys = strlen(ptymajors) * num_minors;
	    for (i = 0; i < num_ptys; i++) {
		sprintf(buf, "/dev/ptym/pty%c%02d",
			 ptymajors[i / num_minors],
			 i % num_minors);
		sprintf(tbuf, "/dev/pty/tty%c%02d",
			 ptymajors[i / num_minors], i % num_minors);
		if (strlcpy(namebuf, tbuf, namebuflen) >= namebuflen) {
		  warn("ERROR: pty_allocate: ttyname truncated");
		  return 0;
		}
		
		if(stat(buf, &sb))
		    break;	/* file does not exist, skip rest */
		*ptyfd = open(buf, O_RDWR | O_NOCTTY);
		if (*ptyfd >= 0 && open_slave(ptyfd, ttyfd, namebuf, namebuflen))
		    break;
		namebuf[0] = 0;
	    }
	    if (*ptyfd >= 0)
		break;
	}
#endif /* HAVE_DEV_PTYM */

	{
	    /* BSD-style pty code. */
	    char buf[64];
	    char tbuf[64];
	    int i;
	    const char *ptymajors = "pqrstuvwxyzabcdefghijklmnoABCDEFGHIJKLMNOPQRSTUVWXYZ";
	    const char *ptyminors = "0123456789abcdefghijklmnopqrstuv";
	    int num_minors = strlen(ptyminors);
	    int num_ptys = strlen(ptymajors) * num_minors;

#if PTY_DEBUG
	    if (print_debug)
	      fprintf(stderr, "trying BSD /dev/pty??...\n");
#endif
	    for (i = 0; i < num_ptys; i++) {
		sprintf(buf, "/dev/pty%c%c",
			ptymajors[i / num_minors],
			ptyminors[i % num_minors]);
		sprintf(tbuf, "/dev/tty%c%c",
			ptymajors[i / num_minors],
			ptyminors[i % num_minors]);
		if (strlcpy(namebuf, tbuf, namebuflen) >= namebuflen) {
		  warn("ERROR: pty_allocate: ttyname truncated");
		  return 0;
		}
		*ptyfd = open(buf, O_RDWR | O_NOCTTY);
		if (*ptyfd >= 0 && open_slave(ptyfd, ttyfd, namebuf, namebuflen))
		    break;

		/* Try SCO style naming */
		sprintf(buf, "/dev/ptyp%d", i);
		sprintf(tbuf, "/dev/ttyp%d", i);
		if (strlcpy(namebuf, tbuf, namebuflen) >= namebuflen) {
		  warn("ERROR: pty_allocate: ttyname truncated");
		  return 0;
		}
		*ptyfd = open(buf, O_RDWR | O_NOCTTY);
		if (*ptyfd >= 0 && open_slave(ptyfd, ttyfd, namebuf, namebuflen))
		    break;

		/* Try BeOS style naming */
		sprintf(buf, "/dev/pt/%c%c",
			ptymajors[i / num_minors],
			ptyminors[i % num_minors]);
		sprintf(tbuf, "/dev/tt/%c%c",
			ptymajors[i / num_minors],
			ptyminors[i % num_minors]);
		if (strlcpy(namebuf, tbuf, namebuflen) >= namebuflen) {
		  warn("ERROR: pty_allocate: ttyname truncated");
		  return 0;
		}
		*ptyfd = open(buf, O_RDWR | O_NOCTTY);
		if (*ptyfd >= 0 && open_slave(ptyfd, ttyfd, namebuf, namebuflen))
		    break;

		/* Try z/OS style naming */
		sprintf(buf, "/dev/ptyp%04d", i);
		sprintf(tbuf, "/dev/ttyp%04d", i);
		if (strlcpy(namebuf, tbuf, namebuflen) >= namebuflen) {
		  warn("ERROR: pty_allocate: ttyname truncated");
		  return 0;
		}
		*ptyfd = open(buf, O_RDWR | O_NOCTTY);
		if (*ptyfd >= 0 && open_slave(ptyfd, ttyfd, namebuf, namebuflen))
		    break;

		namebuf[0] = 0;
	    }
	    if (*ptyfd >= 0)
		break;
	}

    } while (0);

    if (*ptyfd < 0 || namebuf[0] == 0)
	return 0;		/* we failed to allocate one */

    return 1;			/* whew, finally finished successfully */
} /* end allocate_pty */



#line 802 "Tty.c"
#ifndef PERL_UNUSED_VAR
#  define PERL_UNUSED_VAR(var) if (0) var = var
#endif

#ifndef dVAR
#  define dVAR		dNOOP
#endif


/* This stuff is not part of the API! You have been warned. */
#ifndef PERL_VERSION_DECIMAL
#  define PERL_VERSION_DECIMAL(r,v,s) (r*1000000 + v*1000 + s)
#endif
#ifndef PERL_DECIMAL_VERSION
#  define PERL_DECIMAL_VERSION \
	  PERL_VERSION_DECIMAL(PERL_REVISION,PERL_VERSION,PERL_SUBVERSION)
#endif
#ifndef PERL_VERSION_GE
#  define PERL_VERSION_GE(r,v,s) \
	  (PERL_DECIMAL_VERSION >= PERL_VERSION_DECIMAL(r,v,s))
#endif
#ifndef PERL_VERSION_LE
#  define PERL_VERSION_LE(r,v,s) \
	  (PERL_DECIMAL_VERSION <= PERL_VERSION_DECIMAL(r,v,s))
#endif

/* XS_INTERNAL is the explicit static-linkage variant of the default
 * XS macro.
 *
 * XS_EXTERNAL is the same as XS_INTERNAL except it does not include
 * "STATIC", ie. it exports XSUB symbols. You probably don't want that
 * for anything but the BOOT XSUB.
 *
 * See XSUB.h in core!
 */


/* TODO: This might be compatible further back than 5.10.0. */
#if PERL_VERSION_GE(5, 10, 0) && PERL_VERSION_LE(5, 15, 1)
#  undef XS_EXTERNAL
#  undef XS_INTERNAL
#  if defined(__CYGWIN__) && defined(USE_DYNAMIC_LOADING)
#    define XS_EXTERNAL(name) __declspec(dllexport) XSPROTO(name)
#    define XS_INTERNAL(name) STATIC XSPROTO(name)
#  endif
#  if defined(__SYMBIAN32__)
#    define XS_EXTERNAL(name) EXPORT_C XSPROTO(name)
#    define XS_INTERNAL(name) EXPORT_C STATIC XSPROTO(name)
#  endif
#  ifndef XS_EXTERNAL
#    if defined(HASATTRIBUTE_UNUSED) && !defined(__cplusplus)
#      define XS_EXTERNAL(name) void name(pTHX_ CV* cv __attribute__unused__)
#      define XS_INTERNAL(name) STATIC void name(pTHX_ CV* cv __attribute__unused__)
#    else
#      ifdef __cplusplus
#        define XS_EXTERNAL(name) extern "C" XSPROTO(name)
#        define XS_INTERNAL(name) static XSPROTO(name)
#      else
#        define XS_EXTERNAL(name) XSPROTO(name)
#        define XS_INTERNAL(name) STATIC XSPROTO(name)
#      endif
#    endif
#  endif
#endif

/* perl >= 5.10.0 && perl <= 5.15.1 */


/* The XS_EXTERNAL macro is used for functions that must not be static
 * like the boot XSUB of a module. If perl didn't have an XS_EXTERNAL
 * macro defined, the best we can do is assume XS is the same.
 * Dito for XS_INTERNAL.
 */
#ifndef XS_EXTERNAL
#  define XS_EXTERNAL(name) XS(name)
#endif
#ifndef XS_INTERNAL
#  define XS_INTERNAL(name) XS(name)
#endif

/* Now, finally, after all this mess, we want an ExtUtils::ParseXS
 * internal macro that we're free to redefine for varying linkage due
 * to the EXPORT_XSUB_SYMBOLS XS keyword. This is internal, use
 * XS_EXTERNAL(name) or XS_INTERNAL(name) in your code if you need to!
 */

#undef XS_EUPXS
#if defined(PERL_EUPXS_ALWAYS_EXPORT)
#  define XS_EUPXS(name) XS_EXTERNAL(name)
#else
   /* default to internal */
#  define XS_EUPXS(name) XS_INTERNAL(name)
#endif

#ifndef PERL_ARGS_ASSERT_CROAK_XS_USAGE
#define PERL_ARGS_ASSERT_CROAK_XS_USAGE assert(cv); assert(params)

/* prototype to pass -Wmissing-prototypes */
STATIC void
S_croak_xs_usage(const CV *const cv, const char *const params);

STATIC void
S_croak_xs_usage(const CV *const cv, const char *const params)
{
    const GV *const gv = CvGV(cv);

    PERL_ARGS_ASSERT_CROAK_XS_USAGE;

    if (gv) {
        const char *const gvname = GvNAME(gv);
        const HV *const stash = GvSTASH(gv);
        const char *const hvname = stash ? HvNAME(stash) : NULL;

        if (hvname)
	    Perl_croak_nocontext("Usage: %s::%s(%s)", hvname, gvname, params);
        else
	    Perl_croak_nocontext("Usage: %s(%s)", gvname, params);
    } else {
        /* Pants. I don't think that it should be possible to get here. */
	Perl_croak_nocontext("Usage: CODE(0x%" UVxf ")(%s)", PTR2UV(cv), params);
    }
}
#undef  PERL_ARGS_ASSERT_CROAK_XS_USAGE

#define croak_xs_usage        S_croak_xs_usage

#endif

/* NOTE: the prototype of newXSproto() is different in versions of perls,
 * so we define a portable version of newXSproto()
 */
#ifdef newXS_flags
#define newXSproto_portable(name, c_impl, file, proto) newXS_flags(name, c_impl, file, proto, 0)
#else
#define newXSproto_portable(name, c_impl, file, proto) (PL_Sv=(SV*)newXS(name, c_impl, file), sv_setpv(PL_Sv, proto), (CV*)PL_Sv)
#endif /* !defined(newXS_flags) */

#if PERL_VERSION_LE(5, 21, 5)
#  define newXS_deffile(a,b) Perl_newXS(aTHX_ a,b,file)
#else
#  define newXS_deffile(a,b) Perl_newXS_deffile(aTHX_ a,b)
#endif

#line 946 "Tty.c"

XS_EUPXS(XS_IO__Pty_pty_allocate); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_IO__Pty_pty_allocate)
{
    dVAR; dXSARGS;
    if (items != 0)
       croak_xs_usage(cv,  "");
    PERL_UNUSED_VAR(ax); /* -Wall */
    SP -= items;
    {
#line 799 "Tty.xs"
	int ptyfd, ttyfd, ret;
	char name[256];
#ifdef PTY_DEBUG
        SV *debug;
#endif

#line 964 "Tty.c"
#line 806 "Tty.xs"
#ifdef PTY_DEBUG
        debug = perl_get_sv("IO::Tty::DEBUG", FALSE);
  	if (SvTRUE(debug))
          print_debug = 1;
#endif
	ret = allocate_pty(&ptyfd, &ttyfd, name, sizeof(name));
	if (ret) {
	    name[sizeof(name)-1] = 0;
	    EXTEND(SP,3);
	    PUSHs(sv_2mortal(newSViv(ptyfd)));	
	    PUSHs(sv_2mortal(newSViv(ttyfd)));	
	    PUSHs(sv_2mortal(newSVpv(name, strlen(name))));	
        } else {
	    /* empty list */
	}
#line 981 "Tty.c"
	PUTBACK;
	return;
    }
}


XS_EUPXS(XS_IO__Tty_ttyname); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_IO__Tty_ttyname)
{
    dVAR; dXSARGS;
    if (items != 1)
       croak_xs_usage(cv,  "handle");
    {
	InOutStream	handle = IoIFP(sv_2io(ST(0)))
;
	char *	RETVAL;
	dXSTARG;
#line 829 "Tty.xs"
#ifdef HAVE_TTYNAME
	if (handle)
	    RETVAL = ttyname(PerlIO_fileno(handle));
	else {
	    RETVAL = Nullch;
	    errno = EINVAL;
	}
#else
	warn("IO::Tty::ttyname not implemented on this architecture");
	RETVAL = Nullch;
#endif
#line 1011 "Tty.c"
	sv_setpv(TARG, RETVAL);
	XSprePUSH;
	PUSHTARG;
    }
    XSRETURN(1);
}


XS_EUPXS(XS_IO__Tty_pack_winsize); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_IO__Tty_pack_winsize)
{
    dVAR; dXSARGS;
    if (items < 2 || items > 4)
       croak_xs_usage(cv,  "row, col, xpixel = 0, ypixel = 0");
    {
	int	row = (int)SvIV(ST(0))
;
	int	col = (int)SvIV(ST(1))
;
	int	xpixel;
	int	ypixel;
	SV *	RETVAL;

	if (items < 3)
	    xpixel = 0;
	else {
	    xpixel = (int)SvIV(ST(2))
;
	}

	if (items < 4)
	    ypixel = 0;
	else {
	    ypixel = (int)SvIV(ST(3))
;
	}
#line 850 "Tty.xs"
	struct winsize ws;
#line 1050 "Tty.c"
#line 852 "Tty.xs"
	ws.ws_row = row;
	ws.ws_col = col;
	ws.ws_xpixel = xpixel;
	ws.ws_ypixel = ypixel;
	RETVAL = newSVpvn((char *)&ws, sizeof(ws));
#line 1057 "Tty.c"
	RETVAL = sv_2mortal(RETVAL);
	ST(0) = RETVAL;
    }
    XSRETURN(1);
}


XS_EUPXS(XS_IO__Tty_unpack_winsize); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_IO__Tty_unpack_winsize)
{
    dVAR; dXSARGS;
    if (items != 1)
       croak_xs_usage(cv,  "winsize");
    PERL_UNUSED_VAR(ax); /* -Wall */
    SP -= items;
    {
	SV *	winsize = ST(0)
;
#line 864 "Tty.xs"
	struct winsize ws;
#line 1078 "Tty.c"
#line 866 "Tty.xs"
	if(SvCUR(winsize) != sizeof(ws))
	    croak("IO::Tty::unpack_winsize(): Bad arg length - got %zd, expected %zd",
		SvCUR(winsize), sizeof(ws));
	Copy(SvPV_nolen(winsize), &ws, sizeof(ws), char);
	EXTEND(SP, 4);
	PUSHs(sv_2mortal(newSViv(ws.ws_row)));
	PUSHs(sv_2mortal(newSViv(ws.ws_col)));
	PUSHs(sv_2mortal(newSViv(ws.ws_xpixel)));
	PUSHs(sv_2mortal(newSViv(ws.ws_ypixel)));
#line 1089 "Tty.c"
	PUTBACK;
	return;
    }
}

#ifdef __cplusplus
extern "C"
#endif
XS_EXTERNAL(boot_IO__Tty); /* prototype to pass -Wmissing-prototypes */
XS_EXTERNAL(boot_IO__Tty)
{
#if PERL_VERSION_LE(5, 21, 5)
    dVAR; dXSARGS;
#else
    dVAR; dXSBOOTARGSXSAPIVERCHK;
#endif
#if PERL_VERSION_LE(5, 8, 999) /* PERL_VERSION_LT is 5.33+ */
    char* file = __FILE__;
#else
    const char* file = __FILE__;
#endif

    PERL_UNUSED_VAR(file);

    PERL_UNUSED_VAR(cv); /* -W */
    PERL_UNUSED_VAR(items); /* -W */
#if PERL_VERSION_LE(5, 21, 5)
    XS_VERSION_BOOTCHECK;
#  ifdef XS_APIVERSION_BOOTCHECK
    XS_APIVERSION_BOOTCHECK;
#  endif
#endif

        newXS_deffile("IO::Pty::pty_allocate", XS_IO__Pty_pty_allocate);
        newXS_deffile("IO::Tty::ttyname", XS_IO__Tty_ttyname);
        newXS_deffile("IO::Tty::pack_winsize", XS_IO__Tty_pack_winsize);
        newXS_deffile("IO::Tty::unpack_winsize", XS_IO__Tty_unpack_winsize);

    /* Initialisation Section */

#line 878 "Tty.xs"
{
  HV *stash;
  SV *config;

  stash = gv_stashpv("IO::Tty::Constant", TRUE);
  config = perl_get_sv("IO::Tty::CONFIG", TRUE);    
#include "xssubs.c"
}

#line 1140 "Tty.c"

    /* End of Initialisation Section */

#if PERL_VERSION_LE(5, 21, 5)
#  if PERL_VERSION_GE(5, 9, 0)
    if (PL_unitcheckav)
        call_list(PL_scopestack_ix, PL_unitcheckav);
#  endif
    XSRETURN_YES;
#else
    Perl_xs_boot_epilog(aTHX_ ax);
#endif
}

