#include "config.h"
/*
 Copyright (c) 1986, 1990 by The Trustees of Columbia University in
 the City of New York.  Permission is granted to any individual or
 institution to use, copy, or redistribute this software so long as it
 is not sold for profit, provided this copyright notice is retained.

 Author: Andrew Lowry
*/

/* Machine dependent code for Unix systems -- Use preprocessor
** conditionals for peculiarities of particular systems, but
** PLEASE -- Avoid nesting preprocessor conditionals!
**/

#include "ccmdlib.h"
#include "cmfncs.h"			/* and internal symbols */

#define raw	Raw		/* avoid conflict with name in curses.h */
#define nl	Nl		/* avoid conflict with name in curses.h */

/* needTERMIOS undeclared */                      
#ifndef ECHO
# define ECHO		0x00000008U /* enable echoing */
#endif

PASSEDSTATIC int outc ARGS((int c_));

#if BSD
extern short ospeed;			/* declared in termlib */
#endif /* BSD */

/* NOTE: For BSD systems this code was formerly using
** signal handlers declared as
**      handler(int sig_, int code_, struct sigcontext *scp);
** However, there doesn't seem to have been any real need for this,
** since sigcontext could only be given to sigreturn() which CCMD
** never uses.  Thus, the sigcontext version is now gone.
*/

static SIG sighand ARGS((int sig_));

static const char *gttype ARGS((int fd_));
static char *strindex ARGS((const char *src_, const char *str_));
static int cmermsg ARGS((const char *str_, flag_t flags_));
static int crmod ARGS((int val_));
static void cmselect ARGS((int fd_));
static void cmtsize ARGS((void));
static void intsoff ARGS((void));
static void intson ARGS((void));
static void raw ARGS((int fd_));
static void tc_setents ARGS((char *areap_));
static void tc_setsize ARGS((int li_, int co_));
static void unraw ARGS((int fd_));

#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif

extern int kill ARGS((pid_t, int));	/* often missing if __STDC__ defined */

#if defined(__hpux)
  /* HPUX defines sigblock() and sigsetmask() with long arguments and long
     return values */
#else
#ifdef SIGTSTP
extern int sigblock ARGS((int mask_));
#endif

extern int sigsetmask ARGS((int mask_));
#endif /* defined(__hpux) */
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif

#if 0					/* no longer used */
static int ctty ARGS((void));
static void cttycls ARGS((void));
#endif



/* cmrpjmp
**
** Purpose:
**   Automatic reparse handler, installed via the cmsetrp macro from
**   ccmd.h.  If this handler is installed in a CSB for which a reparse
**   is needed, it will perform a longjmp to restart execution at the
**   point following the installing cmsetrp invocation.  This point
**   should be following the call to cmini that set up parsing for
**   the current command line, and before the comnd call for the first
**   field in the command.
**
** Input arguments: None
** Output arguments: None
** Returns: Nothing
**/

jmp_buf cmrpjb;			/* global jump buffer for autoreparse */

int
cmrpjmp(VOID)
{
  longjmp(cmrpjb,1);		/* do the jump */
  return(CMxNOAR);		/* if it returns, it failed */
}



/* cmerjmp, cmerjnp
**
** Purpose:
**   Automatic parse error handler, much like the automatic reparse
**   handler described above.  The cmseter macro should be invoked
**   just prior to issuing a prompt.  When a parsing error
**   subsequently occurs (that is, the parse function is about to
**   return anything but CMxOK), cmperr will be called to print the
**   error, and then execution will jump back to the site of the macro
**   invocation.  When the automatic error handler is installed, the
**   user program can ignore the codes returned by parse, since they
**   will always be CMxOK.  CSB field _cmerr may be examined to see
**   whether the the prior command line was terminated by an error,
**   and if so, which error.
**
**   Note: Reparse situations will be handled by the error handler if
**   no reparse handler has been installed.
**
**   Cmerjnp is the same as cmerjmp, except that the error message is
**   not printed.
**
** Input arguments: None.
** Output arguments: None.
** Returns: Nothing.
**/

jmp_buf cmerjb;				/* global jump buffer */

int
#if HAVE_STDC
cmerjmp(int ret, const char *str, flag_t flags)
#else /* K&R style */
cmerjmp(ret,str,flags)
int ret;				/* code that triggered the handler */
const char *str;
flag_t flags;
#endif /* HAVE_STDC */
{
  if (str) return(cmermsg(str,flags));
  cmperr(ret,flags);			/* issue error message */
  longjmp(cmerjb,1);			/* take the jump */
  return(CMxNOAE);			/* failed */
}

static int
#if HAVE_STDC
cmermsg(const char *str, flag_t flags)
                			/* error msg */
#else /* K&R style */
cmermsg(str,flags)
const char *str;			/* error msg */
flag_t flags;
#endif /* HAVE_STDC */
{
  cmpemsg(str,flags);			/* issue error message */
  longjmp(cmerjb,1);			/* take the jump */
  return(CMxNOAE);			/* failed */
}

int
#if HAVE_STDC
cmerjnp(int ret)
#else /* K&R style */
cmerjnp(ret)
int ret;				/* code that triggered the handler */
#endif /* HAVE_STDC */
{
  longjmp(cmerjb,1);			/* take the jump */
  return(CMxNOAE);			/* failed */
}




#if BSD
/*
 * if handling nonblocking I/O, and a EWOULDBLOCK error comes up
 * Then jump to the point set with a cmsetbl() call.
 * The user then could wait for input.
 */

static jmp_buf cmbljb;			/* global jump buffer */

int
#if HAVE_STDC
cmbljmp(int ret)
#else /* K&R style */
cmbljmp(ret)
int ret;				/* code that triggered the handler */
#endif /* HAVE_STDC */
{
  if (errno != EWOULDBLOCK)
      return(ret);
  longjmp(cmbljb,1);			/* take the jump */
  return(CMxNOAE);			/* failed */
}
#endif


/*
** Machine-dependent IO routines...  Generally, a file descriptor is
** supplied as an argument to the calls.
**/

static int autowr;			/* TRUE if automatic wrap at eol */
static int li;				/* lines on screen */
static char tcapent[1024];		/* complete termcap entry */
static char tcarea[100];		/* decoded termcap entries */
static const char *nl = "\n";
static const char *cr = "\r";
static const char *cl,*ce,*up;		/* pointers to decoded entries */
static const char *le,*nd,*down;

/* cmgetc - get a character from the input source.  Return standard return
 * code.
*/

int
#if HAVE_STDC
cmgetc(int *c, FILE *fd)
#else /* K&R style */
cmgetc(c,fd)
int *c;					/* pointer where char is placed */
FILE * fd;				/* input file descriptor */
#endif /* HAVE_STDC */
{
  int cc;				/* value from read */

  if (cmcsb._cmoj != NULL)
    cmflsh(cmcsb._cmoj);		/* flush pending output */
  if (fd == NULL)			/* no file descriptor.  EOF */
    return(CMxEOF);

#if defined(HAVE_GNU_LINUX)
  if ((fd->_IO_read_ptr == fd->_IO_read_end) && (cmcsb._cmflg & CM_ITTY))
    cmselect(fileno(fd));
#elif defined(HAVE_NEW_BSD)
  if (fd->_r == 0 && (cmcsb._cmflg & CM_ITTY))
    cmselect(fileno(fd));
#else
  if (fd->_cnt == 0 && (cmcsb._cmflg & CM_ITTY))
    cmselect(fileno(fd));
#endif

  *c = cc = getc(fd);
  if (cc == EOF)
    return(CMxEOF);			/* end of file */
  else
    return(CMxOK);			/* good read */
}

/* cmputc - Output a single character to the terminal */

void
#if HAVE_STDC
cmputc(int c, FILE *fd)
      					/* char to output */
          				/* output filedesc */
#else /* K&R style */
cmputc(c,fd)
int c;					/* char to output */
FILE * fd;				/* output filedesc */
#endif /* HAVE_STDC */
{

  if (fd != NULL) {
    putc(c,fd);
    if (c == '\n')
      cmflsh(fd);
  }
}

/* cmputs - Output null-terminated string to the terminal */

void
#if HAVE_STDC
cmputs(const char *s, FILE *fd)
              				/* output string */
         				/* output filedesc */
#else /* K&R style */
cmputs(s,fd)
const char *s;				/* output string */
FILE *fd;				/* output filedesc */
#endif /* HAVE_STDC */
{
  while(*s != '\0')
    cmputc(*s++,fd);
}

/* cmcr - Move to the beginning of the current line */

void
#if HAVE_STDC
cmcr(FILE *fd)
          				/* output filedesc */
#else /* K&R style */
cmcr(fd)
FILE * fd;				/* output filedesc */
#endif /* HAVE_STDC */
{
  cmputs(cr,fd);			/* use term specific sequence */
}

/* cmnl - Output a newline sequence to the comman stream */

void
#if HAVE_STDC
cmnl(FILE *fd)
         				/* output filedesc */
#else /* K&R style */
cmnl(fd)
FILE *fd;				/* output filedesc */
#endif /* HAVE_STDC */
{
  cmputs(nl,fd);			/* use term-specific sequence */
}

/* cmflsh - flush output on fd */
void
#if HAVE_STDC
cmflsh(FILE *fd)
#else /* K&R style */
cmflsh(fd)
FILE *fd;
#endif /* HAVE_STDC */
{
  if (fd != NULL)
    fflush(fd);
}

/* cmwrap - Make sure that cursor wraps when it is required */

void
#if HAVE_STDC
cmwrap(FILE *fd)
          				/* output filedesc */
#else /* K&R style */
cmwrap(fd)
FILE * fd;				/* output filedesc */
#endif /* HAVE_STDC */
{
  if (!autowr)
    cmnl(fd);				/* newline if not automatic */
}

/* cmcls - Clear the screen.  Current IOJ value in the CSB is used for
** character output.  Only invoked if that IOJ is for a CRT terminal.
** Returns TRUE iff the operation succeeds.
**/

int
cmcls(VOID)
{
  if (cl == NULL)
    return(FALSE);			/* no clear screen sequence */
  else {
    tputs(cl,li,outc);			/* output the clear sequence */
    if (cmcsb._cmoj)
	fflush(cmcsb._cmoj);
    return(TRUE);
  }
}

/* cmceol - Clear to end of line.  Current IOJ value in the CSB is
** used for character output.  Only invoked if that IOJ is for a CRT
** terminal.  Returns TRUE iff the operation succeeds.
**/

int
cmceol(VOID)
{
  if (ce == NULL)
    return(FALSE);			/* no ceol sequence */
  else {
    tputs(ce,1,outc);			/* else do the operation */
    return(TRUE);
  }
}

/* cmupl - Moves up on line in the display without changing column
** position.  Should not wrap to bottom of screen or cause destructive
** downward scrolling.  Current IOJ value in the CSB is used for
** character output.  Only invoked if that IOJ is for a CRT terminal.
** Returns TRUE if the operation succeeds.
*/

int
cmupl(VOID)
{
  if (up == NULL)
    return(FALSE);			/* no upline sequence */
  else {
    tputs(up,1,outc);			/* else do the operation */
    return(TRUE);
  }
}

int
cmdownl(VOID)
{
  int oldcrmod;
  if (down == NULL)
    return(FALSE);			/* no upline sequence */
  else {
    if (cmcsb._cmoj) {
	oldcrmod = crmod(FALSE);
	tputs(down,1,outc);			/* else do the operation */
	if (cmcsb._cmoj)
	    fflush(cmcsb._cmoj);
	crmod(oldcrmod);
	return(TRUE);
    }
    else
	return(FALSE);
  }
}

int
cmleft(VOID)
{
  if (le == NULL)
    return(FALSE);			/* no move left sequence */
  else {
    tputs(le,1,outc);			/* else do the operation */
    cmcsb._cmcol--;
    return(TRUE);
  }
}

int
cmright(VOID)
{
  if (nd == NULL)
    return(FALSE);			/* no move right sequence */
  else {
    tputs(nd,1,outc);			/* else do the operation */
    cmcsb._cmcol++;
    return(TRUE);
  }
}

/* cmcpos - Returns the current column position on the display.
** We just assume the ccmd package is correct, since there's
** no facility in termlib for extracting column position, and
** the price for never knowing the cursor position is too high
** (really ugly user interface due to many blank lines).
**/

int
cmcpos(VOID)
{
  return(cmcsb._cmcol);
}

/* cmflush - Flush all pending input on the input source */

void
#if HAVE_STDC
cmflush(FILE *fd)
#else /* K&R style */
cmflush(fd)
FILE *fd;
#endif /* HAVE_STDC */
{
  if (fd != NULL) {
    if (isatty(fileno(fd)))		/* if it's a terminal */
#if defined(HAVE_GNU_LINUX) && !defined(TIOCFLUSH) && defined(TCFLSH)
#define TIOCFLUSH TCFLSH
#endif
#if SYSV && !defined(TIOCFLUSH) && defined(TCFLSH)
#define TIOCFLUSH TCFLSH
#endif
      ioctl(fileno(fd),TIOCFLUSH,NULL);	/* flush input and output */

#if defined(HAVE_GNU_LINUX) || defined(HAVE_NEW_BSD)
    (void)fflush(fd);
#else
    fd->_cnt = 0;			/* flush stdio IOBUF. */
#endif

  }
}

/* cmtset - Initialize the source terminal, as currently set in the
** CSB.  If the file is a terminal, a termcap entry is obtained and
** examined to see whether or not it is a hardcopy terminal.  If not,
** various control strings are read from the termcap entry and saved
** for screen operations.  In any case, terminals are placed in cbreak
** mode without echoing, and INT and TSTP signals are caught to
** prevent the terminal remaining in a funny state upon exit, and to
** place it back into the required state upon continuation.
**/

void
cmtset(VOID)
{
  int ofd, ifd;
  const char *ttype;			/* terminal type */
  int tret;

  if (cmcsb._cmoj != NULL)
    ofd = fileno(cmcsb._cmoj);		/* input file descriptor */
  if (cmcsb._cmij != NULL)
    ifd = fileno(cmcsb._cmij);		/* output file designator */

  cmcsb._cmflg &= ~CM_TTY;
  if (cmcsb._cmoj != NULL && isatty(ofd)) { /* check if it is a terminal */
    cmcsb._cmflg |= CM_OTTY;		/* yup */
    ttype = gttype(ofd);		/* get the terminal type name */
    if (tcapent[0] == '\0')
      tret = tgetent(tcapent,ttype);	/* get termcap entry */
    else
      tret = 1;
    if (tret != 1) {
      cmcsb._cmflg2 &= ~CM_CRT;	/* no luck... assume hardcopy */
      cmcsb._cmcmx = 79;		/* use default max column */
      cmcsb._cmwrp = 79;		/* and wrap column */
      nl = "\n";			/* and set default newline */
      cr = "\r";			/* and return sequences */
    }
    else if (cmcsb._cmoj != NULL) {
      if (tgetflag("hc")) 		/* hardcopy indicated? */
	cmcsb._cmflg2 &= ~CM_CRT;	/* yup, note it */
      else {
	cmcsb._cmflg2 |= CM_CRT;	/* else flag a crt */
      }
      cmtsize();			/* setup term size & termcap vars */
      cmcsb._cmwrp = cmcsb._cmcmx;	/* set up autowrap column */
    }
    if (cmcsb._cmij != NULL && isatty(ifd)) {
      cmcsb._cmflg |= CM_ITTY;		/* yup */
      raw(ifd);				/* set up the terminal properly */
      intson();				/* install our interrupt handlers */
    }
  }
  else {
    if (cmcsb._cmij != NULL && isatty(ifd)) {
      raw(ifd);				/* set up the terminal properly */
      intson();				/* install our interrupt handlers */
    }
    else {
      cmcsb._cmflg &= ~CM_TTY;		/* not a tty, so not a crt either */
      cmcsb._cmflg2 &= ~CM_CRT;
      cmcsb._cmcmx = 79;		/* and just use default width */
    }
  }
}

/* cmtend - Clean up after prior input source */

void
cmtend(VOID)
{
  int fd;

  if (cmcsb._cmij != NULL) {
    fd = fileno(cmcsb._cmij);		/* file desc to shut down */

    if (cmcsb._cmflg & CM_TTY) {
      unraw(fd);			/* reset former tty params */
      intsoff();			/* remove our interrupt handlers */
    }
  }
}

/*
 * cmtsize()
 * set up terminal size.  try using TIOCGWINSZ if possible.  If not set,
 * use termcap entry.  If ioctl() succeeds, fix up termcap entry, so inferior
 * processes inherit correctly.
 */
static void
cmtsize(VOID)
{
  int li = -1, co = -1;
#ifdef TIOCGWINSZ
  struct winsize w;

  if (ioctl(fileno(cmcsb._cmoj), TIOCGWINSZ, &w) == 0) {
    if ((int)w.ws_col > 0 && cmcsb._cmcmx != w.ws_col) {
      co = cmcsb._cmcmx = w.ws_col;
    }
    if ((int)w.ws_row > 0 && cmcsb._cmrmx != w.ws_row) {
      li = cmcsb._cmrmx = w.ws_row;
    }
  }
#else /* TIOCGWINSZ */
#ifdef TIOCGSIZE
  struct ttysize t;

  if (ioctl(fileno(cmcsb._cmoj), TIOCGSIZE, &t) == 0) {
    if (t.ts_col > 0 && cmcsb._cmcmx != t.ts_col) {
      co = cmcsb._cmcmx = t.ts_col;
    }
    if (t.ts_row > 0 && cmcsb._cmrmx != t.ts_row) {
      li = cmcsb._cmrmx = t.ts_row;
    }
  }
#endif /* TIOCGSIZE */
#endif /* !TIOCGWINSZ */
  if (co <= 0)
    cmcsb._cmcmx = tgetnum("co");	/* get col count */
  if (cmcsb._cmcmx <= 0)
    cmcsb._cmcmx = 79;			/* use default if not given */
  else
    cmcsb._cmcmx--;			/* else drop to max col position */
  if (li <= 0)				/*  */
    cmcsb._cmrmx = tgetnum("li");	/* get row count */
  if (cmcsb._cmrmx == -1)
    cmcsb._cmrmx = 24;			/* or use default if necesary */
  li = --cmcsb._cmrmx;
  tc_setsize(cmcsb._cmrmx+1,cmcsb._cmcmx+1);
}

/*
 * set the "li" and "co" entries in the termcap entry
 */
static void
#if HAVE_STDC
tc_setsize(int li, int co)
#else /* K&R style */
tc_setsize(li, co)
int li;
int co;
#endif /* HAVE_STDC */
{
  char buf[1024], *p1 = NULL, *p2=NULL, *p3=NULL, *p4=NULL;
  static char empty_string[] = "";

  p1 = strindex(tcapent,"co#");		/* find num cols */
  if (p1) {
    p2 = index(p1+1,':');		/* and the end of it. */
  }
  p3 = strindex(tcapent,"li#");		/* find num lines */
  if (p3) {
    p4 = index(p3+1,':');		/* and the end of it */
  }
  if (p3)
    *p3 = '\0';
  if (p1)
    *p1 = '\0';
  if (p2)
    *(p2++) = '\0';
  else p2 = empty_string;
  if (p4)
    *(p4++) = '\0';
  else
    p4 = empty_string;
					/* build new termcap string */
  sprintf(buf,"%sco#%d:%sli#%d:%s", tcapent, co, p2, li, p4);
  strcpy(tcapent, buf);
  setenv("TERMCAP", tcapent, TRUE);	/* put it in the environment. */
  tc_setents(tcarea);
}

static void
#if HAVE_STDC
tc_setents(char *areap)
#else /* K&R style */
tc_setents(areap)
char *areap;
#endif /* HAVE_STDC */
{
  if (cmcsb._cmflg2 & CM_CRT) {
    cl = tgetstr("cl",&areap);		/* get clear screen sequence */
    ce = tgetstr("ce",&areap);		/* and clear end-of-line */
    up = tgetstr("up",&areap);		/* and upline sequence */
    le = tgetstr("le",&areap);		/* move left */
    nd = tgetstr("nd",&areap);		/* move right */
    down = tgetstr("do",&areap);	/* move down */
    if (le == NULL)
        le = "\b";			/* default if not specified */
  }
  nl = tgetstr("nl",&areap);		/* alwasy get newline sequence */
  if (nl == NULL)
    nl = "\n";				/* default if not specified */
  cr = tgetstr("cr",&areap);		/* get return sequence */
  if (cr == NULL)
    cr = "\r";				/* or set default */
  autowr = tgetflag("am");		/* check for autowrap */
  li = cmcsb._cmrmx;
}

/*
 * search for an embedded string
 */
static char *
#if HAVE_STDC
strindex(const char *src, const char *str)
#else /* K&R style */
strindex(src, str)
const char *src, *str;
#endif /* HAVE_STDC */
{
  const char *cp = src, *cp1;

  while((cp1 = index(cp, *str), cp1)) {
    if (strncmp(cp1, str, strlen(str)) == 0) {
      return((char*)cp1);
    }
    cp = cp1+1;
  }
  return(NULL);
}

/* gttype - Return terminal type name for given tty file descriptor
** Auxiliary routine for cmtset
**/

static const char *
#if HAVE_STDC
gttype(int fd)
#else /* K&R style */
gttype(fd)
int fd;
#endif /* HAVE_STDC */
{
  char *type;				/* type name of terminal */
  char *name;				/* terminal name */
  char *cname;				/* controlling terminal name */
  int cttyfd;				/* controlling terminal file desc */
  int ctty;				/* TRUE if fd is controlling tty */
  FILE *typedb;				/* stream for ttytype database */
  static char typelin[80];		/* line from ttytype database */
  char *typecp;				/* pointer into type db entry */

  cttyfd = open("/dev/tty",O_RDWR,0);	/* open the controlling tty */
  if (cttyfd < 0)
    ctty = FALSE;			/* bad open - assume not ctty */
  else {
    name = ttyname(fd);			/* get the terminal name */
    cname = ttyname(cttyfd);		/* and controlling tty name */
    if (strcmp(name,cname) == 0)		/* same? */
      ctty = TRUE;			/* yup, it is ctrl tty */
    else
      ctty = FALSE;			/* nope, some other tty */
    close(cttyfd);			/* no more use for this */
  }
					/*  */
  if (ctty) {				/* controlling terminal? */
    type = getenv("TERM");		/* yup, use environment var */
    if (type != NULL)
      return(type);			/* give it back if successful */
  }

  name += 5;				/* skip the "/dev/" prefix */
  typedb = fopen("/etc/ttytype","r"); 	/* open type database */
  if (typedb == NULL)
    return("unknown");			/* give up if bad open */
  while (fgets(typelin,80,typedb) != NULL) { /* scan the database */
    typecp = typelin;
    while ((*typecp++) != SPACE);	/* scan for space in entry */
    *(typecp-1) = NULCHAR;		/* change it to null */

    if (strcmp(name,typecp) == 0) {	/* this our entry? */
      fclose(typedb);			/* yup, shut database */
      return(typelin);			/* and return type name */
    }
  }
  fclose(typedb);			/* not found... close database */
  return("unknown");			/* and give default */
}

/* auxiliary routines to take terminals into and out of raw mode */

#if defined(needTERMIOS)		/* POSIX.1 */
   static struct termios ttyblk, ttysav;
#else
#  if defined(needTERMIO)		/* System V */
      static struct termio ttyblk, ttysav;
#  else					/* must be BSD sgtty style */
      static struct sgttyb ttyblk, ttysav;	/* tty parameter blocks */
      static struct ltchars ltc,ltcsav;	/* local special chars for new */
#  endif
#endif

/* raw - put the terminal into raw (actually cbreak) mode, turn off
** echoing and output translations, and extract the output speed for
** the termcap library.  On BSD unix systems, literal-next processing
** is also disabled.
**/

static void
#if HAVE_STDC
raw(int fd)
#else /* K&R style */
raw(fd)
int fd;
#endif /* HAVE_STDC */
{
#if defined(needTERMIOS)		/* POSIX.1 */
  if (tcgetattr(fd, &ttysav) < 0)
	fprintf(stderr,"Cannot set tty attributes: error %d\n", errno);
  ttyblk = ttysav;
  ttyblk.c_lflag &= ~(ICANON|ECHO);
  ttyblk.c_cc[VINTR] = 003;		/* interrupt char is control-c */
  ttyblk.c_cc[VMIN] = 1;
  ttyblk.c_cc[VTIME] = 1;
  if (tcsetattr(fd, TCSANOW, &ttyblk) < 0)
	fprintf(stderr,"Cannot set tty attributes: error %d\n", errno);
#else
#  if defined(needTERMIO)
  ioctl(fd, TCGETA, &ttysav);
  ttyblk = ttysav;
  ttyblk.c_lflag &= ~(ICANON|ECHO);
  ttyblk.c_cc[0] = 003;			/* interrupt char is control-c */
  ttyblk.c_cc[4] = 1;
  ttyblk.c_cc[5] = 1;
  ioctl(fd,TCSETAW,&ttyblk);		/* set new modes . */
#  else 				/* must be BSD sgtty style */
  ioctl(fd,TIOCGETP,&ttysav);		/* get original parameters */
  ttyblk = ttysav;			/* copy into new parameter block */
  ospeed = ttysav.sg_ospeed;		/* save output speed for termlib */
  ttyblk.sg_flags &= ~(RAW | ECHO | LCASE); /* no echoing or xlates */
  ttyblk.sg_flags |= CBREAK;		/* single character reads */
  ioctl(fd,TIOCSETN,&ttyblk);		/* set params, leave typeahead */
  ioctl(fd,TIOCGLTC,&ltc);		/* get current local special chars */
  ltcsav = ltc;				/* copy it for later restore */
  ltc.t_lnextc = -1;			/* disable literal-next */
  ioctl(fd,TIOCSLTC,&ltc);		/* set the new chars in place */
#  endif
#endif
}

/* unraw - restore the tty modes in effect before raw was performed. */

static void
#if HAVE_STDC
unraw(int fd)
#else /* K&R style */
unraw(fd)
int fd;
#endif /* HAVE_STDC */
{
#if defined(needTERMIOS)
  if (tcsetattr(fd, TCSANOW, &ttysav) < 0)
	fprintf(stderr,"Cannot restore tty attributes: error %d\n", errno);
#else
#  if defined(needTERMIO)
  ioctl(fd,TCSETAW, &ttysav);		/* put back saved params */
#  else
  ioctl(fd,TIOCSETN,&ttysav);		/* put back saved params */
  ioctl(fd,TIOCSLTC,&ltcsav);		/* restore local special chars */
#  endif
#endif
}

/* outc - aux routine to be passed to termlib routines - output one char */

PASSEDSTATIC int
#if HAVE_STDC
outc(int c)
#else /* K&R style */
outc(c)
int c;
#endif /* HAVE_STDC */
{
  FILE *fd = cmcsb._cmoj;		/* get output filedesc */

  if (fd != NULL)
    putc(c,fd);				/* do the write */
  return((int)c);			/* tputs() requires int function */
}


/* sighand - Handler for INT and TSTP signals.  We first fix
** the terminal to its normal settings, then remove our handler
** and generate whichever signal invoked us to get the default
** action.  If the program is continued, the terminal is adjusted
** again, and our handler is reinstalled.  (This should only happen
** with TSTP signals).
**/

#if BSD
static SIG
#if HAVE_STDC
sighand(int sig)
#else /* K&R style */
sighand(sig)
int sig;
#endif /* HAVE_STDC */

{
  long oldmask;				/* prior interrupt mask */

  switch (sig)
  {
#ifdef SIGWINCH
  case SIGWINCH:
    cmtsize();
    break;
#endif

  default:
    cmtend();				/* fix the terminal */
    oldmask = sigsetmask(0);		/* let our signal get through */
#ifdef SIGTSTP
    if (sig == SIGTSTP)
      cmnl(stdout);			/* move to new line for looks */
#endif /* SIGTSTP */
    kill(getpid(),sig);			/* get the default action */
    cmtset();				/* redo the terminal if continued */
    sigsetmask(oldmask);		/* set mask back to before */
  }
}
#else /* NOT BSD */
static SIG
#if HAVE_STDC
sighand(int sig)
#else /* K&R style */
sighand(sig)
int sig;
#endif /* HAVE_STDC */
{
  switch (sig)
  {
#ifdef SIGWINCH
  case SIGWINCH:
    cmtsize();
    break;
#endif

  default:
    cmtend();				/* fix the terminal */
#ifdef SIGTSTP
    if (sig == SIGTSTP)
      cmnl(stdout);			/* move to new line for looks */
#endif /* SIGTSTP */
    kill(getpid(),sig);			/* get the default action */
    cmtset();				/* redo the terminal if continued */
  }
}
#endif /* BSD */

/* intson - Install our interrupt handlers for INT and STOP, so
** any terminal settings we have installed will be undone before
** the program exits.  Any handlers that are already installed
** are left in place.  Those handlers should call cmdone if
** they expect to exit with the terminal set correctly.
**/

#ifdef V7				/* used improve calls in version 7 */
#define signal sigsys
#endif
#define mask(sig)   (1 << ((sig)-1))

static void
intson(VOID)
{
  long oldmask;				/* signal mask before we play */
  Sigval oldhand;			/* old handler */

#ifdef SIGTSTP
  oldmask = sigblock(mask(SIGINT) | mask(SIGTSTP));	/* hold these */
#endif
  oldhand = signal(SIGINT,(Sigval)sighand); /* install our handler, get prior */
  if (oldhand != (Sigval)SIG_DFL) 	/* did they have something? */
    signal(SIGINT,oldhand);		/* yup, leave it there */
#ifdef SIGTSTP
  oldhand = signal(SIGTSTP,(Sigval)sighand);	/* install ours for TSTP too */
  if (oldhand != (Sigval)SIG_DFL)
    signal(SIGTSTP,oldhand);		/* but leave theirs intact */
  sigsetmask(oldmask);			/* now unblock the signals */
#endif /* SIGTSTP */
#ifdef SIGWINCH
  oldhand = signal(SIGWINCH,(Sigval)sighand);	/* install ours for TSTP too */
  if (oldhand != (Sigval)SIG_DFL)
    signal(SIGWINCH,oldhand);		/* but leave theirs intact */
  sigsetmask(oldmask);			/* now unblock the signals */
#endif /* SIGWINCH */
}


/* intsoff - Remove our interrupt handlers.  If we remove something
** that isn't ours, put it back.
**/

static void
intsoff(VOID)
{
  long oldmask;				/* sig mask prior to our diddling */
  Sigval oldhand;			/* prior handler for a signal */

#ifdef SIGTSTP
  oldmask = sigblock(mask(SIGINT) | mask(SIGTSTP));	/* block our sigs */
#endif
  oldhand = signal(SIGINT,(Sigval)SIG_DFL);	/* remove INT handler */
  if (oldhand != (Sigval)sighand)
    signal(SIGINT,oldhand);		/* replace if not ours */
#ifdef SIGTSTP
  oldhand = signal(SIGTSTP,(Sigval)SIG_DFL);	/* remove TSTP handler */
  if (oldhand != (Sigval)sighand)
    signal(SIGTSTP,oldhand);		/* replace if not ours */
  sigsetmask(oldmask);			/* replace old signal mask */
#endif
#ifdef SIGWINCH
  oldhand = signal(SIGWINCH,(Sigval)SIG_DFL);	/* remove TSTP handler */
  if (oldhand != (Sigval)sighand)
    signal(SIGWINCH,oldhand);		/* replace if not ours */
  sigsetmask(oldmask);			/* replace old signal mask */
#endif
}




#if 0					/* no longer used */
/* ctty, cttycls - Ctty opens the controlling terminal and returns a
** file descriptor for it.  After the first call, it just returns the
** file descriptor opened previously.  Cttycls closes the file
** descriptor opened by ctty, after which another call to ctty will
** open another one.
**/

static int ttyfd = -1;

static int
ctty(VOID)
{
  if (ttyfd == -1)
    ttyfd = open("/dev/tty",O_RDWR,0);
  return(ttyfd);
}

static void
cttycls(VOID)
{
  if (ttyfd != -1)
    close(ttyfd);
  ttyfd = -1;
}
#endif

static int
#if HAVE_STDC
crmod(int val)
#else /* K&R style */
crmod(val)
int val;
#endif /* HAVE_STDC */
{
#if defined(TIOCSETN) && !defined(needTERMIO) && !defined(needTERMIOS)
  struct sgttyb ttyblk;
  int oldval;
  int fd;

  if (cmcsb._cmoj == NULL)
      return(-1);
  fd = fileno(cmcsb._cmoj);

  ioctl(fd,TIOCGETP,&ttyblk);		/* get original parameters */

  oldval = ttyblk.sg_flags & CRMOD;
  if (val)
      ttyblk.sg_flags |= CRMOD;		/* turn on CRMOD */
  else
      ttyblk.sg_flags &= ~CRMOD;	/* turn off CRMOD */
  ioctl(fd,TIOCSETN,&ttyblk);		/* set params, leave typeahead */
  return(oldval);
#else
  return (0);
#endif
}

static void
#if HAVE_STDC
cmselect(int fd)
#else /* K&R style */
cmselect(fd)
int fd;
#endif /* HAVE_STDC */
{
#ifdef FD_SETSIZE
  fd_set rfds, efds;
  int r;

  while(1) {
    FD_ZERO(&rfds);
    FD_ZERO(&efds);
    FD_SET(fd, &rfds);
    FD_SET(fd, &efds);
#if defined(__hpux)
    r = select(FD_SETSIZE, (int*)&rfds, NULL, (int*)&efds, NULL);
#else
    r = select(FD_SETSIZE, &rfds, NULL, &efds, NULL);
#endif
    if (r >= 0 || errno != EINTR)
	return;
  }
#endif
}
